When a remote server connects to a global VPN, the change in its default route can break existing SSH sessions. If the actual need is just to route a subset of commands or applications through a different network egress, a more robust approach is to deploy an application-layer forward proxy and have only the clients that need it use it explicitly.
1. Preparing the Squid Configuration
Create a working directory:
mkdir -p ~/squid
cd ~/squid
Save the following configuration as squid.conf:
# Only allow trusted internal and Tailscale/CGNAT addresses.
acl localnet src 10.0.0.0/8
acl localnet src 100.64.0.0/10
acl localnet src 172.16.0.0/12
acl localnet src 192.168.0.0/16
acl localnet src fc00::/7
acl localnet src fe80::/10
acl SSL_ports port 443
acl Safe_ports port 80
acl Safe_ports port 21
acl Safe_ports port 443
acl Safe_ports port 70
acl Safe_ports port 210
acl Safe_ports port 280
acl Safe_ports port 488
acl Safe_ports port 591
acl Safe_ports port 777
acl Safe_ports port 1025-65535
acl CONNECT method CONNECT
http_access deny !Safe_ports
http_access deny CONNECT !SSL_ports
http_access allow localnet
http_access deny all
http_port 3128
coredump_dir /var/spool/squid
refresh_pattern ^ftp: 1440 20% 10080
refresh_pattern ^gopher: 1440 0% 1440
refresh_pattern -i (/cgi-bin/|\?) 0 0% 0
refresh_pattern . 0 20% 4320
The key part here isn’t the port — it’s the order of http_access:
- First deny unsafe destination ports;
- Only allow sources defined in
localnet; - Finally, deny everything else with
http_access deny all.
If you only need a smaller subnet, tighten the ACL to the actual address range. For example, if you only want to allow the AOI internal network, use 10.10.10.0/24 instead of allowing the whole 10.0.0.0/8.
2. Starting the Squid Container
This uses Ubuntu’s official Squid image, mapping the host’s 3129 to the container’s 3128:
docker run -d \
--name squid \
--restart unless-stopped \
-e TZ=UTC \
-p 3129:3128 \
-v "$PWD/squid.conf:/etc/squid/squid.conf:ro" \
ubuntu/squid:5.2-22.04_beta
If SELinux is enabled on the host, change the mount option to :Z,ro:
-v "$PWD/squid.conf:/etc/squid/squid.conf:Z,ro"
Logs and cache can also be persisted as needed:
| Mount path | Purpose |
|---|---|
/var/log/squid | Squid logs |
/var/spool/squid | Disk cache |
/etc/squid/squid.conf | Main configuration file |
/etc/squid/conf.d/ | Configuration snippets |
3. Verifying the Container and Configuration
Check the container’s status and logs:
docker ps --filter name=squid
docker logs --tail=100 squid
Verify the configuration syntax inside the container:
docker exec squid squid -k parse
Test HTTP from a client that’s allowed access:
curl -I -x http://10.10.10.11:3129 http://example.com
Test HTTPS:
curl -I -x http://10.10.10.11:3129 https://example.com
Ordinary HTTPS access uses a CONNECT tunnel, so there’s no need to generate a self-signed certificate on Squid. TLS between the client and the destination site stays end-to-end encrypted.
4. Configuring the Client
Command-line tools generally read the following environment variables:
export http_proxy=http://10.10.10.11:3129
export https_proxy=http://10.10.10.11:3129
Even though the second variable is named https_proxy, the proxy URL still uses http://: the client first negotiates a CONNECT with the HTTP proxy, then establishes a TLS connection to the destination site inside that tunnel.
If you need this to persist, add the environment variables to ~/.bashrc or ~/.zshrc. Not every program reads these variables — browsers and desktop applications may need to be configured separately.
5. Using It over Tailscale
The configuration allows 100.64.0.0/10, so clients can reach the proxy over their Tailscale address without exposing 3129 to the public internet:
export http_proxy=http://100.x.y.z:3129
export https_proxy=http://100.x.y.z:3129
Replace 100.x.y.z with the Tailscale address of the node running Squid. Besides Squid’s own ACL, also restrict access sources through the host firewall and Tailscale ACLs.
6. Updating the Configuration
After changing squid.conf, verify it first, then recreate the container:
docker exec squid squid -k parse
docker restart squid
If you need to deploy from a local machine to a remote server, copy the configuration over and run the start command via SSH — but don’t hardcode usernames, public IPs, or passwords in the script:
scp squid.conf server:~/squid/squid.conf
ssh server 'cd ~/squid && docker restart squid'
7. Security Boundaries
Don’t use http_access allow all in the configuration, and don’t expose the proxy port directly to the public internet. Squid’s application-layer ACL, the host firewall, and Tailscale ACLs should form layered restrictions. If username/password authentication is needed, proxy_auth can be added on top of this — but Basic credentials shouldn’t be transmitted in the clear over an untrusted network either.