Files

5.3 KiB

CASAN Linux Production Handoff

This directory turns the local production-like lab into a deployment that fails closed. It is deliberately parameterized: a real DNS name, enterprise OIDC tenant, Vault policy/token and S3 bucket are tenant-owned secrets and must not be invented or committed.

One-time infrastructure

  1. Create the DNS record for CASAN_PUBLIC_FQDN to the Linux host and allow TCP 443 only. Obtain a certificate whose SAN includes that exact name; place it at /opt/casan-control-panel/tls/tls.crt and tls.key (mode 0600).
  2. Register https://<fqdn>/oauth2/callback with the enterprise IdP. Map the groups claim to CASAN groups such as casan-org-admin and casan-approver. Export the IdP's RS256 verification public key to /opt/casan-control-panel/idp-public.pem; rotation requires replacing this file and restarting the API. Copy oauth2-proxy.env.example outside the repo and fill it using the secret manager.
  3. Create a Vault Transit key casan-audit-key with exportable=false; give a workload identity only transit/sign/casan-audit-key and read-key metadata. Render a short-lived token into vault.env outside Git. Never use Vault dev mode or the root token.
  4. Create a versioned S3 bucket with Object Lock enabled at creation time. Require default COMPLIANCE retention, SSE-KMS with CASAN_S3_KMS_KEY_ID and deny delete/retention bypass. The runtime identity requires s3:PutObject plus s3:GetObjectLockConfiguration, scoped to the audit prefix.
  5. Let CI publish the API/UI as immutable @sha256 images, attest those artifacts and put the resulting digests in casan-prod.env.

Deploy

cp infra/production/casan-prod.env.example infra/production/casan-prod.env
# Fill non-secret paths, FQDN, image digests, S3 bucket and region.

sudo install -d -m 0700 /opt/casan-control-panel/tls /srv/casan/state /srv/casan/output
sudo install -m 0600 /dev/null /opt/casan-control-panel/oauth2-proxy.env
sudo install -m 0600 /dev/null /opt/casan-control-panel/runtime.env
sudo install -m 0600 /dev/null /opt/casan-control-panel/vault.env
sudo install -m 0644 /secure/export/idp-public.pem /opt/casan-control-panel/idp-public.pem

bash packages/casan-harness/scripts/bash/production-preflight.sh infra/production/casan-prod.env
set -a; source infra/production/casan-prod.env; set +a
docker compose -f docker-compose.control-panel.yml up -d

The preflight performs a real Object Lock anchor write. It will reject local URLs, image tags, expired/near-expiry TLS, dev/root Vault tokens, incomplete OIDC, issuer/audience mismatches, missing IdP keys, local trust providers, missing paths, and buckets without Object Lock. After login, run managed-prod-smoke.sh with an authenticated cookie jar.

The API does not trust X-CASAN-* or forwarded-user headers from Nginx. oauth2-proxy returns the signed access token, Nginx removes caller-supplied identity headers, and the API verifies RS256 signature, issuer, audience, expiry and clock bounds before deriving actor, role, tenant and project.

Operational anchors

Run audit-ship-s3.sh after every audit seal from the same workload identity. Use a systemd timer or an orchestrated job; it produces a unique immutable key per audit head and asks S3 for COMPLIANCE retention on every write.

Install the included timer after the first successful preflight (the runtime identity must also have s3:GetObject so retries can be idempotent):

sudo install -D -m 0644 infra/production/systemd/casan-audit-anchor.service /etc/systemd/system/casan-audit-anchor.service
sudo install -D -m 0644 infra/production/systemd/casan-audit-anchor.timer /etc/systemd/system/casan-audit-anchor.timer
sudo install -D -m 0600 infra/production/casan-prod.env /etc/casan/production.env
sudo systemctl daemon-reload
sudo systemctl enable --now casan-audit-anchor.timer
systemctl list-timers casan-audit-anchor.timer

Backup and restore drill

Back up CASAN runtime state using state-backup.sh. In production the command requires CASAN_BACKUP_PASSPHRASE_FILE; store that file via the customer secret manager, not in the repository. Restore refuses a non-empty target directory, so a drill is non-destructive by default.

export CASAN_PROFILE=prod
export CASAN_BACKUP_PASSPHRASE_FILE=/run/secrets/casan-backup-passphrase
export CASAN_BACKUP_ROOT=/srv/casan/backups

bash packages/casan-harness/scripts/bash/state-backup.sh create "$(date -u +%Y%m%dT%H%M%SZ)"
bash packages/casan-harness/scripts/bash/state-backup.sh verify "<backup-id>"
bash packages/casan-harness/scripts/bash/state-backup.sh restore "<backup-id>" --to /srv/casan/restore-drill

Perform and retain a restore drill before enabling write actions. A production runbook must define retention, off-site replication, an owner, RPO/RTO targets, and approval for any restore into a live state directory.

Hardened runner requirement

Production sandbox execution requires a rootless Docker daemon plus a non-root container process (65532:65532 by default). CASAN rejects a rootful daemon when CASAN_PROFILE=prod; the workload has no network, read-only root filesystem, no Linux capabilities, no-new-privileges, bounded PID/memory/CPU, and only its explicit workspace bind mount. Provision the workspace so the non-root runner identity can write it.