15 KiB
StellaOps Console Security Posture
Audience: Security Guild, Console & Authority teams, deployment engineers.
Scope: OIDC/DPoP flows, scope model, session controls, CSP and transport headers, evidence handling, offline posture, and monitoring expectations for the StellaOps Console (Sprint 23).
The console is an Angular SPA fronted by the StellaOps Web gateway. It consumes Authority for identity, Concelier/Excititor for aggregation data, Policy Engine for findings, and Attestor for evidence bundles. This guide captures the security guarantees and required hardening so that the console can ship alongside the Aggregation-Only Contract (AOC) without introducing new attack surface.
1 · Identity & Authentication
1.1 Authorization sequence
- Browser→Authority uses OAuth 2.1 Authorization Code + PKCE (
S256). - Upon code exchange the console requests a DPoP-bound access token (
aud=console,tenant=<id>) with 120 s TTL and optional rotating refresh token (rotate=true). - Authority includes
cnf.jktfor the ephemeral WebCrypto keypair; console stores the private key in IndexedDB (non-exportable) and keeps the public JWK in memory. - All API calls attach
Authorization: Bearer <token>+DPoPproof header. Nonces from the gateway are replay-protected (dpopt-nonceheader). - Tenanted API calls flow through the Web gateway which forwards
X-Stella-Tenantand enforces tenancy headers. Missing or mismatched tenants trigger403withERR_TENANT_MISMATCH.
1.2 Fresh-auth gating
- Sensitive actions (tenant edits, token revocation, policy promote, signing key rotation) call
Authority /fresh-authusingprompt=login+max_age=300. - Successful fresh-auth yields a 300 s scoped token (
fresh_auth=true) stored only in memory; the UI disables guarded buttons when the timer expires. - Audit events:
authority.fresh_auth.start,authority.fresh_auth.success,authority.fresh_auth.expired(link to correlation IDs for the gated action).
1.3 Offline & sealed mode
- When
console.offlineMode=truethe console presents an offline banner and suppresses fresh-auth prompts, replacing them with CLI guidance (stella auth fresh-auth --offline). - Offline mode requires pre-issued tenant-scoped tokens bundled with the Offline Kit; tokens must include
offline=trueclaim and 15 m TTL. - Authority availability health is polled via
/api/console/status. HTTP failures raise the offline banner and switch to read-only behaviour.
2 · Session & Device Binding
- Access and refresh tokens live in memory; metadata (subject, tenant, expiry) persists in
sessionStoragefor reload continuity. Never store raw JWTs inlocalStorage. - Inactivity timeout defaults to 15 minutes. Idle sessions trigger silent refresh; on failure the UI shows a modal requiring re-auth.
- Tokens are device-bound through DPoP; if a new device logs in, Authority revokes the previous DPoP key and emits
authority.token.binding_changed. - CSRF mitigations: bearer tokens plus DPoP remove cookie reliance. If cookies are required (e.g., same-origin analytics) they must be
HttpOnly,SameSite=Lax,Secure. - Browser hardening: enforce
Strict-Transport-Security,X-Content-Type-Options: nosniff,Referrer-Policy: no-referrer,Permissions-Policy: camera=(), microphone=(), geolocation=().
3 · Authorization & Scope Model
The console client is registered in Authority as console-ui with scopes:
| Feature area | Required scopes | Notes |
|---|---|---|
| Base navigation (Dashboard, Findings, SBOM, Runs) | ui.read, findings:read, advisory:read, vex:read, aoc:verify |
findings:read enables Policy Engine overlays; advisory:read/vex:read load ingestion panes; aoc:verify allows on-demand guard runs. |
| Admin workspace | ui.admin, authority:tenants.read, authority:tenants.write, authority:roles.read, authority:roles.write, authority:tokens.read, authority:tokens.revoke, authority:clients.read, authority:clients.write, authority:audit.read |
Scope combinations are tenant constrained. Role changes require fresh-auth. |
| Policy approvals | policy:read, policy:review, policy:approve, policy:operate, policy:simulate |
policy:operate (promote/activate/run) requires fresh-auth. |
| Observability panes (status ticker, telemetry) | ui.telemetry, scheduler:runs.read, advisory:read, vex:read |
ui.telemetry drives OTLP export toggles. |
| Orchestrator dashboard (queues, workers, rate limits) | orch:read |
Provision via Orch.Viewer role; read-only access to job state and telemetry. |
| Orchestrator control actions (pause/resume, retry, sync-now, backfill) | orch:operate (plus orch:read) |
CLI/Console must request tokens with operator_reason and operator_ticket; Authority denies issuance when either value is missing. |
| Downloads parity (SBOM, attestation) | downloads:read, attestation:verify, sbom:export |
Console surfaces digests only; download links require CLI parity for write operations. |
Guidance:
- Role mapping: Provision Authority role
role/ui-console-adminencapsulating the admin scopes above. - Orchestrator viewers: Assign Authority role
role/orch-viewer(Authority role stringOrch.Viewer) to consoles that require read-only access to Orchestrator telemetry. - Orchestrator operators: Assign Authority role
role/orch-operator(Authority role stringOrch.Operator) to identities allowed to pause/resume or backfill. Tokens must includeoperator_reason(≤256 chars) andoperator_ticket(≤128 chars); Authority records the values in audit logs. - Tenant enforcement: Gateway injects
X-Stella-Tenantfrom token claims. Requests missing the header must be rejected by downstream services (Concelier, Excititor, Policy Engine) and logged. - Separation of duties: Never grant
ui.adminandpolicy:approve/policy:operateto the same human role without SOC sign-off; automation accounts should use least-privilege dedicated clients.
3.1 Console Authority endpoints
Console uses dedicated Authority endpoints scoped under /console/*. All requests must include the tenant header injected by the gateway (X-Stella-Tenant); calls without the header fail with tenant_header_missing and emit a structured audit event. Keep reverse proxies configured to pass the header end-to-end.
| Endpoint | Required scopes | Purpose | Notes |
|---|---|---|---|
GET /console/tenants |
authority:tenants.read |
Returns the tenant catalogue for the authenticated principal. | Validates X-Stella-Tenant; rejects tenants not configured in Authority. |
GET /console/profile |
ui.read |
Surfaces subject metadata (roles, scopes, session id, fresh-auth state). | Response includes freshAuth (bool) based on a 300 s window since auth_time. |
POST /console/token/introspect |
ui.read |
Introspects the access token currently in use and reports expiry + tenant. | Console polls this endpoint to drive session inactivity prompts; intended for SPA usage via fetch POST. |
Fresh-auth & session inactivity: Authority stamps auth_time on issued tokens and considers privileged actions “fresh” for five minutes. When /console/profile returns freshAuth: false, the UI must require an interactive re-authentication before allowing admin operations (ui.admin, authority:* mutations, policy:activate, exceptions:approve). Access tokens remain short-lived (00:02:00 by default); pair this with Console session timeouts so idle dashboards prompt the user before two minutes of inactivity.
DPoP + tenant binding: All /console/* endpoints require DPoP-bound access tokens. Audit events include tenant.resolved, scope, correlationId, and (when applicable) token.expires_at. Staple the same headers into downstream services so cross-component troubleshooting uses the same correlation identifiers.
4 · Transport, CSP & Browser Hardening
4.1 Gateway requirements
- TLS 1.2+ with modern cipher suites; enable HTTP/2 for SSE streams.
- Terminate TLS at the reverse proxy (Traefik, NGINX) and forward
X-Forwarded-*headers (ASPNETCORE_FORWARDEDHEADERS_ENABLED=true). - Rate-limit
/authorizeand/tokenaccording to Authority rate-limit guidance.
4.2 Content Security Policy
Default CSP served by the console container:
default-src 'self';
connect-src 'self' https://*.stella-ops.local;
img-src 'self' data:;
script-src 'self';
style-src 'self' 'unsafe-inline';
font-src 'self';
frame-ancestors 'none';
Recommendations:
- Extend
connect-srconly for known internal APIs (e.g., telemetry collector). Useconsole.config.cspOverridesinstead of editing NGINX directly. - Enable COOP/COEP (
Cross-Origin-Opener-Policy: same-origin,Cross-Origin-Embedder-Policy: require-corp) to support WASM policy previews. - Use Subresource Integrity (SRI) hashes when adding third-party fonts or scripts.
- For embedded screenshots/GIFs sourced from Offline Kit, use
img-src 'self' data: blob:and verify assets during build. - Enforce
X-Frame-Options: DENY,X-XSS-Protection: 0, andCache-Control: no-storeon JSON API responses (HTML assets remain cacheable).
4.3 SSE & WebSocket hygiene
- SSE endpoints (
/console/status/stream,/console/runs/{id}/events) must setCache-Control: no-storeand disable proxy buffering. - Gate SSE behind the same DPoP tokens; reject without
Authorization. - Proxy timeouts ≥ 60 s to avoid disconnect storms; clients use exponential backoff with jitter.
5 · Evidence & Data Handling
- Evidence bundles: Download links trigger
attestor.verifyordownloads.manifestAPIs. The UI never caches bundle contents; it only surfaces SHA-256 digests and cosign signatures. Operators must use CLI to fetch the signed artefact. - Secrets: UI redacts tokens, emails, and attachment paths in logs. Structured logs include only
subject,tenant,action,correlationId. - Aggregation data: Console honours Aggregation-Only contract—no client-side rewriting of Concelier/Excititor precedence. Provenance badges display source IDs and merge-event hashes.
- PII minimisation: User lists show minimal identity (display name, email hash). Full email addresses require
ui.admin+ fresh-auth. - Downloads parity: Every downloadable artefact includes a CLI parity link (e.g.,
stella downloads fetch --artifact <id>). If CLI parity fails, the console displays a warning banner and links to troubleshooting docs.
6 · Logging, Monitoring & Alerts
- Structured logs:
ui.action,tenantId,subject,scope,correlationId,dpop.jkt. Log levelInformationfor key actions;Warningfor security anomalies (failed DPoP, tenant mismatch). - Metrics (Prometheus):
ui_request_duration_seconds,ui_dpop_failure_total,ui_fresh_auth_prompt_total,ui_tenant_switch_total,ui_offline_banner_seconds. - Alerts:
- Fresh-auth failures > 5 per minute per tenant → security review.
- DPoP mismatches sustained > 1 % of requests → potential replay attempt.
- Tenant mismatches > 0 triggers an audit incident (could indicate scope misconfiguration).
- Correlate with Authority audit events (
authority.scope.granted,authority.token.revoked) and Concelier/Excititor ingestion logs to trace user impact.
7 · Offline & Air-Gapped Posture
- Offline deployments require mirrored container images and Offline Kit manifest verification (see
/docs/deploy/console.md§7). - Console reads
offlineManifest.jsonat boot to validate asset digests; mismatches block startup until the manifest is refreshed. - Tenant and role edits queue change manifests for export; UI instructs operators to run
stella auth apply --bundle <file>on the offline Authority host.
– Evidence viewing remains read-only; download buttons provide scripts to export from local Attestor snapshots. - Fresh-auth prompts display instructions for hardware-token usage on bastion hosts; system logs mark actions executed under offline fallback.
8 · Threat Model Alignment
| Threat (Authority TM §5) | Console control |
|---|---|
| Spoofed revocation bundle | Console verifies manifest signatures before showing revocation status; links to stella auth revoke verify. |
Parameter tampering on /token |
PKCE + DPoP enforced; console propagates correlation IDs so Authority logs can link anomalies. |
| Bootstrap invite replay | Admin UI surfaces invite status with expiry; fresh-auth required before issuing new invites. |
| Token replay by stolen agent | DPoP binding prevents reuse; console surfaces revocation latency warnings sourced from Zastava metrics. |
| Offline bundle tampering | Console refuses unsigned Offline Kit assets; prompts operators to re-import verified bundles. |
| Privilege escalation via plug-in overrides | Plug-in manifest viewer warns when a plug-in downgrades password policy; UI restricts plug-in activation to fresh-auth + ui.admin scoped users. |
Document gaps and remediation hooks in SEC5.* backlog as they are addressed.
9 · Compliance checklist
- Authority client
console-uiregistered with PKCE, DPoP, tenant claim requirement, and scopes from §3. (see console security sign-off) - CSP enforced per §4 with overrides documented in deployment manifests. (see console security sign-off)
- Fresh-auth timer (300 s) validated for admin and policy actions; audit events captured. (see console security sign-off)
- DPoP binding tested (replay attempt blocked; logs show
ui_dpop_failure_totalincrement). (see console security sign-off) - Offline mode exercises performed (banner, CLI guidance, manifest verification). (see console security sign-off)
- Evidence download parity verified with CLI scripts; console never caches sensitive artefacts. (see console security sign-off)
- Monitoring dashboards show metrics and alerts outlined in §6; alert runbooks reviewed with Security Guild. (see console security sign-off)
- Security review sign-off recorded in sprint log with links to Authority threat model references. (see console security sign-off)
/consoleAuthority endpoints validated for tenant header enforcement, fresh-auth prompts, and introspection flows (Audit IDsauthority.console.tenants.read,authority.console.profile.read,authority.console.token.introspect). (see console security sign-off)
Last updated: 2025-10-31 (Sprint 23).