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
- Create the DNS record for
CASAN_PUBLIC_FQDNto 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.crtandtls.key(mode0600). - Register
https://<fqdn>/oauth2/callbackwith the enterprise IdP. Map thegroupsclaim to CASAN groups such ascasan-org-adminandcasan-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. Copyoauth2-proxy.env.exampleoutside the repo and fill it using the secret manager. - Create a Vault Transit key
casan-audit-keywithexportable=false; give a workload identity onlytransit/sign/casan-audit-keyand read-key metadata. Render a short-lived token intovault.envoutside Git. Never use Vault dev mode or theroottoken. - Create a versioned S3 bucket with Object Lock enabled at creation time.
Require default COMPLIANCE retention, SSE-KMS with
CASAN_S3_KMS_KEY_IDand deny delete/retention bypass. The runtime identity requiress3:PutObjectpluss3:GetObjectLockConfiguration, scoped to the audit prefix. - Let CI publish the API/UI as immutable
@sha256images, attest those artifacts and put the resulting digests incasan-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.