in networking, Tools

Build a OpenVPN server on Ubuntu to provide a IPv6 tunnel over IPv4

Like a lot of computer engineers, I use to work from various places such as my office, home, at a customer’s, in the train… It’s important to always have same LAN/Internet configuration wherever I am and the best solution to achieve this is of course using a VPN.

If you have a dedicated server connected to the Internet running Ubuntu (or any other Linux OS), this can be achieved quite easily and for totally free with OpenVPN. OpenVPN clients are available on almost all platforms, Linux, MacOS, Windows, Android and iOS. If your server has IPv6 connectivity, you can also take advantage of it, even if all you have is a terrible IPv4 connection with a lot of restrictions.

Here is how I did it, on a fresh Ubuntu 14.04 LTS installation.

All commands in this tutorial must be run as root.

We consider the server has external IPv4 1.2.3.4 and IPv6 aaaa:bbbb:cccc:dddd::/64 on interface eth0. The subnet aaaa:bbbb:cccc:dddd:80::/112 is allocated to the VPN clients on interface tun0.

Enable NAT and NDP proxy

The first step is to enable NAT for IPv4 and NDP proxy for IPv6. We could do NAT with IPv6 but it’s worthless since only the IP address of the VPN server would be reachable externally. NDP proxy is the only way to make the clients directly reachable from the Internet using their IPv6 address.

First, allow the server’s kernel to forward traffic from client devices out to the Internet.
vim /etc/sysctl.conf

Uncomment/add the following lines:

Now we can enable NAT for IPv4 (considering eth0 is the interface that is connected to the Internet):
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables-save > /etc/iptables.rules

To enable NAT again after reboot, create the following up script:
vim /etc/network/if-up.d/iptables

Make the script executable.
chmod +x /etc/network/if-up.d/iptables

You can now restart the networking services
sysctl -p
/etc/init.d/networking restart

Install OpenVPN on the server

First of all, update all your packages.
apt-get update

Then install the OpenVPN packages.
apt-get install openvpn easy-rsa

Generate server certificate and key

Here comes the boring part: generating the certificate files and other security-related stuff that is necessary to make the connection possible.

First copy over the Easy-RSA generation scripts.
cp -r /usr/share/easy-rsa/ /etc/openvpn

Then create the storage directory for the generated keys.
mkdir /etc/openvpn/easy-rsa/keys

Now, modify the default values that will be used to build the certificates. This will avoid you to type them every time you generate a new certificate. Edit the file /etc/openvpn/easy-rsa/vars to modify the following keys:
vim /etc/openvpn/easy-rsa/vars

The server keys will be named server so in the same file, also modify the KEY_NAME value as follows:

Save.

Now generate the Diffie-Hellman parameters. This takes a few minutes to process so feel free to take a break and a cup of coffee.
openssl dhparam -out /etc/openvpn/dh2048.pem 2048

Then, build the certificate authority (CA). You will be invited to modify the values, just press [ENTER] to keep the information you previously entered in the vars file.
cd /etc/openvpn/easy-rsa
. ./vars
./clean-all
./build-ca

It’s now time to generate the certificate and the key for the server. As before, you will be invited to modify the values. Leave the 2 last fields blank (challenge password and optional company name) then press Y.
cd /etc/openvpn/easy-rsa
./build-key-server server

When you’re done, copy the files to the OpenVPN directory.
cp /etc/openvpn/easy-rsa/keys/{server.crt,server.key,ca.crt} /etc/openvpn

Server configuration

Here is an example of configuration for the following settings:

  • Full IPv6 connectivity over IPv4.
  • IPv4 TCP connection on port 443. The default setting (UDP 1194) may offer better performance but may be blocked on some connections such as mobile networks.
  • Clients can communicate with each other which can be useful if you are developing a mobile website and you want to test it directly on the device.
  • Fixed IP addresses for some specific clients, for example for the laptop on which the web server of the development website is running.
  • NDP proxying is enabled on demand using client-connect scripts and provides two-way Internet access to the clients.

First of all, create a vpn user for the daemon:
sudo useradd -r -s /bin/false vpn

Create the client config directory:
mkdir /etc/openvpn/ccd

Create a OpenVPN variables file that will be used by the client connection scripts. Modify the tunnel subnet prefix by yours:
sudo vim /etc/openvpn/variables

Create the server-clientconnect.sh and server-clientdisconnect.sh scripts. These scripts will create and remove the NDP proxy rules for each connecting client.
sudo vim /etc/openvpn/server-clientconnect.sh

sudo vim /etc/openvpn/server-clientdisconnect.sh

Make the scripts executable.
chmod +x /etc/openvpn/server-clientconnect.sh
chmod +x /etc/openvpn/server-clientdisconnect.sh

OpenVPN needs root privileges to run the scripts so add the following in the sudoers file:
visudo

Now, let’s create the main config file:
vim /etc/openvpn/server.conf

Start the server

The server should now be fully operational by starting its service.
service openvpn restart

You can check if it’s correctly running by typing:
service openvpn status

Client configuration

The last step consists in creating the configuration files for each client. The same client can’t open simultaneous sessions so it’s recommended to create a client configuration per user and per device “Peter PC, Paul iPhone, Mary laptop…”.

Let’s create a configuration for Nicolas’s iPhone, that we’ll name iphone-nicolas.

We first need to create the key and certificate, just the same way we did for the server. Leave the 2 last fields blank (challenge password and optional company name) then press Y.
cd /etc/openvpn/easy-rsa
source ./vars
./build-key iphone-nicolas

