I have a Hetzner VPS that I use as a Tailscale exit node for, among other things, circumventing geolocation blocks. This works great for that purpose, but causes problems with sites like Reddit, imgur, CloudFlare, and Google, which limit or block IPs from datacenters like Hetzner’s. I can only turn the exit node on or off for all traffic, not just some apps or tabs, so I have to toggle it on and off all the time. Annoying.
The other day I realized that Squid fits very nicely into this problem. The exit node already trusts that other nodes on the encrypted tailnet are authorized and tunnels traffic for them without caching by design. If I added a regular unauthenticated non-caching HTTP proxy that listened exclusively on the Tailscale address this wouldn’t open up any security holes and would be extremely simple to configure.
And the key: applications can often be configured individually to use a proxy server or not, so unlike the exit node, I can run a proxied application and/or browser profile on the same device as an unproxied one.
How to
I deployed Squid to my exit node with this configuration:
Squid configuration file
# Squid configuration for a Tailscale open proxy
# Replace 100.x.y.z with the Tailscale IP address
http_port 100.x.y.z:3128
# Only allow traffic from the Tailscale CGNAT range
acl pprox_allow_net src 100.64.0.0/10
# Safe ports (standard ports we allow CONNECT to)
acl SSL_ports port 443
acl Safe_ports port 80
acl Safe_ports port 443
acl CONNECT method CONNECT
# Deny requests to unsafe ports
http_access deny !Safe_ports
# Deny CONNECT to non-SSL ports
http_access deny CONNECT !SSL_ports
# Allow requests from pprox_allow_net network
http_access allow pprox_allow_net
# Deny all other access
http_access deny all
# Disable caching
cache deny all
cache_mem 0 MB
maximum_object_size 0 KB
# Enable logging if you need it
#access_log daemon:/var/log/squid/access.log
#cache_log /var/log/squid/cache.log
# Disable via header to be more transparent
via offHere’s a simple example with curl, using your exit node’s MagicDNS name:
# Get the public IP address from my ISP
curl https://icanhazip.com
# Get the public IP address of my exit node
curl -x http://MACHINE.TAILNET.ts.net:3128 https://icanhazip.com
I wrote an ansible role to deploy this to my exit node (tip of master, specific commit).
Application support
Most applications can configure their own proxies:
- Firefox connection settings are set per-profile
- Chrome with the FoxyProxy extension can configure a proxy, and Chrome extensions are inherently per-profile
- iCab Mobile for iOS can set a proxy of its own, separate from other apps
curlcan use a proxy via theHTTP_PROXY=environment variable or-xcommand line flagyoutube-dlcan use a proxy via the--proxycommand line flag
Caveats
- Some applications, including Safari on macOS and iOS, cannot set proxies independent of the system
- This only works for normal HTTPS TCP-over-443 traffic like HTTPS 1.1, HTTP/2, and WebSockets; it cannot handle other traffic like QUIC HTTP/3, WebRTC (often used for telephony), or WebTorrent.