diff --git a/CHANGELOG.md b/CHANGELOG.md index caf2fa3..96e8ea4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,8 +30,10 @@ Notable changes to the Evomedia.net Token Savers. masked prompt and streams it over SSH stdin — never a command argument, never echoed. Backs the server `.env` up to a timestamped `.bak` first, updates the key atomically (matches or appends), auto-detects - `backend/.env` from `deploy.preserve`, restarts only with `-Restart`, and - `-WhatIf` previews the plan without touching anything. + `backend/.env` from `deploy.preserve`, and with `-Restart` **recreates** + the container (`up -d --force-recreate`, so the new values actually load — + a plain restart keeps the old environment). `-WhatIf` previews the plan + without touching anything. - **`zdeploy` server-side health verification (`verify` block)** — projects not published through the edge proxy can declare `"verify": { "port": ..., "path": "/health", "expect": "..." }` and the diff --git a/README.md b/README.md index 121fd53..a0f53fb 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ zstop [ ...] zec2_rotatekeys [-Rotate KEY,KEY] [-Set KEY,KEY] [-EnvFile rel/path] [-Restart] [-WhatIf] ``` -For when a secret leaks or a deploy overwrites a production `.env` with dev values: rotate or reset keys in a project's **server-side** `.env` without the values ever passing through this machine's shell history, a command argument, or your screen. `-Rotate` keys are regenerated **on the server** with `openssl rand -hex 32` — the new value is written straight into the `.env` there and never leaves the box. `-Set` keys are typed into a masked prompt and streamed to the server over SSH stdin (never a command argument, never echoed), for operator-known values like `DATABASE_URL` or `ADMIN_EMAIL`. The current server `.env` is copied to a timestamped `.bak` before any change; the KEY line is updated atomically, matching an existing key or appending it. The env file is auto-detected from the project's `deploy.preserve` (first `*.env`) or defaults to `.env` — override with `-EnvFile backend/.env`. Nothing restarts unless you pass `-Restart`. Being high-impact, it confirms before writing; `-WhatIf` prints the exact plan and changes nothing. +For when a secret leaks or a deploy overwrites a production `.env` with dev values: rotate or reset keys in a project's **server-side** `.env` without the values ever passing through this machine's shell history, a command argument, or your screen. `-Rotate` keys are regenerated **on the server** with `openssl rand -hex 32` — the new value is written straight into the `.env` there and never leaves the box. `-Set` keys are typed into a masked prompt and streamed to the server over SSH stdin (never a command argument, never echoed), for operator-known values like `DATABASE_URL` or `ADMIN_EMAIL`. The current server `.env` is copied to a timestamped `.bak` before any change; the KEY line is updated atomically, matching an existing key or appending it. The env file is auto-detected from the project's `deploy.preserve` (first `*.env`) or defaults to `.env` — override with `-EnvFile backend/.env`. Nothing touches the running app unless you pass `-Restart`, which **recreates** the container (`docker compose up -d --force-recreate `) so it actually reloads the new `.env` — a plain `restart` would keep the old environment. Being high-impact, it confirms before writing; `-WhatIf` prints the exact plan and changes nothing. ```powershell # Preview only — see exactly what would change, change nothing: diff --git a/zec2_rotatekeys.ps1 b/zec2_rotatekeys.ps1 index 0ddf460..a4dbc7e 100644 --- a/zec2_rotatekeys.ps1 +++ b/zec2_rotatekeys.ps1 @@ -20,8 +20,10 @@ # * The current server .env is copied to a timestamped .bak before any change. # * -WhatIf prints the exact plan and touches nothing. High-impact, so it # confirms before writing unless you pass -Confirm:$false. -# * It does NOT restart the app unless you pass -Restart (prod restarts are a -# deliberate, separate decision). +# * It does NOT touch the running app unless you pass -Restart, which +# recreates the container (up -d --force-recreate) so it reloads the new +# .env - a plain `restart` reuses the old environment. Prod restarts are a +# deliberate, separate decision. # # Usage: # zec2_rotatekeys [-Rotate K1,K2] [-Set K1,K2] [-EnvFile rel/path] @@ -167,13 +169,13 @@ try { Write-Host " Env file: $remoteEnv" -ForegroundColor DarkGray if ($Rotate.Count) { Write-Host " Rotate (fresh random, server-side): $($Rotate -join ', ')" -ForegroundColor Gray } if ($Set.Count) { Write-Host " Set (masked prompt, streamed): $($Set -join ', ')" -ForegroundColor Gray } - if ($Restart) { Write-Host " Then: restart the app container" -ForegroundColor Gray } + if ($Restart) { Write-Host " Then: recreate the app container (reloads the new .env)" -ForegroundColor Gray } Write-Host "" $action = @() if ($Rotate.Count) { $action += "rotate [$($Rotate -join ',')]" } if ($Set.Count) { $action += "set [$($Set -join ',')]" } - if ($Restart) { $action += "restart" } + if ($Restart) { $action += "recreate" } if (-not $PSCmdlet.ShouldProcess("${target}:$remoteEnv", ($action -join ' + '))) { Write-Host "Preview only - no changes made." -ForegroundColor Yellow Stop-ZTracking; exit 0 @@ -244,18 +246,22 @@ try { ssh @sshOpts $target "rm -f $remoteHelper" | Out-Null } - # --- optional app restart ------------------------------------------------- + # --- optional app recreate ------------------------------------------------ + # A plain `docker compose restart` reuses the container's existing + # environment, so it would NOT pick up the .env we just edited. `up -d + # --force-recreate` rebuilds the container from current config, reloading + # env_file / environment - the reliable way to apply the new secrets. $restarted = $false if ($Restart -and $done.Count -gt 0) { $composeDir = if ($proj.remote.composeDir) { $proj.remote.composeDir } else { $remotePath } $svc = if ($proj.remote.appService) { $proj.remote.appService } else { "app" } Write-Host "" - Write-Host " Restarting service '$svc' in $composeDir ..." -ForegroundColor Cyan - ssh @sshOpts $target "cd $composeDir && sudo COMPOSE_BAKE=false docker compose restart $svc" - if ($LASTEXITCODE -eq 0) { Write-Host " Restarted $svc." -ForegroundColor Green; $restarted = $true } - else { Write-Host " WARNING: restart of '$svc' failed (exit $LASTEXITCODE) - restart it manually." -ForegroundColor Red } + Write-Host " Recreating service '$svc' in $composeDir (to load the new .env) ..." -ForegroundColor Cyan + ssh @sshOpts $target "cd $composeDir && sudo COMPOSE_BAKE=false docker compose up -d --force-recreate $svc" + if ($LASTEXITCODE -eq 0) { Write-Host " Recreated $svc." -ForegroundColor Green; $restarted = $true } + else { Write-Host " WARNING: recreate of '$svc' failed (exit $LASTEXITCODE) - apply it manually: docker compose up -d --force-recreate $svc" -ForegroundColor Red } } elseif ($Restart) { - Write-Host " Skipping restart - no keys were changed." -ForegroundColor Yellow + Write-Host " Skipping recreate - no keys were changed." -ForegroundColor Yellow } # --- summary -------------------------------------------------------------- @@ -265,9 +271,9 @@ try { if ($failed.Count) { Write-Host " Failed: $($failed -join ', ')" -ForegroundColor Red } Write-Host " Backup: $backup (delete once verified)" -ForegroundColor DarkGray if ($Restart -and -not $restarted -and $done.Count -gt 0) { - Write-Host " Restart: NOT done - restart the app so it loads the new values." -ForegroundColor Yellow + Write-Host " Recreate: NOT done - apply the new values with: docker compose up -d --force-recreate " -ForegroundColor Yellow } elseif (-not $Restart -and $done.Count -gt 0) { - Write-Host " Note: the app is still running with the OLD values - restart it (or re-run with -Restart)." -ForegroundColor Yellow + Write-Host " Note: the app is still running with the OLD values - re-run with -Restart, or 'docker compose up -d --force-recreate ' on the server." -ForegroundColor Yellow } Write-Host ""