From 7ed3b7f05bc6b817f64a0f5f71e6025f46d39664 Mon Sep 17 00:00:00 2001 From: Francisco J Lopez-Pellicer Date: Sun, 1 Nov 2026 17:55:12 +0100 Subject: [PATCH] fix(setup): append compose secrets when .env already exists Windows PowerShell has no openssl, and the install docs told operators to append the three secrets with bash openssl after copying a profile. setup.sh and setup.ps1 now fill any missing key on an existing .env. Fixes #138 --- .github/workflows/compose-secrets.yml | 3 + CHANGELOG.md | 1 + README.md | 85 +++++++++-------------- profiles/development-lvh.env | 2 +- profiles/development-oracle.env | 2 +- profiles/development-postgres.env | 2 +- profiles/development.env | 2 +- profiles/oracle.env | 2 +- profiles/postgres.env | 2 +- setup.ps1 | 10 +-- setup.sh | 23 +++--- tools/tests/test_setup_compose_secrets.sh | 76 ++++++++++++++++++++ 12 files changed, 134 insertions(+), 76 deletions(-) create mode 100644 tools/tests/test_setup_compose_secrets.sh diff --git a/.github/workflows/compose-secrets.yml b/.github/workflows/compose-secrets.yml index fec7f64..fac7b58 100644 --- a/.github/workflows/compose-secrets.yml +++ b/.github/workflows/compose-secrets.yml @@ -14,3 +14,6 @@ jobs: - name: Check profile templates and required Compose secrets run: bash tools/scripts/check-compose-secrets.sh + + - name: Check setup appends secrets on an existing .env + run: bash tools/tests/test_setup_compose_secrets.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index e5e89e9..bc3c011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), #### Stack-level +- **Setup**: `setup.sh` and `setup.ps1` append `SITMUN_USER_SECRET`, `MIDDLEWARE_SECRET`, and `SITMUN_MBTILES_JOB_HANDLE_SECRET` when `.env` already exists and a key is absent. An existing value is kept. Windows setup uses `RandomNumberGenerator` and does not call `openssl` ([#138](https://github.com/sitmun/sitmun-application-stack/issues/138)). - **Docker**: Each `profiles/*.env` template names `SITMUN_USER_SECRET`, `MIDDLEWARE_SECRET`, and `SITMUN_MBTILES_JOB_HANDLE_SECRET` and leaves them unset. Copying a profile to `.env` still requires generated values before Compose will start ([#134](https://github.com/sitmun/sitmun-application-stack/issues/134)). - **Liquibase**: `service.type` is limited to `WMS`, `WFS`, and `WMTS` on the development, postgres, and oracle profiles. A stored service of another type is not deleted ([#101](https://github.com/sitmun/sitmun-application-stack/issues/101)). - **Docker**: `docker compose build front` uses the Angular 22 viewer webpack config. `envsubst` replaces only `PUBLIC_BASE_PATH`, so the viewer image build keeps the `jszip`, `util`, and `@spz-loader` exclusions. diff --git a/README.md b/README.md index a7db3f8..ff97d5c 100644 --- a/README.md +++ b/README.md @@ -81,23 +81,9 @@ The stack has four main components: cd sitmun-application-stack ``` -2. **Setup the environment** +2. **Select a profile (`.env`)** - For Unix-based systems (Linux/macOS): - - ```bash - ./setup.sh - ``` - - For Windows systems: - - ```powershell - ./setup.ps1 - ``` - -3. **Select a profile (`.env`)** - - The stack is configured via `.env`. Recommended profiles are under `profiles/`: + The stack is configured via `.env`. Copy a profile before setup. If `.env` is still missing, setup uses `profiles/development-postgres.env`. ```bash # Development (PostgreSQL) @@ -107,17 +93,23 @@ The stack has four main components: # cp profiles/development-oracle.env .env ``` - Notes: - - The setup scripts create `.env` from `profiles/development-postgres.env` **only if** `.env` is missing. They then append `SITMUN_USER_SECRET`, `MIDDLEWARE_SECRET`, and `SITMUN_MBTILES_JOB_HANDLE_SECRET` when those keys are absent. - - A manual copy must append the same three values before Compose will start: +3. **Setup the environment** + + For Unix-based systems (Linux/macOS): ```bash - echo "SITMUN_USER_SECRET=$(openssl rand -hex 32)" >> .env - echo "MIDDLEWARE_SECRET=$(openssl rand -hex 32)" >> .env - echo "SITMUN_MBTILES_JOB_HANDLE_SECRET=$(openssl rand -hex 32)" >> .env + ./setup.sh ``` - - You can keep local overrides in `.env.local` (not committed); Docker Compose loads `.env → .env.local → environment variables`. + For Windows systems: + + ```powershell + ./setup.ps1 + ``` + + The scripts append `SITMUN_USER_SECRET`, `MIDDLEWARE_SECRET`, and `SITMUN_MBTILES_JOB_HANDLE_SECRET` when those keys are absent, including when `.env` was copied from a profile first. An existing value is left as it is. Windows does not need `openssl`. Run the script again after replacing `.env` with another profile. + + You can keep local overrides in `.env.local` (not committed); Docker Compose loads `.env → .env.local → environment variables`. 4. **Start the SITMUN Application Stack** @@ -147,7 +139,14 @@ The stack has four main components: cd sitmun-application-stack ``` -2. **Setup Environment** +2. **Select a development profile (`.env`)** + + ```bash + cp profiles/development-postgres.env .env + # Or: cp profiles/development-oracle.env .env + ``` + +3. **Setup Environment** ```bash # Unix-based systems @@ -157,17 +156,7 @@ The stack has four main components: ./setup.ps1 ``` -3. **Select a development profile (`.env`)** - - ```bash - cp profiles/development-postgres.env .env - # Or: cp profiles/development-oracle.env .env - echo "SITMUN_USER_SECRET=$(openssl rand -hex 32)" >> .env - echo "MIDDLEWARE_SECRET=$(openssl rand -hex 32)" >> .env - echo "SITMUN_MBTILES_JOB_HANDLE_SECRET=$(openssl rand -hex 32)" >> .env - ``` - - `./setup.sh` and `./setup.ps1` append those three keys when they create `.env` and the keys are absent. Do not commit the values. + `./setup.sh` and `./setup.ps1` append `SITMUN_USER_SECRET`, `MIDDLEWARE_SECRET`, and `SITMUN_MBTILES_JOB_HANDLE_SECRET` when those keys are absent. An existing value is kept. Do not commit the values. 4. **Start Services** @@ -227,14 +216,7 @@ The stack has four main components: 2. **Secrets (mandatory)** - Production profiles (`profiles/postgres`, `profiles/oracle`) do **not** ship secret defaults. You must set these three variables in `.env`, each at least 32 characters: - - ```bash - # Backend JWT signing secret, backend-proxy shared secret, and MBTiles job handle - echo "SITMUN_USER_SECRET=$(openssl rand -hex 32)" >> .env - echo "MIDDLEWARE_SECRET=$(openssl rand -hex 32)" >> .env - echo "SITMUN_MBTILES_JOB_HANDLE_SECRET=$(openssl rand -hex 32)" >> .env - ``` + Production profiles (`profiles/postgres`, `profiles/oracle`) do **not** ship secret defaults. After the copy, run `./setup.sh` on Unix or `./setup.ps1` on Windows. The script appends `SITMUN_USER_SECRET`, `MIDDLEWARE_SECRET`, and `SITMUN_MBTILES_JOB_HANDLE_SECRET` when a key is absent. Each value is at least 32 characters. An existing value is kept. Windows does not need `openssl`. Enforcement is fail-fast, so misconfiguration is caught before the platform serves traffic: @@ -607,22 +589,20 @@ profiles/ postgres/ # PostgreSQL Liquibase and backend config, mounted by the root Compose file ``` -PostgreSQL and Oracle start from the repository root. Copy the profile, append the three secrets when they are absent, then start the root Compose file. `SITMUN_CONFIG_DIR` mounts `profiles/postgres` or `profiles/oracle`. +PostgreSQL and Oracle start from the repository root. Copy the profile, run `./setup.sh` or `./setup.ps1` so the three secrets are appended when absent, then start the root Compose file. `SITMUN_CONFIG_DIR` mounts `profiles/postgres` or `profiles/oracle`. ```bash cp profiles/postgres.env .env -echo "SITMUN_USER_SECRET=$(openssl rand -hex 32)" >> .env -echo "MIDDLEWARE_SECRET=$(openssl rand -hex 32)" >> .env -echo "SITMUN_MBTILES_JOB_HANDLE_SECRET=$(openssl rand -hex 32)" >> .env +./setup.sh docker compose up -d cp profiles/oracle.env .env -echo "SITMUN_USER_SECRET=$(openssl rand -hex 32)" >> .env -echo "MIDDLEWARE_SECRET=$(openssl rand -hex 32)" >> .env -echo "SITMUN_MBTILES_JOB_HANDLE_SECRET=$(openssl rand -hex 32)" >> .env +./setup.sh docker compose up -d ``` +On Windows, run `./setup.ps1` instead of `./setup.sh`. + ### Development Workflow 1. **Update Repository and Submodules** @@ -899,10 +879,7 @@ MIDDLEWARE_SECRET=<32+ char random value> # shared backend <-> proxy secret SITMUN_MBTILES_JOB_HANDLE_SECRET=<32+ char random value> # proxy MBTiles job handle ``` -```bash -# Generate secure random values -openssl rand -hex 32 -``` +`./setup.sh` and `./setup.ps1` append a value for each missing key. Windows does not need `openssl`. **IMPORTANT for production**: diff --git a/profiles/development-lvh.env b/profiles/development-lvh.env index e961914..7f32907 100644 --- a/profiles/development-lvh.env +++ b/profiles/development-lvh.env @@ -54,7 +54,7 @@ ENVIRONMENT=development # --- Security --- # Required. Compose refuses to start until each value is set. -# Generate with: openssl rand -hex 32 +# setup.sh and setup.ps1 append a value when this key is absent. # SITMUN_USER_SECRET= # MIDDLEWARE_SECRET= # SITMUN_MBTILES_JOB_HANDLE_SECRET= diff --git a/profiles/development-oracle.env b/profiles/development-oracle.env index 81236a8..0bd76e6 100644 --- a/profiles/development-oracle.env +++ b/profiles/development-oracle.env @@ -54,7 +54,7 @@ DATABASE_URL=jdbc:oracle:thin:@//oracle:1521/ # --- Security --- # Required. Compose refuses to start until each value is set. -# Generate with: openssl rand -hex 32 +# setup.sh and setup.ps1 append a value when this key is absent. # SITMUN_USER_SECRET= # MIDDLEWARE_SECRET= # SITMUN_MBTILES_JOB_HANDLE_SECRET= diff --git a/profiles/development-postgres.env b/profiles/development-postgres.env index 085c702..695beae 100644 --- a/profiles/development-postgres.env +++ b/profiles/development-postgres.env @@ -55,7 +55,7 @@ ENVIRONMENT=development # --- Security --- # Required. Compose refuses to start until each value is set. -# Generate with: openssl rand -hex 32 +# setup.sh and setup.ps1 append a value when this key is absent. # SITMUN_USER_SECRET= # MIDDLEWARE_SECRET= # SITMUN_MBTILES_JOB_HANDLE_SECRET= diff --git a/profiles/development.env b/profiles/development.env index 64a0e59..54bb1e7 100644 --- a/profiles/development.env +++ b/profiles/development.env @@ -53,7 +53,7 @@ ENVIRONMENT=development # --- Security --- # Required. Compose refuses to start until each value is set. -# Generate with: openssl rand -hex 32 +# setup.sh and setup.ps1 append a value when this key is absent. # SITMUN_USER_SECRET= # MIDDLEWARE_SECRET= # SITMUN_MBTILES_JOB_HANDLE_SECRET= diff --git a/profiles/oracle.env b/profiles/oracle.env index 33bc98f..cf9d467 100644 --- a/profiles/oracle.env +++ b/profiles/oracle.env @@ -56,7 +56,7 @@ SPRING_LIQUIBASE_CONTEXTS= # --- Security --- # Required. Compose refuses to start until each value is set. -# Generate with: openssl rand -hex 32 +# setup.sh and setup.ps1 append a value when this key is absent. # SITMUN_USER_SECRET= # MIDDLEWARE_SECRET= # SITMUN_MBTILES_JOB_HANDLE_SECRET= diff --git a/profiles/postgres.env b/profiles/postgres.env index b2933f0..ea6028a 100644 --- a/profiles/postgres.env +++ b/profiles/postgres.env @@ -53,7 +53,7 @@ SPRING_LIQUIBASE_CONTEXTS= # --- Security --- # Required. Compose refuses to start until each value is set. -# Generate with: openssl rand -hex 32 +# setup.sh and setup.ps1 append a value when this key is absent. # SITMUN_USER_SECRET= # MIDDLEWARE_SECRET= # SITMUN_MBTILES_JOB_HANDLE_SECRET= diff --git a/setup.ps1 b/setup.ps1 index c9ee6d3..2c91591 100644 --- a/setup.ps1 +++ b/setup.ps1 @@ -93,17 +93,17 @@ function Add-ComposeSecretIfMissing { } } -# Copy default development profile to .env if .env does not exist. -# Profile files do not store secrets. Generate any of the three that are absent. +# Create .env from the development PostgreSQL profile when it is absent. +# Profile files do not store secrets. Append any of the three keys that are missing. function Copy-EnvFileIfMissing { if (-Not (Test-Path ".env")) { Write-Host "${Yellow}.env file is missing. Copying profiles/development-postgres.env to .env...${Reset}" Copy-Item -Path "profiles/development-postgres.env" -Destination ".env" - Add-ComposeSecretIfMissing -Key "SITMUN_USER_SECRET" - Add-ComposeSecretIfMissing -Key "MIDDLEWARE_SECRET" - Add-ComposeSecretIfMissing -Key "SITMUN_MBTILES_JOB_HANDLE_SECRET" Write-Host "${Green}.env file created from profiles/development-postgres.env.${Reset}" } + Add-ComposeSecretIfMissing -Key "SITMUN_USER_SECRET" + Add-ComposeSecretIfMissing -Key "MIDDLEWARE_SECRET" + Add-ComposeSecretIfMissing -Key "SITMUN_MBTILES_JOB_HANDLE_SECRET" } # Main logic diff --git a/setup.sh b/setup.sh index 76fdbd7..7c936f4 100755 --- a/setup.sh +++ b/setup.sh @@ -81,22 +81,23 @@ append_compose_secret_if_missing() { fi } -# Copy default development profile to .env if .env does not exist. -# Profile files do not store secrets. Generate any of the three that are absent. +# Create .env from the development PostgreSQL profile when it is absent. +# Profile files do not store secrets. Append any of the three keys that are missing. copy_env_file_if_missing() { if [ ! -f ".env" ]; then echo -e "${YELLOW}.env file is missing. Copying profiles/development-postgres.env to .env...${NC}" cp profiles/development-postgres.env .env - append_compose_secret_if_missing SITMUN_USER_SECRET - append_compose_secret_if_missing MIDDLEWARE_SECRET - append_compose_secret_if_missing SITMUN_MBTILES_JOB_HANDLE_SECRET echo -e "${GREEN}.env file created from profiles/development-postgres.env.${NC}" fi + append_compose_secret_if_missing SITMUN_USER_SECRET + append_compose_secret_if_missing MIDDLEWARE_SECRET + append_compose_secret_if_missing SITMUN_MBTILES_JOB_HANDLE_SECRET } -# Main logic -display_banner -check_git_repository -check_repository_url -update_from_remote -copy_env_file_if_missing +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + display_banner + check_git_repository + check_repository_url + update_from_remote + copy_env_file_if_missing +fi diff --git a/tools/tests/test_setup_compose_secrets.sh b/tools/tests/test_setup_compose_secrets.sh new file mode 100644 index 0000000..672cf14 --- /dev/null +++ b/tools/tests/test_setup_compose_secrets.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# A copied profile .env has the three secret names only as comments. +# setup must append values without replacing one that is already set. +# +# bash tools/tests/test_setup_compose_secrets.sh +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT + +assert_generated() { + local file="$1" key="$2" + if ! grep -qE "^${key}=[0-9a-f]{64}$" "$file"; then + echo "missing generated ${key} in ${file}" >&2 + exit 1 + fi +} + +run_copy() { + local dir="$1" + ( + cd "$dir" + # shellcheck disable=SC1091 + source "$ROOT/setup.sh" + copy_env_file_if_missing + ) +} + +copied="$work/copied" +mkdir -p "$copied" +cp "$ROOT/profiles/postgres.env" "$copied/.env" +run_copy "$copied" +for key in SITMUN_USER_SECRET MIDDLEWARE_SECRET SITMUN_MBTILES_JOB_HANDLE_SECRET; do + assert_generated "$copied/.env" "$key" +done + +kept="$work/kept" +mkdir -p "$kept" +cp "$ROOT/profiles/postgres.env" "$kept/.env" +printf '\nSITMUN_USER_SECRET=kept-value-0123456789abcdef0123456789abcd\n' >> "$kept/.env" +run_copy "$kept" +if ! grep -qx 'SITMUN_USER_SECRET=kept-value-0123456789abcdef0123456789abcd' "$kept/.env"; then + echo "existing SITMUN_USER_SECRET was replaced" >&2 + exit 1 +fi +if [[ "$(grep -c '^SITMUN_USER_SECRET=' "$kept/.env")" -ne 1 ]]; then + echo "SITMUN_USER_SECRET was duplicated" >&2 + exit 1 +fi +assert_generated "$kept/.env" MIDDLEWARE_SECRET +assert_generated "$kept/.env" SITMUN_MBTILES_JOB_HANDLE_SECRET + +created="$work/created" +mkdir -p "$created/profiles" +cp "$ROOT/profiles/development-postgres.env" "$created/profiles/development-postgres.env" +run_copy "$created" +if ! grep -qx 'COMPOSE_PROFILES=postgres' "$created/.env"; then + echo "missing .env was not created from profiles/development-postgres.env" >&2 + exit 1 +fi +for key in SITMUN_USER_SECRET MIDDLEWARE_SECRET SITMUN_MBTILES_JOB_HANDLE_SECRET; do + assert_generated "$created/.env" "$key" +done + +if grep -q 'openssl rand' "$ROOT/README.md"; then + echo "README still tells operators to run openssl" >&2 + exit 1 +fi + +if grep -q 'openssl rand' "$ROOT"/profiles/*.env; then + echo "a profile env template still tells operators to run openssl" >&2 + exit 1 +fi + +echo "setup appends the three compose secrets when .env already exists"