Docker Proxy turns Squid into a browser-operated proxy appliance: policy editing, PAC/WPAD publishing, TLS-inspection controls, web filtering, ad blocking, ClamAV ICAP scanning, fleet operations, audit history, and observability live behind one MySQL-backed admin UI instead of scattered config files and shell-only runbooks.
It is built for home-lab operators, schools, small offices, MSP-style managed LANs, and advanced network admins who need an auditable proxy stack they can run from containers without pretending that interception, certificates, database backups, and client routing are magic. It is not a VPN, DNS sinkhole, desktop privacy add-on, or turnkey firewall.
Squid is powerful, but day-two operation usually becomes a mix of hand-edited config, one-off scripts, fragile PAC files, untracked certificate changes, and log spelunking. Docker Proxy packages the runtime and control plane so an operator can:
- edit policy in a web UI and validate candidate Squid config on the selected runtime before applying it;
- publish PAC/WPAD and proxy health endpoints from the proxy container while keeping the admin UI separate;
- keep configuration, users, policy revisions, telemetry, block logs, and operation history in MySQL 8+;
- manage one or more proxy runtimes from a scoped admin UI without mixing fleet operations; and
- review traffic, cache behavior, ICAP activity, SSL/TLS diagnostics, block events, exports, and remediation cues from the same console.
The screenshots and animated tour below were captured from an authorized live Docker Proxy deployment; no credentials, secrets, private keys, or tokens are shown.
| Status and observability dashboard | SSL/cache policy workflow | Registered proxy fleet and operations |
|---|---|---|
![]() |
![]() |
![]() |
This path uses the published GHCR images and the committed docker-compose.ghcr.yml plus docker-compose.common.yml files. It assumes you already have Docker Compose v2 and a reachable MySQL 8+ database. For production, create the database and least-privilege runtime user before starting the containers; only use MYSQL_CREATE_DATABASE=1 for a disposable first look with a database account that is allowed to create the schema.
-
Clone the repository and create a local launch environment:
git clone https://github.com/kklouzal/Docker_Proxy.git cd Docker_Proxy cat > .env <<'ENV' MYSQL_HOST=mysql.example.com MYSQL_PORT=3306 MYSQL_USER=docker_proxy MYSQL_PASSWORD=replace_with_the_database_password MYSQL_DATABASE=squid_proxy MYSQL_CREATE_DATABASE=0 PROXY_MANAGEMENT_TOKEN=replace_with_a_long_random_shared_token FLASK_SECRET_KEY=replace_with_a_long_random_flask_secret DOCKER_LOG_DRIVER=json-file DOCKER_LOG_MAX_SIZE=10m DOCKER_LOG_MAX_FILE=3 ENV
Replace every
replace_with_...value before starting. In particular,PROXY_MANAGEMENT_TOKENis required and must be the same private random value for the Admin UI and proxy; missing values and the publicchange-me,replace-with-a-long-random-token, andreplace_with_a_long_random_shared_tokenplaceholders are rejected at container startup. Generate one withopenssl rand -hex 32. -
Pull and start the split containers:
docker compose -f docker-compose.ghcr.yml pull docker compose -f docker-compose.ghcr.yml up -d docker compose -f docker-compose.ghcr.yml ps
-
Smoke-check the public proxy endpoints from the Docker host:
curl -fsS http://localhost/health curl -fsS http://localhost/proxy.pac | head -
Before the first Admin UI start, set a unique local bootstrap account in the Compose
.env(the password must be 12-1024 characters), then openhttp://localhost:5000and sign in:ADMIN_BOOTSTRAP_USERNAME=admin ADMIN_BOOTSTRAP_PASSWORD=choose-a-strong-password
Both variables must be set together. They create an account only while the users table is empty, so upgrades and existing accounts are unchanged. Remove the variables after the account is created. Docker Proxy does not generate, print, log, render, or audit a bootstrap password. At startup it consumes the values and removes them from its mutable process environment; Python and container runtimes cannot guarantee erasure of all prior copies. A deployment with no local users may instead use an already configured external provider; otherwise the login page remains fail-closed and shows setup guidance.
-
Before routing real clients, review the generated proxy record, PAC/WPAD URLs, certificate authority trust plan, no-bump policy, and management-plane exposure. Do not expose the admin UI directly to the internet, and do not enable TLS inspection for unmanaged clients or clients that do not trust the proxy CA.
Default local endpoints after the Compose stack starts:
- Admin UI:
http://localhost:5000 - Explicit HTTP proxy:
http://localhost:3128 - HTTP NAT intercept listener:
localhost:3129when enabled and routed by your network - HTTPS NAT intercept listener:
localhost:3130when enabled and routed by your network - Proxy public health:
http://localhost/health - PAC file:
http://localhost/proxy.pac - WPAD:
http://localhost/wpad.dat
- Split control plane and runtime:
admin-uimanages policy and fleet state;proxyruns Squid, ICAP helpers, PAC/WPAD, local policy materialization, and a small management API. - MySQL-backed source of truth: configuration revisions, proxy registration, policy state, users, audit events, telemetry, block logs, PAC profiles, and operation status live in MySQL 8+.
- Validated configuration workflow: proxy runtimes validate candidate Squid configs with their own Squid binary/includes before activation and keep a last-known-good rollback path.
- Fleet-aware operation ledger: admin actions queue proxy-scoped operations for config, certificates, PAC refresh, adblock artifacts, cache clears, and manual sync.
- PAC/WPAD as a first-class runtime service: each proxy serves public
/health,/proxy.pac, and/wpad.datwithout exposing the admin UI on port 80. - TLS inspection controls: CA generation/upload, SSL-bump policy, compatibility presets, no-bump/no-cache rules, client-CIDR splicing, SSL error analysis, and one-click exclusions.
- Web filtering and threat intelligence: UT1-style category filtering, whitelists, proxy-local SQLite snapshots for request-path lookups, and optional Google Safe Browsing v5 local-hash-prefix checks.
- ICAP security services: EasyList-style ad blocking through a SQLite-backed REQMOD helper and ClamAV response scanning through c-icap RESPMOD with remote
clamd. - Operational visibility: live traffic, clients, destinations, cache behavior, ICAP activity, SSL/TLS diagnostics, block events, exports, remediation cues, and maintenance actions.
- Bounded health checks: navigation health uses lightweight proxy probes, remediation views can request full runtime health, and slow ICAP/ClamAV checks are isolated behind explicit timeouts and short caches.
- Multi-architecture images: GitHub Actions builds and publishes
linux/amd64andlinux/arm64images to GHCR after deterministic and live-stack tests pass.
+----------------------------+
| MySQL 8+ |
| config, policy, telemetry |
| users, audit, operations |
+-------------+--------------+
|
+--------------+--------------+
| |
+--------v--------+ +---------v---------+
| admin-ui | | proxy |
| Flask/Gunicorn | | Squid + ICAP |
| policy + fleet |<-------->| sync + PAC/WPAD |
| port 5000 | mgmt API | ports 80/3128/3129|
+-----------------+ +-------------------+
The admin UI can run with local or remote proxy runtimes. Each proxy registers its management URL and public PAC/proxy coordinates in MySQL. The admin UI targets the selected proxy for runtime checks and queues durable operations when policy changes need to be materialized.
Docker Proxy is a good fit when you want a managed proxy appliance that can be operated from a browser, backed by an auditable SQL control plane, and deployed as one or more Dockerized proxy runtimes. It is meant for networks where clients are managed, proxy policy is intentional, and operators can own the surrounding infrastructure: DNS/WPAD or PAC distribution, firewall redirects for intercept modes, certificate trust, database backups, and management-plane exposure.
It is not a DNS sinkhole, a personal VPN, a desktop privacy add-on, or a turnkey firewall. The proxy can enforce Squid policy, PAC routing, web category blocks, request-time adblock decisions, TLS-inspection policy, and ICAP antivirus scanning where traffic actually traverses it. It does not enroll devices, install router rules, bypass application certificate pinning, or make HTTPS interception appropriate for unmanaged users.
- Docker Engine with Docker Compose v2.
- A reachable MySQL 8+ database; runtime and admin state are MySQL-backed.
- A remote
clamdservice when ClamAV response scanning is enabled. - Managed clients must trust the proxy CA before TLS inspection is enabled for them.
The admin UI can use local users, one LDAP or Active Directory provider, or one metadata-backed SAML provider. Local users remain available as break-glass access even when external authentication is active.
Only one external provider is enabled at a time. LDAP, Active Directory, and SAML authorization currently govern admin UI login only; they do not by themselves enable transparent proxy identity, captive-portal identity, policy-user attribution, or block-page bypass.
Configure LDAP or Active Directory from Administration -> LDAP or Administration -> Active Directory:
- Enter one or more
ldap://orldaps://server URLs and the bind DN / service account. Use LDAPS or StartTLS for normal deployments so bind and user credentials are not sent in clear text. - Upload or paste a PEM CA bundle when directory TLS chains to an internal CA that is not in system trust.
- Save and test the provider. A provider cannot be enabled until its current connection settings have passed a test.
- Use
Scan directoryto populate Base DN, search-base, and group choices from the submitted connection details, then select the required admin group. - Enable the provider after the test succeeds. If directory login fails or rejects a user, local admin accounts remain available for break-glass access.
Configure SAML from Administration -> SAML:
- Set the IdP metadata URL. For AD FS this is usually
https://idp.example.com/FederationMetadata/2007-06/FederationMetadata.xml. - Keep
Require HTTPS metadata URLandVerify TLS certificateenabled for normal deployments. Add a PEM CA bundle only when the AD FS TLS certificate chains to an internal CA that is not in system trust. - Set
Public admin base URLto the externally visible admin UI origin when the UI is behind a reverse proxy. The generated service-provider metadata is available at/auth/saml/metadata, and the assertion consumer service is/auth/saml/acs. - Add the SP metadata URL to AD FS as a relying party trust, or enter the SP entity ID and ACS URL shown on the SAML tab.
- Map AD FS claims to the configured SAML claim names. The defaults expect username from
NameIDand groups fromgroups; common AD FS alternatives areemail,upn, or a custom group claim. - Set
Required group valueto the exact admin group claim value when SAML logins must be group-restricted. If it is blank, any authenticated SAML user accepted by the IdP can sign in to the admin UI. - Click
Refresh metadata, then save with SAML enabled after the refresh succeeds.
SAML login is hidden and rejected until the provider is enabled, IdP metadata has refreshed successfully, and the metadata cache is still current. AD FS signing certificates are read from cached IdP metadata; refresh metadata after AD FS certificate rollover, or before rollover if AD FS publishes both current and next signing certificates. The cache expiry follows IdP validUntil/cacheDuration when present and otherwise defaults to 24 hours.
The implementation requires signed assertions/messages, does not log raw SAML responses in audit records, and only redirects RelayState to local admin UI paths. Live LDAP, Active Directory, and AD FS interoperability still needs validation in the target domain because bind policy, directory schemas, group membership rules, claim issuance rules, TLS trust, and reverse-proxy public URLs vary by deployment.
docker-compose.yml builds both containers from the repository and extends the shared service definition in docker-compose.common.yml.
docker compose up -d --buildSource builds use Alpine's edge image tag by default so Squid and runtime packages track the newest Alpine packages available at build time. For a pinned base image, pass an explicit build argument:
docker compose build --build-arg ALPINE_VERSION=3.23.4The proxy image defaults its c-icap sources to release refs and independently pinned commit IDs. The build verifies each checked-out HEAD before compilation and fails closed if a ref moves or resolves to different source. Advanced source overrides must supply each ref and its matching full commit together, for example --build-arg CICAP_GIT_REF=<ref> --build-arg CICAP_GIT_COMMIT=<40-character-commit> (and the corresponding CICAP_MODULES_GIT_REF / CICAP_MODULES_GIT_COMMIT pair). Changing only a ref or only its expected commit intentionally fails the build.
docker-compose.ghcr.yml runs the published split images:
ghcr.io/kklouzal/docker_proxy-admin-ui:mainghcr.io/kklouzal/docker_proxy-proxy:main
The publish workflow runs deterministic tests, builds both images, runs the live Compose test stack, and then publishes multi-architecture images with SBOM and provenance metadata.
Run only the admin UI when proxy runtimes are deployed elsewhere:
docker compose up -d --build admin-uiThe admin UI still requires MySQL. Proxy-specific actions become available after proxy runtimes register management URLs and public PAC/proxy metadata. On admin-UI-only hosts, keep the proxy service out of the active Compose project and use --remove-orphans during updates so an old local proxy container is not recreated accidentally.
Admin-UI HTTPS still depends on the active SSL inspection CA material at /etc/squid/ssl/certs/ca.crt and /etc/squid/ssl/certs/ca.key to sign a dedicated Admin UI server leaf certificate at /etc/squid/ssl/certs/admin-ui.crt and /etc/squid/ssl/certs/admin-ui.key. The packaged Compose definitions mount ./squid/ssl/certs into the admin UI for this purpose; standalone admin-UI deployments must keep that same mount available and writable when using the Certificates page HTTPS toggle.
When multiple proxy runtimes share one MySQL/admin-ui control plane, every proxy container must have a stable, unique identity and public coordinates:
PROXY_INSTANCE_ID=site-a-proxy-1
PROXY_DISPLAY_NAME=Site A Proxy 1
PROXY_PUBLIC_HOST=site-a-proxy-1.example.com
PROXY_PUBLIC_PAC_URL=http://site-a-proxy-1.example.com/proxy.pac
PROXY_MANAGEMENT_URL=http://site-a-proxy-1.example.com:5000Set DEFAULT_PROXY_ID only on the admin UI host to choose the initial UI
selection. Do not reuse the same PROXY_INSTANCE_ID on multiple proxy hosts;
registration, heartbeat, queued operations, PAC metadata, and health status are
keyed by that ID. If PROXY_PUBLIC_PAC_URL points at a non-default path such as
/wpad.dat or a reverse-proxy route, generated PAC metadata preserves that
path instead of rewriting it to /proxy.pac.
Keep each proxy container's shared-memory allocation at or above the Compose
default PROXY_SHM_SIZE=512m unless you also lower Squid memory cache settings.
The default Squid template uses shared memory for cache metadata; Docker's bare
docker run default /dev/shm size is too small for that production profile.
For a six-proxy fleet plus one admin UI, budget MySQL capacity explicitly. The
bundled MySQL Compose profile defaults MYSQL_MAX_CONNECTIONS=160 and
MYSQL_MAX_ALLOWED_PACKET=256M; external MySQL deployments should set
equivalent connection and packet headroom so compiled adblock artifacts and
larger policy snapshots can persist cleanly. Leave DB_POOL_SIZE blank unless
you have measured a need to override it: the application derives a small
per-process idle pool from WEB_THREADS, and six default proxy containers plus
one default admin UI stay well inside the 160-connection budget.
The Admin UI starts scheduled MySQL housekeeping when background services are
enabled. In multi-worker Gunicorn deployments, a file lock allows one process to
run background tailers, samplers, and housekeeping while the other workers serve
requests normally. Daily runs prune stored observability rows and stale
control-plane history; weekly runs also refresh optimizer statistics.
Control-plane cleanup preserves active config/certificate/adblock artifact
revisions, keeps recent apply/operation/policy history, expires stale temporary
policy exceptions, and removes expired Safe Browsing cache rows. Tune
MYSQL_CONTROL_PLANE_RETENTION_DAYS, MYSQL_HOUSEKEEPING_KEEP_REVISIONS,
MYSQL_HOUSEKEEPING_KEEP_APPLICATIONS, MYSQL_HOUSEKEEPING_KEEP_POLICY_ROWS, and
MYSQL_HOUSEKEEPING_KEEP_MAINTENANCE_RUNS only when a deployment needs more
audit depth or tighter storage bounds. Operations are always hard-capped at 128
rows per proxy (active rows are protected); MYSQL_HOUSEKEEPING_KEEP_OPERATIONS
can lower, but never raise, that scheduled-housekeeping bound.
- Template-backed Squid configuration editor with structured controls and raw config access.
- Tunable caching baseline using Squid
rockstorage, bounded memory/disk cache settings, conservative refresh semantics, explicit timeout/network/DNS knobs, and SMP-aware worker sizing. - Config revisions stored in MySQL with audit trails.
- Proxy-side validation, apply, reload, and last-known-good rollback.
- Cache clear and manual synchronization actions scoped to the selected proxy.
- Per-proxy PAC profiles selected by client IP/CIDR.
- Forwarded client IP headers are ignored unless the immediate peer is listed in
PAC_TRUSTED_PROXY_CIDRS, preventing clients from spoofing PAC profile selection. - Direct-domain and direct-destination-network rules for fragile applications or local routes.
- Runtime-rendered PAC files served by the selected proxy, not by the admin UI.
- WPAD-compatible
/wpad.datand direct/proxy.pacendpoints. - Emergency PAC fallback when rendered state is unavailable.
- Self-signed CA generation and certificate download.
- PKCS#12 and certificate/key upload validation.
- SSL-bump policy management with domain and client-CIDR no-bump/no-cache rules.
- Dedicated HTTPS NAT intercept listener controls, including an optional splice-only mode for redirected TCP/443 traffic that should be tunneled without decryption.
- Source-backed compatibility presets for common SaaS, identity, update, collaboration, and device ecosystems that are poor candidates for TLS break-and-inspect.
- SSL/TLS error aggregation, export, and exclusion workflows.
- UT1-style category feed ingestion and category selection.
- Exact and wildcard whitelist support.
- MySQL-backed category tables with proxy-local lookup snapshots for request-path decisions.
- Squid external ACL integration and a custom
ERR_WEBFILTER_BLOCKEDpage. - Blocked-request logging with client, destination, URL, category, and timestamp.
- Optional Google Safe Browsing v5 integration using local hash-prefix lists and full-hash lookups only after a local prefix match.
- EasyList-style subscription download and compilation.
- SQLite-backed REQMOD service at
icap://127.0.0.1:${CICAP_PORT:-14000}/adblockreq. - Header-only adblock decisions for browsing and common API methods; CONNECT tunnel setup is deliberately excluded because authority-only requests lack safe URL/resource/party context. Request bodies are bounded and drained only enough to keep ICAP transactions healthy.
- ABP-style network rules, exceptions, resource-type options, third-party checks, domain scoping, wildcard hosts, regex rules, and
$badfiltersuppression are compiled into proxy-local lookup artifacts. - Domain, host-pattern, regex-token, generic-literal, resource-type, and domain-scope SQLite indexes are staged locally in the proxy container so request-path checks do not scan every parsed rule.
- Cosmetic, scriptlet, and HTML-filter rules are parsed into artifact buckets for visibility and future use, but the proxy runtime does not inject browser-side cosmetic filtering.
- Block counters, recent event logging, and artifact application tracking.
- c-icap request/upload antivirus service at
icap://127.0.0.1:${CICAP_AV_PORT:-14001}/avrespmod. - Remote
clamdbackend configured withCLAMD_HOSTandCLAMD_PORT. - Download/RESPMOD AV scanning uses a local ICAP helper that streams response
bytes to remote
clamdwith the TCP INSTREAM protocol whenCLAMD_HOSTis non-local; it listens onCICAP_AV_RESP_PORTwhen set, otherwise the first non-overlapping port after the request/upload c-icap worker range. This avoids proxy-local temp-file path handoff. - Fail-open/fail-closed policy controls, c-icap
virus_scantuning for uploads, and stream-based RESPMOD scanning for downloads. CLAMAV_STREAM_MAX_BYTESis the proxy helper's explicit per-response scan ceiling (256 MiB by default). Remote clamdStreamMaxLength/MaxFileSizesettings are not discoverable through the INSTREAM protocol and should be at least this large for full scanning. If either side's limit is reached, the helper never labels the response clean: optional/fail-open AV drains and disk-spools the complete response before transparent unscanned replay, while required/fail-closed AV drains it and returns a safe policy block.- Per-proxy health view that separates Squid policy, AV c-icap listener health, and remote
clamdreachability. - EICAR and sample ICAP verification actions executed through the selected proxy runtime.
- Fleet page with registered proxies, live health, and per-proxy observability status.
- Lightweight navigation-time proxy health plus full runtime health for remediation workflows.
- Runtime health components for supervisor state, Squid listeners, ICAP services, ClamAV/
clamd, policy/config/certificate/adblock alignment, and the operation ledger. - Live traffic pages for clients, domains, cache behavior, transactions, and ICAP activity.
- Diagnostic ingestion from Squid and ICAP helper logs into MySQL-backed rollups.
- SSL error store, block logs, CSV-style exports, remediation recommendations, and log maintenance actions.
- Operations page for queued, applying, applied, superseded, and failed proxy operations, including rollback support when a failed operation has a rollback target.
- Policy request workflow for users to submit unblock/review requests from custom block pages.
The containers expose the common production knobs as environment variables, either from Compose/root .env interpolation or a mounted /config/app.env. The most important settings are:
| Area | Variables |
|---|---|
| Database | DATABASE_URL, MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DATABASE, MYSQL_CREATE_DATABASE, MYSQL_CONNECT_TIMEOUT, MYSQL_READ_TIMEOUT, MYSQL_WRITE_TIMEOUT, MYSQL_CONNECT_RETRIES, MYSQL_CONNECT_RETRY_DELAY_SECONDS, MYSQL_RETRY_JITTER_SECONDS, MYSQL_LOCK_WAIT_TIMEOUT, MYSQL_INNODB_LOCK_WAIT_TIMEOUT, MYSQL_SESSION_WAIT_TIMEOUT, MYSQL_TRANSACTION_ISOLATION, MYSQL_SCHEMA_LOCK_TIMEOUT_SECONDS, MYSQL_MAX_CONNECTIONS, MYSQL_MAX_ALLOWED_PACKET, DB_POOL_SIZE, DB_POOL_ACQUIRE_TIMEOUT_SECONDS, DB_POOL_MAX_IDLE_SECONDS |
| Container logging | DOCKER_LOG_DRIVER, DOCKER_LOG_MAX_SIZE, DOCKER_LOG_MAX_FILE |
| Security | FLASK_SECRET_KEY, SESSION_COOKIE_SECURE, SESSION_TIMEOUT_HOURS, PROXY_MANAGEMENT_TOKEN, DISABLE_CSRF for controlled test/dev bypasses |
| Runtime health | PROXY_HEALTH_UI_TIMEOUT_SECONDS, PROXY_CLAMAV_HEALTH_UI_TIMEOUT_SECONDS, PROXY_HEALTH_UI_CACHE_TTL_SECONDS, PROXY_HEALTH_UI_STALE_IF_ERROR_SECONDS, PROXY_OBSERVABILITY_UI_CACHE_TTL_SECONDS, PROXY_HEALTH_CACHE_TTL_SECONDS, PROXY_CLAMAV_HEALTH_PROBE_TIMEOUT_SECONDS |
| Proxy identity | DEFAULT_PROXY_ID, PROXY_INSTANCE_ID, PROXY_DISPLAY_NAME, PROXY_MANAGEMENT_URL, PROXY_PUBLIC_HOST, PROXY_PUBLIC_PAC_URL |
| Proxy recovery | PROXY_RECOVERY_MAX_BUNDLE_BYTES (decimal bytes; default 134217728 / 128 MiB, hard cap 536870912 / 512 MiB) |
| Public ports | PROXY_PUBLIC_PAC_SCHEME, PROXY_PUBLIC_PAC_PORT, PROXY_PUBLIC_HTTP_PROXY_PORT, PAC_HTTP_HOST, PAC_HTTP_PORT, FORWARDING_CANARY_HOST, FORWARDING_CANARY_PORT, FORWARDING_CANARY_PATH, PAC_TRUSTED_PROXY_CIDRS, SQUID_HTTP_PORT, SQUID_INTERCEPT_ENABLED, SQUID_INTERCEPT_PORT, PROXY_PUBLIC_INTERCEPT_PORT, SQUID_HTTPS_INTERCEPT_ENABLED, SQUID_HTTPS_INTERCEPT_PORT, SQUID_HTTPS_INTERCEPT_SPLICE_ONLY, PROXY_PUBLIC_HTTPS_INTERCEPT_PORT |
| Public policy requests | POLICY_REQUEST_MAX_CONTENT_LENGTH (default 16384 bytes), POLICY_REQUEST_MAX_PENDING_PER_PROXY (default 5000; 1–100000), POLICY_REQUEST_MAX_PENDING_PER_CLIENT (default 20; 1–1000). Equivalent pending submissions reuse the existing request; admission limits are scoped by proxy and client IP. Invalid or out-of-range values use the safe defaults. |
| Squid sizing | SQUID_WORKERS, SQUID_CACHE_MEM_MB, PROXY_SHM_SIZE, SQUID_SSLCRTD_CHILDREN, SQUID_DYNAMIC_CERT_MEM_CACHE_MB, SQUID_MAX_FILEDESCRIPTORS, ULIMIT_NOFILE |
| ICAP and AV | CICAP_PORT, CICAP_AV_PORT, CICAP_AV_RESP_PORT, CLAMD_HOST, CLAMD_PORT |
| Adblock helper | ADBLOCK_CACHE_TTL, ADBLOCK_CACHE_MAX, ADBLOCK_UPDATE_INTERVAL (scheduled subscription download/build cadence; default 43200 seconds / 12 hours), ADBLOCK_ARTIFACT_EXTRACT_MAX_BYTES (shared builder/runtime/recovery uncompressed-artifact budget; default 1073741824 / 1 GiB, hard cap 4 GiB), ADBLOCK_ARTIFACT_EXTRACT_MAX_MEMBERS (default 256), ADBLOCK_RULE_CACHE_MAX, ADBLOCK_ICAP_MAX_REQUEST_BYTES, ADBLOCK_ICAP_MAX_BODY_DRAIN_BYTES, ADBLOCK_ICAP_REQUEST_TIMEOUT, ADBLOCK_ICAP_MAX_KEEPALIVE_REQUESTS |
| Web filtering helpers | WEBFILTER_HELPERS, WEBFILTER_CACHE_ENTRIES, WEBFILTER_CACHE_TTL_SECONDS, WEBFILTER_CACHE_NEGATIVE_TTL_SECONDS, WEBFILTER_SNAPSHOT_REFRESH_SECONDS, WEBFILTER_FAIL, SAFE_BROWSING_POLL_SECONDS, SAFE_BROWSING_HELPER_CACHE_ENTRIES, SAFE_BROWSING_HELPER_PREFIX_HIT_TTL_SECONDS, SAFE_BROWSING_HELPER_PREFIX_MISS_TTL_SECONDS, SAFE_BROWSING_FAIL |
| Runtime cadence | PROXY_HEARTBEAT_INTERVAL_SECONDS, PROXY_SYNC_INTERVAL_SECONDS, LIVE_STATS_COMMIT_BATCH, LIVE_STATS_COMMIT_INTERVAL_SECONDS, LIVE_STATS_POLL_INTERVAL_SECONDS, LIVE_STATS_DB_WRITE_BACKOFF_INITIAL_SECONDS, LIVE_STATS_DB_WRITE_BACKOFF_MAX_SECONDS, LIVE_STATS_DB_WRITE_BACKOFF_JITTER_RATIO, LIVE_STATS_MAX_PENDING_ROWS, DIAGNOSTIC_COMMIT_BATCH, DIAGNOSTIC_COMMIT_INTERVAL_SECONDS, DIAGNOSTIC_POLL_INTERVAL_SECONDS, DIAGNOSTIC_DB_WRITE_BACKOFF_INITIAL_SECONDS, DIAGNOSTIC_DB_WRITE_BACKOFF_MAX_SECONDS, DIAGNOSTIC_DB_WRITE_BACKOFF_JITTER_RATIO, DIAGNOSTIC_PENDING_MAX_ROWS, TIMESERIES_STARTUP_JITTER_SECONDS, TIMESERIES_ROLLUP_INTERVAL_SECONDS, TIMESERIES_SAMPLE_DB_BACKOFF_INITIAL_SECONDS, TIMESERIES_SAMPLE_DB_BACKOFF_MAX_SECONDS, TIMESERIES_SAMPLE_DB_BACKOFF_JITTER_RATIO, TIMESERIES_ROLLUP_DB_BACKOFF_INITIAL_SECONDS, TIMESERIES_ROLLUP_DB_BACKOFF_MAX_SECONDS, TIMESERIES_ROLLUP_DB_BACKOFF_JITTER_RATIO, SSL_ERRORS_COMMIT_BATCH, SSL_ERRORS_COMMIT_INTERVAL_SECONDS, SSL_ERRORS_POLL_INTERVAL_SECONDS, STATS_CACHE_DIR_SIZE_TTL_SECONDS |
| Background and housekeeping | DISABLE_BACKGROUND, BACKGROUND_LOCK_PATH, BACKGROUND_FORCE, MYSQL_CONTROL_PLANE_RETENTION_DAYS, MYSQL_HOUSEKEEPING_KEEP_REVISIONS, MYSQL_HOUSEKEEPING_KEEP_APPLICATIONS, MYSQL_HOUSEKEEPING_KEEP_OPERATIONS, MYSQL_HOUSEKEEPING_KEEP_POLICY_ROWS, MYSQL_HOUSEKEEPING_KEEP_MAINTENANCE_RUNS |
| Admin UI | WEB_WORKERS, WEB_THREADS, WEB_TIMEOUT, WEB_GRACEFUL_TIMEOUT, WEB_KEEPALIVE, ADMIN_UI_HTTPS_ENABLED |
The proxy entrypoint sanitizes PAC_HTTP_HOST, PAC_HTTP_PORT, local-only FORWARDING_CANARY_*, and WEB_* numeric launcher knobs before supervisor expands them, and the container healthcheck probes the effective PAC bind host (falling back to loopback for wildcard binds). The forwarding canary binds only inside the proxy container on loopback and gives full health a deterministic Squid/RESPMOD target without self-proxying the public PAC listener.
Both containers also load /config/app.env at startup when mounted. Use this for host-managed deployments that prefer a mounted environment file over a root .env.
The Admin UI serves plain HTTP on container port 5000 by default. The Certificates page includes an Admin UI HTTPS toggle that uses a dedicated Admin UI server leaf certificate signed by the active generated or uploaded SSL inspection CA bundle. When enabled, gunicorn reads /etc/squid/ssl/certs/admin-ui.crt and /etc/squid/ssl/certs/admin-ui.key; Squid SSL inspection continues to use /etc/squid/ssl/certs/ca.crt and /etc/squid/ssl/certs/ca.key. The mount is writable so the Certificates page can materialize the generated Admin UI leaf before restarting the Admin UI web process.
Saving the preference does not rewrite Compose files or mutate .env; it stores the setting in the control-plane DB and asks supervisor to restart only the Admin UI web process so gunicorn re-execs with HTTP or HTTPS. Enabling is rejected until a generated or uploaded SSL inspection CA bundle is active. On startup, the saved DB setting is the source of truth after the first UI save. ADMIN_UI_HTTPS_ENABLED remains a bootstrap fallback when the DB is unavailable or no UI preference has been saved yet; ADMIN_UI_SSL_CERTFILE and ADMIN_UI_SSL_KEYFILE are internal fallback knobs for custom launchers, not part of the packaged UI workflow.
The Compose services set Docker json-file rotation by default (10m, 3 files) so the Admin UI, proxy, and optional bundled MySQL service do not leave unbounded stdout/stderr logs on Docker hosts. These Compose interpolation values must be supplied from the root .env or shell environment; mounted /config/app.env files are loaded inside containers too late to affect Docker logging.
Docker_Proxy normally targets an external MySQL 8+ server. If you deploy MySQL alongside the stack, include the optional MySQL Compose file:
docker compose -f docker-compose.yml -f docker-compose.mysql.yml up -d --buildThe bundled MySQL service is attached to the Compose control network and is
not published to the host by default. That is intentional for single-host
stacks. For physically remote proxy containers, either use an externally managed
MySQL service or add an explicit host-port mapping on the MySQL host, restrict it
with host/network firewalls, and point every admin/proxy container at that
reachable address with MYSQL_HOST and MYSQL_PORT.
The bundled MySQL service mounts config/mysql/conf.d/99-docker-proxy-bounded-logs.cnf, which disables general and slow query logs by default, sets log_error_verbosity=2, sets max_connections=160, sets max_allowed_packet=256M, caps innodb_redo_log_capacity=256M, and expires binary logs after one day when binlogs are enabled. Operators who need verbose SQL logging, a different connection or packet budget, or longer PITR retention should override these settings with a later-mounted MySQL config file and explicit disk monitoring.
For externally managed MySQL containers, apply equivalent MySQL settings and Docker log rotation on that host. Host-global Docker daemon rotation, if desired for every container on the host, still belongs in /etc/docker/daemon.json; this application can provide Compose defaults but cannot safely rewrite the host daemon policy.
Older or disk-constrained hosts can legitimately take longer to answer management health requests. The Admin UI defaults to a 1.5 second navigation-health timeout, a 5 second ClamAV-health timeout, a 10 second UI cache, and a 60 second stale-if-error fallback for previously cached health payloads, while the proxy runtime caches health snapshots for 10 seconds by default. Normal navigation uses /api/manage/health for a lightweight supervisor/listener snapshot; remediation views can request /api/manage/health?full=1 for the heavier policy/config/certificate/adblock/operation-ledger view. The ClamAV page uses /api/manage/health/clamav so AV c-icap and clamd status does not depend on the full runtime snapshot. Tune PROXY_HEALTH_UI_TIMEOUT_SECONDS, PROXY_CLAMAV_HEALTH_UI_TIMEOUT_SECONDS, PROXY_HEALTH_UI_STALE_IF_ERROR_SECONDS, PROXY_HEALTH_CACHE_TTL_SECONDS, and PROXY_CLAMAV_HEALTH_PROBE_TIMEOUT_SECONDS for slower deployments.
Authoritative state lives in MySQL. The proxy container also persists local runtime assets in named volumes:
proxy_data->/var/lib/squid-flask-proxyfor policy artifacts, PAC renders, web-filter snapshots, adblock artifacts, and proxy-local state.squid_cache->/var/spool/squidfor Squid cache storage.squid_ssl_db->/var/lib/ssl_dbfor Squid sslcrtd state../squid/ssl/certs->/etc/squid/ssl/certsfor the generated or uploaded CA material in the default Compose setup.
docker compose down -v removes named volumes.
Published ports in the default Compose configuration:
5000/tcp: admin UI.80/tcp: public proxy health, PAC, and WPAD only.3128/tcp: explicit HTTP proxy.3129/tcp: plain-HTTP NAT intercept listener, useful only when intercept mode is enabled and client traffic is redirected by the surrounding network.3130/tcp: HTTPS NAT intercept listener, useful only when HTTPS intercept mode is enabled and client TCP/443 traffic is redirected by the surrounding network.
Destination-port policy:
- Non-standard HTTP and HTTPS destination ports are allowed by default.
- The baseline Squid template does not enforce restrictive
Safe_portsorSSL_portsdeny ACLs unless an operator adds them.
Operational guidance:
- Do not publish the admin UI to untrusted networks.
- Put the admin UI behind a management VLAN, VPN, reverse proxy, or SSH tunnel for shared environments.
- Set a strong, private
PROXY_MANAGEMENT_TOKEN; the admin UI and proxy management API must agree on this token. Both containers fail startup when it is missing or set to the publicchange-me,replace-with-a-long-random-token, orreplace_with_a_long_random_shared_tokenplaceholder. - Set
FLASK_SECRET_KEYwhen you want host-managed session-secret rotation instead of the MySQL-backed generated secret. - Session cookies are automatically marked
Securewhen the packaged Admin UI HTTPS runtime is active or the trusted WSGI request scheme is HTTPS. UseSESSION_COOKIE_SECURE=1when TLS is terminated by a reverse proxy that does not pass a trusted HTTPS WSGI scheme. - Treat SSL-bump as managed-device infrastructure: clients must trust the proxy CA, and applications with certificate pinning should be spliced.
- HTTP and HTTPS NAT intercept modes require external router or host firewall rules; the container listens on the intercept ports but does not install topology-specific redirect rules.
- Keep the proxy recovery directory on the proxy durable volume; see
docs/proxy-recovery.mdfor first-connection adoption and fail-closed recovery behavior. - Interception only covers TCP flows. If proxy enforcement matters for managed clients, block or reject UDP/443 at the network edge so HTTP/3/QUIC-capable applications fall back to TCP.
Docker Proxy is intended to be operated as managed network infrastructure, not as a drop-in desktop privacy tool or a turnkey firewall. It does not install router rules, enroll client trust stores, run a local clamd daemon, or make TLS interception safe for unmanaged devices automatically.
The project requires a MySQL 8+ backend today. SQLite and ad-hoc local state are not supported runtime modes. The default Compose stack publishes useful LAN-facing ports for testing and appliance deployment, but operators remain responsible for host firewalls, management-network exposure, database backups, certificate distribution, legal/organizational consent for inspection, and any compliance controls around retained logs.
Local deterministic tests:
.venv\Scripts\python.exe -m pytest -m "not live and not mysql" -p no:cacheprovider --durations=10 -ra web\testsLive Compose test stack:
docker compose -f docker-compose.yml -f docker-compose.live-tests.yml up --build --abort-on-container-exit --exit-code-from live-tests live-testsTeardown:
docker compose -f docker-compose.yml -f docker-compose.live-tests.yml down -vDeterministic tests cover stores, route boundaries, Squid/config rendering, certificate handling, LDAP/Active Directory and SAML admin-auth flows, PAC rendering, web-filter and Safe Browsing helpers, adblock parsing/lookups/materialization, ICAP request behavior, runtime rollback/self-heal paths, packaging contracts, and operational data parsing without needing the live Compose stack.
The live harness starts MySQL, the admin UI, two proxy runtimes, a traffic fixture, and a dedicated pytest runner. It verifies real login, public/admin health separation, PAC/WPAD serving, authenticated proxy management APIs, sync and config validation/apply paths, multi-proxy selection and scoping, certificate and policy workflows, adblock/web-filter enforcement, selected-proxy ClamAV reporting, cache clear, runtime disruption behavior, security headers/session/CSRF contracts, observability pages, exports, and proxied request telemetry paths.
GitHub Actions runs the release gate on main: Ruff lint -> deterministic tests and MySQL lifecycle integration tests -> image build tests -> live tests -> GHCR publish.
.github/workflows/ CI, live tests, and GHCR publication
docker/ Dockerfiles, entrypoints, health checks, supervisord, c-icap config
proxy/ Proxy-runtime Flask management API
scripts/ Certificate and sslcrtd helpers
squid/ Squid template, MIME data, and custom error pages
web/app.py Admin UI routes and workflows
web/services/ MySQL stores, policy engines, PAC rendering, proxy sync, observability
web/templates/ Admin UI pages
web/tools/ Squid helper programs and artifact builders
web/tests/ Deterministic and live pytest coverage
# Container state
docker compose ps
# Admin UI and proxy logs
docker compose logs -f admin-ui proxy
# Health endpoints
curl http://localhost:5000/health
curl http://localhost/health
# PAC/WPAD
curl http://localhost/proxy.pac
curl http://localhost/wpad.dat
# Explicit proxy smoke test
curl --proxy http://localhost:3128 http://example.com/Common issues:
- UI works locally but not from the LAN: check host firewall rules for inbound
5000,80,3128, and any intercept port you publish. - Proxy actions are unavailable: verify the selected proxy is registered and has a reachable management URL.
- AV health is red: verify
CLAMD_HOST:CLAMD_PORT; the proxy container expects a remoteclamdservice. - Modern SaaS or meeting apps break under TLS inspection: splice the vendor domains, use the compatibility presets, and prefer PAC-based routing for clients that need DIRECT fallbacks.
- Transparent HTTP interception loops: exempt the proxy host/container source traffic before redirecting client TCP/80 to the intercept listener.
- HTTPS interception appears bypassed for some apps: check for HTTP/3/QUIC over UDP/443 and enforce TCP fallback at the router/firewall if those clients must traverse Squid policy.
The most useful contributions are reproducible bug reports, focused fixes, documentation corrections, deployment notes from real networks, and tests that cover proxy/runtime behavior without hiding operational risk. Before sending a pull request, run the smallest relevant deterministic test locally and explain what was not covered.
There is not yet a dedicated funding link in this repository. If a public sponsorship channel is added later, support should go toward appliance polish, documentation, security hardening, multi-architecture validation, live-stack coverage, and the operational work needed to make proxy deployments safer to run.
See LICENSE.



