Files
CASAN/packages/casan-harness/tests/phase-production-preflight-tests.sh
T

162 lines
7.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# Contract tests for production handoff controls. Network calls are replaced by
# a narrow aws stub so we test fail-closed command construction without cloud credentials.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../scripts/bash/casan-paths.sh"
SHIP="$CASAN_HARNESS_ROOT/scripts/bash/audit-ship-s3.sh"
PREFLIGHT="$CASAN_HARNESS_ROOT/scripts/bash/production-preflight.sh"
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
PASS=0; FAIL=0
pass() { echo "PASS: $1"; PASS=$((PASS+1)); }
fail() { echo "FAIL: $1"; FAIL=$((FAIL+1)); }
rc() { set +e; "$@" >/dev/null 2>&1; local code=$?; set -e; echo "$code"; }
mkdir -p "$WORK/bin"
cat > "$WORK/bin/aws" <<'AWS'
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' "$*" >> "${AWS_LOG:?}"
if [[ "$1 $2" == "s3api get-object-lock-configuration" ]]; then
printf '%s\n' '{"ObjectLockConfiguration":{"ObjectLockEnabled":"Enabled","Rule":{"DefaultRetention":{"Mode":"COMPLIANCE","Days":365}}}}'
fi
if [[ "$1 $2" == "s3api head-object" ]]; then
exit 1
fi
AWS
chmod +x "$WORK/bin/aws"
printf '%064d\n' 0 > "$WORK/head.txt"
AWS_LOG="$WORK/aws.log" PATH="$WORK/bin:$PATH" CASAN_PROFILE=prod \
CASAN_S3_BUCKET=casan-production-audit CASAN_S3_REGION=ap-northeast-1 \
CASAN_S3_RETENTION_DAYS=365 CASAN_S3_KMS_KEY_ID=alias/casan-audit bash "$SHIP" "$WORK/head.txt" >/dev/null 2>&1 \
&& grep -q -- '--object-lock-mode COMPLIANCE' "$WORK/aws.log" \
&& grep -q -- '--server-side-encryption aws:kms' "$WORK/aws.log" \
&& grep -q -- '--ssekms-key-id alias/casan-audit' "$WORK/aws.log" \
&& pass "S3 anchor uses COMPLIANCE Object Lock and SSE-KMS" || fail "S3 anchor missing immutable controls"
[[ "$(rc env PATH="$WORK/bin:$PATH" CASAN_PROFILE=dev CASAN_S3_BUCKET=casan-production-audit CASAN_S3_REGION=ap-northeast-1 CASAN_S3_KMS_KEY_ID=alias/casan-audit bash "$SHIP" "$WORK/head.txt")" -ne 0 ]] \
&& pass "non-production profile is refused" || fail "non-production profile accepted"
cat > "$WORK/bin/aws" <<'AWS'
#!/usr/bin/env bash
if [[ "$1 $2" == "s3api get-object-lock-configuration" ]]; then
printf '%s\n' '{"ObjectLockConfiguration":{"ObjectLockEnabled":"Disabled","Rule":{"DefaultRetention":{"Mode":"COMPLIANCE","Days":365}}}}'
fi
AWS
chmod +x "$WORK/bin/aws"
[[ "$(rc env PATH="$WORK/bin:$PATH" CASAN_PROFILE=prod CASAN_S3_BUCKET=casan-production-audit CASAN_S3_REGION=ap-northeast-1 CASAN_S3_KMS_KEY_ID=alias/casan-audit bash "$SHIP" "$WORK/head.txt")" -ne 0 ]] \
&& pass "bucket without Object Lock is refused" || fail "Object-Lock-free bucket accepted"
printf 'not-a-digest\n' > "$WORK/bad-head.txt"
[[ "$(rc env PATH="$WORK/bin:$PATH" CASAN_PROFILE=prod CASAN_S3_BUCKET=casan-production-audit CASAN_S3_REGION=ap-northeast-1 CASAN_S3_KMS_KEY_ID=alias/casan-audit bash "$SHIP" "$WORK/bad-head.txt")" -ne 0 ]] \
&& pass "malformed audit head is refused" || fail "malformed audit head accepted"
echo "===== Production configuration boundary ====="
mkdir -p "$WORK/tls" "$WORK/state/logs/audit" "$WORK/output"
openssl req -x509 -newkey rsa:2048 -nodes -days 60 \
-subj '/CN=control.casan.internal' -addext 'subjectAltName=DNS:control.casan.internal' \
-keyout "$WORK/tls/tls.key" -out "$WORK/tls/tls.crt" >/dev/null 2>&1
openssl genrsa -out "$WORK/idp-private.pem" 2048 >/dev/null 2>&1
openssl rsa -in "$WORK/idp-private.pem" -pubout -out "$WORK/idp-public.pem" >/dev/null 2>&1
printf 'test-ca\n' > "$WORK/vault-ca.pem"
printf '%064d\n' 0 > "$WORK/state/logs/audit/audit-head.txt"
cat > "$WORK/oauth.env" <<'EOF'
OAUTH2_PROXY_PROVIDER=oidc
OAUTH2_PROXY_OIDC_ISSUER_URL=https://id.casan.internal/realms/casan
OAUTH2_PROXY_CLIENT_ID=casan-control-plane
OAUTH2_PROXY_CLIENT_SECRET=secret-manager-injected
OAUTH2_PROXY_COOKIE_SECRET=base64-cookie-secret-value
OAUTH2_PROXY_COOKIE_SECURE=true
OAUTH2_PROXY_REDIRECT_URL=https://control.casan.internal/oauth2/callback
OAUTH2_PROXY_OIDC_GROUPS_CLAIM=groups
OAUTH2_PROXY_SET_XAUTHREQUEST=true
OAUTH2_PROXY_PASS_ACCESS_TOKEN=true
OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER=true
EOF
cat > "$WORK/vault.env" <<EOF
VAULT_ADDR=https://vault.casan.internal
VAULT_TOKEN=short-lived-workload-token
VAULT_CACERT=$WORK/vault-ca.pem
EOF
write_runtime() {
local audience="$1" signing="$2" anchor="$3"
cat > "$WORK/runtime.env" <<EOF
CASAN_PROFILE=prod
CASAN_CP_AUTH_MODE=jwt
CASAN_CP_JWT_ISSUER=https://id.casan.internal/realms/casan
CASAN_CP_JWT_AUDIENCE=$audience
CASAN_CP_JWT_PUBLIC_KEY_FILE=/run/casan-idp/idp-public.pem
CASAN_CP_JWT_CLOCK_SKEW_SECONDS=60
CASAN_SIGNING_PROVIDER=$signing
CASAN_IMMUTABLE_ANCHOR_PROVIDER=$anchor
EOF
}
write_prod_env() {
local public_key="$1"
cat > "$WORK/casan-prod.env" <<EOF
CASAN_PUBLIC_FQDN=control.casan.internal
CASAN_CP_HTTPS_PORT=443
CASAN_CP_TLS_DIR=$WORK/tls
CASAN_CP_OAUTH_ENV=$WORK/oauth.env
CASAN_CP_RUNTIME_ENV=$WORK/runtime.env
CASAN_CP_VAULT_ENV=$WORK/vault.env
CASAN_CP_STATE_DIR=$WORK/state
CASAN_CP_OUTPUT_DIR=$WORK/output
CASAN_CP_IDP_PUBLIC_KEY=$public_key
CASAN_CP_API_IMAGE=registry.casan.internal/api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
CASAN_CP_UI_IMAGE=registry.casan.internal/ui@sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
CASAN_CP_OAUTH2_PROXY_IMAGE=registry.casan.internal/oauth@sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
CASAN_S3_BUCKET=casan-production-audit
CASAN_S3_PREFIX=audit-anchors
CASAN_S3_REGION=ap-northeast-1
CASAN_S3_RETENTION_DAYS=365
CASAN_S3_KMS_KEY_ID=alias/casan-audit
EOF
}
cat > "$WORK/bin/curl" <<'EOF'
#!/usr/bin/env bash
printf '%s\n' '{"data":{"ttl":300,"policies":["casan-audit-sign"]}}'
EOF
cat > "$WORK/bin/docker" <<'EOF'
#!/usr/bin/env bash
exit 0
EOF
cat > "$WORK/bin/aws" <<'EOF'
#!/usr/bin/env bash
if [[ "$1 $2" == "s3api get-object-lock-configuration" ]]; then
printf '%s\n' '{"ObjectLockConfiguration":{"ObjectLockEnabled":"Enabled","Rule":{"DefaultRetention":{"Mode":"COMPLIANCE","Days":365}}}}'
elif [[ "$1 $2" == "s3api head-object" ]]; then
exit 1
fi
exit 0
EOF
chmod +x "$WORK/bin/curl" "$WORK/bin/docker" "$WORK/bin/aws"
write_runtime casan-control-plane vault_kms s3_object_lock
write_prod_env "$WORK/idp-public.pem"
[[ "$(rc env PATH="$WORK/bin:$PATH" bash "$PREFLIGHT" "$WORK/casan-prod.env")" -eq 0 ]] \
&& pass "complete JWT/Vault/Object-Lock production contract passes with local service stubs" \
|| fail "valid production configuration rejected"
write_runtime wrong-audience vault_kms s3_object_lock
[[ "$(rc env PATH="$WORK/bin:$PATH" bash "$PREFLIGHT" "$WORK/casan-prod.env")" -ne 0 ]] \
&& pass "OIDC audience mismatch is refused" || fail "OIDC audience mismatch accepted"
write_runtime casan-control-plane local_openssl local_hash_chain
[[ "$(rc env PATH="$WORK/bin:$PATH" bash "$PREFLIGHT" "$WORK/casan-prod.env")" -ne 0 ]] \
&& pass "production local signing and local ledger configuration is refused" \
|| fail "production local trust fallback accepted"
write_runtime casan-control-plane vault_kms s3_object_lock
write_prod_env "$WORK/missing-idp-public.pem"
[[ "$(rc env PATH="$WORK/bin:$PATH" bash "$PREFLIGHT" "$WORK/casan-prod.env")" -ne 0 ]] \
&& pass "missing IdP verification key is refused" || fail "missing IdP key accepted"
echo "===== PRODUCTION HANDOFF SUMMARY: PASS=$PASS FAIL=$FAIL ====="
[[ "$FAIL" -eq 0 ]]