# Cerberus Technical Documentation This document is for developers and advanced users who want to understand how Cerberus works under the hood. **Looking for the user guide?** See [Security Features](security.md) instead. --- ## What Is Cerberus? Cerberus is the optional security suite built into Charon. It includes: - **WAF (Web Application Firewall)** — Inspects requests for malicious payloads - **CrowdSec** — Blocks IPs based on behavior and reputation - **Access Lists** — Static allow/deny rules (IP, CIDR, geo) - **Rate Limiting** — Volume-based abuse prevention (placeholder) All components are disabled by default and can be enabled independently. --- ## Architecture ### Request Flow When a request hits Charon: 1. **Check if Cerberus is enabled** (global setting + dynamic database flag) 2. **WAF evaluation** (if `waf_mode != disabled`) - Increment `charon_waf_requests_total` metric - Check payload against loaded rulesets - If suspicious: - `block` mode: Return 403 + increment `charon_waf_blocked_total` - `monitor` mode: Log + increment `charon_waf_monitored_total` 3. **ACL evaluation** (if enabled) - Test client IP against active access lists - First denial = 403 response 4. **CrowdSec check** (placeholder for future) 5. **Rate limit check** (placeholder for future) 6. **Pass to downstream handler** (if not blocked) ### Middleware Integration Cerberus runs as Gin middleware on all `/api/v1` routes: ```go r.Use(cerberusMiddleware.RequestLogger()) ``` This means it protects the management API but does not directly inspect traffic to proxied websites (that happens in Caddy). --- ## Threat Model & Protection Coverage ### What Cerberus Protects | Threat Category | CrowdSec | ACL | WAF | Rate Limit | |-----------------|----------|-----|-----|------------| | Known attackers (IP reputation) | ✅ | ❌ | ❌ | ❌ | | Geo-based attacks | ❌ | ✅ | ❌ | ❌ | | SQL Injection (SQLi) | ❌ | ❌ | ✅ | ❌ | | Cross-Site Scripting (XSS) | ❌ | ❌ | ✅ | ❌ | | Remote Code Execution (RCE) | ❌ | ❌ | ✅ | ❌ | | **Zero-Day Web Exploits** | ⚠️ | ❌ | ✅ | ❌ | | DDoS / Volume attacks | ❌ | ❌ | ❌ | ✅ | | Brute-force login attempts | ✅ | ❌ | ❌ | ✅ | | Credential stuffing | ✅ | ❌ | ❌ | ✅ | **Legend:** - ✅ Full protection - ⚠️ Partial protection (time-delayed) - ❌ Not designed for this threat ## Zero-Day Exploit Protection (WAF) The WAF provides **pattern-based detection** for zero-day exploits: **How It Works:** 1. Attacker discovers new vulnerability (e.g., SQLi in your login form) 2. Attacker crafts exploit: `' OR 1=1--` 3. WAF inspects request → matches SQL injection pattern → **BLOCKED** 4. Your application never sees the malicious input **Limitations:** - Only protects HTTP/HTTPS traffic - Cannot detect completely novel attack patterns (rare) - Does not protect against logic bugs in application code **Effectiveness:** - **~90% of zero-day web exploits** use known patterns (SQLi, XSS, RCE) - **~10% are truly novel** and may bypass WAF until rules are updated ## Request Processing Pipeline ``` 1. [CrowdSec] Check IP reputation → Block if known attacker 2. [ACL] Check IP/Geo rules → Block if not allowed 3. [WAF] Inspect request payload → Block if malicious pattern 4. [Rate Limit] Count requests → Block if too many 5. [Proxy] Forward to upstream service ``` ## Configuration Model ### Database Schema **SecurityConfig** table: ```go type SecurityConfig struct { ID uint `gorm:"primaryKey"` Name string `json:"name"` Enabled bool `json:"enabled"` AdminWhitelist string `json:"admin_whitelist"` // CSV of IPs/CIDRs CrowdsecMode string `json:"crowdsec_mode"` // disabled, local, external CrowdsecAPIURL string `json:"crowdsec_api_url"` CrowdsecAPIKey string `json:"crowdsec_api_key"` WafMode string `json:"waf_mode"` // disabled, monitor, block WafRulesSource string `json:"waf_rules_source"` // Ruleset identifier WafLearning bool `json:"waf_learning"` RateLimitEnable bool `json:"rate_limit_enable"` RateLimitBurst int `json:"rate_limit_burst"` RateLimitRequests int `json:"rate_limit_requests"` RateLimitWindowSec int `json:"rate_limit_window_sec"` } ``` ### Environment Variables (Fallbacks) If no database config exists, Charon reads from environment: - `CERBERUS_SECURITY_WAF_MODE` — `disabled` | `monitor` | `block` - `CERBERUS_SECURITY_CROWDSEC_MODE` — `disabled` | `local` | `external` - `CERBERUS_SECURITY_CROWDSEC_API_URL` — URL for external CrowdSec bouncer - `CERBERUS_SECURITY_CROWDSEC_API_KEY` — API key for external bouncer - `CERBERUS_SECURITY_ACL_ENABLED` — `true` | `false` - `CERBERUS_SECURITY_RATELIMIT_ENABLED` — `true` | `false` --- ## WAF (Web Application Firewall) ### Current Implementation **Status:** Prototype with placeholder detection The current WAF checks for `", "ip": "203.0.113.50" } ``` Use these for dashboard creation and alerting. --- ## Access Control Lists (ACLs) ### How They Work Each `AccessList` defines: - **Type:** `whitelist` | `blacklist` | `geo_whitelist` | `geo_blacklist` | `local_only` - **IPs:** Comma-separated IPs or CIDR blocks - **Countries:** Comma-separated ISO country codes (US, GB, FR, etc.) **Evaluation logic:** - **Whitelist:** If IP matches list → allow; else → deny - **Blacklist:** If IP matches list → deny; else → allow - **Geo Whitelist:** If country matches → allow; else → deny - **Geo Blacklist:** If country matches → deny; else → allow - **Local Only:** If RFC1918 private IP → allow; else → deny Multiple ACLs can be assigned to a proxy host. The first denial wins. ### GeoIP Database Uses MaxMind GeoLite2-Country database: - Path configured via `CHARON_GEOIP_DB_PATH` - Default: `/app/data/GeoLite2-Country.mmdb` (Docker) - Update monthly from MaxMind for accuracy --- ## CrowdSec Integration ### Current Status **Placeholder.** Configuration models exist but bouncer integration is not yet implemented. ### Planned Implementation **Local mode:** - Run CrowdSec agent inside Charon container - Parse logs from Caddy - Make decisions locally **External mode:** - Connect to existing CrowdSec bouncer via API - Query IP reputation before allowing requests --- ## Security Decisions The `SecurityDecision` table logs all security actions: ```go type SecurityDecision struct { ID uint `gorm:"primaryKey"` Source string `json:"source"` // waf, crowdsec, acl, ratelimit, manual IPAddress string `json:"ip_address"` Action string `json:"action"` // allow, block, challenge Reason string `json:"reason"` Timestamp time.Time `json:"timestamp"` } ``` **Use cases:** - Audit trail for compliance - UI visibility into recent blocks - Manual override tracking --- ## Self-Lockout Prevention ### Admin Whitelist **Purpose:** Prevent admins from blocking themselves **Implementation:** - Stored in `SecurityConfig.admin_whitelist` as CSV - Checked before applying any block decision - If requesting IP matches whitelist → always allow **Recommendation:** Add your VPN IP, Tailscale IP, or home network before enabling Cerberus. ### Break-Glass Token **Purpose:** Emergency disable when locked out **How it works:** 1. Generate via `POST /api/v1/security/breakglass/generate` 2. Returns one-time token (plaintext, never stored hashed) 3. Token can be used in `POST /api/v1/security/disable` to turn off Cerberus 4. Token expires after first use **Storage:** Tokens are hashed in database using bcrypt. ### Localhost Bypass Requests from `127.0.0.1` or `::1` may bypass security checks (configurable). Allows local management access even when locked out. --- ## API Reference ### Status ```http GET /api/v1/security/status ``` Returns: ```json { "enabled": true, "waf_mode": "monitor", "crowdsec_mode": "local", "acl_enabled": true, "ratelimit_enabled": false } ``` ### Enable Cerberus ```http POST /api/v1/security/enable Content-Type: application/json { "admin_whitelist": "198.51.100.10,203.0.113.0/24" } ``` Requires either: - `admin_whitelist` with at least one IP/CIDR - OR valid break-glass token in header ### Disable Cerberus ```http POST /api/v1/security/disable ``` Requires either: - Request from localhost - OR valid break-glass token in header ### Get/Update Config ```http GET /api/v1/security/config POST /api/v1/security/config ``` See SecurityConfig schema above. ### Rulesets ```http GET /api/v1/security/rulesets POST /api/v1/security/rulesets DELETE /api/v1/security/rulesets/:id ``` ### Decisions (Audit Log) ```http GET /api/v1/security/decisions?limit=50 POST /api/v1/security/decisions # Manual override ``` --- ## Testing ### Integration Test Run the Coraza integration test: ```bash bash scripts/coraza_integration.sh ``` Or via Go: ```bash cd backend go test -tags=integration ./integration -run TestCorazaIntegration -v ``` ### Manual Testing 1. Enable WAF in `monitor` mode 2. Send request with `