feat(casan): establish assurance kernel and harden control plane

This commit is contained in:
thanhnv
2026-08-02 23:24:51 +07:00
parent 8b477f3800
commit 5745519126
51 changed files with 4076 additions and 180 deletions
+7 -1
View File
@@ -10,6 +10,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import parse_qs, urlencode, urlparse
import jwt
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
@@ -33,7 +34,12 @@ USERS = {
},
}
KID = "casan-local-prod-idp"
KEY = rsa.generate_private_key(public_exponent=65537, key_size=2048)
KEY_FILE = os.environ.get("CASAN_IDP_PRIVATE_KEY_FILE", "")
if KEY_FILE:
with open(KEY_FILE, "rb") as key_handle:
KEY = serialization.load_pem_private_key(key_handle.read(), password=None)
else:
KEY = rsa.generate_private_key(public_exponent=65537, key_size=2048)
CODES = {}
APPROVAL_SIGNER_TOKEN = os.environ.get("CASAN_APPROVAL_SIGNER_TOKEN", "")
+12 -3
View File
@@ -12,8 +12,10 @@ not be invented or committed.
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`. Copy `oauth2-proxy.env.example` outside the repo and fill
it using the secret manager.
`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
@@ -35,6 +37,7 @@ sudo install -d -m 0700 /opt/casan-control-panel/tls /srv/casan/state /srv/casan
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
@@ -43,9 +46,15 @@ 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, missing paths, and buckets without Object Lock. After login, run
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.
+2
View File
@@ -9,10 +9,12 @@ CASAN_CP_RUNTIME_ENV=/opt/casan-control-panel/runtime.env
CASAN_CP_VAULT_ENV=/opt/casan-control-panel/vault.env
CASAN_CP_STATE_DIR=/srv/casan/state
CASAN_CP_OUTPUT_DIR=/srv/casan/output
CASAN_CP_IDP_PUBLIC_KEY=/opt/casan-control-panel/idp-public.pem
# CI must publish immutable image references, not tags such as :latest.
CASAN_CP_API_IMAGE=registry.example.internal/casan/control-panel-api@sha256:replace-with-64-hex-digest
CASAN_CP_UI_IMAGE=registry.example.internal/casan/control-panel-ui@sha256:replace-with-64-hex-digest
CASAN_CP_OAUTH2_PROXY_IMAGE=quay.io/oauth2-proxy/oauth2-proxy@sha256:replace-with-64-hex-digest
# WORM anchor destination. The deploy identity needs only PutObject and
# GetObjectLockConfiguration for this bucket/prefix; use workload identity,
+2 -2
View File
@@ -8,6 +8,6 @@ OAUTH2_PROXY_COOKIE_SECURE=true
OAUTH2_PROXY_REDIRECT_URL=https://control.casan.company.internal/oauth2/callback
OAUTH2_PROXY_OIDC_GROUPS_CLAIM=groups
OAUTH2_PROXY_SET_XAUTHREQUEST=true
OAUTH2_PROXY_PASS_ACCESS_TOKEN=false
OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER=false
OAUTH2_PROXY_PASS_ACCESS_TOKEN=true
OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER=true
OAUTH2_PROXY_SCOPE="openid profile email groups"
+10 -1
View File
@@ -2,10 +2,19 @@
# companion Vault file and the production preflight validation.
CASAN_PROFILE=prod
CASAN_CP_STRICT=1
CASAN_CP_TRUST_AUTH_PROXY=1
CASAN_CP_AUTH_MODE=jwt
CASAN_CP_JWT_ISSUER=https://id.example.internal/realms/casan
CASAN_CP_JWT_AUDIENCE=casan-control-plane
CASAN_CP_JWT_PUBLIC_KEY_FILE=/run/casan-idp/idp-public.pem
CASAN_CP_JWT_ROLE_CLAIM=groups
CASAN_CP_JWT_TENANT_CLAIM=casan_tenant
CASAN_CP_JWT_PROJECT_CLAIM=casan_project
CASAN_CP_JWT_CLOCK_SKEW_SECONDS=60
CP_BIND=0.0.0.0
CP_PORT=3010
CASAN_APP_ROOT=/app
CASAN_DASHBOARD_STALE_S=300
CASAN_PREFLIGHT=1
CASAN_CHAT_MODEL_MODE=deterministic
CASAN_SIGNING_PROVIDER=vault_kms
CASAN_IMMUTABLE_ANCHOR_PROVIDER=s3_object_lock