Files
CASAN/docs/security/CONTROL_PANEL_AUTH_HARDENING.md
T

5.5 KiB

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

flowchart LR
    B["Browser"] -->|TLS + OIDC cookie| N["Nginx"]
    N -->|auth_request| O["oauth2-proxy"]
    N -->|signed bearer token; identity headers removed| A["Control Panel API"]
    A -->|verify RS256 + issuer + audience + time claims| I["VerifiedClaims"]
    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 removes X-CASAN-* and forwarded identity headers. The API refuses every production or non-loopback bind unless CASAN_CP_AUTH_MODE=jwt has a valid cryptographic verifier. Proxy headers alone are never authenticated identity.

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 removed at Nginx.
  • The API verifies RS256 signature, issuer, audience, subject, expiry, not-before/issued-at bounds and clock skew before deriving request identity.
  • 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. Mount the IdP RS256 public key and configure exact issuer/audience; coordinate key rotation with an API restart until JWKS rollover is implemented.
  6. Use a CA-issued certificate and a fixed production hostname.
  7. Set a digest-pinned CASAN_CP_API_IMAGE, CASAN_CP_UI_IMAGE, and CASAN_CP_OAUTH2_PROXY_IMAGE.
  8. Keep the API on a private container/network segment with Nginx as its only caller.
  9. Restrict egress from the API to allowlisted model providers, Vault/KMS, object storage and required observability endpoints.
  10. Store state on encrypted storage; send audit heads to KMS/WORM/Object Lock.
  11. Export rate-limit, auth failure, bridge-disabled and audit-chain metrics to alerting.
  12. Back up and restore-test tenant state before enabling write actions.
  13. 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.
  • Static public-key rotation requires a coordinated file replacement and API restart; automated JWKS rollover is not implemented.
  • A compromised ingress container can interfere with availability or bearer forwarding, but cannot mint a valid IdP signature without the IdP key.
  • 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:

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.