A Go-based proxychains alternative that routes any application's traffic through a Tailscale network and exit node using tsnet and LD_PRELOAD. Useful if you're in an environment where you can't install tailscale (like https://sprites.dev) and you want to access machines/apps on your tailnet, or want to route traffic through an exit node you control (much faster than trying to setup a socks proxy somewhere).
AND you can put it in reverse mode to SERVE any application's traffic and make it accessible through your tailnet! Think instant python http server or instant claude-code-webui, all without installing tailscale.
- Routes any application's traffic through Tailscale (not just proxy-aware apps)
- Auto-export listeners: Expose any server to your tailnet without configuration
- Support for Tailscale exit nodes
- SOCKS5 proxy implementation with tsnet
- LD_PRELOAD syscall interception (like proxychains)
- Works with applications that don't support proxies
- Configuration file support
- Automatic Tailscale authentication
- Transparent operation - no application modification needed
TailProxy uses the same technique as proxychains: LD_PRELOAD syscall interception. When you run a command through TailProxy, it:
- Starts an embedded Tailscale instance using
tsnet - Configures the specified exit node (if provided)
- Launches a local SOCKS5 proxy server on localhost
- Injects
libtailproxy.soviaLD_PRELOADto intercept network syscalls - Intercepts
connect(),getaddrinfo(), and other network calls - Redirects all TCP connections through the SOCKS5 proxy
- Routes all traffic through the Tailscale network
When export listeners mode is enabled (-export-listeners), TailProxy also:
- Intercepts
bind()calls and rewrites them to use loopback only - Detects when your application starts listening on a port
- Automatically creates a tsnet listener on the same port
- Forwards connections from the tailnet to your local service
[Your Application]
↓
[libtailproxy.so] (LD_PRELOAD intercepts connect())
↓
[SOCKS5 Proxy] (localhost:1080)
↓
[tsnet] (Embedded Tailscale)
↓
[Exit Node] (Optional)
↓
[Destination]
The C library (libtailproxy.so) intercepts all connect() syscalls at runtime and redirects them to the local SOCKS5 proxy, which then routes traffic through Tailscale.
# Clone the repository
git clone <repository-url>
cd tailproxy
# Build both components
make build
# This creates:
# - tailproxy (main binary)
# - libtailproxy.so (preload library)# Install to /usr/local/bin
sudo make install- Go 1.21+ (for tsnet)
- GCC (for compiling C library)
- Linux with glibc (for LD_PRELOAD)
- Tailscale account
Important: Both tailproxy and libtailproxy.so must be in the same directory!
tailproxy curl https://ifconfig.metailproxy -exit-node=exit-node-hostname curl https://ifconfig.meFor unattended setup (useful in CI/CD or scripts):
tailproxy -authkey=tskey-auth-xxxxx curl https://api.example.comtailproxy -hostname=my-proxy-node curl https://ifconfig.metailproxy -verbose curl https://ifconfig.meRun any server and automatically expose it to your tailnet:
# Expose a Python HTTP server to your tailnet
tailproxy -export-listeners python -m http.server 8000
# Expose a Node.js app
tailproxy -export-listeners node server.js
# With port filtering (only export specific ports)
tailproxy -export-listeners -export-allow-ports="3000,8080" ./my-serverWhen using export listeners:
- Your server binds only to loopback (127.0.0.1) - no LAN/WAN exposure
- The same port is accessible from any device on your tailnet
- Access via
<tailproxy-hostname>:<port>(e.g.,tailproxy:8000)
Create a config.json:
{
"exit_node": "exit-node-hostname",
"hostname": "tailproxy",
"authkey": "tskey-auth-xxxxx",
"proxy_port": 1080,
"verbose": false
}Then run:
tailproxy -config=config.json curl https://ifconfig.me-exit-node string
Tailscale exit node to use (hostname or IP)
-config string
Path to configuration file
-hostname string
Hostname for this tsnet node (default "tailproxy")
-authkey string
Tailscale auth key (optional, for unattended setup)
-port int
SOCKS5 proxy port (default 1080)
-verbose
Verbose logging
Export Listeners Options:
-export-listeners
Enable automatic port export via tsnet
-export-allow-ports string
Comma-separated ports or ranges to allow (e.g., "3000,8080,10000-10100")
-export-deny-ports string
Comma-separated ports or ranges to deny
-export-max int
Maximum number of simultaneous exported ports (default 32)
{
"exit_node": "exit-node-hostname",
"hostname": "tailproxy",
"authkey": "tskey-auth-xxxxx",
"proxy_port": 1080,
"verbose": false,
"export_listeners": false,
"export_allow_ports": "",
"export_deny_ports": "",
"export_max": 32
}All fields are optional. Command-line flags override configuration file values.
tailproxy -exit-node=us-exit curl https://ifconfig.metailproxy -exit-node=eu-exit python scraper.pytailproxy -exit-node=asia-exit wget https://example.comtailproxy -verbose ./my-app# Your dev server is now accessible to your tailnet at tailproxy:3000
tailproxy -export-listeners npm run dev# Expose pgAdmin only to your tailnet (not your LAN)
tailproxy -export-listeners -export-allow-ports="5050" pgadmin4TailProxy works with any dynamically-linked application that makes TCP connections, including:
- Command-line tools (curl, wget, ssh, git, etc.)
- Programming language runtimes (Python, Node.js, Ruby, etc.)
- Network utilities (telnet, nc, nmap, etc.)
- Custom applications
- Browsers and GUI applications
- Only works with dynamically-linked binaries (not statically-linked Go binaries)
- Only intercepts TCP connections (UDP requires different approach)
- Doesn't work with applications that use raw sockets or custom network stacks
- Some security-sensitive programs may block LD_PRELOAD
Exit nodes must be configured in your Tailscale network first. To set up an exit node:
-
On the exit node machine:
sudo tailscale up --advertise-exit-node
-
Approve the exit node in the Tailscale admin console
-
Use the exit node's hostname or IP in TailProxy:
tailproxy -exit-node=exit-node-hostname curl https://ifconfig.me
On first run, TailProxy will authenticate with Tailscale. You can either:
- Interactive: Follow the authentication URL printed to the console (first run only)
- Unattended: Provide an auth key with
-authkeyflag
To generate an auth key:
- Go to https://login.tailscale.com/admin/settings/keys
- Generate a new auth key
- Use it with the
-authkeyflag
TailProxy stores its state in /tmp/tailproxy-<hostname>/. Each unique hostname creates a separate Tailscale node.
Make sure the proxy port (default 1080) is not already in use:
tailproxy -port=1081 curl https://ifconfig.meVerify the exit node is approved and online in your Tailscale admin console.
Some applications may not respect proxy environment variables. Check the application's proxy configuration documentation.
Use -verbose flag to see detailed authentication logs:
tailproxy -verbose curl https://ifconfig.me| Feature | TailProxy | Proxychains |
|---|---|---|
| Method | LD_PRELOAD | LD_PRELOAD |
| Platform | Linux/Unix | Linux/Unix |
| Network | Tailscale (WireGuard) | Any SOCKS/HTTP proxy chain |
| Exit nodes | Built-in Tailscale | Manual proxy chain config |
| Transparency | Fully transparent | Fully transparent |
| Proxy server | Embedded (tsnet) | External |
| Authentication | Tailscale auth | Per-proxy auth |
| Encryption | WireGuard | Depends on proxy |
- State directory contains authentication tokens - protect it appropriately
- Auth keys should be kept secret and rotated regularly
- Exit node traffic is encrypted via WireGuard
- Local SOCKS5 proxy only listens on 127.0.0.1
- Export listeners mode: Services are only exposed to your tailnet, protected by Tailscale ACLs
- Bind address rewriting ensures services never accidentally listen on LAN/WAN interfaces
MIT
Contributions welcome! Please open an issue or PR.