Here is how I managed to extend my OpenVPN setup on my Linode VPS to include IPv6 traffic. This ensures that clients can route all of their traffic through the VPN and avoid leaking IPv6 traffic, for example. It also enables clients on IPv4-only networks to receive a routable IPv6 address and connect to IPv6-only servers (i.e. running your own IPv6 broker).
Request an additional IPv6 block
The first thing you need to do is get a new IPv6 address block (or "pool" as Linode calls it) from which you can allocate a single address to each VPN client that connects to the server.
If you are using a Linode VPS, there are
instructions on how to request a new IPv6 pool.
Note that you need to get an address block between /64
and /112
. A
/116
like Linode offers won't work in OpenVPN. Thankfully, Linode
is happy to allocate you an extra /64
for free.
Setup the new IPv6 address
If your server only has an single IPv4 address and a single IPv6 address,
then a simple DHCP-backed network configuration will work fine.
To add the second IPv6 block on the other hand, I had to change my network
configuration (/etc/systemd/network/20-wired.network
) to this:
[Match]
Name=eth0
[Network]
DHCP=yes
Address=2600:3c01::xxxx:xxxx:xxxx:939f/64
Gateway=fe80::1
and create a new /etc/systemd/network/21-vpn.network
:
[Match]
Name=tun0
[Network]
Address=10.8.0.1/24
Address=2600:3c01:xxxx:xxxx::/64
where 2600:3c01::xxxx:xxxx:xxxx:939f/64
(bound to eth0
) is your main
IPv6 address and
2600:3c01:xxxx:xxxx::/64
(bound to tun0
) is the new block you
requested.
Once you've setup the new IPv6 block, test it from another IPv6-enabled host using:
ping6 2600:3c01:xxxx:xxxx::1
OpenVPN configuration
The only thing I had to change in my
OpenVPN configuration
(/etc/openvpn/server.conf
) was to change:
proto udp
to:
proto udp6
in order to make the VPN server available over both IPv4 and IPv6, and to add the following lines:
server-ipv6 2600:3c01:xxxx:xxxx::/64
push "route-ipv6 2000::/3"
to bind to the right V6 address and to tell clients to tunnel all V6 Internet traffic through the VPN.
In addition to updating the OpenVPN config, you will need to add the
following line to /etc/sysctl.d/openvpn.conf
:
net.ipv6.conf.all.forwarding=1
and the following to your firewall (e.g. /etc/network/ip6tables.up.rules
):
# openvpn
-A INPUT -p udp --dport 1194 -j ACCEPT
-A FORWARD -m state --state NEW -i tun0 -o eth0 -s 2600:3c01:xxxx:xxxx::/64 -j ACCEPT
-A FORWARD -m state --state NEW -i eth0 -o tun0 -d 2600:3c01:xxxx:xxxx::/64 -j ACCEPT
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
in order to ensure that
IPv6 packets are forwarded from the eth0
network interface to tun0
on the VPN server.
With all of this done, apply the settings by running:
sysctl -p /etc/sysctl.d/openvpn.conf
ip6tables-apply
systemctl restart openvpn.service
Testing the connection
Now connect to the VPN using your desktop client and check that the default
IPv6 route is set correctly using ip -6 route
.
Then you can ping the server's new IP address:
ping6 2600:3c01:xxxx:xxxx::1
and from the server, you can ping the client's IP (which you can see in the network settings):
ping6 2600:3c01:xxxx:xxxx::1002
Once both ends of the tunnel can talk to each other, you can try pinging an IPv6-only server from your client:
ping6 ipv6.google.com
and then pinging your client from an IPv6-enabled host somewhere:
ping6 2600:3c01:xxxx:xxxx::1002
This works well, and clients will be assigned an address in the prefix.
However, some applications want clients to talk to each other through the tunnel.
For IPv4, they could just use each other's 10.8.0.x addresses, but with this configuration, the prefix is not a private value and therefore subject to (unlikely but possible) change.
A solution is to use IPv6 ULAs, but then clients can't reach the IPv6 internet without prefix translation. In reality, we want to be able to give each client an IPv4 10.8.0.x address, an IPv6 GUA, and an IPv6 ULA.
How do we extend this configuration to achieve that?