OpenVPN with global IPv6 addressing

October 28th, 2023, last changes on October 29th, 2023

The Goal

The Solution

VPN Target Solution

How to setup the environment of Amazon AWS? Scroll down!


tldr; Hiding the VPN behind TCP 443 makes is reachable from almost everywhere for the price of speed and being continuously scanned vs. hiding it behind a random UDP makes is fast and secure for the price of being blocked by some (many?) public hotspots/networks.

Selection of the port type and number for the OpenVPN server is tricky. There is no one solution that fits all. Taking UDP improves speed and security of your server (UDP port scans are not as easy as with TCP). Taking TCP improves compatibility (as some public hotspots/hotel networks block UDP or may have issues with masquerading returning UDP traffic). Selection of the official OpenVPN port number (1194) maybe supports compatibility, but opens a risk of the server being regularly scanned. Taking some other random port would make it better. On the other hand public networks may block unusual ports and allow only for like 80, 443 and maybe some more.

Fast Approach

There is a helping script OpenVPN road warrior installer published by Nyr that, as the name says, automates installation and configuration of the OpenVPN server on Ubuntu, Debian, AlmaLinux, Rocky Linux, CentOS and Fedora. It downloads necessary packages, installs them, configures iptables, configures OpenVPN service and generates the first client's key. The same script can be called later on to generate additional client profiles (keys) or to revoke existing ones.

It's worth taking a look inside of the script to understand what is happening there:


Lets you configure:

Performs the installation:

Coming Soon

Fine Tuning

A few things were not configured the way we wanted it:

See below (IPv6 Routing) on how to determine the correct values for server-ipv6 <IPv6 network range>/<prefix size> and push "route-ipv6 <IPv6 network range>/64".


# comment it out, not necessary
# local <IPv4 address of your machine>

# choose a port OpenVPN will listen on (configured by the script)
port <OpenVPN's port> 

# change to IPv6-able protocol (IPv4 will be included)
# udp6 or tcp6
proto udp6

# no changes in this block
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem
auth SHA512
tls-crypt tc.key
topology subnet

# private IPv4 network (must match iptables)

# public IPv6 network (:1 will take the server, :1000 and following for clients)
server-ipv6 <IPv6 network range>/<prefix size>

# push IPv6 routing configuration to the clients (see explanation below)
push "route-ipv6 <IPv6 network range>/64"

# add "ipv6" here: routes the whole IPv6 unicast space via VPN
# "def1" overrides the default gateway in a smart way without deleting the original one
# "bypass-dhcp" allows local DHCP, otherwise it would be redirected to the VPN as well
push "redirect-gateway def1 ipv6 bypass-dhcp"

# persistent IP addresses for the clients
ifconfig-pool-persist ipp.txt

# adapt DNS servers as needed - you need servers supporting IPv6 queries (AAAA)
# here as an example - Google ones
push "dhcp-option DNS 2001:4860:4860::8888"
push "dhcp-option DNS 2001:4860:4860::8844"
push "dhcp-option DNS"
push "dhcp-option DNS"

# uncomment to block DNS servers on other (than the tunnel) network adapters
# push "block-outside-dns"

# no changes
keepalive 10 120
cipher AES-256-CBC
user nobody
group nogroup
verb 3
crl-verify crl.pem

# uncomment to optimize tunnel restart notifications
# explicit-exit-notify


The installation script creates a new service persisting the iptables settings. Changes to the addressing schema do require changes here as well.

# IPv4
ExecStart=/usr/sbin/iptables -t nat -A POSTROUTING -s <private network> ! -d <private network> -j SNAT --to <public address>
ExecStart=/usr/sbin/iptables -I FORWARD -s <private network> -j ACCEPT
ExecStop=/usr/sbin/iptables -t nat -D POSTROUTING -s <private network> ! -d <private network> -j SNAT --to <public address>
ExecStop=/usr/sbin/iptables -D INPUT -p udp --dport <OpenVPN port> -j ACCEPT

# IPv6
ExecStart=/usr/sbin/ip6tables -t nat -A POSTROUTING -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to <global address>
ExecStart=/usr/sbin/ip6tables -I FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT
ExecStop=/usr/sbin/ip6tables -t nat -D POSTROUTING -s fddd:1194:1194:1194::/64 ! -d fddd:1194:1194:1194::/64 -j SNAT --to <global address>
ExecStop=/usr/sbin/ip6tables -D FORWARD -s fddd:1194:1194:1194::/64 -j ACCEPT

