Configuration
All configuration lives in the .env file in the project root, which is read by docker-compose.yml. A small number of additional, mostly-optional values can be overridden by exporting them in the calling shell.
Authentication (required)
| Variable | Description |
|---|---|
BASIC_AUTH_USERNAME | HTTP Basic auth username for the web UI and API |
BASIC_AUTH_PASSWORD | HTTP Basic auth password (use a strong password) |
SECRET_KEY | HMAC key used to sign JWT access and refresh tokens. Auto-generated when empty, but tokens then invalidate on every restart — set a stable value in production |
TLS for the web UI
| Variable | Default | Description |
|---|---|---|
LETSENCRYPT_DOMAIN | empty | When set together with LETSENCRYPT_EMAIL, the web service obtains a real certificate via Let's Encrypt |
LETSENCRYPT_EMAIL | empty | Email address used for the ACME account |
CORS_ALLOWED_ORIGINS | https://localhost:8443 | Comma-separated list of origins allowed by the backend CORS policy. Add your public hostname when fronting the UI behind your own domain. |
Networking
| Variable | Default | Description |
|---|---|---|
BACKEND_URL | http://backend:5000 | Backend URL the web service uses on the internal Docker network |
REQUEST_TIMEOUT | 120 | Web UI request timeout in seconds |
PROXY_HOST | proxy | Squid container hostname (used by the backend to send reload requests) |
PROXY_PORT | 3128 | Squid proxy port |
PROXY_BIND_IP | 0.0.0.0 | Host interface that the Squid port is bound to. Set to 127.0.0.1 to restrict access to the local machine |
PROXY_CONTAINER_NAME | secure-proxy-manager-proxy | Docker container name used when issuing reconfigure signals |
PROXY_IP | empty | LAN IP of the host. When set, dnsmasq publishes a WPAD record for browser auto-discovery |
GUI_IP_WHITELIST | empty | Comma-separated client IPs allowed to connect to the proxy on the management ports even when blocked by other rules |
WAF
| Variable | Default | Description |
|---|---|---|
BASIC_AUTH_USERNAME / BASIC_AUTH_PASSWORD | (required) | Used to authenticate the WAF when it calls back to POST /api/internal/alert |
WAF_BLOCK_THRESHOLD | 10 | Anomaly score at or above which a request is blocked |
WAF_DISABLED_CATEGORIES | empty | Comma-separated rule category names to disable globally (for example DEBUG_LEAK,RESPONSE_ANOMALY) |
WAF_H_ENTROPY | 1 | Toggle the Shannon entropy heuristic |
WAF_H_ENTROPY_MAX | 7.5 | Maximum allowed entropy before the heuristic contributes to the score |
WAF_H_BEACONING | 1 | Toggle C2 beaconing detection |
WAF_H_PII | 1 | Toggle the PII leak heuristic |
WAF_H_SHARDING | 1 | Toggle the destination sharding heuristic |
WAF_H_MORPHING | 0 | Toggle the header morphing heuristic (off by default; noisy) |
WAF_H_GHOSTING | 1 | Toggle the protocol ghosting heuristic |
WAF_H_SEQUENCE | 0 | Toggle the request sequence heuristic (off by default; needs tuning) |
Each toggle accepts 1/0 or true/false.
DNS
| Variable | Default | Description |
|---|---|---|
DNS_UPSTREAM_1 | 1.1.1.3 | Primary upstream resolver (Cloudflare malware-blocking) |
DNS_UPSTREAM_2 | 9.9.9.9 | Secondary upstream resolver (Quad9) |
DNS_UPSTREAM_3 | 8.8.8.8 | Tertiary upstream resolver (Google) |
Tailscale (optional sidecar)
| Variable | Default | Description |
|---|---|---|
TS_AUTHKEY | empty | Tailscale authentication key. The sidecar refuses to start without one |
TAILSCALE_HOSTNAME | secure-proxy | Hostname registered on the tailnet |
The sidecar only runs under docker compose --profile tailscale.
Squid configuration
The proxy/startup.sh script generates squid.conf at container start. You cannot replace the base configuration entirely — the startup script enforces certain protections (direct-IP block ACLs, ICAP integration, log paths) regardless. You can append custom directives by placing them at:
/config/custom_squid_extra.confThe file is concatenated to the generated squid.conf at startup. If a legacy /config/custom_squid.conf exists, it is renamed to custom_squid_extra.conf automatically.
WARNING
Custom directives are appended after the base configuration; later directives override earlier ones in Squid only for a subset of options. Test your additions with docker compose exec proxy squid -k parse before relying on them.
Default Squid settings
| Setting | Default |
|---|---|
| Listening port | 3128 |
Memory cache (PROXY_MEMORY_CACHE_MB) | 256 MB |
Disk cache (PROXY_CACHE_SIZE_MB) | 2000 MB at /var/spool/squid |
| Maximum object size | 100 MB |
connect_timeout | 30 seconds |
dns_timeout | 5 seconds |
These can be overridden through the Settings page of the web UI, which writes the values to /config/squid_settings.env.
Default-deny egress (optional)
By default the proxy is a deny-list gateway: clients may reach any destination that is not blacklisted. You can optionally switch it into a default-deny egress gateway, where local clients can reach only the destinations on an explicit allowlist and everything else is refused with a Squid 403.
This mode is off by default and is configured entirely from the web UI and API, not via .env:
- Enable it with the Default-deny egress toggle on the Settings page (next to SSL inspection). It maps to the
egress_default_denysetting, which defaults tofalse. - Manage allowed destinations on the Egress Allowlist page (add, delete, search, paginate). Each entry is either an IP/CIDR or a domain; the type is auto-classified when you add it. A client is allowed only if the destination matches the IP allowlist or the domain allowlist.
When the toggle is off, behaviour is unchanged and no destinations are blocked by this feature. Use default-deny egress when you need a strict "allow only these destinations" outbound policy — for example, letting an application reach only an approved ACME server, package mirror, or internal API.
WAF custom rules
Create /config/waf_custom_rules.txt with one regex per line. Lines starting with # are ignored. Each line is rejected if longer than 512 characters or if it contains a NUL byte. Restart the waf service to reload:
docker compose restart wafExample:
# Block requests containing internal codename
project-codename
(?i)pre-release-buildSSL certificates (HTTPS filtering)
If /config/ssl_cert.pem and /config/ssl_key.pem are missing, the proxy generates a self-signed RSA-2048 certificate valid for 3 650 days (ten years) at first start.
To install your own certificate:
cp your-cert.pem config/ssl_cert.pem
cp your-key.pem config/ssl_key.pem
docker compose restart proxyClients must trust this certificate to avoid browser warnings when SSL bump (HTTPS filtering) is active.
Installing the CA on clients
| Platform | Steps |
|---|---|
| Windows | Import to Trusted Root Certification Authorities via certmgr.msc |
| macOS | Add to Keychain Access, then mark Always Trust for SSL |
| Linux | Copy to /usr/local/share/ca-certificates/ and run update-ca-certificates |
| iOS / Android | Email the .pem file to the device, install via Settings → General → VPN and Device Management (iOS) or Security → Install certificate (Android) |