feat(casan): establish assurance kernel and harden control plane
This commit is contained in:
@@ -101,8 +101,10 @@ Goal workspace context:
|
||||
a different actor. The executor verifies the artifact hash, applies it, runs fixed
|
||||
project build/test commands, and reverses the patch if verification fails.
|
||||
|
||||
Local management headers: `x-casan-actor`, `x-casan-role`, `x-casan-project`,
|
||||
`x-casan-tenant`. Missing role defaults to `viewer`, so writes fail closed.
|
||||
In explicit loopback development mode only, local management headers are
|
||||
`x-casan-actor`, `x-casan-role`, `x-casan-project`, and `x-casan-tenant`.
|
||||
Missing role defaults to `viewer`, so writes fail closed. JWT mode discards
|
||||
these caller assertions and derives them only from verified token claims.
|
||||
|
||||
Kill-switch management:
|
||||
|
||||
@@ -169,12 +171,19 @@ App root + telemetry paths resolve via the same marker walk-up as `casan-paths.s
|
||||
compatibility aliases. Freshness is calculated independently from each file's mtime using
|
||||
`CASAN_DASHBOARD_STALE_S` (default `3600`).
|
||||
|
||||
## Security posture (MVP)
|
||||
Binds `127.0.0.1` by default. Refuses a non-loopback bind under `CASAN_PROFILE=prod` /
|
||||
`CASAN_CP_STRICT=1` unless `CASAN_CP_TRUST_AUTH_PROXY=1` is set for an authenticated reverse
|
||||
proxy that overwrites identity headers. Management endpoints are RBAC-gated via the harness
|
||||
`rbac-check.py`; IdP group claims such as `casan-approver` are mapped to RBAC roles through
|
||||
the same harness engine.
|
||||
## Security posture
|
||||
Binds `127.0.0.1` in explicit local development mode. Every non-loopback bind,
|
||||
and every production profile, requires `CASAN_CP_AUTH_MODE=jwt`. Production
|
||||
requires an RS256 public key plus configured issuer and audience; missing or
|
||||
invalid configuration refuses startup. The API verifies signature, expiry,
|
||||
issuer, audience, `nbf`/`iat`, and bounded clock skew in-process, then maps
|
||||
verified group claims through `rbac-check.py`. Arbitrary `X-CASAN-*` and
|
||||
forwarded-user headers are not an authentication mechanism.
|
||||
|
||||
Production variables are documented in `infra/production/runtime.env.example`.
|
||||
The packaged boundary is oauth2-proxy → Nginx header stripping/bearer forwarding
|
||||
→ API cryptographic verification. The current provider uses a mounted RS256
|
||||
public key; automated JWKS discovery/rotation remains future work.
|
||||
|
||||
## Test
|
||||
```bash
|
||||
@@ -189,8 +198,8 @@ bash packages/casan-control-panel/scripts/local-prod-smoke.sh
|
||||
|
||||
The scaffold includes `Dockerfile.control-panel-api`, `Dockerfile.control-panel-ui`, and
|
||||
`nginx/control-panel.conf`. Nginx protects UI/API through oauth2-proxy `auth_request`,
|
||||
overwrites browser-supplied `X-CASAN-*` headers, and passes IdP group claims to the API for
|
||||
RBAC mapping. The local smoke starts a self-signed HTTPS stack with a mock OIDC IdP and
|
||||
removes browser-supplied identity headers, and forwards the signed bearer token for API
|
||||
verification. The local smoke starts a self-signed HTTPS stack with a mock OIDC IdP and
|
||||
expects `CP_LOCAL_SMOKE_PASS https_oidc=true actor=oidc-ops role=org-admin`; it also
|
||||
asserts the Command Center returns all nine widgets with provenance envelopes and invokes
|
||||
`managed-prod-smoke.sh` with the authenticated mock-IdP cookie jar. A passing local run
|
||||
|
||||
Reference in New Issue
Block a user