Files
CASAN/docs/security/CONTROL_PANEL_AUTH_HARDENING.md
T

87 lines
5.0 KiB
Markdown

# Control Panel authentication hardening
## Scope
This document defines the boundary between the local workstation experience and a production deployment. Local browser login to Codex/Claude is a developer convenience; it is not a production identity or secret distribution mechanism.
## Trust boundaries
```mermaid
flowchart LR
B["Browser"] -->|TLS + OIDC cookie| N["Nginx"]
N -->|auth_request| O["oauth2-proxy"]
N -->|overwritten identity headers| A["Control Panel API"]
A -->|tenant-scoped encrypted store| S["CASAN state"]
A -->|random bridge token, local only| H["Mac host bridge"]
H -->|official CLI auth| P["Codex / Claude"]
```
The API must not be published directly. Nginx is the only ingress and overwrites `X-CASAN-Actor`, `X-CASAN-Groups`, and `X-CASAN-Role`. The API refuses a non-loopback strict bind unless `CASAN_CP_TRUST_AUTH_PROXY=1` is explicit.
## Implemented controls
- TLS at Nginx; production accepts only TLS 1.2/1.3.
- OIDC authentication through oauth2-proxy.
- Secure, HttpOnly, SameSite=Lax session cookies with bounded expiry/refresh.
- Browser-supplied identity headers are overwritten at Nginx.
- CSP, frame denial, MIME sniffing protection, referrer and browser permission restrictions.
- API request body capped at 1 MiB.
- SSE buffering disabled for trace streams; no intermediate proxy cache.
- Provider account bridge is disabled unless `CASAN_PROVIDER_ACCOUNT_AUTH_ENABLED=1`.
- Production Compose pins account auth to disabled.
- Bridge requests require a random 256-bit token, valid Host header, bounded JSON body and fixed provider/command allowlist.
- Account model calls run in a temporary directory with tools disabled or a read-only sandbox and ephemeral sessions.
- One concurrent account model call per provider and ten calls per ten minutes.
- Bridge audit stores only timestamp, provider, status, prompt hash, prompt length and latency. It never stores prompt text or credentials.
- Bridge token file is mode 0600 and rotates after a clean local stop/start.
- Goal starts are limited to five per actor per ten minutes and two concurrent jobs per actor.
- Goal state is tenant-scoped; IDs are validated before file access.
- Goal audit is hash-linked and serialized under a file lock to prevent concurrent writers from forking the chain.
- Trace evidence excludes raw prompts and secrets.
## Local-only controls
The host bridge listens on a workstation port because Docker Desktop must reach the Mac host. Its token is the primary authorization barrier. Keep macOS firewall enabled and do not port-forward `20130`.
The local TLS certificate is self-signed. Manual trust is acceptable only for localhost testing. Never reuse this certificate or the mock IdP in production.
## Production requirements
Before production deployment:
1. Keep `CASAN_PROVIDER_ACCOUNT_AUTH_ENABLED=0`.
2. Do not deploy `provider-auth-bridge.py`.
3. Use managed OpenAI/Anthropic credentials from Vault/KMS or workload identity.
4. Use an enterprise IdP and explicit group-to-role mapping.
5. Use a CA-issued certificate and a fixed production hostname.
6. Set a digest-pinned `CASAN_CP_API_IMAGE`, `CASAN_CP_UI_IMAGE`, and `CASAN_CP_OAUTH2_PROXY_IMAGE`.
7. Keep the API on a private container/network segment with Nginx as its only caller.
8. Restrict egress from the API to allowlisted model providers, Vault/KMS, object storage and required observability endpoints.
9. Store state on encrypted storage; send audit heads to KMS/WORM/Object Lock.
10. Export rate-limit, auth failure, bridge-disabled and audit-chain metrics to alerting.
11. Back up and restore-test tenant state before enabling write actions.
12. Run the production preflight and security suites against the exact deployment images.
## Known residual risks
- Local bridge authorization is bearer-token based; a process with access to the token file can call it.
- In-memory rate windows reset when the API or bridge restarts. Production should also rate-limit at ingress.
- Goal output is stored in tenant-scoped local state but is not currently envelope-encrypted as a whole.
- A compromised container on the private Control Panel network may attempt to forge proxy headers. Production network policy must keep unrelated workloads off that network.
- Developer account subscriptions have provider-specific quotas and are not an availability SLA.
- The local mock IdP and self-signed TLS do not prove enterprise SSO readiness.
## Incident response
If account bridge token exposure is suspected:
```bash
packages/casan-harness/scripts/bash/local-full.sh stop
packages/casan-harness/scripts/bash/local-full.sh start
```
This terminates the bridge, deletes the old token and creates a new one. Then inspect `tmp/control-panel-local/auth-bridge/model-audit.jsonl` for unexpected provider/status/hash activity. The log contains no raw prompts.
If a goal audit chain is suspected of tampering, stop new goal submissions, preserve `.specify/logs/audit/goal-orchestrator.jsonl` and its head, and compare each `prev_hash`/`record_hash` before restoring service.