From 66c5ccf82df0621a2d47950fd3978005b7d3bb14 Mon Sep 17 00:00:00 2001 From: tangowithfoxtrot <5676771+tangowithfoxtrot@users.noreply.github.com> Date: Mon, 6 Nov 2023 13:58:32 -0800 Subject: [PATCH] Vscode devcontainers (#3080) * add devcontainers for `server` * run db migrations automatically in dev environment * remove curl * remove trailing comma; causes parsing with `jq` * use existing .env * add initializeCommand * use better search string * restructure common files * chmod +x scripts * remove problematic env config scripts * add mention of var that is needed for devcontainer * remove ref to deleted script * Update .devcontainer/community_dev/devcontainer.json Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> * Update .devcontainer/internal_dev/devcontainer.json Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> * use dev image for `6.0.416` SDK * revert to manual DB migrations * reuse SQL connection string var --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> --- .../bitwarden_common/docker-compose.yml | 32 +++++++ .devcontainer/community_dev/devcontainer.json | 14 +++ .../community_dev/postCreateCommand.sh | 63 ++++++++++++++ .devcontainer/internal_dev/devcontainer.json | 16 ++++ .../internal_dev/docker-compose.override.yml | 9 ++ .../internal_dev/postCreateCommand.sh | 85 +++++++++++++++++++ dev/.env.example | 4 + 7 files changed, 223 insertions(+) create mode 100644 .devcontainer/bitwarden_common/docker-compose.yml create mode 100644 .devcontainer/community_dev/devcontainer.json create mode 100755 .devcontainer/community_dev/postCreateCommand.sh create mode 100644 .devcontainer/internal_dev/devcontainer.json create mode 100644 .devcontainer/internal_dev/docker-compose.override.yml create mode 100755 .devcontainer/internal_dev/postCreateCommand.sh diff --git a/.devcontainer/bitwarden_common/docker-compose.yml b/.devcontainer/bitwarden_common/docker-compose.yml new file mode 100644 index 000000000..295fd08da --- /dev/null +++ b/.devcontainer/bitwarden_common/docker-compose.yml @@ -0,0 +1,32 @@ +version: '3' + +services: + bitwarden_server: + image: mcr.microsoft.com/devcontainers/dotnet:dev-6.0 + volumes: + - ../../:/workspace:cached + # Overrides default command so things don't shut down after the process ends. + command: sleep infinity + + bitwarden_mssql: + image: mcr.microsoft.com/azure-sql-edge:latest + restart: unless-stopped + env_file: + ../../dev/.env + environment: + ACCEPT_EULA: "Y" + MSSQL_PID: Developer + volumes: + - edgesql_dev_data:/var/opt/mssql + - ../../util/Migrator:/mnt/migrator/ + - ../../dev/helpers/mssql:/mnt/helpers + - ../../dev/.data/mssql:/mnt/data + network_mode: service:bitwarden_server + + bitwarden_mail: + image: sj26/mailcatcher:latest + restart: unless-stopped + network_mode: service:bitwarden_server + +volumes: + edgesql_dev_data: diff --git a/.devcontainer/community_dev/devcontainer.json b/.devcontainer/community_dev/devcontainer.json new file mode 100644 index 000000000..b9c31709a --- /dev/null +++ b/.devcontainer/community_dev/devcontainer.json @@ -0,0 +1,14 @@ +{ + "name": "Bitwarden Community Dev", + "dockerComposeFile": "../../.devcontainer/bitwarden_common/docker-compose.yml", + "service": "bitwarden_server", + "workspaceFolder": "/workspace", + "customizations": { + "vscode": { + "settings": {}, + "features": {}, + "extensions": ["ms-dotnettools.csdevkit"] + } + }, + "postCreateCommand": "bash .devcontainer/community_dev/postCreateCommand.sh" +} diff --git a/.devcontainer/community_dev/postCreateCommand.sh b/.devcontainer/community_dev/postCreateCommand.sh new file mode 100755 index 000000000..afb852dc1 --- /dev/null +++ b/.devcontainer/community_dev/postCreateCommand.sh @@ -0,0 +1,63 @@ +#!/usr/bin/env bash +export DEV_DIR=/workspace/dev +export CONTAINER_CONFIG=/workspace/.devcontainer/community_dev +git config --global --add safe.directory /workspace + +get_installation_id_and_key() { + pushd ./dev >/dev/null || exit + echo "Please enter your installation id and key from https://bitwarden.com/host:" + read -r -p "Installation id: " INSTALLATION_ID + read -r -p "Installation key: " INSTALLATION_KEY + jq ".globalSettings.installation.id = \"$INSTALLATION_ID\" | + .globalSettings.installation.key = \"$INSTALLATION_KEY\"" \ + secrets.json.example >secrets.json # create/overwrite secrets.json + popd >/dev/null || exit +} + +configure_other_vars() { + pushd ./dev >/dev/null || exit + cp secrets.json .secrets.json.tmp + # set DB_PASSWORD equal to .services.mssql.environment.MSSQL_SA_PASSWORD, accounting for quotes + DB_PASSWORD="$(grep -oP 'MSSQL_SA_PASSWORD=["'"'"']?\K[^"'"'"'\s]+' $DEV_DIR/.env)" + CERT_OUTPUT="$(./create_certificates_linux.sh)" + #shellcheck disable=SC2086 + IDENTITY_SERVER_FINGERPRINT="$(echo $CERT_OUTPUT | awk -F 'Identity Server Dev: ' '{match($2, /[[:alnum:]]+/); print substr($2, RSTART, RLENGTH)}')" + #shellcheck disable=SC2086 + DATA_PROTECTION_FINGERPRINT="$(echo $CERT_OUTPUT | awk -F 'Data Protection Dev: ' '{match($2, /[[:alnum:]]+/); print substr($2, RSTART, RLENGTH)}')" + SQL_CONNECTION_STRING="Server=localhost;Database=vault_dev;User Id=SA;Password=$DB_PASSWORD;Encrypt=True;TrustServerCertificate=True" + echo "Identity Server Dev: $IDENTITY_SERVER_FINGERPRINT" + echo "Data Protection Dev: $DATA_PROTECTION_FINGERPRINT" + jq \ + ".globalSettings.sqlServer.connectionString = \"$SQL_CONNECTION_STRING\" | + .globalSettings.postgreSql.connectionString = \"Host=localhost;Username=postgres;Password=$DB_PASSWORD;Database=vault_dev;Include Error Detail=true\" | + .globalSettings.mySql.connectionString = \"server=localhost;uid=root;pwd=$DB_PASSWORD;database=vault_dev\" | + .globalSettings.identityServer.certificateThumbprint = \"$IDENTITY_SERVER_FINGERPRINT\" | + .globalSettings.dataProtection.certificateThumbprint = \"$DATA_PROTECTION_FINGERPRINT\"" \ + .secrets.json.tmp >secrets.json + rm -f .secrets.json.tmp + popd >/dev/null || exit +} + +one_time_setup() { + read -r -p \ + "Would you like to configure your secrets and certificates for the first time? +WARNING: This will overwrite any existing secrets.json and certificate files. +Proceed? [y/N] " response + if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then + echo "Running one-time setup script..." + sleep 1 + get_installation_id_and_key + configure_other_vars + pushd ./dev >/dev/null || exit + pwsh ./setup_secrets.ps1 || true + popd >/dev/null || exit + + echo "Running migrations..." + sleep 5 # wait for DB container to start + dotnet run --project ./util/MsSqlMigratorUtility "$SQL_CONNECTION_STRING" + + fi +} + +# main +one_time_setup diff --git a/.devcontainer/internal_dev/devcontainer.json b/.devcontainer/internal_dev/devcontainer.json new file mode 100644 index 000000000..d86d0576a --- /dev/null +++ b/.devcontainer/internal_dev/devcontainer.json @@ -0,0 +1,16 @@ +{ + "name": "Bitwarden Dev", + "dockerComposeFile": [ + "../../.devcontainer/bitwarden_common/docker-compose.yml", + "../../.devcontainer/internal_dev/docker-compose.override.yml" + ], "service": "bitwarden_server", + "workspaceFolder": "/workspace", + "customizations": { + "vscode": { + "settings": {}, + "features": {}, + "extensions": ["ms-dotnettools.csdevkit"] + } + }, + "postCreateCommand": "bash .devcontainer/internal_dev/postCreateCommand.sh" +} diff --git a/.devcontainer/internal_dev/docker-compose.override.yml b/.devcontainer/internal_dev/docker-compose.override.yml new file mode 100644 index 000000000..9aaee9ee6 --- /dev/null +++ b/.devcontainer/internal_dev/docker-compose.override.yml @@ -0,0 +1,9 @@ +version: '3' + +services: + bitwarden_storage: + image: mcr.microsoft.com/azure-storage/azurite:latest + restart: unless-stopped + volumes: + - ../../dev/.data/azurite:/data + network_mode: service:bitwarden_server diff --git a/.devcontainer/internal_dev/postCreateCommand.sh b/.devcontainer/internal_dev/postCreateCommand.sh new file mode 100755 index 000000000..db074e218 --- /dev/null +++ b/.devcontainer/internal_dev/postCreateCommand.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +export DEV_DIR=/workspace/dev +export CONTAINER_CONFIG=/workspace/.devcontainer/internal_dev +git config --global --add safe.directory /workspace + +get_installation_id_and_key() { + pushd ./dev >/dev/null || exit + echo "Please enter your installation id and key from https://bitwarden.com/host:" + read -r -p "Installation id: " INSTALLATION_ID + read -r -p "Installation key: " INSTALLATION_KEY + jq ".globalSettings.installation.id = \"$INSTALLATION_ID\" | + .globalSettings.installation.key = \"$INSTALLATION_KEY\"" \ + secrets.json.example >secrets.json # create/overwrite secrets.json + popd >/dev/null || exit +} + +remove_comments() { + # jq will not parse files with comments + file="$1" + + if [[ -f "$file" ]]; then + sed -e '/^\/\//d' -e 's@[[:blank:]]\{1,\}//.*@@' "$file" >"$file.tmp" + mv "$file.tmp" "$file" + fi +} + +configure_other_vars() { + pushd ./dev >/dev/null || exit + cp secrets.json .secrets.json.tmp + # set DB_PASSWORD equal to .services.mssql.environment.MSSQL_SA_PASSWORD, accounting for quotes + DB_PASSWORD="$(grep -oP 'MSSQL_SA_PASSWORD=["'"'"']?\K[^"'"'"'\s]+' $DEV_DIR/.env)" + CERT_OUTPUT="$(./create_certificates_linux.sh)" + #shellcheck disable=SC2086 + IDENTITY_SERVER_FINGERPRINT="$(echo $CERT_OUTPUT | awk -F 'Identity Server Dev: ' '{match($2, /[[:alnum:]]+/); print substr($2, RSTART, RLENGTH)}')" + #shellcheck disable=SC2086 + DATA_PROTECTION_FINGERPRINT="$(echo $CERT_OUTPUT | awk -F 'Data Protection Dev: ' '{match($2, /[[:alnum:]]+/); print substr($2, RSTART, RLENGTH)}')" + SQL_CONNECTION_STRING="Server=localhost;Database=vault_dev;User Id=SA;Password=$DB_PASSWORD;Encrypt=True;TrustServerCertificate=True" + echo "Identity Server Dev: $IDENTITY_SERVER_FINGERPRINT" + echo "Data Protection Dev: $DATA_PROTECTION_FINGERPRINT" + jq \ + ".globalSettings.sqlServer.connectionString = \"$SQL_CONNECTION_STRING\" | + .globalSettings.postgreSql.connectionString = \"Host=localhost;Username=postgres;Password=$DB_PASSWORD;Database=vault_dev;Include Error Detail=true\" | + .globalSettings.mySql.connectionString = \"server=localhost;uid=root;pwd=$DB_PASSWORD;database=vault_dev\" | + .globalSettings.identityServer.certificateThumbprint = \"$IDENTITY_SERVER_FINGERPRINT\" | + .globalSettings.dataProtection.certificateThumbprint = \"$DATA_PROTECTION_FINGERPRINT\"" \ + .secrets.json.tmp >secrets.json + rm .secrets.json.tmp + popd >/dev/null || exit +} + +one_time_setup() { + read -r -p \ + "Would you like to configure your secrets and certificates for the first time? +WARNING: This will overwrite any existing secrets.json and certificate files. +Proceed? [y/N] " response + if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then + echo "Running one-time setup script..." + sleep 1 + read -r -p \ + "Place the secrets.json and dev.pfx files from our shared Collection in the ./dev directory. +Press to continue." + remove_comments ./dev/secrets.json + configure_other_vars + echo "Installing Az module. This will take ~a minute..." + pwsh -Command "Install-Module -Name Az -Scope CurrentUser -Repository PSGallery -Force" + pwsh ./dev/setup_azurite.ps1 + + dotnet tool install dotnet-certificate-tool -g >/dev/null + + read -r -s -p "Paste the \"Licensing Certificate - Dev\" password: " CERT_PASSWORD + echo + pushd ./dev >/dev/null || exit + certificate-tool add --file ./dev.pfx --password "$CERT_PASSWORD" + echo "Injecting dotnet secrets..." + pwsh ./setup_secrets.ps1 || true + popd >/dev/null || exit + + echo "Running migrations..." + sleep 5 # wait for DB container to start + dotnet run --project ./util/MsSqlMigratorUtility "$SQL_CONNECTION_STRING" + fi +} + +# main +one_time_setup diff --git a/dev/.env.example b/dev/.env.example index 18346872e..d0ebf50ef 100644 --- a/dev/.env.example +++ b/dev/.env.example @@ -1,7 +1,11 @@ COMPOSE_PROJECT_NAME=bitwardenserver # Ensure the MSSQL_PASSWORD is complex and follows the password policy defined at # https://docs.microsoft.com/en-us/sql/relational-databases/security/password-policy?view=sql-server-ver15 + +# The MSSQL*_PASSWORD variables can be the same value; MSSQL_SA_PASSWORD is used for VS Code devcontainers +# and MSSQL_PASSWORD is used for docker-compose for traditional dev configurations. MSSQL_PASSWORD=SET_A_PASSWORD_HERE_123 +MSSQL_SA_PASSWORD=SET_A_PASSWORD_HERE_123 MAILCATCHER_PORT=1080 # Alternative databases