Skip to main content

Security

This page covers the security practices and requirements for Proxima Console.

Secrets Management

Environment Variables Only

All secrets (database credentials, API keys, tokens) are stored in environment variables. Secrets must never appear in source code, configuration files, or commit history.

.env Files

  • .env is listed in .gitignore and must never be committed.
  • .env.example is committed to the repository with empty placeholder values so developers know which variables are required.
# .env.example
PROXIMA_DB_URL=
PROXIMA_NATS_URL=

API Authentication

Multi-Mode Authentication

The backend supports two authentication modes on all /api/v1/* routes:

  1. JWT Bearer tokens — User sessions with per-client RBAC permissions
  2. X-API-Key header — Service accounts with scoped permissions

See the Authentication page for full details.

JWT Security

  • Access tokens: HS256 signed, 15-minute TTL
  • Refresh tokens: 32 cryptographically random bytes, SHA-256 hashed for storage, 7-day TTL
  • Token rotation on refresh: old refresh token invalidated, new one issued

Password Policy (NIST 800-63B)

  • Minimum 12 characters, no complexity rules
  • 10,000 common password blocklist via go:embed
  • 5-password reuse prevention (bcrypt comparison against history table)

MFA (TOTP)

  • TOTP secrets encrypted at rest with AES-256-GCM (32-byte key via PROXIMA_MFA_ENCRYPTION_KEY)
  • Recovery codes: 8 per user, bcrypt-hashed, single-use
  • MFA challenge tokens: single-use, 5-minute TTL, stored in Valkey

Progressive Lockout

Failed login attempts trigger escalating lockout durations (1min at 5 failures, up to 1h at 20+). State stored in Valkey with in-memory fallback.

Rate Limiting

API endpoints are rate-limited per IP address:

  • General API (/api/v1/*): 1000 requests/min (configurable via PROXIMA_API_RATE_LIMIT)
  • Public auth (login, refresh, forgot-password): 5 requests/min (configurable via PROXIMA_AUTH_RATE_LIMIT)
  • Protected auth routes (sessions, me, mfa) use the general API limit only

Email Enumeration Prevention

  • Login returns the same 401 error for wrong email and wrong password
  • Forgot password always returns 200 regardless of email existence

Google OIDC SSO

When configured (PROXIMA_GOOGLE_CLIENT_ID), users can authenticate with Google Workspace accounts:

  • State parameter: 32 random bytes, SHA-256 hashed, stored in Valkey with single-use GETDEL (5-minute TTL) to prevent CSRF
  • Domain restriction: hd claim enforced server-side in ID token validation (the URL hd hint alone is bypassable)
  • Email verification: only email_verified: true Google accounts are accepted
  • One-time auth code pattern: JWT tokens never appear in redirect URLs, preventing exposure in browser history and referrer headers

See the Authentication — Google OIDC SSO page for the full flow.

Service Account Security

Service accounts use API keys for programmatic access:

  • Keys are generated as 32 cryptographically random bytes, base64url-encoded, prefixed with prxm_
  • Only the SHA-256 hash is stored in the database (and a 12-character prefix for identification)
  • The plaintext key is returned once at creation and cannot be retrieved again
  • Deactivated service accounts are filtered from API key lookups (is_active = TRUE in query)
  • Each service account has a mandatory expires_at date (max 1 year)

Agent Authentication

Each agent receives a unique NATS JWT with permissions scoped to its own subjects. Agents enroll over HTTPS using an install token (single-use by default, with a configurable max_uses for reusable fleet enrollment). The agent's private nkey never leaves the host. Credential renewal proves nkey possession over a single-use, server-issued challenge nonce (the challenge/nonce protocol applies to renewal, not the initial enroll). Compromised hosts can be individually revoked without affecting other agents. See NATS Security for the full architecture.

NATS Transport Security

All NATS connections require mutual TLS 1.2+ with Vault-issued certificates:

  • Server certificates: Issued by Vault PKI (EC P-256) with 30-day max TTL, renewed via scripts/nats-cert-renew.sh
  • CA verification: Agents receive the CA certificate during enrollment and verify the NATS server's identity on every connection
  • Cipher suites: Restricted to ECDHE + AES-GCM (256 and 128-bit) — no CBC, no RSA key exchange
  • Client authentication: NATS verifies the agent's JWT signature against the account's signing key

Audit Logging

All security-relevant actions are recorded in the audit_log table:

  • Authentication events (login success/failure, MFA verification, token refresh)
  • User lifecycle (creation, invitation, activation, password change, MFA enable/disable)
  • Admin operations (role changes, team membership, client assignments)
  • Session management (creation, revocation)

Each audit entry includes: actor ID, action, resource type/ID, details (JSON), IP address, and timestamp. Query via GET /api/v1/audit-log with audit:read permission.

Rules:

  • Never log secrets, tokens, or passwords in audit details
  • Always include the actor's IP address (from X-Forwarded-For or X-Real-IP for proxied requests)
  • Audit log entries are append-only (no updates or deletes)

SQL Injection Prevention

All database queries use parameterized queries via sqlx. String concatenation is never used to build SQL queries.

// Correct -- parameterized query
err := db.GetContext(ctx, &host, "SELECT * FROM hosts WHERE id = $1", hostID)

// Wrong -- string concatenation (SQL injection risk)
query := fmt.Sprintf("SELECT * FROM hosts WHERE id = '%s'", hostID)

This rule applies to all query types: SELECT, INSERT, UPDATE, DELETE.

CORS

Cross-Origin Resource Sharing is configured via the PROXIMA_CORS_ORIGINS environment variable. Only explicitly listed origins are allowed to make requests to the backend API.

# Allow specific origins
PROXIMA_CORS_ORIGINS=http://localhost:5173,https://console.proxima.example.com

File Permissions

Agent Identity Files

The agent creates identity files (agent.id, host_id) on the local filesystem. These files are created with 0600 permissions (owner read/write only) to prevent other users on the system from reading agent identifiers.

os.WriteFile(filepath, data, 0600)

Container Scanning

Trivy in CI

The CI pipeline runs Trivy to scan container images for known vulnerabilities. The scan is configured to block the pipeline on HIGH and CRITICAL severity findings.

# CI stage
container_scan:
script:
- trivy image --severity HIGH,CRITICAL --exit-code 1 $IMAGE

All container images must pass the Trivy scan before they can be deployed.

Systemd Security Hardening

The agent runs as a systemd service with security hardening options enabled to limit its access to the host system:

  • NoNewPrivileges=yes -- Prevents the agent from gaining additional privileges.
  • ProtectSystem=strict -- Mounts the filesystem as read-only except for explicitly allowed paths.
  • ProtectHome=yes -- Prevents access to user home directories.
  • PrivateTmp=yes -- Gives the agent its own private /tmp directory.

These settings limit the blast radius if the agent process is compromised.