wg-fulltunnel sets up a full-tunnel WireGuard VPN: a Kubuntu (or other Debian-based) client routes all of its traffic through a remote exit-node server, while staying reachable on its own original IP (e.g. for SSH) via policy routing – so the remote box doesn’t lock you out of itself the moment it becomes its own default route.
internet
|
| UDP 51820 (WireGuard handshake/data)
|
,-----------------+-----------------, ,--------------------------,
| EXIT NODE ("server" role) | | ORIGINAL UPSTREAM |
| e.g. a Debian box | | (client's ISP/LAN |
| | | gateway, unchanged) |
| eth0 <public IP> <----------------------->| |
| wg-debian-vpn 10.0.0.1/24 | `--------------------------'
`-----------------+-----------------' ^
| WireGuard tunnel | inbound SSH etc.
| (all client traffic, 0.0.0.0/0) | to client's
v | ORIGINAL ip
,-----------------+---------------------------------------+-----,
| CLIENT ("client" role) - e.g. Kubuntu PC |
| eth0 <original LAN IP> <-----------------------------------'
| wg-debian-vpn 10.0.0.2/24, default route (0.0.0.0/0) |
`----------------------------------------------------------------'
What it does
Everything is implemented by one script, wg-fulltunnel.sh, copied to and
run once on each machine. There is no central orchestrator – each box
chooses its own role with --role server or --role client.
- Full-tunnel client routing. The client’s default route points at the WireGuard interface, so all outbound traffic leaves through the exit node.
- No SSH lockout. Inbound connections to the client’s original IP are marked and routed back out the original gateway via a separate policy routing table, so opening the tunnel never cuts off the session you used to open it.
- DNS-leak kill switch. An nftables chain rejects any port-53 traffic
that isn’t going through the tunnel or the configured resolver, while
still exempting
127.0.0.53so localsystemd-resolvedkeeps working. - Automatic rollback. Before the tunnel comes up, a watchdog schedules an automatic revert (default 5 minutes). Confirm from a fresh connection once you’ve verified it’s working, or let it self-heal if something’s broken.
- Idempotent and safe to re-run. Config writes are backed up first,
firewall chains are flushed and rebuilt cleanly on every run, and
--dry-runshows exactly what would change before touching the system.
Quick start
Both roles need the other machine’s public key up front, so generate keys first.
1. On each machine, generate a keypair:
sudo ./wg-fulltunnel.sh --keygen --iface wg-debian-vpn
This prints that machine’s public key. Exchange the two – server’s pubkey goes to the client, client’s pubkey goes to the server.
2. On the exit-node server:
sudo ./wg-fulltunnel.sh --role server \
--wg-address 10.0.0.1/24 \
--peer-pubkey <client_public_key> \
--peer-allowed-ips 10.0.0.2/32
3. On the client:
sudo ./wg-fulltunnel.sh --role client \
--wg-address 10.0.0.2/24 \
--peer-pubkey <server_public_key> \
--endpoint <server_public_ip>:51820
Add --dry-run to either command first to see exactly what would change
without touching the system.
Safety net (client only)
From a new connection to the box’s original IP, confirm the change worked:
sudo ./wg-fulltunnel.sh --confirm-revert --iface wg-debian-vpn
If you don’t confirm in time, or the script’s own post-up checks fail, it reverts itself automatically.
sudo ./wg-fulltunnel.sh --rollback --iface wg-debian-vpn # undo manually
sudo ./wg-fulltunnel.sh --status --iface wg-debian-vpn # inspect current state
Full flag reference: ./wg-fulltunnel.sh --help.
Requirements
wireguard-tools, nftables, ip (iproute2), curl, and systemd. The
script checks for these and tells you what’s missing.