fix: harden owner bootstrap and auth persistence

This commit is contained in:
2026-06-23 18:33:51 +02:00
parent 5df5194651
commit a2272c5df6
13 changed files with 481 additions and 143 deletions
+12 -35
View File
@@ -52,9 +52,8 @@ jobs:
ENV_POSTGRES_PASSWORD: ${{ secrets.ENV_POSTGRES_PASSWORD }}
ENV_JWT_KEY: ${{ secrets.ENV_JWT_KEY }}
ENV_OPENCLAW_TOKEN: ${{ secrets.ENV_OPENCLAW_TOKEN }}
# OWNER_PASSWORD is read from the host's persistent .env — NOT from a Gitea secret.
# This ensures the password stays consistent across deploys and the DB is the
# single source of truth after initial seed (enforced by SeedAudit guard).
# Owner password is not injected at deploy time.
# After first seed, the database is the only password source.
steps:
# ═══════════════════════════════════════════════════
@@ -113,37 +112,25 @@ jobs:
echo "mutated_main=false" >> "$GITEA_OUTPUT"
# ═══════════════════════════════════════════════════
# Step 4: Build .env from secrets + host .env (SAFE)
# Step 4: Build .env from secrets (SAFE)
#
# Secrets are written to /tmp/nexus-deploy-env — NEVER
# to a file inside the workspace that gets rsync'd to
# the host. The temp file is deleted immediately after
# compose operations complete.
#
# OWNER_PASSWORD is read from the host's persistent .env
# to ensure it stays the single source of truth. Other
# secrets (POSTGRES_PASSWORD, JWT_KEY, OPENCLAW_TOKEN)
# Owner password is deliberately omitted so production deploys
# cannot overwrite the persisted DB password.
# Other secrets (POSTGRES_PASSWORD, JWT_KEY, OPENCLAW_TOKEN)
# come from Gitea secrets.
# ═══════════════════════════════════════════════════
- name: Prepare .env (secrets + host .env → temp file)
- name: Prepare .env (secrets → temp file)
run: |
set -euo pipefail
# Read OWNER_PASSWORD from the host's persistent .env
HOST_OWNER_PASSWORD=""
if [ -f "${DEPLOY_PATH}/.env" ]; then
HOST_OWNER_PASSWORD=$(grep '^OWNER_PASSWORD=' "${DEPLOY_PATH}/.env" | cut -d= -f2- || true)
fi
if [ -z "${HOST_OWNER_PASSWORD}" ]; then
echo "❌ OWNER_PASSWORD not found in ${DEPLOY_PATH}/.env"
echo " The host .env is the single source of truth for the owner password."
echo " Ensure OWNER_PASSWORD is set in the deploy-path .env before deploying."
exit 1
fi
cat > "${ENV_TMPFILE}" <<EOF
# Nexus Production Environment — auto-generated by CD pipeline
# Managed via Gitea Secrets + host .env → do NOT edit manually on the host.
# Managed via Gitea Secrets → do NOT edit manually.
# This file lives in /tmp and is removed after deploy completes.
POSTGRES_DB=nexus
POSTGRES_USER=nexus
@@ -151,9 +138,7 @@ jobs:
JWT_KEY=${ENV_JWT_KEY}
JWT_ISSUER=nexus
JWT_AUDIENCE=nexus-web
OWNER_EMAIL=vmbao62@hotmail.de
OWNER_PASSWORD=${HOST_OWNER_PASSWORD}
OWNER_DISPLAY_NAME=
BOOTSTRAP_OWNER_EMAIL=vmbao62@hotmail.de
OPENCLAW_BASE_URL=http://host.docker.internal:18789
OPENCLAW_GATEWAY_TOKEN=${ENV_OPENCLAW_TOKEN}
OPENCLAW_GATEWAY_PASSWORD=
@@ -205,18 +190,10 @@ set -e
trap 'rm -f /tmp/nexus-deploy-env' EXIT
cat > /tmp/nexus-deploy-env
# ── Clean up zombie containers ──
# ── Graceful shutdown (preserves DB volume integrity) ──
docker compose --env-file /tmp/nexus-deploy-env stop postgres 2>/dev/null || true
docker compose --env-file /tmp/nexus-deploy-env down --remove-orphans 2>/dev/null || true
docker rm -f nexus-postgres-1 nexus-api-1 nexus-web-1 2>/dev/null || true
# ── WAL recovery ──
PG_VOL=$(docker volume ls -q --filter name=nexus-postgres 2>/dev/null | head -1)
if [ -n "$PG_VOL" ]; then
echo "Checking postgres WAL integrity..."
docker run --rm -v "$PG_VOL:/var/lib/postgresql/data" --entrypoint sh postgres:17-alpine -c "pg_resetwal -f /var/lib/postgresql/data && echo 'WAL reset OK'" 2>&1 || echo "pg_resetwal failed (may be benign)"
else
echo "Postgres volume not found - will be created fresh"
fi
echo "Postgres volume preserved (nexus-postgres) — no WAL reset"
BUILD_ARGS="${DEPLOY_BUILD_ARGS:-}"
SERVICE="${DEPLOY_SERVICE:-}"