Litmus Forward Proxy
Overview
The Forward Proxy is a containerized HTTP/HTTPS proxy with a web UI. Machines on a restricted network route outbound traffic through it instead of reaching the internet directly. From the browser you set which source networks may connect, which domains are blocked, and see what traffic is flowing.
The engine is Squid. Every change is validated before it is saved, so a bad edit cannot take the proxy offline.
Use it to give a plant network one controlled exit point: a single host allowed outbound at the firewall, only approved source networks permitted, every request logged, and repeated downloads served from a local cache.
Getting Started
Run on any LitmusEdge device:
docker run -d --name forwardproxy --restart unless-stopped -p 3128:3128 -p 3130:3130 -v squid-conf:/etc/squid -v squid-logs:/var/log/squid -v squid-cache:/var/spool/squid -v viewer-data:/var/lib/squidviewer -e UI_USERNAME=admin -e UI_PASSWORD=<your-password> us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/forwardproxy:0.5.0- Navigate to the {edgeUrl}:3130 and sign in with the username and password you set
- Clients point at {edgeUrl}:3128, which is the proxy itself
Notes on the command above:
- Set a real password. There is no default, and an empty UI_PASSWORD starts the UI unauthenticated
- Pin the version tag. An orchestrator that caches latest will serve an older build and report success
- Keep all four volumes, or rules, logs, and the password are lost when the container is recreated
- Add --platform linux/amd64 on an ARM host
- Container ports are fixed at 3128 and 3130. If Litmus Edge maps them itself, use the host ports from the container detail view
How to Route Traffic Through the Proxy
Step 1: Allow Your Client Networks
On the Rules page, add the networks permitted to use the proxy, then click Reload Squid. The shipped configuration allows 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, and 127.0.0.1/32, and clients outside those ranges are refused with 403. A CIDR network or a single address is accepted, IPv4 or IPv6.
Step 2: Pointing Litmus Edge Itself at the Proxy
Follow Configure an HTTPs Proxy: navigate to System > Network, select the Proxy tab, click Add Proxy on the tile for each component you need (Device Management, Authentication, Licensing), and enter http://<edge-ip>:3128.
Two related behaviors: Litmus Edge Manager registration does a raw TLS dial that ignores the proxy, so it needs one-time direct reachability to the LEM, and WireGuard remote access uses UDP port 51820 and cannot traverse an HTTP proxy.
Step 3: Verify
curl -x http://<edge-ip>:3128 http://example.com
curl -x http://<edge-ip>:3128 https://example.comBoth should return the page, and both should appear on the Logs page. A TCP_DENIED/403 line means the proxy received the request and refused it, almost always because the client address is not in an allowed network.
Step 4: Block Domains (Optional)
On the Rules page, add domains the proxy should refuse even for an allowed client, then click Reload Squid. Paste a full URL and the domain is extracted for you. A leading dot matches subdomains, so .example.com blocks everything beneath it.
Other Deployment Options
- Docker Compose: cd docker-compose, cp .env.example .env, set UI_PASSWORD, then docker compose up -d. .env holds UI_USERNAME, UI_PASSWORD, SQUID_PUBLISH_PORT, and UI_PUBLISH_PORT
- Air-gapped host: docker load -i forwardproxy.tar.gz on the target. The UI fetches no external assets, so it works fully offline
- LEM Marketplace: upload forwardproxy.tar.gz to the LEM Docker registry, import the listing under Features > Marketplace, then deploy supplying UI_PASSWORD
Web UI
Page | What it does |
|---|---|
Rules | Allowed source networks and blocked domains |
Config | Full squid.conf editor. Validate parses, Save writes only if it parses, Reload Squid applies. Also sets the configuration path, log path, and proxy address. |
Stats | Requests, bytes, cache hit ratio, top domains, methods, and status codes, over the last 20,000 log lines |
Logs | Live tail of the access log, last 200 lines by default |
Password | Set or change the sign-in password |
A configuration Squid would reject is never written. If a reload fails, the page shows Squid's first fatal line and the proxy keeps serving its previous configuration.
A password set in the UI is stored hashed and takes precedence over UI_PASSWORD, so an operator can secure a deployment without touching the container. Changing it signs out every session, including your own.
Troubleshooting
- Connection refused: Wrong host or port. Confirm the published port with docker ps
- Request denied with 403: The client address is outside the allowed networks. Add it on the Rules page, then Reload Squid. NAT changes the source address, so take it from the log line
- One site denied, others work: The domain is blocked, or the site uses a port other than 80 or 443
- Web UI not reachable: Run docker exec forwardproxy supervisorctl -c /etc/supervisor/conf.d/supervisord.conf status and expect squid, viewer, and logrotate all RUNNING
- Changes saved but behavior unchanged: Click Reload Squid
- Locked out of the UI: Recreate the container with a new UI_PASSWORD and remove the settings volume, since a stored password takes precedence
- Rules or logs lost after redeploy: A volume was not mounted
To separate a client problem from a proxy problem, test from the proxy host itself. A 200 means the proxy works and the problem is between the client and the proxy:
docker exec forwardproxy curl -s -o /dev/null -w '%{http_code}\n' -x http://127.0.0.1:3128 http://example.comSecurity Considerations
The web UI is an administrative tool for a trusted operator on a trusted network.
- Do not expose the proxy port to the internet. An open forward proxy will be found and abused
- Do not expose the UI port outside a trusted network. Bind it to localhost and reach it over SSH or a VPN, or put it behind an authenticating TLS reverse proxy
- Terminate TLS in front of the UI. The credential is sent in the clear over plain HTTP. With TLS in front, set SESSION_COOKIE_SECURE=1
- Anyone with UI access controls the proxy. The blast radius is the container: it mounts no Docker socket and holds no host credentials
- No rate limiting or account lockout on sign-in
Reference
Defaults
Setting | Value |
|---|---|
Proxy port | 3128 |
Web UI port | 3130 |
Configuration | /etc/squid/squid.conf |
Access log | /var/log/squid/access.log, rotated daily, 5 generations kept |
Cache | /var/spool/squid, 500 MB, 2 MB maximum object size |
Allowed sources | 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.1/32 |
Allowed ports | 80 for HTTP, 443 for HTTPS CONNECT |
Image | us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/forwardproxy, linux/amd64 only |
Environment Variables
Variable | Default | Description |
|---|---|---|
UI_USERNAME | admin | Sign-in username |
UI_PASSWORD | (unset) | Sign-in password. Unset serves every page unauthenticated. Superseded by a password set in the UI. |
SESSION_MAX_AGE | 43200 | Session lifetime in seconds |
SESSION_COOKIE_SECURE | (unset) | Set to 1 when the UI is served over HTTPS |
LOG_LINES | 200 | Log lines shown by default |
MAX_LOG_LINES | 5000 | Upper bound on the lines query parameter |
STATS_LINES | 20000 | Log lines analyzed for the Stats page |
The configuration path, log path, and proxy address have variables too (SQUID_CONF_PATH, SQUID_LOG_PATH, SQUID_HOST, SQUID_PORT), but the Config page sets them, persists them, and overrides the variable.
Volumes
Mount | Holds |
|---|---|
/etc/squid | squid.conf. The shipped default is restored if the volume is empty. |
/var/log/squid | access.log, what Logs and Stats read |
/var/spool/squid | Disk cache |
/var/lib/squidviewer | Paths set from the UI, and the hashed password |
Anything not on a volume is lost when the container is recreated.