tswg turns a standard WireGuard config into a tiny Tailscale-compatible
control plane for testing Tailscale's WireGuard-only exit node path.
It has two modes:
serve: run a fake control server that accepts only the node key derived from the WireGuard[Interface] PrivateKey, then serves a netmap containing oneIsWireGuardOnlypeer from the WireGuard[Peer]section.state: write atailscaledFileStore JSON state file that points at the fake control server and preselects the WireGuard-only peer as the exit node.
This module is wired to a local Tailscale checkout:
go build -o ./tswg ./cmd/tswgStart the fake control server:
./tswg serve -config /path/to/wg.ini -listen 127.0.0.1:18080Generate a state file for tailscaled:
./tswg state \
-config /path/to/wg.ini \
-out /tmp/tswg-run/tailscaled.state \
-server http://127.0.0.1:18080Run tailscaled in userspace networking mode with an isolated socket and SOCKS
proxy:
tailscaled \
--tun=userspace-networking \
--state=/tmp/tswg-run/tailscaled.state \
--socket=/tmp/tswg-run/tailscaled.sock \
--socks5-server=127.0.0.1:18081 \
--no-logs-no-supportQuery that daemon with a matching tailscale binary:
tailscale --socket=/tmp/tswg-run/tailscaled.sock status
tailscale --socket=/tmp/tswg-run/tailscaled.sock exit-node listCheck the exit path through the SOCKS proxy:
curl -fsS https://canhazip.com/
curl -fsS --socks5 127.0.0.1:18081 https://canhazip.com/The second IP should be the WireGuard provider's egress IP.
The input is standard WireGuard config syntax with one [Interface] and one
[Peer], for example:
[Interface]
PrivateKey = ...
Address = 10.2.0.2/32, 2001:db8::2/128
DNS = 10.2.0.1, 2001:db8::1
[Peer]
PublicKey = ...
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 203.0.113.10:51820
PersistentKeepalive = 25PrivateKey is used as the Tailscale node key. PublicKey, AllowedIPs, and
Endpoint become the single WireGuard-only peer in the served netmap.
Only one [Peer] is supported. If the config contains more than one peer,
tswg panics with only one peer is supported at the moment.
PersistentKeepalive is parsed, but is not currently used. The generated
Tailscale netmap does not carry a WireGuard persistent keepalive setting, and
the current tailscaled WireGuard-only peer path leaves the WireGuard
persistent keepalive value disabled.