From 10465917a546b9ca910a57831b4116a90b07f34e Mon Sep 17 00:00:00 2001 From: mptyl Date: Sun, 12 Jul 2026 11:47:43 +0200 Subject: [PATCH] test(compose): validate bundle deployment contract --- .dockerignore | 3 +- .superpowers/sdd/task-4-report.md | 7 +++++ scripts/test-container-deployment.sh | 46 ++++++++++++++++++---------- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/.dockerignore b/.dockerignore index e1c8604d..b4d168ab 100644 --- a/.dockerignore +++ b/.dockerignore @@ -28,6 +28,7 @@ harness/sessions harness/artifacts harness/indexes harness/corpus -deploy +deploy/* !deploy/vector/ +deploy/vector/* !deploy/vector/secret-policy.sh diff --git a/.superpowers/sdd/task-4-report.md b/.superpowers/sdd/task-4-report.md index 119100f9..a4850adc 100644 --- a/.superpowers/sdd/task-4-report.md +++ b/.superpowers/sdd/task-4-report.md @@ -24,6 +24,9 @@ Updated: Compose services without the legacy `external` profile. - `scripts/test-default-compose.sh` asserts the exact installation command, tracked templates, and absence of the legacy setup in the guide. +- `scripts/test-container-deployment.sh` now validates the bundle mount and rejects legacy + per-secret references; `.dockerignore` explicitly re-includes only the required vector policy + helper so the Docker build context remains safe. The docs explicitly state that a PEM CA chain cannot be put in the strict single-line bundle. A reviewed Compose override/secret-manager mount is required for `THT_SSL_CA`. Direct PostgreSQL @@ -34,9 +37,13 @@ the base bundle mount is the only default mount. - `sh -n scripts/docker-smoke.sh scripts/test-default-compose.sh` — passed. - `./scripts/test-default-compose.sh` — passed. +- `./scripts/test-container-deployment.sh` — passed after migrating its local-vector assertions + to the single bundle and checking the `.dockerignore` deployment allowlist. - `git diff --check` — passed. - `./scripts/test-docker-smoke.sh` — passed after updating its static assertion to the default no-profile invocation. +- `docker buildx build --file docker/core.Dockerfile --check .` — passed; BuildKit reported no + warnings after the `.dockerignore` parent-directory fix. ## Concerns diff --git a/scripts/test-container-deployment.sh b/scripts/test-container-deployment.sh index a15a3fbe..819b933b 100755 --- a/scripts/test-container-deployment.sh +++ b/scripts/test-container-deployment.sh @@ -6,6 +6,18 @@ cd "$(dirname "$0")/.." tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT HUP INT TERM +bundle="$tmp/thothii.secrets" +cat >"$bundle" <<'EOF' +# disposable deployment-contract bundle +THT_MODEL_API_KEY=test-model +THT_VECTOR_BOOTSTRAP_PASSWORD=contract-bootstrap +THT_VECTOR_MIGRATOR_PASSWORD=contract-migrator +THT_VECTOR_READER_PASSWORD=contract-reader +THT_VECTOR_WRITER_PASSWORD=contract-writer +EOF +chmod 0600 "$bundle" +export THT_SECRETS_FILE="$bundle" + docker compose config >"$tmp/base.yaml" grep -q '^ core:' "$tmp/base.yaml" grep -q '^ frontend:' "$tmp/base.yaml" @@ -18,25 +30,20 @@ if grep -q 'THT_[A-Z0-9_]*_SECRET_FILE:' "$tmp/base.yaml"; then exit 1 fi -for secret in bootstrap migrator local_reader local_writer; do - printf '%s' "contract-$secret" >"$tmp/$secret" - chmod 0600 "$tmp/$secret" -done -THT_VECTOR_BOOTSTRAP_PASSWORD_SECRET_FILE="$tmp/bootstrap" \ -THT_VECTOR_MIGRATOR_PASSWORD_SECRET_FILE="$tmp/migrator" \ -THT_VECTOR_READER_PASSWORD_SECRET_FILE="$tmp/local_reader" \ -THT_VECTOR_WRITER_PASSWORD_SECRET_FILE="$tmp/local_writer" \ - docker compose -f compose.yaml -f deploy/compose.local-vector.yaml \ - --profile local-vector config >"$tmp/local-vector.yaml" -grep -q 'THT_VECTOR_READER_PASSWORD_FILE: /run/secrets/vector_reader_password' "$tmp/local-vector.yaml" -grep -q 'THT_VECTOR_WRITER_PASSWORD_FILE: /run/secrets/vector_writer_password' "$tmp/local-vector.yaml" -if grep -q 'contract-local_' "$tmp/local-vector.yaml"; then - echo "rendered local-vector config leaked a direct database secret" >&2 +docker compose -f compose.yaml -f deploy/compose.local-vector.yaml \ + --profile local-vector config >"$tmp/local-vector.yaml" +grep -q 'target: thothii.secrets' "$tmp/local-vector.yaml" +if grep -Eq 'vector_(bootstrap|migrator|reader|writer)_password|THT_[A-Z0-9_]+_SECRET_FILE' "$tmp/local-vector.yaml"; then + echo "rendered local-vector config contains legacy per-secret references" >&2 + exit 1 +fi +if grep -q 'contract-' "$tmp/local-vector.yaml"; then + echo "rendered local-vector config leaked a bundle secret value" >&2 exit 1 fi docker compose -f compose.yaml -f deploy/compose.local.yaml \ - --profile external config >"$tmp/local.yaml" + config >"$tmp/local.yaml" if grep -q 'env_file:' "$tmp/local.yaml"; then echo "local Compose must use the root .env interpolation file" >&2 exit 1 @@ -48,7 +55,7 @@ THT_SECRETS_FILE="$tmp/thothii.secrets" \ THT_DB_NAME=test THT_DWH_REST_URL=https://dwh.example.test \ THT_VEC_REST_URL=https://vector.example.test THT_OLLAMA_URL=https://embed.example.test \ docker compose -f compose.yaml -f deploy/compose.production.yaml \ - --profile external config >"$tmp/production.yaml" + config >"$tmp/production.yaml" grep -q 'AUTH_MODE: upstream' "$tmp/production.yaml" grep -q 'THOTH_PUBLIC_EXPOSURE: "true"' "$tmp/production.yaml" grep -q 'THT_SECRETS_FILE: /run/secrets/thothii.secrets' "$tmp/production.yaml" @@ -67,7 +74,7 @@ if grep -q 'must-not-leak' "$tmp/legacy-model.err"; then echo "legacy model credential leaked through entrypoint diagnostics" >&2 exit 1 fi -if grep -q 'THT_VECTOR_READER_PASSWORD_FILE\|THT_VECTOR_WRITER_PASSWORD_FILE\|target: vector_reader_password\|target: vector_writer_password\|dwh_api_key\|model_api_key' "$tmp/production.yaml"; then +if grep -Eq 'THT_VECTOR_(BOOTSTRAP|MIGRATOR|READER|WRITER)_PASSWORD_FILE|target: vector_(bootstrap|migrator|reader|writer)_password|dwh_api_key|model_api_key|THT_[A-Z0-9_]+_SECRET_FILE' "$tmp/production.yaml"; then echo "production external config contains local direct vector secrets" >&2 exit 1 fi @@ -78,4 +85,9 @@ if awk '/^FROM / && $2 !~ /@sha256:/ { found=1 } END { exit !found }' \ exit 1 fi +grep -qx 'deploy/\*' .dockerignore +grep -qx '!deploy/vector/' .dockerignore +grep -qx 'deploy/vector/\*' .dockerignore +grep -qx '!deploy/vector/secret-policy.sh' .dockerignore + echo "container deployment security contract passed."