Now download these 3 files on your workstation:

  • /etc/openvpn/easy-rsa/keys/ca.crt
  • /etc/openvpn/easy-rsa/keys/iphone-nicolas.crt
  • /etc/openvpn/easy-rsa/keys/iphone-nicolas.key

Then, create the .ovpn client config file that you will then provide to the end user. Replace 1.2.3.4 By your VPN server IP address or domain name. You may have to comment the user nobody and group nobody statements on some Unix OS (for example when using Tunnelblick on MacOS X).

If you want to set a static IP for some clients, you have to create a configuration file named after the client name on the server. For example, to give IPv4 10.8.0.101 and IPv6 aaaa:bbbb:cccc:dddd:80::1001/112 to the client iphone-nicolas:

vim /etc/openvpn/ccd/iphone-nicolas

Testing

If everything went fine, you should now be able to use IPv6 services using your OpenVPN server. A good way to find out if it’s working is to do a IPv6 traceroute on the client:
traceroute6 ipv6.google.com

You can also test your setup compatibility by heading to ipv6-test.com with a IPv6-capable browser such as Google Chrome.

  • Hi, I’m having problem with your configuration:

    MULTI_sva: pool returned IPv4=10.8.0.4, IPv6=260X:a88X:cad:d0:80::1002
    Error: an inet address is expected rather than “260X:a88X:cad:d0:80:4”.
    WARNING: Failed running command (–client-connect): external program exited with error status: 1

    My server side IPV6 IP is

    inet6 addr: 260X:a88X:cad:d0::124:a001/64 Scope:Global
    inet6 addr: fe80::601:bbff:fed2:f301/64 Scope:Link

    I changed the aaaa:bbbb:cccc:dddd to 260X:a88X:cad:d0, is that right?

  • tycho

    it seems that ipv6=”$prefix$hexipp” should be
    ipv6=”$prefix:$hexipp” in the shell scripts

    aaaa:bbbb:cccc:dddd:80:4 gives invalid inet6 adress for 10.8.0.4

    aaaa:bbbb:cccc:dddd:80::4 works like a charm

    (with my own prefix)

  • Julian Lugod

    Just what I need, it really pains me a lot on configuring my ipv6 routings.

  • Astro

    Excellent. This solved a lingering problem I had for months. THANK YOU.

  • belkone

    Nice article! is possible to do this if I have only single (/128) IPv6 address?

  • Edoardo

    I do anything as you but its not working 🙁 can you help me plz? (I remove the ip from log)
    Log:
    Fri Apr 07 16:58:51 2017 OpenVPN 2.3.13 x86_64-w64-mingw32 [SSL (OpenSSL)] [LZO] [PKCS11] [IPv6] built on Nov 3 2016
    Fri Apr 07 16:58:51 2017 Windows version 6.2 (Windows 8 or greater) 64bit
    Fri Apr 07 16:58:51 2017 library versions: OpenSSL 1.0.1u 22 Sep 2016, LZO 2.09
    Enter Management Password:
    Fri Apr 07 16:58:52 2017 Attempting to establish TCP connection with [AF_INET]*:443 [nonblock]
    Fri Apr 07 16:58:53 2017 TCP connection established with [AF_INET]*:443
    Fri Apr 07 16:58:53 2017 TCPv4_CLIENT link local: [undef]
    Fri Apr 07 16:58:53 2017 TCPv4_CLIENT link remote: [AF_INET]*:443
    Fri Apr 07 16:58:53 2017 Connection reset, restarting [0]
    Fri Apr 07 16:58:53 2017 SIGUSR1[soft,connection-reset] received, process restarting
    Fri Apr 07 16:58:58 2017 Attempting to establish TCP connection with [AF_INET]*:443 [nonblock]
    Fri Apr 07 16:58:59 2017 TCP connection established with [AF_INET]*:443
    Fri Apr 07 16:58:59 2017 TCPv4_CLIENT link local: [undef]
    Fri Apr 07 16:58:59 2017 TCPv4_CLIENT link remote: [AF_INET]*:443
    Fri Apr 07 16:58:59 2017 Connection reset, restarting [0]
    Fri Apr 07 16:58:59 2017 SIGUSR1[soft,connection-reset] received, process restarting

  • Karol Jurak

    Hey I have a problem with this config. I cant make it working. It throws an error when client is connecting
    Mon Jun 12 23:09:07 2017 mail_server/xxx:27386 OPTIONS IMPORT: reading client specific options from: ccd3/mail_server
    Mon Jun 12 23:09:07 2017 mail_server/xxx:27386 MULTI_sva: push_ifconfig_ipv6 2001:xxxx:7402:34b:80::1001/112
    Error: inet6 address is expected rather than “2001:xxxx:7402:34b:80:65”.
    Mon Jun 12 23:09:07 2017 mail_server/xxx:27386 WARNING: Failed running command (–client-connect): external program exited with error status: 1
    Mon Jun 12 23:09:08 2017 mail_server/xxxx:27386 PUSH: Received control message: ‘PUSH_REQUEST’
    Mon Jun 12 23:09:08 2017 mail_server/xxxx:27386 Delayed exit in 5 seconds
    Mon Jun 12 23:09:08 2017 mail_server/xxx:27386 SENT CONTROL [mail_server]: ‘AUTH_FAILED’ (status=1)
    Mon Jun 12 23:09:13 2017 mail_server/xxx:27386 SIGTERM[soft,delayed-exit] received, client-instance exiting