Files
doorman/user-docs/03-security.md
2025-12-10 22:14:34 -05:00

16 KiB
Raw Permalink Blame History

Security Guide

Comprehensive security features and best practices for Doorman API Gateway.

Overview

Doorman implements defense-in-depth security with multiple layers:

  • Transport Layer Security (HTTPS/TLS)
  • Authentication and authorization (JWT, RBAC)
  • CSRF protection
  • CORS policies (platform and per-API)
  • IP access control (global and per-API)
  • Request validation and size limits
  • Security headers
  • Audit logging and request tracking
  • Encryption at rest

Transport Layer Security

HTTPS Enforcement

Production Guard: When ENV=production, Doorman refuses to start unless HTTPS is configured.

# TLS terminated at reverse proxy (Nginx, Traefik, Caddy, ALB, etc.)
HTTPS_ONLY=true  # Enforces secure cookies and CSRF validation

Security behaviors when HTTPS is enabled:

  • Cookies marked with Secure flag (sent only over HTTPS)
  • CSRF validation enforced
  • HSTS headers sent automatically
  • CSP and security headers applied

HTTP Strict Transport Security (HSTS)

Auto-enabled when HTTPS_ONLY=true:

Strict-Transport-Security: max-age=15552000; includeSubDomains; preload

This header tells browsers to:

  • Only access the site over HTTPS for the next 6 months
  • Apply the policy to all subdomains
  • Allow preloading into browser HSTS lists

Authentication and Authorization

Doorman uses HttpOnly cookies to store JWT tokens, preventing XSS attacks:

  • Access token: Short-lived (default 30 minutes)
  • Refresh token: Longer-lived (default 7 days)
  • Both stored in HttpOnly cookies (not accessible via JavaScript)

Token Configuration:

JWT_SECRET_KEY=strong-random-secret-32chars+
AUTH_EXPIRE_TIME=30
AUTH_EXPIRE_TIME_FREQ=minutes
AUTH_REFRESH_EXPIRE_TIME=7
AUTH_REFRESH_EXPIRE_FREQ=days

Token Revocation:

  • Tokens can be revoked per-user or per-JTI
  • Revocation list stored in Redis (if enabled) or in-memory
  • Database-backed revocation for memory-only mode
  • Use /platform/authorization/invalidate to revoke tokens

Role-Based Access Control (RBAC)

Users are assigned roles that grant specific permissions:

Default roles:

  • admin: Full platform access
  • user: Basic gateway access

Permission examples:

  • manage_apis: Create/update/delete APIs
  • manage_endpoints: Manage API endpoints
  • manage_users: User administration
  • view_logs: Access audit logs
  • manage_security: Security settings and IP policies

Groups and Subscriptions

  • Users can belong to groups
  • APIs can restrict access to specific groups
  • Users must subscribe to an API before calling it through the gateway
  • Fine-grained access control per API

CSRF Protection

Doorman uses the double-submit cookie pattern for CSRF protection.

Automatic activation: CSRF is enforced when HTTPS_ONLY=true

How it works:

  1. Server sets csrf_token cookie on login (not HttpOnly)
  2. Client reads the cookie value
  3. Client includes value in X-CSRF-Token header on requests
  4. Server validates header matches cookie

Example request:

curl -b cookies.txt \
  -H "X-CSRF-Token: abc123..." \
  -H "Content-Type: application/json" \
  -d '{"api_name": "demo"}' \
  https://api.example.com/platform/api

Protected endpoints:

  • All mutating operations (POST, PUT, DELETE, PATCH)
  • Platform configuration endpoints
  • User management

Bypassed endpoints:

  • Public read-only endpoints
  • Login/refresh (bootstrap CSRF token)

CORS Security

Doorman provides two levels of CORS control:

Platform-Level CORS

