Files
Charon/docs/security.md
GitHub Actions a5c86fc588 fix: login page browser warnings and password manager support
- Make COOP header conditional on development mode to suppress HTTP warnings
- Add autocomplete attributes to all email/password inputs for password manager compatibility
- Add comprehensive tests for COOP conditional behavior
- Update security documentation for COOP, HTTPS requirements, and autocomplete

Fixes browser console warnings and improves UX by enabling password managers.
All quality gates passed: 85.7% backend coverage, 86.46% frontend coverage,
zero security issues, all pre-commit hooks passed.

Changes:
- Backend: backend/internal/api/middleware/security.go
- Frontend: Login, Setup, Account, AcceptInvite, SMTPSettings pages
- Tests: Added 4 new test cases (2 backend, 2 frontend)
- Docs: Updated security.md, getting-started.md, README.md
2025-12-21 23:46:25 +00:00

978 lines
29 KiB
Markdown

---
title: Security Features
description: Comprehensive security documentation for Charon's Cerberus security suite including CrowdSec, WAF, and access control lists.
---
## Security Features
Charon includes **Cerberus**, a security system that protects your websites. It's **enabled by default** so your sites are protected from the start.
You can disable it in **System Settings → Optional Features** if you don't need it, or configure it using this guide. The sidebar now shows **Cerberus → Dashboard**; the page header reads **Cerberus Dashboard**.
Want the quick reference? See <https://wikid82.github.io/charon/security>.
---
## What Is Cerberus?
Think of Cerberus as a guard dog for your websites. It has three heads (in Greek mythology), and each head watches for different threats:
1. **CrowdSec** — Blocks bad IP addresses
2. **WAF (Web Application Firewall)** — Blocks bad requests
3. **Access Lists** — You decide who gets in
---
## Turn It On (The Safe Way)
**Step 1: Start in "Monitor" Mode**
This means Cerberus watches but doesn't block anyone yet.
Add this to your `docker-compose.yml`:
```yaml
environment:
- CERBERUS_SECURITY_WAF_MODE=monitor
- CERBERUS_SECURITY_CROWDSEC_MODE=local
```
Restart Charon:
```bash
docker-compose restart
```
**Step 2: Watch the Logs**
Check "Security" in the sidebar. You'll see what would have been blocked. If it looks right, move to Step 3.
**Step 3: Turn On Blocking**
Change `monitor` to `block`:
```yaml
environment:
- CERBERUS_SECURITY_WAF_MODE=block
```
Restart again. Now bad guys actually get blocked.
---
## CrowdSec (Block Bad IPs)
**What it does:** Thousands of people share information about attackers. When someone tries to hack one of them, everyone else blocks that attacker too.
**Why you care:** If someone is attacking servers in France, you block them before they even get to your server in California.
### How to Enable It
**Via Web UI (Recommended):**
1. Navigate to **Security** dashboard in the sidebar
2. Find the **CrowdSec** card
3. Toggle the switch to **ON**
4. **Wait 5-15 seconds** for the Local API (LAPI) to start
5. Verify the status badge shows "Active" with a running PID
**What happens during startup:**
When you toggle CrowdSec ON, Charon:
1. Starts the CrowdSec process
2. Loads configuration, parsers, and security scenarios
3. Initializes the Local API (LAPI) on port 8085
4. Polls LAPI health every 500ms for up to 30 seconds
5. Returns one of two states:
-**LAPI Ready** — "CrowdSec started and LAPI is ready" — You can immediately proceed to console enrollment
- ⚠️ **LAPI Initializing** — "CrowdSec started but LAPI is still initializing" — Wait 10 more seconds before enrolling
**Expected timing:**
- **Initial start:** 5-10 seconds
- **First start after container restart:** 10-15 seconds
- **Maximum wait:** 30 seconds (with automatic health checks)
**What you'll see in the UI:**
- **Loading overlay** with message "Starting CrowdSec... This may take up to 30 seconds"
- **Success toast** when LAPI is ready
- **Warning toast** if LAPI needs more time
- **Status badge** changes from "Offline" → "Starting" → "Active"
✅ That's it! CrowdSec starts automatically and begins blocking bad IPs once LAPI is ready.
**Persistence Across Restarts:**
Once enabled, CrowdSec **automatically starts** when the container restarts:
- ✅ Server reboot → CrowdSec auto-starts
- ✅ Docker restart → CrowdSec auto-starts
- ✅ Container update → CrowdSec auto-starts
- ❌ Manual toggle OFF → CrowdSec stays disabled until you re-enable
**How it works:**
- Your preference is stored in two places (Settings and SecurityConfig tables)
- Reconciliation function runs at container startup
- Checks both tables to determine if CrowdSec should auto-start
- Logs show: "CrowdSec reconciliation: starting based on SecurityConfig mode='local'"
**Verification after restart:**
```bash
docker restart charon
sleep 15
docker exec charon cscli lapi status
```
Expected output:
```
✓ You can successfully interact with Local API (LAPI)
```
**Troubleshooting auto-start:** See [CrowdSec Not Starting After Restart](troubleshooting/crowdsec.md#crowdsec-not-starting-after-container-restart)
⚠️ **DEPRECATED:** Environment variables like `CHARON_SECURITY_CROWDSEC_MODE=local` are **no longer used**. CrowdSec is now GUI-controlled, just like WAF, ACL, and Rate Limiting. If you have these environment variables in your docker-compose.yml, remove them and use the GUI toggle instead. See [Migration Guide](migration-guide.md).
**What you'll see:** The Cerberus pages show blocked IPs and why they were blocked.
### Enroll with CrowdSec Console (optional)
**Prerequisites:**
**CrowdSec must be enabled** via the GUI toggle (see above)
**LAPI must be running** — Verify with: `docker exec charon cscli lapi status`
**Feature flag enabled**`crowdsec_console_enrollment` must be ON
**Valid enrollment token** — Obtain from crowdsec.net
**Understanding LAPI Readiness:**
When you enable CrowdSec, the backend returns a response with a `lapi_ready` field:
```json
{
"status": "started",
"pid": 203,
"lapi_ready": true
}
```
- **`lapi_ready: true`** — LAPI is fully initialized and ready for enrollment
- **`lapi_ready: false`** — CrowdSec is running, but LAPI is still starting up (wait 10 seconds)
**Checking LAPI Status Manually:**
```bash
# Quick status check
docker exec charon cscli lapi status
# Expected output when ready:
# ✓ You can successfully interact with Local API (LAPI)
# Health endpoint check
docker exec charon curl -s http://localhost:8085/health
# Expected response:
# {"status":"up"}
```
**Enrollment Steps:**
1. **Ensure CrowdSec is enabled** and **LAPI is running** (check prerequisites above)
2. **Verify LAPI readiness** — Check the success toast message:
- ✅ "CrowdSec started and LAPI is ready" → Proceed immediately
- ⚠️ "LAPI is still initializing" → Wait 10 more seconds
3. Navigate to **Cerberus → CrowdSec**
4. Enable the feature flag `crowdsec_console_enrollment` if not already enabled
5. Click **Enroll with CrowdSec Console**
6. Paste the enrollment key from crowdsec.net
7. Click **Submit**
8. **Automatic retry** — Charon checks LAPI availability (3 attempts, 2 seconds apart)
9. Wait for confirmation (this may take 30-60 seconds)
10. Verify your instance appears on crowdsec.net dashboard
**Important Notes:**
- 🚨 Enrollment **requires an active LAPI connection**. If LAPI is not running, the enrollment will show "enrolled" locally but won't register on crowdsec.net.
- ✅ Enrollment tokens are **reusable** — you can re-submit the same token if enrollment fails
- 🔒 Charon stores the enrollment secret internally (not logged or echoed)
- ♻️ After enrollment, the Console status shows in the CrowdSec card
- 🗑️ You can revoke enrollment from either Charon or crowdsec.net
**Troubleshooting:**
If enrollment shows "enrolled" locally but doesn't appear on crowdsec.net:
1. **Check LAPI status:**
```bash
docker exec charon cscli lapi status
```
Expected: `✓ You can successfully interact with Local API (LAPI)`
2. **Check LAPI health endpoint:**
```bash
docker exec charon curl -s http://localhost:8085/health
```
Expected: `{"status":"up"}`
3. **If LAPI is not running:**
- Go to Security dashboard
- Toggle CrowdSec **OFF**, then **ON**
- **Wait 15 seconds** (LAPI needs time to initialize)
- Re-check LAPI status
- Verify you see the success toast: "CrowdSec started and LAPI is ready"
4. **Re-submit enrollment token:**
- Same token works (enrollment tokens are reusable)
- Go to Cerberus → CrowdSec
- Paste token and submit again
- Charon automatically retries LAPI checks (3 attempts, 2s apart)
5. **Check logs:**
```bash
docker logs charon | grep -i crowdsec
```
Look for:
- ✅ "CrowdSec Local API listening" — LAPI started
- ✅ "enrollment successful" — Registration completed
- ❌ "LAPI not available" — LAPI not ready (retry after waiting)
- ❌ "enrollment failed" — Check enrollment token validity
6. **If enrollment keeps failing:**
- Verify your server has internet access to `api.crowdsec.net`
- Check firewall rules allow outbound HTTPS connections
- Ensure enrollment token is valid (check crowdsec.net)
- Try generating a new enrollment token
See also: [CrowdSec Troubleshooting Guide](troubleshooting/crowdsec.md)
### Hub Presets (Configuration Packages)
Charon lets you install security configurations (Collections, Parsers, Scenarios) directly from the CrowdSec Hub.
- **Search & Sort:** Use the search bar to find specific packages (e.g., "wordpress", "nginx"). Sort by name, status, or popularity.
- **One-Click Install:** Click "Install" on any package. Charon handles the download and configuration.
- **Safe Apply:** Changes are applied safely. If something goes wrong, Charon can restore the previous configuration.
- **Updates:** Charon checks for updates automatically. You'll see an "Update" button when a new version is available.
### Troubleshooting
Having trouble with CrowdSec? Check out the [CrowdSec Troubleshooting Guide](troubleshooting/crowdsec.md).
---
## WAF (Block Bad Behavior)
**What it does:** Looks at every request and checks if it's trying to do something nasty—like inject SQL code or run JavaScript attacks.
**Why you care:** Even if your app has a bug, the WAF might catch the attack first.
### How to Enable It
```yaml
environment:
- CERBERUS_SECURITY_WAF_MODE=block
```
**Start with `monitor` first!** This lets you see what would be blocked without actually blocking it.
---
## Access Lists (You Decide Who Gets In)
Access lists let you block or allow specific countries, IP addresses, or networks.
### Example 1: Block a Country
**Scenario:** You only need access from the US, so block everyone else.
1. Go to **Access Lists**
2. Click **Add List**
3. Name it "US Only"
4. **Type:** Geo Whitelist
5. **Countries:** United States
6. **Assign to your proxy host**
Now only US visitors can access that website. Everyone else sees "Access Denied."
### Example 2: Private Network Only
**Scenario:** Your admin panel should only work from your home network.
1. Create an access list
2. **Type:** Local Network Only
3. Assign it to your admin panel proxy
Now only devices on `192.168.x.x` or `10.x.x.x` can access it. The public internet can't.
### Example 3: Block One Country
**Scenario:** You're getting attacked from one specific country.
1. Create a list
2. **Type:** Geo Blacklist
3. Pick the country
4. Assign to the targeted website
---
## Certificate Management Security
**What it protects:** Certificate deletion is a destructive operation that requires proper authorization.
**How it works:**
- Certificates cannot be deleted while in use by proxy hosts (conflict error)
- Automatic backup is created before any certificate deletion
- Authentication required (when auth is implemented)
**Backup & Recovery:**
- Every certificate deletion triggers an automatic backup
- Find backups in the "Backups" page
- Restore from backup if you accidentally delete the wrong certificate
**Best Practice:**
- Review which proxy hosts use a certificate before deleting it
- When deleting proxy hosts, use the cleanup prompt to delete orphaned certificates
- Keep custom certificates you might reuse later
---
## Don't Lock Yourself Out
**Problem:** If you turn on security and misconfigure it, you might block yourself.
**Solution:** Add your IP to the "Admin Whitelist" first.
### How to Add Your IP
1. Go to **Settings → Security**
2. Find "Admin Whitelist"
3. Add your IP address (find it at [ifconfig.me](https://ifconfig.me))
4. Save
Now you can never accidentally block yourself.
### Break-Glass Token (Emergency Exit)
If you do lock yourself out:
1. Log into your server directly (SSH)
2. Run this command:
```bash
docker exec charon charon break-glass
```
It generates a one-time token that lets you disable security and get back in.
---
## Recommended Settings by Service Type
### Internal Admin Panels (Router, Pi-hole, etc.)
```
Access List: Local Network Only
```
Blocks all public internet traffic.
### Personal Blog or Portfolio
```
No access list
WAF: Enabled
CrowdSec: Enabled
```
Keep it open for visitors, but protect against attacks.
### Password Manager (Vaultwarden, etc.)
```
Access List: IP Whitelist (your home IP)
Or: Geo Whitelist (your country only)
```
Most restrictive. Only you can access it.
### Media Server (Plex, Jellyfin)
```
Access List: Geo Blacklist (high-risk countries)
CrowdSec: Enabled
```
Allows friends to access, blocks obvious threat countries.
---
## Check If It's Working
1. Go to **Security → Decisions** in the sidebar
2. You'll see a list of recent blocks
3. If you see activity, it's working!
---
## Live Security Monitoring
### Live Log Viewer
**What it does:** Stream security events in real-time directly in the Cerberus Dashboard.
**Where to find it:** Cerberus → Dashboard → Scroll to "Live Activity" section
**What you'll see:**
- Real-time WAF blocks and detections
- CrowdSec decisions as they happen
- ACL denials (geo-blocking, IP filtering)
- Rate limiting events
- All Cerberus security activity
**Controls:**
- **Pause** — Stop the stream to examine specific events
- **Clear** — Remove old entries from the display
- **Auto-scroll** — Automatically follow new events
- **Filter** — Search logs by text, level, or source
**How to use it:**
1. Open Cerberus Dashboard
2. Scroll to the Live Activity section
3. Watch events appear in real-time
4. Click "Pause" to stop streaming and review events
5. Use the filter box to search for specific IPs, rules, or messages
6. Click "Clear" to remove old entries
**Technical details:**
- Uses WebSocket for real-time streaming (no polling)
- Keeps last 500 entries by default (configurable)
- Server-side filtering reduces bandwidth
- Automatic reconnection on disconnect
### Security Notifications
**What it does:** Sends alerts when critical security events occur.
**Why you care:** Get immediate notification of attacks or suspicious activity without watching the dashboard 24/7.
#### Configure Notifications
1. Go to **Cerberus Dashboard**
2. Click **"Notification Settings"** button (top-right)
3. Configure your preferences:
**Basic Settings:**
- **Enable Notifications** — Master toggle
- **Minimum Log Level** — Choose: debug, info, warn, or error
- `error` — Only critical events (recommended)
- `warn` — Important warnings and errors
- `info` — Normal operations plus warnings/errors
- `debug` — Everything (very noisy, not recommended)
**Event Types:**
- **WAF Blocks** — Notify when firewall blocks an attack
- **ACL Denials** — Notify when access control rules block requests
- **Rate Limit Hits** — Notify when traffic thresholds are exceeded
**Delivery Methods:**
- **Webhook URL** — Send to Discord, Slack, or custom integrations
- **Email Recipients** — Comma-separated email addresses (requires SMTP setup)
#### Webhook Integration
**Security considerations:**
1. **Use HTTPS webhooks only** — Never send security alerts over unencrypted HTTP
2. **Validate webhook endpoints** — Ensure the URL is correct before saving
3. **Protect webhook secrets** — If your webhook requires authentication, use environment variables
4. **Rate limiting** — Charon does NOT rate-limit webhook calls; configure your webhook provider to handle bursts
5. **Sensitive data** — Webhook payloads may contain IP addresses, request URIs, and user agents
**Supported platforms:**
- Discord (use webhook URL from Server Settings → Integrations)
- Slack (create incoming webhook in Slack Apps)
- Microsoft Teams (use incoming webhook connector)
- Custom HTTPS endpoints (any server that accepts POST requests)
**Webhook payload example:**
```json
{
"event_type": "waf_block",
"severity": "error",
"timestamp": "2025-12-09T10:30:45Z",
"message": "WAF blocked SQL injection attempt",
"details": {
"ip": "203.0.113.42",
"rule_id": "942100",
"request_uri": "/api/users?id=1' OR '1'='1",
"user_agent": "curl/7.68.0"
}
}
```
**Discord webhook format:**
Charon automatically formats notifications for Discord:
```json
{
"embeds": [{
"title": "🛡️ WAF Block",
"description": "SQL injection attempt blocked",
"color": 15158332,
"fields": [
{ "name": "IP Address", "value": "203.0.113.42", "inline": true },
{ "name": "Rule", "value": "942100", "inline": true },
{ "name": "URI", "value": "/api/users?id=1' OR '1'='1" }
],
"timestamp": "2025-12-09T10:30:45Z"
}]
}
```
**Testing your webhook:**
1. Add your webhook URL in Notification Settings
2. Save the settings
3. Trigger a test event (try accessing a blocked URL)
4. Check your Discord/Slack channel for the notification
**Troubleshooting webhooks:**
- No notifications? Check webhook URL is correct and HTTPS
- Wrong format? Verify your platform's webhook documentation
- Too many notifications? Increase minimum log level to "error" only
- Notifications delayed? Check your network connection and firewall rules
### Log Privacy Considerations
**What's logged:**
- IP addresses of blocked requests
- Request URIs and query parameters
- User-Agent strings
- Rule IDs that triggered blocks
- Timestamps of security events
**What's NOT logged:**
- Request bodies (POST data)
- Authentication credentials
- Session cookies
- Response bodies
**Privacy best practices:**
1. **Filter logs before sharing** — Remove sensitive IPs or URIs before sharing logs externally
2. **Secure webhook endpoints** — Use HTTPS and authenticate webhook requests
3. **Respect GDPR** — IP addresses are personal data in some jurisdictions
4. **Retention policy** — Live logs are kept for the current session only (not persisted to disk)
5. **Access control** — Only authenticated users can access live logs (when auth is implemented)
**Compliance notes:**
- Live log streaming does NOT persist logs to disk
- Logs are only stored in memory during active WebSocket sessions
- Notification webhooks send log data to third parties (Discord, Slack)
- Email notifications may contain sensitive data
---
## Turn It Off
If security is causing problems:
**Option 1: Via Web UI**
1. Go to **Settings → Security**
2. Toggle "Enable Cerberus" off
**Option 2: Via Environment Variable**
Remove the security lines from `docker-compose.yml` and restart.
---
## TLS Security
### TLS Version Enforcement
Charon (via Caddy) enforces a minimum TLS version of 1.2 by default. This prevents TLS downgrade attacks that attempt to force connections to use vulnerable TLS 1.0 or 1.1.
**What's Protected:**
- ✅ TLS 1.0/1.1 downgrade attacks
- ✅ BEAST, POODLE, and similar protocol-level attacks
- ✅ Weak cipher suite negotiation
**HSTS (HTTP Strict Transport Security):**
Charon sets HSTS headers with:
- `max-age=31536000` (1 year)
- `includeSubDomains`
- `preload` (for browser preload lists)
This ensures browsers always use HTTPS after the first visit.
---
## DNS Security
### Protecting Against DNS Hijacking
While Charon cannot directly control your DNS resolver, you can protect against DNS hijacking and cache poisoning by configuring your host to use encrypted DNS.
**Docker Host Configuration (systemd-resolved):**
```bash
# /etc/systemd/resolved.conf
[Resolve]
DNS=1.1.1.1#cloudflare-dns.com 1.0.0.1#cloudflare-dns.com
DNSOverTLS=yes
```
Then restart: `sudo systemctl restart systemd-resolved`
**Alternative DNS Providers with DoH/DoT:**
- Cloudflare: `1.1.1.1` / `1.0.0.1`
- Google: `8.8.8.8` / `8.8.4.4`
- Quad9: `9.9.9.9`
**Additional DNS Protections:**
1. **DNSSEC**: Enable at your domain registrar to prevent DNS spoofing
2. **CAA Records**: Restrict which Certificate Authorities can issue certificates for your domain
---
## Container Hardening
### Running Charon with Maximum Security
For production deployments, apply these Docker security configurations:
```yaml
services:
charon:
image: ghcr.io/wikid82/charon:latest
read_only: true
tmpfs:
- /tmp:size=100M
- /config:size=50M
- /data/logs:size=100M
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
security_opt:
- no-new-privileges:true
volumes:
- ./data:/data
```
**Security Options Explained:**
- `read_only: true` — Prevents filesystem modifications (defense against malware)
- `cap_drop: ALL` — Removes all Linux capabilities
- `cap_add: NET_BIND_SERVICE` — Only allows binding to ports (required for reverse proxy)
- `no-new-privileges` — Prevents privilege escalation attacks
- `tmpfs` mounts — Provides writable directories for logs and temp files without persistent storage
---
## Common Questions
### "Will this slow down my websites?"
No. The checks happen in milliseconds. Humans won't notice.
### "Can I whitelist specific paths?"
Not yet, but it's planned. For now, access lists apply to entire websites.
### "What if CrowdSec blocks a legitimate visitor?"
You can manually unblock IPs in the Security → Decisions page.
### "Do I need all three security features?"
No. Use what you need:
- **Just starting?** CrowdSec only
- **Public service?** CrowdSec + WAF
- **Private service?** Access Lists only
---
## Zero-Day Protection
### What We Protect Against
**Web Application Exploits:**
- ✅ SQL Injection (SQLi) — even zero-days using SQL syntax
- ✅ Cross-Site Scripting (XSS) — new XSS vectors caught by pattern matching
- ✅ Remote Code Execution (RCE) — command injection patterns
- ✅ Path Traversal — attempts to read system files
- ⚠️ CrowdSec — protects hours/days after first exploitation (crowd-sourced)
### How It Works
The WAF (Coraza) uses the OWASP Core Rule Set to detect attack patterns. Even if the exploit is brand new, the pattern is usually recognizable.
**Example:** A zero-day SQLi exploit discovered today:
```
https://yourapp.com/search?q=' OR '1'='1
```
- **Pattern:** `' OR '1'='1` matches SQL injection signature
- **Action:** WAF blocks request → attacker never reaches your database
### What We DON'T Protect Against
- ❌ Zero-days in Charon itself (keep Charon updated)
- ❌ Zero-days in Docker, Linux kernel (keep OS updated)
- ❌ Logic bugs in your application code (need code reviews)
- ❌ Insider threats (need access controls + auditing)
- ❌ Social engineering (need user training)
### Recommendation: Defense in Depth
1. **Enable all Cerberus layers:**
- CrowdSec (IP reputation)
- ACLs (restrict access by geography/IP)
- WAF (request inspection)
- Rate Limiting (slow down attacks)
2. **Keep everything updated:**
- Charon (watch GitHub releases)
- Docker images (rebuild regularly)
- Host OS (enable unattended-upgrades)
3. **Monitor security logs:**
- Check "Security → Decisions" weekly
- Set up alerts for high block rates
---
## COOP (Cross-Origin-Opener-Policy) Behavior
### Development Mode
When accessing Charon over HTTP on non-localhost IP addresses (e.g., `http://192.168.1.100:8080`), you may see this browser console warning:
```
Cross-Origin-Opener-Policy policy would block the window.closed call.
```
**This is expected behavior and safe to ignore in local development.**
### Why Does This Happen?
The COOP header is conditionally applied based on the environment:
- **Development (HTTP):** COOP header is **disabled** to allow convenient local testing
- **Production (HTTPS):** COOP header is **enabled** with `same-origin-allow-popups` to protect against Spectre-class attacks
The browser warning appears because:
1. Your development server is accessed via HTTP (not HTTPS)
2. The IP address is not `localhost` (e.g., accessing from another device on your network)
3. Browsers enforce stricter security checks for non-localhost HTTP connections
### Production HTTPS Requirements
**⚠️ All production deployments MUST use HTTPS.** Running Charon in production over HTTP disables critical security protections:
**Security Headers Disabled on HTTP:**
- ✅ HSTS (HTTP Strict Transport Security)
- ✅ COOP (Cross-Origin-Opener-Policy)
- ✅ Secure cookie attributes
**Why HTTPS is Required:**
1. **Spectre Attack Protection:** COOP isolates browsing contexts to prevent cross-origin memory leaks
2. **Secure Cookies:** Session cookies with `Secure` flag only work over HTTPS
3. **Mixed Content:** Modern browsers block HTTP content loaded from HTTPS pages
4. **Compliance:** PCI-DSS, HIPAA, and other regulations mandate encryption in transit
### Load Balancer Configuration
If Charon runs behind a load balancer or reverse proxy (Cloudflare, nginx, Traefik):
**Required Header Forwarding:**
```nginx
# nginx example
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Real-IP $remote_addr;
```
**Why this matters:** Charon detects HTTPS mode via the `X-Forwarded-Proto` header. If your load balancer terminates TLS but doesn't forward this header, Charon thinks it's running in HTTP mode and disables security features.
**Verification:**
Check your browser's developer tools → Network → Response Headers:
```
Cross-Origin-Opener-Policy: same-origin-allow-popups
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
```
If these headers are missing on HTTPS, verify your load balancer configuration.
### Caddy TLS Termination
When using Charon's built-in Caddy reverse proxy:
- ✅ TLS termination happens at Caddy (port 443)
- ✅ Charon backend receives `X-Forwarded-Proto: https` automatically
- ✅ Security headers applied correctly
- ✅ No additional configuration needed
**Docker Network:** Caddy and Charon communicate internally over HTTP, but the `X-Forwarded-Proto` header ensures Charon knows the client connection was HTTPS.
---
## Autocomplete Security
### Why Autocomplete is Enabled
Charon enables the `autocomplete` attribute on password and authentication fields. This is a **security best practice** recommended by OWASP and NIST.
**Benefits:**
1. **Stronger Passwords:** Password managers generate cryptographically secure passwords (20+ characters, high entropy)
2. **Unique Passwords:** Users are more likely to use unique passwords per-site when managers handle storage
3. **Reduced Phishing:** Password managers verify domain names before autofilling, protecting against phishing sites
4. **Better UX:** Improves accessibility and reduces password reuse
### OWASP Recommendations
From [OWASP Authentication Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html):
> "Do not disable the browser autocomplete on credential inputs. Modern password managers and browsers have secure implementations that rely on autocomplete attributes."
### NIST Guidelines
From [NIST SP 800-63B](https://pages.nist.gov/800-63-3/sp800-63b.html):
> "Verifiers SHOULD permit the use of paste functionality and password managers."
### Implementation in Charon
```html
<!-- Login form -->
<input
type="text"
name="username"
autocomplete="username"
required
/>
<input
type="password"
name="password"
autocomplete="current-password"
required
/>
```
**Autocomplete values used:**
- `username` — For login username/email fields
- `current-password` — For password login fields
- `new-password` — For password creation/change fields (future implementation)
### Compliance Considerations
**For most organizations:** Autocomplete is secure and recommended.
**For highly regulated industries (PCI-DSS Level 1, HIPAA, government):** Some compliance frameworks may require disabling autocomplete. If your organization has specific policies against password managers, you can:
1. Enforce company-wide password managers (preferred)
2. Disable browser autocomplete via group policy (not recommended)
3. Use hardware security keys (WebAuthn, FIDO2) as primary authentication
**Charon's position:** We follow modern security best practices. Disabling autocomplete reduces security for 99% of users to accommodate legacy compliance interpretations.
---
## Testing & Validation
### Integration Testing
Cerberus includes a comprehensive integration test suite to validate all security features work correctly together.
**Run the full test suite:**
```bash
# Integration script
bash scripts/cerberus_integration.sh
# Go test wrapper
cd backend && go test -tags=integration ./integration -run TestCerberusIntegration -v
```
**What's tested:**
- ✅ All features enable without conflicts
- ✅ Correct handler pipeline order
- ✅ WAF doesn't interfere with rate limiting
- ✅ Security decisions enforced at correct layer
- ✅ Legitimate traffic passes through all layers
- ✅ Performance benchmarks (< 50ms overhead)
### UI/UX Testing
The Cerberus Dashboard has extensive UI testing coverage:
- Security card status display verification
- Loading overlay animations
- Error handling and toast notifications
- Mobile responsive layout testing (375px → 1920px)
**Test documentation:**
- [Integration Testing Plan](plans/cerberus_integration_testing_plan.md)
- [UI/UX Testing Plan](plans/cerberus_uiux_testing_plan.md)
### VS Code Tasks
Run tests directly from VS Code using the provided tasks:
- **Cerberus: Run Full Integration Script** — Full shell-based integration test
- **Cerberus: Run Full Integration Go Test** — Go test wrapper
---
## More Technical Details
Want the nitty-gritty? See [Cerberus Technical Docs](cerberus.md).