I wanted to set up Wireguard on a VPS, not only to tunnel IPv4 traffic, but also allowing me to tunnel IPv6 traffic. As this is IPv6 of course I preferred not to use NAT, but to assign a public IP address to the client. I read some documentation and blog posts, but I struggled getting it to work. Most tutorials I found on the Internet, create a separate IPv6 subnet for the VPN but I could not get this to work. For some reason, IPv6 traffic successfully went through the VPN tunnel and then exited the VPN gateway, but then any response never reached my VPN gateway and hence also not the client.
I decided to try another way: using an NDP proxy. NDP or the Neighbour Discovery Protocol, is similar to ARP which is used in IPv4. Using this protocol, network devices can discover where on the network a certain IP is located. By letting the VPN gateway answer NDP requests for the VPN client, the gateway would correctly send back all responses to the VPN gateway, which then forwards it to the VPN clients.
Configuring the network on the VPN gateway
I use systemd-networkd to set up the network. It’s the most modern way of network configuration and works the same on all distributions using systemd, but of course you can make the same settings in /etc/network/interfaces
or whatever your distribution uses. Of course when making changes to a remote server, make sure you can access a console without needing a working network connection on the server, in case things go wrong and the network connection breaks.
On my VPN server, the public network interface is named ens192 (use the command $ ip addr
to find it on your system). My public IPv4 address is www.xxx.yyy.zzz with subnet 255.255.255.0 and gateway ww.xx.yy.1. I have the 64 bit IPV6 prefix aaaa:bbbb:cccc:dddd and the IPv6 gateway is fe80::1.
I set this in /etc/systemd/network/internet.net:
[Match]
Name=ens192
[Network]
Address=aaaa:bbbb:cccc:dddd:0000:0000:0000:0001/64
Gateway=fe80::1
DNS=1.1.1.2
DNS=1.0.0.2
Address= www.xxx.yyy.zzz/24
Gateway=www.xxx.yyy.1
DNS=2606:4700:4700::1112
DNS=2606:4700:4700::1002
In this example I’m using the Cloudflare malware blocking DNS filters, but you can of course just use your ISP’s DNS servers here.
Setting up Wireguard
Run these commands on the Wireguard VPN gateway, and on all clients:
# apt install wireguard-tools
# cd /etc/wireguard
# umask 077
# wg genkey | tee privatekey | wg pubkey > publickey
Then create /etc/wireguard/wg0.conf on the VPN gateway with these contents:
[Interface]
Address = 192.168.7.1,fd42:42:42::1/64
PrivateKey = contents_of_file_privatekey
ListenPort = 51820
#client1
[Peer]
PublicKey = contents_of_publickey_of_client
AllowedIPs = 192.168.7.2/32,aaaa:bbbb:cccc:dddd:ffff::2/128
Add a [Peer]
section for every client, and change the both the IPv4 and IPv6 address in AllowedIPs so that they are unique (replace 2 by 3 and so on) .
On the clients, create /etc/wireguard/wg0.conf with these contents:
[Interface]
Address = 192.168.7.2/32,aaaa:bbbb:cccc:dddd:ffff::2/128
PrivateKey = contents_of_privatekey_of_client
DNS = 2606:4700:4700::1112, 2606:4700:4700::1002, 1.1.1.2, 1.0.0.2
[Peer]
PublicKey = contents_of_publickey_of_vpn_gateway
Endpoint = vpngateway.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
In the [Interface]
section make sure to use the same IP addresses as the ones you have set in the corresponding [Peer]
section on the VPN gateway. Set the DNS name (or IP address) of the VPN gateway as Endpoint in the [Peer]
section. The hostname’s DNS entry can have both an A and AAAA record. You can replace your DNS servers by your preferred ones. You can also consider running your own DNS server on the VPN gateway.
Make sure that all wg*.conf files on client and server are only readable by root, because they contain private keys.
Configuring the firewall (Shorewall)
I use the Shoreline firewall, Shorewall, as firewall.
Make sure you have shorewall and shorewall6 installed:
# apt install shorewall shorewall6
Shorewall6
First we create a separate zone for our VPN in /etc/shorewall6/zones:
fw firewall
net ipv6
vpn ipv6
Then we configure the network interfaces and assign it to the right zone in /etc/shorewall6/interfaces:
net NET_IF tcpflags,routeback,proxyndp,physical=ens192
vpn wg0 tcpflags,routeback,optional
Then we allow connections from the VPN to the firewall and to the Internet in /etc/shorewall6/policy:
$FW net ACCEPT
vpn net ACCEPT
vpn $FW ACCEPT
net all DROP $LOG_LEVEL
# The FOLLOWING POLICY MUST BE LAST
all all REJECT $LOG_LEVEL
Keep in mind that your VPN client will have a public IPv6 address, which is accessible from the Internet. The rule net all DROP
protects your VPN clients against access from the Internet.
Then we create some rules which allows access to the SSH server and the Wireguard VPN server from the Internet in /etc/shorewall6/rules:
Invalid(DROP) net $FW tcp
Ping(DROP) net $FW
ACCEPT $FW net ipv6-icmp
AllowICMPs(ACCEPT) all all
ACCEPT all all ipv6-icmp echo-request
SSH(ACCEPT) net $FW
ACCEPT net $FW udp 51820 # Wireguard
We allow some required ICMPv6 message types defined in /usr/share/shorewall/action.AllowICMPs, as well as the echo-request type, which should not be dropped on IPv6.
For security reasons you could even choose to not open the SSH port for the net zone. SSH will only be accessible via the VPN then.
Finally we need to enable IP forwarding in /etc/shorewall6/shorewall6.conf:
IP_FORWARDING=Yes
Then we check whether everything compiles fine and enable and start the service:
# shorewall6 compile
# systemctl restart shorewall6
# systemctl enable shorewall6
Shorewall
For IPv4 we configure Shorewall to use NAT to provide Internet access to the VPN clients.
/etc/shorewall/zones:
fw firewall
net ipv4
vpn ipv4
/etc/shorewall/interfaces:
net NET_IF dhcp,tcpflags,logmartians,nosmurfs,sourceroute=0,routefilter,routeback,physical=ens192
vpn wg0 tcpflags,logmartians,nosmurfs,sourceroute=0,optional,routefilter,routeback
/etc/shorewall/policy:
$FW net ACCEPT
vpn net ACCEPT
vpn $FW ACCEPT
net all DROP $LOG_LEVEL
# The FOLLOWING POLICY MUST BE LAST
all all REJECT $LOG_LEVEL
/etc/shorewall/rules:
# Drop packets in the INVALID state
Invalid(DROP) net $FW tcp
# Drop Ping from the "bad" net zone.. and prevent your log from being flooded..
Ping(DROP) net $FW
SSH(ACCEPT) net $FW
ACCEPT net $FW udp 51820
/etc/shorewall/snat:
MASQUERADE 192.168.7.0/24 NET_IF
/etc/shorewall/shorewall.conf:
IP_FORWARDING=Yes
Compile and load the rules and enable Shorewall permanently:
# shorewall compile
# systemctl restart shorewall
# systemctl enable shorewall
Setting up NDP proxying
Then in order to make sure that the gateway knows that the VPN client aaa:bbb:cccc:dddd::2 is reachable via the VPN gateway, we need to set up NDP proxying. The Neighbor Discovery Protocol is similar to ARP in IPv6.
In a previous version of this guide, I configured NDP proxying in Shorewall6. However, we can directly set this up with systemd-networkd, so this will also work if you don’t use Shorewall6 but another firewall like Firewalld. Furthermore I also experienced problems with NDP proxy settings being lost after some time, requiring a restart of Shorewall6 to make the IPv6 connection over Wireguard work again. I hope this will be fixed by settings this up in systemd-networkd.
Edit again the file /etc/systemd/network/internet.net and in the [NETWORK]
section add this
IPv6ProxyNDP=1
IPv6ProxyNDPAddress=aaaa:bbbb:cccc:dddd:ffff::2
If you have more than 1 Wireguard client, you can add multiple IPv6ProxyNDPAddress
lines to the file, one for each IPv6 address you want to proxy.
Then restart the systemd-networkd service:
# systemctl restart systemd-networkd
With this command you can check whether they have been set up correctly:
# ip -6 neighbour show proxy
Enabling and testing the VPN
On the server run this to enable the Wireguard server:
# systemctl enable --now wg-quick@wg0
To connect to the VPN, run this on the client:
# systemctl start wg-quick@wg0
Check if you can browse the world wide web. Use these websites to check your IP address and whether you have a working IPv6 connection:
You can also use traceroute and traceroute6 to test whether traffic is correctly going through the VPN tunnel:
# traceroute www.google.com
# traceroute6 www.google.com
Debugging Wireguard
If things don’t work as expected, you can enable debug logging in the Wireguard module with this command:
# echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control
Replace +p
by -p
in order to disable debug logging. You can find the logs in your kernel messages, for example by running
# journalctl -f -k
Also firewall log messages will appear here.
You can use tcpdump to check the traffic on the wire (or in the VPN tunnel). For example to see all ipv6 traffic in the tunnel on the gateway:
# tcpdump -nettti wg0 "ip6"
Sources
Setup WireGuard with global IPv6
Reddit: Wireguard doesn’t seem to work with IPv6
One thought on “Setting up Wireguard VPN with IPv6”