fix(deploy): support bootstrap password rotation

This commit is contained in:
2026-07-12 01:57:33 +02:00
parent 62e0ff1f12
commit 144acf2093
7 changed files with 269 additions and 1 deletions
+36 -1
View File
@@ -195,8 +195,43 @@ fi
compose up --force-recreate --no-deps --wait core
probe_vector read
old_bootstrap_password=$(cat "$secret_dir/bootstrap")
printf '%s' "wrong-bootstrap-${smoke_project}" >"$secret_dir/bootstrap-wrong"
printf '%s' "next-bootstrap-'quoted-${smoke_project}" >"$secret_dir/bootstrap-next"
cp "$secret_dir/bootstrap" "$secret_dir/bootstrap-before-negative"
if COMPOSE_PROJECT_NAME="$smoke_project" \
./scripts/vector-rotate-bootstrap-password.sh \
"$secret_dir/bootstrap-wrong" "$secret_dir/bootstrap-next" \
>/dev/null 2>&1; then
echo "bootstrap rotation accepted the wrong old secret" >&2
exit 1
fi
cmp "$secret_dir/bootstrap" "$secret_dir/bootstrap-before-negative"
COMPOSE_PROJECT_NAME="$smoke_project" \
./scripts/vector-rotate-bootstrap-password.sh \
"$secret_dir/bootstrap" "$secret_dir/bootstrap-next"
new_bootstrap_password=$(cat "$secret_dir/bootstrap")
test "$new_bootstrap_password" != "$old_bootstrap_password"
if compose run --rm --no-deps --entrypoint psql \
-e PGPASSWORD="$old_bootstrap_password" vector-reconcile \
--host vector-db --username postgres --dbname thoth --command 'SELECT 1' \
>/dev/null 2>&1; then
echo "old bootstrap credential still works after rotation" >&2
exit 1
fi
compose run --rm --no-deps --entrypoint psql \
-e PGPASSWORD="$new_bootstrap_password" vector-reconcile \
--host vector-db --username postgres --dbname thoth --command 'SELECT 1' \
>/dev/null
compose run --rm vector-reconcile
bootstrap_rotation_status=$(compose run --rm --no-deps vector-migrate)
printf '%s\n' "$bootstrap_rotation_status" | grep -q '"pending": \[\]'
compose up --force-recreate --no-deps --wait core
probe_vector read
compose restart vector-db core
compose up --wait vector-db core
probe_vector read
echo "Local pgvector migration, credential rotation, least-privilege roles, and persistence passed."
echo "Local pgvector runtime/bootstrap rotation, least-privilege roles, and persistence passed."
+39
View File
@@ -0,0 +1,39 @@
#!/bin/sh
set -eu
cd "$(dirname "$0")/.."
tmp=$(mktemp -d)
trap 'rm -rf "$tmp"' EXIT HUP INT TERM
fake="$tmp/docker"
log="$tmp/docker.log"
cat >"$fake" <<'SH'
#!/bin/sh
set -eu
printf '%s\n' "$*" >>"$FAKE_DOCKER_LOG"
exit "${FAKE_DOCKER_EXIT:-0}"
SH
chmod 0755 "$fake"
printf '%s' old-password >"$tmp/old"
printf '%s' "new-'quoted-\$-password" >"$tmp/new"
cp "$tmp/old" "$tmp/original"
if PATH="$tmp:$PATH" FAKE_DOCKER_LOG="$log" FAKE_DOCKER_EXIT=1 \
./scripts/vector-rotate-bootstrap-password.sh "$tmp/old" "$tmp/new" \
>"$tmp/out" 2>"$tmp/err"; then
echo "rotation unexpectedly succeeded when database verification failed" >&2
exit 1
fi
cmp "$tmp/old" "$tmp/original"
: >"$log"
PATH="$tmp:$PATH" FAKE_DOCKER_LOG="$log" \
./scripts/vector-rotate-bootstrap-password.sh "$tmp/old" "$tmp/new" \
>"$tmp/out" 2>"$tmp/err"
cmp "$tmp/old" "$tmp/new"
grep -q '/run/secrets/bootstrap-old:ro' "$log"
grep -q '/run/secrets/bootstrap-new:ro' "$log"
grep -q 'atomically replaced only after verified database login' "$tmp/out"
echo "bootstrap rotation ordering and no-config-change failure contracts passed."
+48
View File
@@ -0,0 +1,48 @@
#!/bin/sh
set -eu
cd "$(dirname "$0")/.."
if [ "$#" -ne 2 ]; then
echo "usage: $0 OLD_SECRET_FILE NEW_SECRET_FILE" >&2
exit 2
fi
absolute_file() {
directory=$(CDPATH= cd -- "$(dirname -- "$1")" && pwd)
printf '%s/%s\n' "$directory" "$(basename -- "$1")"
}
old_secret=$(absolute_file "$1")
new_secret=$(absolute_file "$2")
for secret in "$old_secret" "$new_secret"; do
if [ ! -f "$secret" ] || [ ! -r "$secret" ] || [ ! -s "$secret" ]; then
echo "secret file must be a readable, non-empty regular file: $secret" >&2
exit 2
fi
done
if [ "$old_secret" -ef "$new_secret" ]; then
echo "old and new secret files must be distinct" >&2
exit 2
fi
project=${COMPOSE_PROJECT_NAME:-thothii}
replacement=$(mktemp "${old_secret}.rotate.XXXXXX")
trap 'rm -f "$replacement"' EXIT HUP INT TERM
cp "$new_secret" "$replacement"
chmod 0600 "$replacement"
docker compose --project-name "$project" --profile local-vector run --rm --no-deps \
--user 0:0 \
--entrypoint /opt/venv/bin/python \
--volume "$old_secret:/run/secrets/bootstrap-old:ro" \
--volume "$new_secret:/run/secrets/bootstrap-new:ro" \
--volume "$(pwd)/deploy/vector/rotate-bootstrap-password.py:/opt/thoth/rotate-bootstrap-password.py:ro" \
core /opt/thoth/rotate-bootstrap-password.py \
/run/secrets/bootstrap-old /run/secrets/bootstrap-new
mv -f "$replacement" "$old_secret"
trap - EXIT HUP INT TERM
echo "Deployment bootstrap secret atomically replaced only after verified database login."
echo "Re-run: docker compose --project-name $project --profile local-vector up --wait vector-reconcile vector-migrate core"