WireGuard as IPv6 Tunnel with 1:1 Port Forwarding
It’s quite unbelievable that in this era of the 25th and most part of the world still doesn’t have IPv6 working1, either intentionally or technology limitation. But anyways, nerds like you reader and me sure need dualstack right??….right?
¯\_(ツ)_/¯
Welp If you are, then you’re at the right place!
This blog will cover on topics of:
- IPv4 -> IPv6 Tunnel using WireGuard
- WireGuard 1:1 IPv6 port forwarding using
iptables
TLDR Full Configuration at the bottom
Prepare the HOST’s system
Requirements
- VPS or whatever with DualStack
- Preferably low latency to your client
- In Theory VPS with NAT IPv4 should be working too, tho i didn’t attempt this yet
- Secondary IPv6 address dedicated for WireGuard Client
Setup secondary IPv6 Address
Add the additional IPv6 Address alongside your main one, if your provider doesn’t give you more than 1 IPv6 Address by default(sad) please check on your provider’s panel on the information about the additional address or to assign them
iface eth0 inet6 static address PublicIPv6Address::1/64 # Main IPv6 Address(::1 this example) address PublicIPv6Address::ClientAddress/64 # Client IP gateway IPv6Gateway
PS. while this blog mainly focuses on /etc/network/interfaces
but regardless of netplan or whatever you’re using. There shouldn’t be a significants differences between configuration
Basic WireGuard Setup
I know this part is quite boring as most people would expect the finished or completed version but i made it almost step by step to make people understand how it’s work and atleast aware of what they’re doing
We’ll start by a simple standard WireGuard setup like this AKA the basic version
And in this tutorial, i assigned 10.10.0.1/24
and fd10:10:10::1/64
to be a WireGuard interface’s IP Address
Host’s :
net.ipv6.conf.all.forwarding
is set to 1
to allow forwarding IPv6 packets cross interfaces2, which make it possible for WireGuard Client to access internet via Host’s Network AKA VPN for general internet activity
and assigned 10.10.0.4
and fd10:10:10::123
to be “Client A” Addrress then filled the RED highlighted with respective Host’s PrivateKey and Client’s PublicKey
[Interface] Address = 10.10.0.1/24, fd10:10:10::1/64 ListenPort = 51820 PrivateKey = Server_PrivateKey # IPv6 forwarding PreUp = sysctl -w net.ipv6.conf.all.forwarding=1 # client A [Peer] PublicKey = Client_PublicKey AllowedIPs = 10.10.0.4/32, fd10:10:10::123/64
Client’s :
Now to the Client part, we set Address to 10.10.0.4
and fd10:10:10::123
as we assigned in the Server
We set AllowedIPs
to ::/0
which is basically all IPv6, to route all IPv6 traffic(in this ::/0
scenario) via secure WireGuard tunnel to remote server which is defined by the endpoint
section
The PersistentKeepalive
is set to make Peer behind NAT or FireWall to be reachable by another peers3
then filled the RED highlighted with respective Client’s PrivateKey and Host’s PublicKey
[Interface] Address = 10.10.0.4/24, fd10:10:10::123/64 DNS = 1.1.1.1, 1.0.0.1 PrivateKey = Client_PrivateKey [Peer] PublicKey = Server_publicKey AllowedIPs = ::/0 Endpoint = EndPoint PersistentKeepalive = 25
Port Forwarding
As you may notice in the Basic WireGuard Setup part net.ipv6.conf.all.forwarding=1
is set to allow forwarding IPv6 packets cross interfaces2 Which might be enough for some, but if we wanted to allow Port Forwarding from Client? This part we’ll right into it!
Mark packets
in Iptables, we can mark packets using MARK target in the mangle table for traffic filtering purposes which we’ll use this to manage our traffic in later stage.4
In this case, we’ll mark traffic into our wireguard interface with 0x30
but you can change it to whatever you like
PS. we might see %i
in ip6tables rules, this is copied from WireGuard configuration where interface name is supplied by WireGuard itself, so, please don’t forget to replace the interface name if you’re going to use it anywhere else
Add
ip6tables -t mangle -A PREROUTING -i %i -j MARK --set-mark 0x30
Delete
ip6tables -t mangle -D PREROUTING -i %i -j MARK --set-mark 0x30
1:1 Port Forwarding
here’s the fun part…..or the part you’re waiting for idk but anyways
READ More -> POST/PRE ROUTING graph for nerds: Link via linuxquestions
SNAT
Rewrite Source Address for MARKED traffic to Public_IPv6
before the packet leaves via any interface except the WireGuard interface, this is where the traffic marker become useful
Add
ip6tables -t nat -A POSTROUTING ! -o %i -m mark --mark 0x30 -j SNAT --to-source Public_IPv6
Delete
ip6tables -t nat -D POSTROUTING ! -o %i -m mark --mark 0x30 -j SNAT --to-source Public_IPv6
DNAT
FORWARD IN traffic into Public_IPv6
via network_interface
(eth0 in most case) to Client’s WireGuard interface IP, which is fd10:10:10::123
Add
ip6tables -t nat -A PREROUTING -i network_interface(eth0 etc.) -d Public_IPv6 -j DNAT --to-destination fd10:10:10::123
Delete
ip6tables -t nat -D PREROUTING -i network_interface(eth0 etc.) -d Public_IPv6 -j DNAT --to-destination fd10:10:10::123
(Optional) Specific Port Forward
In case you don’t want to 1:1 Port Forwarding and instead, just wanted to forward a specific port to Public_IPv6
SNAT
Rewrite source address for marked traffic same as above but only for PORT
Add
ip6tables -t nat -A POSTROUTING ! -o %i -m mark --mark 0x30 -p protocol(tcp/udp) --sport PORT -j SNAT --to-source Public_IPv6
Delete
ip6tables -t nat -D POSTROUTING ! -o %i -m mark --mark 0x30 -p protocol(tcp/udp) --sport PORT -j SNAT --to-source Public_IPv6
DNAT
FORWARD IN traffic into Public_IPv6
via network_interface
like above but just for PORT
and to [fd10:10:10::123]
:PORT
Add
ip6tables -t nat -A PREROUTING -i network_interface(eth0 etc.) -d Public_IPv6 -p tcp --dport PORT -j DNAT --to-destination [fd10:10:10::123]:PORT
Delete
ip6tables -t nat -D PREROUTING -i network_interface(eth0 etc.) -d Public_IPv6 -p tcp --dport PORT -j DNAT --to-destination [fd10:10:10::123]:PORT
Full Configuration:
Don’t want to read all those yapping? just copy configuration below :D
Server
[Interface] Address = 10.10.0.1/24, fd10:10:10::1/64 ListenPort = 51820 PrivateKey = Server_PrivateKey # IPv6 forwarding PreUp = sysctl -w net.ipv6.conf.all.forwarding=1 # IPv6 masquerading PreUp = ip6tables -t mangle -A PREROUTING -i %i -j MARK --set-mark 0x30 PreUp = ip6tables -t nat -A POSTROUTING ! -o %i -m mark --mark 0x30 -j SNAT --to-source Public_IPv6 PreUp = ip6tables -t nat -A PREROUTING -i network_interface(eth0 etc.) -d Public_IPv6 -j DNAT --to-destination fd10:10:10::123 PostDown = ip6tables -t mangle -D PREROUTING -i %i -j MARK --set-mark 0x30 PostDown = ip6tables -t nat -D POSTROUTING ! -o %i -m mark --mark 0x30 -j SNAT --to-source Public_IPv6 PostDown = ip6tables -t nat -D PREROUTING -i network_interface(eth0 etc.) -d Public_IPv6 -j DNAT --to-destination fd10:10:10::123 # client A [Peer] PublicKey = Client_PublicKey AllowedIPs = 10.10.0.4/32, fd10:10:10::123/64
Client
[Interface] Address = 10.10.0.4/24, fd10:10:10::123/64 DNS = 1.1.1.1, 1.0.0.1 PrivateKey = Client_PrivateKey [Peer] PublicKey = Server_publicKey AllowedIPs = ::/0 Endpoint = EndPoint PersistentKeepalive = 25