| Container | User | Privileges | Notes |
|---|---|---|---|
| backend (Go) | app (UID 1000) |
docker.sock:ro | Reload signals for Squid/dnsmasq |
| waf (Go) | waf (UID 1000) |
none | ICAP server, internal only |
| web (Nginx) | nginx |
none | TLS termination, static files |
| proxy (Squid) | proxy |
NET_ADMIN | Transparent proxy mode |
| dns (dnsmasq) | root |
none | Internal DNS only |
The backend mounts /var/run/docker.sock:ro to send SIGHUP to Squid/dnsmasq when blacklists change. Read-only, non-root, no --privileged. Remove the mount if not needed (manual reload required).
- JWT Bearer tokens (8h expiry) with persistent token blacklist (survives restarts via SQLite)
- bcrypt password hashing (default cost 10) — plaintext fallback removed in v3.1
- Per-IP login rate limiting: 5 attempts / 5 min
- Global rate limiting: Token bucket per-IP (20 req/s, 60 burst) on all endpoints
- WebSocket origin validation against CORS allowlist
- HTTPS by default (self-signed 1-year cert or Let's Encrypt)
- Sensitive settings encrypted at rest: Webhook URLs, Gotify/Telegram/ntfy tokens stored with AES-256-GCM
- Encryption key auto-generated and persisted to
/data/.enc_key(mode 0600) - JWT secret auto-generated and persisted to
/data/.jwt_secret(mode 0600) - TLS 1.2/1.3 only, strong ciphers (
HIGH:!aNULL:!MD5:!RC4), HSTS 1 year
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data:;
font-src 'self' data:;
connect-src 'self' ws: wss:
- 170 regex rules + 6 heuristics across 21 Security Packs (+ per-request feature-vector profiling)
- Anomaly scoring, dual-scan (raw + decoded), 55MB body limit
- All inputs validated with max-length constraints
- Circuit breaker protects backend against WAF service failures
- Circuit breaker: WAF calls trip after 3 failures, auto-recover after 30s
- Notification retry: Failed deliveries retry 3x with exponential backoff (1s, 2s, 4s)
- Graceful shutdown: All background workers respond to SIGTERM via context cancellation
- Atomic file exports: Temp file + rename pattern prevents torn reads by Squid/dnsmasq
- gosec: Automated Go security scanning on every push/PR
- npm audit: Frontend dependency vulnerability checking (high severity)
- Backend tests: Race detector enabled, 60% coverage threshold enforced
- Docker build verification: All 5 images built and verified in CI
Every published GHCR image is keyless-signed with cosign and ships an SBOM and SLSA build provenance attestation. Verify a pulled image came from this repo's CI before trusting it:
cosign verify \
--certificate-identity-regexp 'https://github.com/fabriziosalmi/secure-proxy-manager/.github/workflows/.+' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/fabriziosalmi/secure-proxy-manager-proxy:latest
# Inspect the attached SBOM / provenance:
cosign download sbom ghcr.io/fabriziosalmi/secure-proxy-manager-proxy:latestComponent-specific advisories and how SPM mitigates them — e.g. the Squid "Squidbleed" FTP-gateway leak (CVE-2026-47729) — are tracked in docs/guide/security-advisories.md.
- Do NOT open a public issue
- Use GitHub Security Advisories
- We respond within 48 hours
| Version | Status |
|---|---|
| 3.10.x | Current |
| 3.9.x | Security fixes only |
| < 3.9 | EOL |