Controls access to /platform/* routes (admin/config APIs):

ALLOWED_ORIGINS=https://admin.example.com,https://app.example.com
ALLOW_CREDENTIALS=True
ALLOW_METHODS=GET,POST,PUT,DELETE,OPTIONS
ALLOW_HEADERS=Content-Type,X-CSRF-Token,Authorization
CORS_STRICT=true  # Reject wildcard when credentials enabled

Security best practices:

  • Never use ALLOWED_ORIGINS=* with ALLOW_CREDENTIALS=True
  • Set CORS_STRICT=true in production
  • Explicitly list only necessary origins
  • Limit methods and headers to minimum required

Per-API CORS

Each gateway API can define its own CORS policy via API configuration:

{
  "api_cors_allow_origins": ["https://app.example.com"],
  "api_cors_allow_methods": ["GET", "POST"],
  "api_cors_allow_headers": ["Content-Type", "Authorization"],
  "api_cors_allow_credentials": false,
  "api_cors_expose_headers": ["X-Request-ID", "X-RateLimit-Remaining"]
}

Preflight validation:

  • Origin must be in api_cors_allow_origins
  • Method must be in api_cors_allow_methods
  • Headers must be in api_cors_allow_headers

IP Access Control

Doorman provides global (platform-wide) and per-API IP filtering.

Global IP Policy

Configure via Security Settings UI or API:

{
  "ip_whitelist": ["10.0.0.0/8", "192.168.1.100"],
  "ip_blacklist": ["1.2.3.4", "5.6.7.0/24"],
  "trust_x_forwarded_for": true,
  "xff_trusted_proxies": ["10.0.1.10", "10.0.1.11"]
}

Precedence:

  1. Blacklist is always evaluated first if the client IP matches ip_blacklist, access is denied.
  2. Whitelist is evaluated next (if non-empty) when a global whitelist is configured, any IP not in ip_whitelist is denied.

Client IP detection:

  • Direct connection: Socket IP used
  • Behind proxy: Use X-Forwarded-For, X-Real-IP, or CF-Connecting-IP headers
  • Trust validation: Headers only used if source IP matches xff_trusted_proxies

Localhost bypass:

LOCAL_HOST_IP_BYPASS=true  # Allow 127.0.0.1/::1 to bypass (dev only!)

Set to false in production to prevent localhost abuse.

Per-API IP Policy

Each API can define additional IP restrictions:

{
  "api_ip_mode": "whitelist",           // "allow_all" (default) or "whitelist"
  "api_ip_whitelist": ["203.0.113.0/24"],
  "api_ip_blacklist": ["203.0.113.50"],  // Always enforced, regardless of mode
  "api_trust_x_forwarded_for": true
}

Evaluation logic:

  • api_ip_blacklist is always applied first. If the effective client IP matches, the request is denied.
  • If api_ip_mode == "whitelist" then api_ip_whitelist is enforced: the effective IP must be in the whitelist or the request is denied.
  • If api_ip_mode == "allow_all", the per-API whitelist is ignored, but the blacklist still applies.

Use cases:

  • Restrict internal APIs to VPN range
  • Block abusive IPs per API
  • Geo-restriction via proxy headers

Audit trail: All IP denials logged with:

  • Reason (blacklisted, not_in_whitelist)
  • Source IP and effective IP
  • XFF header value if present

Security Headers

Doorman automatically applies security headers to all responses.

Content Security Policy (CSP)

Default policy (restrictive baseline):

default-src 'none';
frame-ancestors 'none';
base-uri 'none';
form-action 'self';
img-src 'self' data:;
connect-src 'self';

Override:

CONTENT_SECURITY_POLICY="default-src 'self'; script-src 'self' 'unsafe-inline';"

Additional Headers

Applied to every response:

Header Value Purpose
X-Content-Type-Options nosniff Prevent MIME sniffing
X-Frame-Options DENY Prevent clickjacking
Referrer-Policy no-referrer Prevent referrer leakage
Permissions-Policy geolocation=(), microphone=(), camera=() Restrict browser features
Strict-Transport-Security max-age=15552000; includeSubDomains Force HTTPS (when enabled)

Request Validation and Limits

Body Size Limits

Protect against DoS via large payloads:

# Global default (1MB)
MAX_BODY_SIZE_BYTES=1048576

# Per-API-type overrides
MAX_BODY_SIZE_BYTES_REST=1048576        # REST: 1MB
MAX_BODY_SIZE_BYTES_SOAP=2097152        # SOAP: 2MB (larger XML envelopes)
MAX_BODY_SIZE_BYTES_GRAPHQL=524288      # GraphQL: 512KB (queries are smaller)
MAX_BODY_SIZE_BYTES_GRPC=1048576        # gRPC: 1MB

# Multipart uploads (proto files, etc.)
MAX_MULTIPART_SIZE_BYTES=10485760       # 10MB

Enforcement:

  • Checks Content-Length header before reading body
  • Returns 413 Payload Too Large with error code REQ001
  • Logs violation to audit trail
  • Applied universally to all routes

Endpoint-Level Validation

Attach JSON schema validation to endpoints:

{
  "endpoint_id": "abc123",
  "validation_enabled": true,
  "validation_schema": {
    "user.name": {"required": true, "type": "string", "min": 2},
    "user.email": {"required": true, "type": "string", "format": "email"}
  }
}

Benefits:

  • Requests failing validation return 400 without hitting upstream
  • Reduces load on backend services
  • Provides consistent error messages

Audit Trail and Logging

Request ID Propagation

Every request gets a unique ID for tracing:

Headers accepted:

  • X-Request-ID (preferred)
  • Request-ID

Headers returned:

  • X-Request-ID
  • request_id

Usage:

  • All logs include request ID
  • Search logs by request ID for full trace
  • Pass to upstream services for distributed tracing

Audit Trail

Separate audit log: doorman-trail.log

Logged events:

  • User authentication (login, logout, refresh)
  • Authorization changes (role/group assignments)
  • IP policy violations
  • Security configuration changes
  • API/endpoint creation/deletion
  • Token revocation

Log structure:

{
  "timestamp": "2024-01-15T10:30:00Z",
  "request_id": "abc123",
  "username": "admin",
  "action": "api.create",
  "target": "orders/v1",
  "status": "success",
  "client_ip": "192.168.1.100",
  "effective_ip": "203.0.113.50",
  "details": {"api_type": "REST"}
}

Log Redaction

Sensitive data automatically redacted:

Patterns redacted:

  • Authorization: Bearer [REDACTED]
  • "access_token": "[REDACTED]"
  • "refresh_token": "[REDACTED]"
  • "password": "[REDACTED]"
  • Cookie: [REDACTED]
  • X-CSRF-Token: [REDACTED]

Applied to:

  • File logs
  • Console output
  • Audit logs

Encryption and Secrets

API Key Encryption

Encrypt API keys at rest:

TOKEN_ENCRYPTION_KEY=your-strong-encryption-key-32chars+

Transparent encryption:

  • Encrypt when storing API keys in database/memory
  • Decrypt when injecting into requests
  • Uses Fernet symmetric encryption

Memory Dump Encryption

Required for memory-only mode dumps:

MEM_ENCRYPTION_KEY=your-memory-dump-encryption-key-32chars+
MEM_DUMP_PATH=backend-services/generated/memory_dump.bin

Security:

  • AES encryption via Fernet
  • Dumps written on shutdown or manual save
  • Restored automatically on startup

JWT Secret

Critical: This key signs all access tokens.

JWT_SECRET_KEY=strong-random-secret-change-this-in-production

Best practices:

  • Use at least 32 random characters
  • Store in secret manager (Vault, AWS Secrets Manager, etc.)
  • Rotate periodically (requires token invalidation)
  • Never commit to version control

Security Best Practices

Production Checklist

  • Set ENV=production
  • Enable HTTPS (HTTPS_ONLY=true)
  • Use valid TLS certificates (not self-signed)
  • Change JWT_SECRET_KEY from default
  • Set strong TOKEN_ENCRYPTION_KEY and MEM_ENCRYPTION_KEY
  • Configure explicit ALLOWED_ORIGINS (no wildcard)
  • Set CORS_STRICT=true
  • Configure COOKIE_DOMAIN to match your domain
  • Set LOCAL_HOST_IP_BYPASS=false
  • Enable LOG_FORMAT=json for structured logs
  • Configure IP whitelisting/blacklisting as needed
  • Set appropriate MAX_BODY_SIZE_BYTES limits
  • Enable Redis for distributed rate limiting
  • Enable MongoDB for persistence
  • Review and test CSRF protection
  • Configure trusted proxy IPs if behind load balancer
  • Rotate admin password after initial setup
  • Create least-privilege users for operations
  • Enable audit log monitoring

Development vs Production

Feature Development Production
HTTPS Required Optional Required
CSRF Validation Optional Enabled
CORS Permissive (*) Strict whitelist
Cookie Secure Flag No Yes
HSTS Header No Yes
Log Format Plain JSON
IP Bypass May enable Disabled
Secrets Simple Strong random

Secrets Management

Never commit secrets to git:

# Add to .gitignore
.env
.env.*
!.env.example

Use secret managers in production:

  • AWS Secrets Manager
  • HashiCorp Vault
  • Azure Key Vault
  • GCP Secret Manager

Rotate secrets regularly:

  • JWT secret: Every 90 days
  • Encryption keys: Every 180 days
  • Admin passwords: Every 60 days

Common Security Scenarios

Scenario 1: API for Internal Services Only

Goal: Restrict API to internal VPN range

{
  "api_name": "internal-orders",
  "api_ip_mode": "whitelist",
  "api_ip_whitelist": ["10.0.0.0/8", "192.168.0.0/16"],
  "api_trust_x_forwarded_for": false
}

Scenario 2: API with Geographic Restrictions

Goal: Only allow traffic from specific countries (using Cloudflare)

{
  "trust_x_forwarded_for": true,
  "xff_trusted_proxies": ["173.245.48.0/20", "103.21.244.0/22"],  # Cloudflare IPs
}

Then use Cloudflare Firewall Rules or per-API IP lists based on CF-IPCountry header.

Scenario 3: API for Mobile App

Goal: Prevent CSRF, allow CORS from app origin

{
  "api_cors_allow_origins": ["https://app.example.com"],
  "api_cors_allow_credentials": false,  # Use Bearer tokens, not cookies
  "api_cors_allow_methods": ["GET", "POST", "PUT", "DELETE"],
  "api_cors_allow_headers": ["Content-Type", "Authorization"]
}

Use Authorization: Bearer <token> header instead of cookies to avoid CSRF concerns.

Scenario 4: High-Security Admin API

Goal: Maximum protection for sensitive operations

# Environment
HTTPS_ONLY=true
CORS_STRICT=true
ALLOWED_ORIGINS=https://admin.example.com  # Single origin only
ALLOW_CREDENTIALS=True

# IP whitelist
{
  "ip_whitelist": ["203.0.113.0/24"],  # Office IP range only
  "trust_x_forwarded_for": false       # Direct connection required
}

Security Testing

Doorman includes comprehensive security test coverage:

Test suites:

  • backend-services/tests/test_auth_csrf_https.py - CSRF validation
  • backend-services/tests/test_production_https_guard.py - HTTPS enforcement
  • backend-services/tests/test_ip_policy_allow_deny_cidr.py - IP filtering
  • backend-services/tests/test_security.py - General security features
  • backend-services/tests/test_request_id_and_logging_redaction.py - Audit trail

Run security tests:

cd backend-services
pytest backend-services/tests/test_auth_csrf_https.py -v
pytest backend-services/tests/test_production_https_guard.py -v
pytest backend-services/tests/test_ip_policy_allow_deny_cidr.py -v

Incident Response

Suspected Token Compromise

  1. Immediately revoke affected tokens:

    POST /platform/authorization/invalidate
    {"username": "affected-user"}
    
  2. Rotate JWT secret (requires restart):

    # Update JWT_SECRET_KEY in .env
    python doorman.py stop
    python doorman.py start
    
  3. Audit logs for suspicious activity:

    grep "affected-user" logs/doorman-trail.log
    
  4. Force password reset for affected users

Suspected IP Spoofing

  1. Verify xff_trusted_proxies is configured correctly

  2. Check trust_x_forwarded_for setting

  3. Review audit logs for anomalous IPs:

    grep "ip.global_deny" logs/doorman-trail.log
    
  4. Add malicious IPs to global blacklist

Elevated Error Rates

  1. Check health endpoints:

    GET /platform/monitor/readiness
    
  2. Review logs for errors:

    tail -f logs/doorman.log | grep ERROR
    
  3. Check Redis/MongoDB connectivity

  4. Verify CORS configuration if preflight failures


Compliance and Standards

Doorman addresses common security frameworks:

OWASP Top 10:

  • A01: Broken Access Control - RBAC, IP policies, subscriptions
  • A02: Cryptographic Failures - TLS, encryption at rest
  • A03: Injection - Input validation, request size limits
  • A05: Security Misconfiguration - Secure defaults, startup validation
  • A07: Identification and Auth Failures - JWT, token revocation, CSRF

Industry standards:

  • RFC 6797 (HSTS)
  • RFC 7519 (JWT)
  • CSP Level 3
  • CSRF Double Submit Pattern

References

For security concerns or questions, review the code or open an issue on GitHub.