# Reachability of the OpenVPN server
ExecStart=/usr/sbin/iptables -I INPUT -p udp --dport <OpenVPN port> -j ACCEPT
ExecStop=/usr/sbin/iptables -D FORWARD -s <private network> -j ACCEPT

IPv6 Routing

For the Tunneled Traffic

IPv6 Networks Schema

The prefixes must be chosen in the way, everyone knows where to send the packets to. One of the simplest cases would be:

Note: This example has been adapted to better reflect the actual situation on AWS.

All these settings must be reflected in the iptables (or firewalld) settings. The installation script assumes you want to get a private network. If you switch to global addresses as described above, you should remove NATting for IPv6 completely.

At this point you should get global IPv6 assigned to your clients and be able to access the IPv6-based Internet. Surprisingly you will not be able to ping the main network adapter of the virtual machine. Let's take a look at the routing table on a client (Windows):

C:\WINDOWS\system32>route print -6

Interface List
 25...XX XX XX XX XX XX ......TAP-Windows Adapter V9 for OpenVPN Connect
  4...XX XX XX XX XX XX ......Intel(R) Ethernet Connection (2) I218-V
  1...........................Software Loopback Interface 1

IPv6 Route Table
Active Routes:
 If Metric Network Destination                        Gateway
 25    281 ::/1                                       fe80::8
  1    331 ::1/128                                    On-link
 25    281 2001:1111:2222:3333::/64                   On-link
 25    281 2001:1111:2222:3333:4444:2::/96            fe80::8
 25    281 2001:1111:2222:3333:4444:2:0:1000/128      On-link
 25    281 8000::/1                                   fe80::8
 25    281 fe80::/64                                  On-link
  4    281 fe80::/64                                  On-link
  4    281 fe80::XXXX:XXXX:XXXX:XXXX/128              On-link
 25    281 fe80::XXXX:XXXX:XXXX:XXXX/128              On-link
  1    331 ff00::/8                                   On-link
 25    281 ff00::/8                                   On-link
  4    281 ff00::/8                                   On-link
Persistent Routes:

The interface No. 25 is the one that we should look at.

And exactly the line No. 16 is the problematic one. This route is not pushed by the OpenVPN server to the client. Instead, the client generates it automatically. Allegedly this practice is recommended in RFC 4291 and/or RFC 5375 and/or RFC 4193 (note to be verified).

You can't remotely remove this route. The solution is to tell the client that this range also needs to be reached via the OpenVPN server. You can achieve that by adding the line to the /etc/openvpn/server/server.conf:

push "route-ipv6 2001:1111:2222:3333::/64"

This option route-ipv6 takes up to 3 arguments: the destination, optionally a gateway and optionally a metrics. Leaving the gateway empty will automatically evaluate to our server.

For the OpenVPN Service

The last point is reachability of the OpenVPN server via its global IPv6 address as well. This is done by:

TODO: add info on sysctl net.ipv6.bindv6only

Packet Forwarding

Ensure that packet forwarding is turned on for both, IPv4:


The script makes it permanent in the file /etc/sysctl.d/99-openvpn-forward.conf.

and IPv6:


The script makes it permanent only if there was an IPv6 address available in the same file /etc/sysctl.d/99-openvpn-forward.conf.

Restart OpenVPN

The service can be restarted by systemctl restart openvpn.service, but a few things will stay outdated: the iptables and the configuration of the tunnel interface. A clean reboot may be a better choice.

Client Configuration

The client may need IPv6 to be turned on in the advanced settings.

Amazon AWS


You will need an EC2 instance (which is a simple virtual machine). The t3.micro is eligible for the free tier (please read all conditions before assuming that it would be for free in your case). This tiny VM is in most cases absolutely powerful enough for acting as a simple VPN gateway. During the setup a few services will be created. Some settings can be configured ad-hoc, some others will need to be tuned later on:

If you wish to create a setup compatible with the above notes, you should:

Next: Understanding the internal design of a TK-905-4G

Previous: nginx + Uvicorn + FastAPI + systemd

Main Menu