mirror of
https://github.com/bitwarden/server.git
synced 2024-11-21 12:05:42 +01:00
docker setup
This commit is contained in:
parent
ee8b0a25a8
commit
9bc6ba554a
@ -28,7 +28,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Billing", "src\Billing\Bill
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Identity", "src\Identity\Identity.csproj", "{04148736-3C0B-445E-8B74-2020E7A53502}"
|
||||
EndProject
|
||||
Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "Docker", "docker\Docker.dcproj", "{026DDB58-F0DB-4089-8168-83015AF785AE}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Setup", "util\Setup\Setup.csproj", "{EF2164EF-1FC0-4518-A2ED-CE02D3630B00}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -62,10 +62,10 @@ Global
|
||||
{04148736-3C0B-445E-8B74-2020E7A53502}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{04148736-3C0B-445E-8B74-2020E7A53502}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{04148736-3C0B-445E-8B74-2020E7A53502}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{026DDB58-F0DB-4089-8168-83015AF785AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{026DDB58-F0DB-4089-8168-83015AF785AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{026DDB58-F0DB-4089-8168-83015AF785AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{026DDB58-F0DB-4089-8168-83015AF785AE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EF2164EF-1FC0-4518-A2ED-CE02D3630B00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EF2164EF-1FC0-4518-A2ED-CE02D3630B00}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EF2164EF-1FC0-4518-A2ED-CE02D3630B00}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EF2164EF-1FC0-4518-A2ED-CE02D3630B00}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@ -77,6 +77,7 @@ Global
|
||||
{B78A6C74-1A24-48C6-802A-13BE3E4DAFF1} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
||||
{02BC2982-ED8D-4A6D-A41E-092B3DAEB98A} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
||||
{04148736-3C0B-445E-8B74-2020E7A53502} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}
|
||||
{EF2164EF-1FC0-4518-A2ED-CE02D3630B00} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
|
||||
|
@ -7,3 +7,4 @@ echo "=================="
|
||||
& $dir\src\Api\build.ps1
|
||||
& $dir\src\Identity\build.ps1
|
||||
& $dir\nginx\build.ps1
|
||||
& $dir\util\Setup\build.ps1
|
||||
|
1
build.sh
1
build.sh
@ -9,3 +9,4 @@ echo -e "=================="
|
||||
$DIR/src/Api/build.sh
|
||||
$DIR/src/Identity/build.sh
|
||||
$DIR/nginx/build.sh
|
||||
$DIR/util/Setup/build.sh
|
||||
|
@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" Sdk="Microsoft.Docker.Sdk">
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectVersion>2.0</ProjectVersion>
|
||||
<DockerTargetOS>Linux</DockerTargetOS>
|
||||
<ProjectGuid>026ddb58-f0db-4089-8168-83015af785ae</ProjectGuid>
|
||||
<DockerLaunchBrowser>True</DockerLaunchBrowser>
|
||||
<DockerServiceUrl>http://localhost:{ServicePort}</DockerServiceUrl>
|
||||
<DockerServiceName>api</DockerServiceName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="docker-compose.yml" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -4,12 +4,11 @@ services:
|
||||
mssql:
|
||||
volumes:
|
||||
- /etc/bitwarden/mssql_data:/var/opt/mssql/data
|
||||
api:
|
||||
volumes:
|
||||
- /etc/bitwarden/core:/etc/core
|
||||
identity:
|
||||
volumes:
|
||||
- /etc/bitwarden/core:/etc/core
|
||||
- /etc/bitwarden/identity:/etc/bitwarden/identity
|
||||
nginx:
|
||||
volumes:
|
||||
- /etc/bitwarden/nginx:/etc/bitwarden/nginx
|
||||
- /etc/bitwarden/letsencrypt:/etc/letsencrypt
|
||||
- /etc/bitwarden/ssl:/etc/certificates
|
||||
|
@ -4,8 +4,13 @@ services:
|
||||
mssql:
|
||||
volumes:
|
||||
- mssql_data:/var/opt/mssql/data
|
||||
identity:
|
||||
volumes:
|
||||
- c:/bitwarden/identity:/etc/bitwarden/identity
|
||||
nginx:
|
||||
volumes:
|
||||
- c:/bitwarden/nginx:/etc/bitwarden/nginx
|
||||
- c:/bitwarden/letsencrypt:/etc/letsencrypt
|
||||
- c:/bitwarden/ssl:/etc/certificates
|
||||
volumes:
|
||||
mssql_data:
|
||||
|
@ -4,14 +4,13 @@ services:
|
||||
mssql:
|
||||
volumes:
|
||||
- mssql_data:/var/opt/mssql/data
|
||||
api:
|
||||
volumes:
|
||||
- c:/bitwarden/core:/etc/core
|
||||
identity:
|
||||
volumes:
|
||||
- c:/bitwarden/core:/etc/core
|
||||
- c:/bitwarden/identity:/etc/bitwarden/identity
|
||||
nginx:
|
||||
volumes:
|
||||
- c:/bitwarden/nginx:/etc/bitwarden/nginx
|
||||
- c:/bitwarden/letsencrypt:/etc/letsencrypt
|
||||
- c:/bitwarden/ssl:/etc/certificates
|
||||
volumes:
|
||||
mssql_data:
|
||||
|
@ -3,7 +3,6 @@ FROM nginx:stable
|
||||
RUN rm /etc/nginx/nginx.conf
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
COPY entrypoint.sh /
|
||||
RUN chmod +x /entrypoint.sh
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
@ -1,73 +0,0 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name bw.kylespearrin.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name bw.kylespearrin.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/bw.kylespearrin.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/bw.kylespearrin.com/privkey.pem;
|
||||
|
||||
ssl_session_timeout 30m;
|
||||
ssl_session_cache shared:SSL:20m;
|
||||
ssl_session_tickets off;
|
||||
|
||||
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
|
||||
ssl_dhparam /etc/letsencrypt/live/bw.kylespearrin.com/dhparam.pem;
|
||||
|
||||
# SSL protocols TLS v1~TLSv1.2 are allowed. Disabed SSLv3
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
# Disabled insecure ciphers suite. For example, MD5, DES, RC4, PSK
|
||||
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4:@STRENGTH";
|
||||
# enables server-side protection from BEAST attacks
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
# OCSP Stapling ---
|
||||
# fetch OCSP records from URL in ssl_certificate and cache them
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
|
||||
## verify chain of trust of OCSP response using Root CA and Intermediate certs
|
||||
ssl_trusted_certificate /etc/letsencrypt/live/bw.kylespearrin.com/fullchain.pem;
|
||||
|
||||
resolver 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=300s;
|
||||
|
||||
# Headers
|
||||
|
||||
# X-Frame-Options is to prevent from clickJacking attack
|
||||
#add_header X-Frame-Options SAMEORIGIN;
|
||||
|
||||
# disable content-type sniffing on some browsers.
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
|
||||
# This header enables the Cross-site scripting (XSS) filter
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
|
||||
# This will enforce HTTP browsing into HTTPS and avoid ssl stripping attack
|
||||
#add_header Strict-Transport-Security max-age=15768000;
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://api/;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Url-Scheme $scheme;
|
||||
proxy_redirect off;
|
||||
}
|
||||
|
||||
location /identity/ {
|
||||
proxy_pass http://identity/;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Url-Scheme $scheme;
|
||||
proxy_redirect off;
|
||||
}
|
||||
}
|
4
nginx/entrypoint.sh
Normal file
4
nginx/entrypoint.sh
Normal file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
cp /etc/bitwarden/nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
nginx -g 'daemon off;'
|
@ -1,7 +1,8 @@
|
||||
$dockerDir="../docker"
|
||||
$dir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$dockerDir="${dir}\..\docker"
|
||||
|
||||
docker --version
|
||||
docker-compose --version
|
||||
|
||||
docker-compose -f $dockerDir/docker-compose.yml -f $dockerDir/docker-compose.windows.yml down
|
||||
docker-compose -f $dockerDir/docker-compose.yml -f $dockerDir/docker-compose.windows.yml up -d
|
||||
docker-compose -f ${dockerDir}\docker-compose.yml -f ${dockerDir}\docker-compose.windows.yml down
|
||||
docker-compose -f ${dockerDir}\docker-compose.yml -f ${dockerDir}\docker-compose.windows.yml up -d
|
||||
|
@ -1,13 +1,12 @@
|
||||
param (
|
||||
[string]$outputDir = "c:/bitwarden",
|
||||
[string]$domain = $( Read-Host "Please enter your domain name (i.e. bitwarden.company.com)" ),
|
||||
[string]$email = $( Read-Host "Please enter your email address (used to generate an HTTPS certificate with LetsEncrypt)" )
|
||||
[string]$email = $( Read-Host "Please enter your email address: " ),
|
||||
[string]$letsencrypt = $( Read-Host "Generate Let's Encrypt Cert (y/n)" )
|
||||
)
|
||||
|
||||
$dockerDir="../docker"
|
||||
$certPassword=-join ((48..57) + (97..122) | Get-Random -Count 32 | % {[char]$_})
|
||||
$databasePassword=-join ((48..57) + (97..122) | Get-Random -Count 32 | % {[char]$_})
|
||||
$duoKey=-join ((48..57) + (97..122) | Get-Random -Count 32 | % {[char]$_})
|
||||
|
||||
docker --version
|
||||
|
||||
@ -15,23 +14,6 @@ docker --version
|
||||
#docker run -it --rm -p 80:80 -v $outputDir/letsencrypt:/etc/letsencrypt/ certbot/certbot certonly --standalone --noninteractive --preferred-challenges http --email $email --agree-tos -d $domain
|
||||
#docker run -it --rm -v $outputDir/letsencrypt/live:/certificates/ bitwarden/openssl openssl dhparam -out /certificates/$domain/dhparam.pem 2048
|
||||
|
||||
mkdir -p $outputDir/core
|
||||
docker run -it --rm -v $outputDir/core:/certificates bitwarden/openssl openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout /certificates/identity.key -out /certificates/identity.crt -subj "/CN=bitwarden IdentityServer" -days 10950
|
||||
docker run -it --rm -v $outputDir/core:/certificates bitwarden/openssl openssl pkcs12 -export -out /certificates/identity.pfx -inkey /certificates/identity.key -in /certificates/identity.crt -certfile /certificates/identity.crt -passout pass:$certPassword
|
||||
rm $outputDir/core/identity.key
|
||||
rm $outputDir/core/identity.crt
|
||||
docker run -it --rm -v ${outputDir}:/bitwarden bitwarden/setup dotnet Setup.dll -domain ${domain} -letsencrypt ${letsencrypt} -db_pass ${databasePassword}
|
||||
|
||||
Add-Content $dockerDir/global.override.env "
|
||||
globalSettings:baseServiceUri:vault=https://$domain
|
||||
globalSettings:baseServiceUri:api=https://$domain/api
|
||||
globalSettings:baseServiceUri:identity=https://$domain/identity
|
||||
globalSettings:sqlServer:connectionString=Server=tcp:mssql,1433;Initial Catalog=vault;Persist Security Info=False;User ID=sa;Password=$databasePassword;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=True;Connection Timeout=30;
|
||||
globalSettings:identityServer:certificatePassword=$certPassword
|
||||
globalSettings:duo:aKey=$duoKey
|
||||
globalSettings:yubico:clientId=REPLACE
|
||||
globalSettings:yubico:REPLACE"
|
||||
|
||||
Add-Content $dockerDir/mssql.override.env "
|
||||
ACCEPT_EULA=Y
|
||||
MSSQL_PID=Express
|
||||
SA_PASSWORD=$databasePassword"
|
||||
echo "Setup complete"
|
||||
|
@ -7,10 +7,7 @@ echo -e "\nPlease enter your email address (used to generate an HTTPS certificat
|
||||
read EMAIL
|
||||
|
||||
OUTPUT_DIR=./bitwarden
|
||||
DOCKER_DIR=../docker
|
||||
CERT_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)
|
||||
DATABASE_PASSWORD=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 32)
|
||||
DUO_KEY=$(LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom | head -c 64)
|
||||
|
||||
docker --version
|
||||
|
||||
@ -18,25 +15,6 @@ docker --version
|
||||
#docker run -it --rm -p 80:80 -v $OUTPUT_DIR/letsencrypt:/etc/letsencrypt/ certbot/certbot certonly --standalone --noninteractive --preferred-challenges http --email $EMAIL --agree-tos -d $DOMAIN
|
||||
#docker run -it --rm -v $OUTPUT_DIR/letsencrypt/live:/certificates/ bitwarden/openssl openssl dhparam -out /certificates/$DOMAIN/dhparam.pem 2048
|
||||
|
||||
mkdir -p $OUTPUT_DIR/core
|
||||
docker run -it --rm -v $OUTPUT_DIR/core:/certificates bitwarden/openssl openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout /certificates/identity.key -out /certificates/identity.crt -subj "/CN=bitwarden IdentityServer" -days 10950
|
||||
docker run -it --rm -v $OUTPUT_DIR/core:/certificates bitwarden/openssl openssl pkcs12 -export -out /certificates/identity.pfx -inkey /certificates/identity.key -in /certificates/identity.crt -certfile /certificates/identity.crt -passout pass:$CERT_PASSWORD
|
||||
rm $OUTPUT_DIR/core/identity.key
|
||||
rm $OUTPUT_DIR/core/identity.crt
|
||||
docker run -it --rm -v $OUTPUT_DIR:/bitwarden bitwarden/setup dotnet Setup.dll -domain $DOMAIN -letsencrypt y -db_pass $DATABASE_PASSWORD
|
||||
|
||||
cat >> $DOCKER_DIR/global.override.env << EOF
|
||||
globalSettings:baseServiceUri:vault=https://$DOMAIN
|
||||
globalSettings:baseServiceUri:api=https://$DOMAIN/api
|
||||
globalSettings:baseServiceUri:identity=https://$DOMAIN/identity
|
||||
globalSettings:sqlServer:connectionString=Server=tcp:mssql,1433;Initial Catalog=vault;Persist Security Info=False;User ID=sa;Password=$DATABASE_PASSWORD;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=True;Connection Timeout=30;
|
||||
globalSettings:identityServer:certificatePassword=$CERT_PASSWORD
|
||||
globalSettings:duo:aKey=$DUO_KEY
|
||||
globalSettings:yubico:clientId=REPLACE
|
||||
globalSettings:yubico:REPLACE
|
||||
EOF
|
||||
|
||||
cat >> $DOCKER_DIR/mssql.override.env << EOF
|
||||
ACCEPT_EULA=Y
|
||||
MSSQL_PID=Express
|
||||
SA_PASSWORD=$DATABASE_PASSWORD
|
||||
EOF
|
||||
echo -e "\nSetup complete"
|
||||
|
@ -92,7 +92,7 @@ namespace Bit.Api
|
||||
|
||||
// Services
|
||||
services.AddBaseServices();
|
||||
services.AddDefaultServices();
|
||||
services.AddDefaultServices(globalSettings);
|
||||
|
||||
// Cors
|
||||
services.AddCors(config =>
|
||||
|
@ -45,7 +45,7 @@ namespace Bit.Billing
|
||||
|
||||
// Services
|
||||
services.AddBaseServices();
|
||||
services.AddDefaultServices();
|
||||
services.AddDefaultServices(globalSettings);
|
||||
|
||||
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
|
||||
|
@ -22,13 +22,22 @@ namespace Bit.Core.Utilities
|
||||
filter = (e) => true;
|
||||
}
|
||||
|
||||
var serilog = new LoggerConfiguration()
|
||||
var config = new LoggerConfiguration()
|
||||
.Enrich.FromLogContext()
|
||||
.Filter.ByIncludingOnly(filter)
|
||||
.WriteTo.AzureDocumentDB(new Uri(globalSettings.DocumentDb.Uri), globalSettings.DocumentDb.Key,
|
||||
timeToLive: TimeSpan.FromDays(7))
|
||||
.CreateLogger();
|
||||
.Filter.ByIncludingOnly(filter);
|
||||
|
||||
if(globalSettings.DocumentDb != null && !string.IsNullOrWhiteSpace(globalSettings.DocumentDb.Uri) &&
|
||||
!string.IsNullOrWhiteSpace(globalSettings.DocumentDb.Key))
|
||||
{
|
||||
config.WriteTo.AzureDocumentDB(new Uri(globalSettings.DocumentDb.Uri), globalSettings.DocumentDb.Key,
|
||||
timeToLive: TimeSpan.FromDays(7));
|
||||
}
|
||||
else
|
||||
{
|
||||
// local file sink
|
||||
}
|
||||
|
||||
var serilog = config.CreateLogger();
|
||||
factory.AddSerilog(serilog);
|
||||
appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);
|
||||
}
|
||||
|
@ -48,10 +48,19 @@ namespace Bit.Core.Utilities
|
||||
services.AddSingleton<IGroupService, GroupService>();
|
||||
}
|
||||
|
||||
public static void AddDefaultServices(this IServiceCollection services)
|
||||
public static void AddDefaultServices(this IServiceCollection services, GlobalSettings globalSettings)
|
||||
{
|
||||
services.AddSingleton<IMailService, RazorViewMailService>();
|
||||
services.AddSingleton<IMailDeliveryService, SendGridMailDeliveryService>();
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(globalSettings.Mail.SendGridApiKey))
|
||||
{
|
||||
services.AddSingleton<IMailDeliveryService, SendGridMailDeliveryService>();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<IMailDeliveryService, NoopMailDeliveryService>();
|
||||
}
|
||||
|
||||
#if NET461
|
||||
services.AddSingleton<IPushNotificationService, NotificationHubPushNotificationService>();
|
||||
services.AddSingleton<IPushRegistrationService, NotificationHubPushRegistrationService>();
|
||||
@ -59,8 +68,23 @@ namespace Bit.Core.Utilities
|
||||
services.AddSingleton<IPushNotificationService, NoopPushNotificationService>();
|
||||
services.AddSingleton<IPushRegistrationService, NoopPushRegistrationService>();
|
||||
#endif
|
||||
services.AddSingleton<IBlockIpService, AzureQueueBlockIpService>();
|
||||
services.AddSingleton<IAttachmentStorageService, AzureAttachmentStorageService>();
|
||||
if(!string.IsNullOrWhiteSpace(globalSettings.Storage.ConnectionString))
|
||||
{
|
||||
services.AddSingleton<IBlockIpService, AzureQueueBlockIpService>();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<IBlockIpService, NoopBlockIpService>();
|
||||
}
|
||||
|
||||
if(!string.IsNullOrWhiteSpace(globalSettings.Attachment.ConnectionString))
|
||||
{
|
||||
services.AddSingleton<IAttachmentStorageService, AzureAttachmentStorageService>();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<IAttachmentStorageService, NoopAttachmentStorageService>();
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddNoopServices(this IServiceCollection services)
|
||||
@ -147,14 +171,18 @@ namespace Bit.Core.Utilities
|
||||
else if(!string.IsNullOrWhiteSpace(globalSettings.IdentityServer.CertificatePassword) &&
|
||||
System.IO.File.Exists("identity.pfx"))
|
||||
{
|
||||
var identityServerCert = CoreHelpers.GetCertificate("identity.pfx",
|
||||
var identityServerCert = CoreHelpers.GetCertificate("identity.pfx",
|
||||
globalSettings.IdentityServer.CertificatePassword);
|
||||
identityServerBuilder.AddSigningCredential(identityServerCert);
|
||||
}
|
||||
else if(!string.IsNullOrWhiteSpace(globalSettings.IdentityServer.CertificateThumbprint))
|
||||
{
|
||||
var identityServerCert = CoreHelpers.GetCertificate(globalSettings.IdentityServer.CertificateThumbprint);
|
||||
identityServerBuilder.AddSigningCredential(identityServerCert);
|
||||
}
|
||||
else
|
||||
{
|
||||
var identityServerCert = CoreHelpers.GetCertificate(globalSettings.IdentityServer.CertificateThumbprint);
|
||||
identityServerBuilder.AddSigningCredential(identityServerCert);
|
||||
throw new Exception("No identity certificate to use.");
|
||||
}
|
||||
|
||||
services.AddScoped<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
|
||||
@ -168,7 +196,9 @@ namespace Bit.Core.Utilities
|
||||
this IServiceCollection services, IHostingEnvironment env, GlobalSettings globalSettings)
|
||||
{
|
||||
#if NET461
|
||||
if(!env.IsDevelopment() && !globalSettings.SelfHosted)
|
||||
if(!env.IsDevelopment() && !globalSettings.SelfHosted &&
|
||||
!string.IsNullOrWhiteSpace(globalSettings.Storage.ConnectionString) &&
|
||||
!string.IsNullOrWhiteSpace(globalSettings.DataProtection.CertificateThumbprint))
|
||||
{
|
||||
var dataProtectionCert = CoreHelpers.GetCertificate(globalSettings.DataProtection.CertificateThumbprint);
|
||||
var storageAccount = CloudStorageAccount.Parse(globalSettings.Storage.ConnectionString);
|
||||
|
@ -49,7 +49,7 @@ namespace Bit.Identity
|
||||
|
||||
// Services
|
||||
services.AddBaseServices();
|
||||
services.AddDefaultServices();
|
||||
services.AddDefaultServices(globalSettings);
|
||||
}
|
||||
|
||||
public void Configure(
|
||||
|
@ -1,5 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
cp /etc/core/identity.pfx /app/identity.pfx
|
||||
|
||||
cp /etc/bitwarden/identity/identity.pfx /app/identity.pfx
|
||||
dotnet /app/Identity.dll
|
||||
|
10
util/Setup/Dockerfile
Normal file
10
util/Setup/Dockerfile
Normal file
@ -0,0 +1,10 @@
|
||||
FROM microsoft/dotnet:2.0.0-preview2-runtime
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
# Dependencies
|
||||
openssl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
COPY obj/Docker/publish .
|
91
util/Setup/Helpers.cs
Normal file
91
util/Setup/Helpers.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Setup
|
||||
{
|
||||
public static class Helpers
|
||||
{
|
||||
public static string SecureRandomString(int length, bool alpha = true, bool upper = true, bool lower = true,
|
||||
bool numeric = true, bool special = false)
|
||||
{
|
||||
return SecureRandomString(length, RandomStringCharacters(alpha, upper, lower, numeric, special));
|
||||
}
|
||||
|
||||
// ref https://stackoverflow.com/a/8996788/1090359 with modifications
|
||||
public static string SecureRandomString(int length, string characters)
|
||||
{
|
||||
if(length < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(length), "length cannot be less than zero.");
|
||||
}
|
||||
|
||||
if((characters?.Length ?? 0) == 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(characters), "characters invalid.");
|
||||
}
|
||||
|
||||
const int byteSize = 0x100;
|
||||
if(byteSize < characters.Length)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
string.Format("{0} may contain no more than {1} characters.", nameof(characters), byteSize),
|
||||
nameof(characters));
|
||||
}
|
||||
|
||||
var outOfRangeStart = byteSize - (byteSize % characters.Length);
|
||||
using(var rng = RandomNumberGenerator.Create())
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var buffer = new byte[128];
|
||||
while(sb.Length < length)
|
||||
{
|
||||
rng.GetBytes(buffer);
|
||||
for(var i = 0; i < buffer.Length && sb.Length < length; ++i)
|
||||
{
|
||||
// Divide the byte into charSet-sized groups. If the random value falls into the last group and the
|
||||
// last group is too small to choose from the entire allowedCharSet, ignore the value in order to
|
||||
// avoid biasing the result.
|
||||
if(outOfRangeStart <= buffer[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
sb.Append(characters[buffer[i] % characters.Length]);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private static string RandomStringCharacters(bool alpha, bool upper, bool lower, bool numeric, bool special)
|
||||
{
|
||||
var characters = string.Empty;
|
||||
if(alpha)
|
||||
{
|
||||
if(upper)
|
||||
{
|
||||
characters += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
}
|
||||
|
||||
if(lower)
|
||||
{
|
||||
characters += "abcdefghijklmnopqrstuvwxyz";
|
||||
}
|
||||
}
|
||||
|
||||
if(numeric)
|
||||
{
|
||||
characters += "0123456789";
|
||||
}
|
||||
|
||||
if(special)
|
||||
{
|
||||
characters += "!@#$%^*&";
|
||||
}
|
||||
|
||||
return characters;
|
||||
}
|
||||
}
|
||||
}
|
225
util/Setup/Program.cs
Normal file
225
util/Setup/Program.cs
Normal file
@ -0,0 +1,225 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Setup
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
private static string[] _args = null;
|
||||
private static IDictionary<string, string> _parameters = null;
|
||||
private static string _domain = null;
|
||||
private static string _certPassword = null;
|
||||
private static bool _ssl = false;
|
||||
private static bool _letsEncrypt = false;
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
_args = args;
|
||||
_parameters = ParseParameters();
|
||||
|
||||
_domain = _parameters.ContainsKey("domain") ? _parameters["domain"].ToLowerInvariant() : "localhost";
|
||||
_letsEncrypt = _parameters.ContainsKey("letsencrypt") ? _parameters["letsencrypt"].ToLowerInvariant() == "y" : false;
|
||||
_ssl = _letsEncrypt || (_parameters.ContainsKey("ssl") ? _parameters["ssl"].ToLowerInvariant() == "y" : false);
|
||||
_certPassword = Helpers.SecureRandomString(32, alpha: true, numeric: true);
|
||||
|
||||
MakeIdentityCert();
|
||||
BuildNginxConfig();
|
||||
BuildEnvironmentFiles();
|
||||
}
|
||||
|
||||
private static void MakeIdentityCert()
|
||||
{
|
||||
Directory.CreateDirectory("/bitwarden/identity/");
|
||||
var identityCertResult = Exec("openssl req -x509 -newkey rsa:4096 -sha256 -nodes -keyout identity.key " +
|
||||
"-out identity.crt -subj \"/CN=bitwarden IdentityServer\" -days 10950");
|
||||
var identityPfxResult = Exec("openssl pkcs12 -export -out /bitwarden/identity/identity.pfx -inkey identity.key " +
|
||||
$"-in identity.crt -certfile identity.crt -passout pass:{_certPassword}");
|
||||
}
|
||||
|
||||
private static void BuildNginxConfig()
|
||||
{
|
||||
Directory.CreateDirectory("/bitwarden/nginx/");
|
||||
var sslCiphers = "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:" +
|
||||
"DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:" +
|
||||
"ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:" +
|
||||
"ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:" +
|
||||
"AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4:@STRENGTH";
|
||||
|
||||
var dh = _letsEncrypt ||
|
||||
(_parameters.ContainsKey("ssl_dh") ? _parameters["ssl_dh"].ToLowerInvariant() == "y" : false);
|
||||
var trusted = _letsEncrypt ||
|
||||
(_parameters.ContainsKey("ssl_trusted") ? _parameters["ssl_trusted"].ToLowerInvariant() == "y" : false);
|
||||
var certPath = _letsEncrypt ? $"/etc/letsencrypt/live/{_domain}" : $"/etc/certificates/{_domain}";
|
||||
|
||||
using(var sw = File.CreateText("/bitwarden/nginx/default.conf"))
|
||||
{
|
||||
sw.WriteLine($@"server {{
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name {_domain};");
|
||||
|
||||
if(_ssl)
|
||||
{
|
||||
sw.WriteLine($@" return 301 https://$server_name$request_uri;
|
||||
}}
|
||||
|
||||
server {{
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name {_domain};
|
||||
|
||||
ssl_certificate {certPath}/fullchain.pem;
|
||||
ssl_certificate_key {certPath}/privkey.pem;
|
||||
|
||||
ssl_session_timeout 30m;
|
||||
ssl_session_cache shared:SSL:20m;
|
||||
ssl_session_tickets off;");
|
||||
|
||||
if(dh)
|
||||
{
|
||||
sw.WriteLine($@"
|
||||
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
|
||||
ssl_dhparam {certPath}/dhparam.pem;");
|
||||
}
|
||||
|
||||
sw.WriteLine($@"
|
||||
# SSL protocols TLS v1~TLSv1.2 are allowed. Disabed SSLv3
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
# Disabled insecure ciphers suite. For example, MD5, DES, RC4, PSK
|
||||
ssl_ciphers ""{sslCiphers}"";
|
||||
# enables server-side protection from BEAST attacks
|
||||
ssl_prefer_server_ciphers on;");
|
||||
|
||||
if(trusted)
|
||||
{
|
||||
sw.WriteLine($@"
|
||||
# OCSP Stapling ---
|
||||
# fetch OCSP records from URL in ssl_certificate and cache them
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
|
||||
## verify chain of trust of OCSP response using Root CA and Intermediate certs
|
||||
ssl_trusted_certificate {certPath}/fullchain.pem;
|
||||
|
||||
resolver 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=300s;");
|
||||
}
|
||||
|
||||
sw.WriteLine($@"
|
||||
# Headers
|
||||
|
||||
# X-Frame-Options is to prevent from clickJacking attack
|
||||
#add_header X-Frame-Options SAMEORIGIN;
|
||||
|
||||
# disable content-type sniffing on some browsers.
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
|
||||
# This header enables the Cross-site scripting (XSS) filter
|
||||
add_header X-XSS-Protection ""1; mode=block"";
|
||||
|
||||
# This will enforce HTTP browsing into HTTPS and avoid ssl stripping attack
|
||||
#add_header Strict-Transport-Security max-age=15768000;");
|
||||
}
|
||||
|
||||
sw.WriteLine($@"
|
||||
location /api/ {{
|
||||
proxy_pass http://api/;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Url-Scheme $scheme;
|
||||
proxy_redirect off;
|
||||
}}
|
||||
|
||||
location /identity/ {{
|
||||
proxy_pass http://identity/;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Url-Scheme $scheme;
|
||||
proxy_redirect off;
|
||||
}}
|
||||
}}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void BuildEnvironmentFiles()
|
||||
{
|
||||
var url = _ssl ? $"https://{_domain}" : $"http://{_domain}";
|
||||
var dbPass = _parameters.ContainsKey("db_pass") ? _parameters["db_pass"].ToLowerInvariant() : "REPLACE";
|
||||
var dbConnectionString = "Server=tcp:mssql,1433;Initial Catalog=vault;Persist Security Info=False;User ID=sa;" +
|
||||
$"Password={dbPass};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=True;" +
|
||||
"Connection Timeout=30;";
|
||||
|
||||
using(var sw = File.CreateText("/bitwarden/global.override.env"))
|
||||
{
|
||||
sw.Write($@"globalSettings:baseServiceUri:vault={url}
|
||||
globalSettings:baseServiceUri:api={url}/api
|
||||
globalSettings:baseServiceUri:identity={url}/identity
|
||||
globalSettings:sqlServer:connectionString={dbConnectionString}
|
||||
globalSettings:identityServer:certificatePassword={_certPassword}
|
||||
globalSettings:duo:aKey={Helpers.SecureRandomString(32, alpha: true, numeric: true)}
|
||||
globalSettings:yubico:clientId=REPLACE
|
||||
globalSettings:yubico:REPLACE");
|
||||
}
|
||||
|
||||
using(var sw = File.CreateText("/bitwarden/mssql.override.env"))
|
||||
{
|
||||
sw.Write($@"ACCEPT_EULA=Y
|
||||
MSSQL_PID=Express
|
||||
SA_PASSWORD={dbPass}");
|
||||
}
|
||||
}
|
||||
|
||||
private static IDictionary<string, string> ParseParameters()
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
for(var i = 0; i < _args.Length; i = i + 2)
|
||||
{
|
||||
if(!_args[i].StartsWith("-"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
dict.Add(_args[i].Substring(1), _args[i + 1]);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
private static string Exec(string cmd)
|
||||
{
|
||||
var process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
}
|
||||
};
|
||||
|
||||
if(!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
var escapedArgs = cmd.Replace("\"", "\\\"");
|
||||
process.StartInfo.FileName = "/bin/bash";
|
||||
process.StartInfo.Arguments = $"-c \"{escapedArgs}\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
process.StartInfo.FileName = "powershell";
|
||||
process.StartInfo.Arguments = cmd;
|
||||
}
|
||||
|
||||
process.Start();
|
||||
string result = process.StandardOutput.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
13
util/Setup/Setup.csproj
Normal file
13
util/Setup/Setup.csproj
Normal file
@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>full</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
11
util/Setup/build.ps1
Normal file
11
util/Setup/build.ps1
Normal file
@ -0,0 +1,11 @@
|
||||
$dir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
|
||||
echo "`n# Building Setup"
|
||||
|
||||
echo "`nBuilding app"
|
||||
echo ".NET Core version $(dotnet --version)"
|
||||
dotnet publish $dir\Setup.csproj -f netcoreapp2.0 -c "Release" -o $dir\obj\Docker\publish
|
||||
|
||||
echo "`nBuilding docker image"
|
||||
docker --version
|
||||
docker build -t bitwarden/setup $dir\.
|
14
util/Setup/build.sh
Normal file
14
util/Setup/build.sh
Normal file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
DIR="$(dirname $(readlink -f $0))"
|
||||
|
||||
echo -e "\n# Building Setup"
|
||||
|
||||
echo -e "\nBuilding app"
|
||||
echo -e ".NET Core version $(dotnet --version)"
|
||||
dotnet publish $DIR/Setup.csproj -f netcoreapp2.0 -c "Release" -o $DIR/obj/Docker/publish
|
||||
|
||||
echo -e "\nBuilding docker image"
|
||||
docker --version
|
||||
docker build -t bitwarden/setup $DIR/.
|
Loading…
Reference in New Issue
Block a user