Sonntag, 8. November 2009

How to setup an IPv6 capable OpenVPN tunnel

This weekend I visited my parents at home; though I was able to convince them that DSL is a good thing some months ago I still have to chain-login to some systems to get to my mail and jabber and the like. So, given the fact that the other machines are already IPv6-enabled, I decided to get myself IPv6 at home, too.

Unfortunately, there is this WLAN-router-thingie at home that connects my parents' PC (and my notebook) to the internet, and as that router is an inaccesible black box I can't just ssh to it and setup 6to4 like I did with my dedicated server over at hetzner. Also, I can't get an account at sixxs, as I already got one :) to supply our dorm with an IPv6 subnet (our telco owns its own sixxs POP that is only 15ms away). I could have gotten an account at a different free IPv6 tunnel provider (e.g. Hurricane Electric), but their POPs are "far away", giving bad latency.

So I tried a different approach: set up my own tunnel to my own IPv6-enabled dedicated server at hetzner in Nuremberg.

First at all, I had to establish a tunnel between the server and my notebook. Never having used other tunneling solutions than simple ip-in-ip tunnels (which can't be used in my situation, for one reason having no public IP address for my notebook and the other being these tunnels not supporting IPv6 traffic) I decided to go with OpenVPN, assuming plenty of documentation floating around.

First at all I didn't want to use simple username/password authentication but use certificates instead. To accomplish this I had to set up my own certificate authority (CA) to generate certificates for my notebook and my server to trust each other. This can easily be done with OpenSSL, with a simple workflow:
  1. set up basic directory structure and initial files

    mkdir CA
    cd CA
    mkdir certs crl newcerts private
    echo "01" > serial
    cp /dev/null index.txt


  2. copy a sample openssl.cnf file and adjust its stored parameters

    cp /usr/lib/ssl/openssl.cnf .
    vim openssl.cnf

    Basically, you must change the dir parameter in section CA_default to /.

  3. create the CA certificate and the corresponding private key:

    openssl req -new -x509 -keyout private/cakey.pem -out cacert.pem -days 3650 -config openssl.cnf

    You can adjust the expiration date of your certificate using the --days switch; I chose 10 years (3650 days) lifetime for the CA cert, as it will be signing all other future certificates (which will have a much lower lifetime, ususally a year).
Now I had a file cacert.pem and its key in private/cakey.pem. The next step was to create two certificates, one for use with my notebook and one for the server:
  1. create a "new certificate request"

    openssl req -nodes -new -x509 -keyout newreq.pem -out newreq.pem  -config openssl.cnf

  2. sign the new certificate

    openssl x509 -x509toreq -in newreq.pem -signkey newreq.pem -out tmp.pem

    openssl ca -config openssl.cnf -policy policy_anything -out newcert.pem -infiles tmp.pem
I had to perform these two steps twice, once for each certificate I needed. Instead of the placeholder filenames above (newreq.pem, newcert.pem) I used unique and sensible names (like notebook-vpn-req.pem and notebook-vpn-cert.pem, server-vpn-req-pem and server-vpn-cert.pem, ...) so that I didn't accidentially overwrite one of my selfmade certificates.

Now that I had my four files (two certificates signed by my CA and their private keys) I copied two of them to my notebook, as I would be using that certificate to authenticate against my OpenVPN server which was yet to be configured. I also had to copy the cacert.pem (but not the cakey.pem! This file is to be kept secure so that nobody else can obtain its contents!) to my notebook, as OpenVPN needs that on the client side, too.

Now that I had the authentication certificates, I began to set up the two OpenVPN endpoints.

I started configuring the server side; though OpenVPN can be set up using command line switches (which may be useful e.g. when testing and playing around for the first time) it is set up best using a configuration file.

After some experimentation I ended up with the following content for the server.config:

tls-server
port 1194
proto tcp-server
dev tun
tun-ipv6

ca cacert.pem
cert tg-openvpn-cert.pem
key tg-openvpn-req.pem  # This file should be kept secret
dh dh1024.pem

keepalive 10 120
persist-key
persist-tun


The first five lines tell OpenVPN to act as a server with TLS authentication (i.e. "use certificates") using TCP as means of communication (OpenVPN defaults to UDP, but I got strange messages about unsendable UDP messages, so I switched to the auto-retransmitting TCP) using normal tunneling devices tunX (instead of the lower-layer-tapX-devices that can relay broadcast/multicast traffic) to avoid the need for a bridge device. The tun-ipv6 parameter allows the tun device OpenVPN will be creating to forward IPv6 traffic.

The next three lines define the certificate to be used when authenticating with the client, while the fourth line specifies a file with parameters for the Diffie-Hellman key exchange protocol; I created this file with

openssl dhparam -out dh1024.pem 1024

Now that the server has a configuration file, I moved on to the client; its client.conf looks like this:

tls-client
remote server.example
proto tcp-client
dev tun
tun-ipv6

ca cacert.pem
cert doc-holodoc-vpn-cert.pem
key doc-holodoc-vpn-req.pem

ping 30

This is quite similar to the server's configuration; I needed to specify the server to connect to (substitute server.example above with your server's hostname or IPv4 address) and the certificate to use.

After starting up OpenVPN at the server side using

openvpn --config server.config

I started OpenVPN on my laptop using

openvpn --config client.config

The notebook connects to the server, they exchange keys etc. and from there on I was ready to go :) - I had a new network interface on each endpoint, tun0, which was not configured yet (no IP addresses, no routing information). So I wrote a small script to setup these devices on each end. First take a look at the server-side script:

ifconfig tun0 up 192.168.234.1 dstaddr 192.168.234.2
ip -6 addr add 2002:dead:beef:1::1/128 dev tun0
ip -6 route add 2002:dead:beef:1::/64 dev tun0

First, I tell tun0 to have an IP address of 192.168.234.1, while the other end of the tunnel (my notebook) has IP 192.168.234.2. Then I assign the IPv6 address 2002:dead:beef:1::1 to the local tunnel endpoint, too. While my server has a whole 2002:dead:beef::/48 subnet, I dedicate the second /64-subnet (2002:dead:beef:1::/64) to my "tunnel network" (total overkill, but hey...), while the server itself has an IPv6 address of the first subnet (2002:dead:beef:0::/64).

This way, when the server gets packets for some IP in 2002:dead:beef:1::/64 he knows to forward it to tun0 (which will send the packets to my notebook).

Now that the server-side routing is done, the client also has to setup its tun0 device and adjust its routing tables:

ifconfig tun0 192.168.234.2 dstaddr 192.168.234.1
ip -6 addr add 2002:dead:beef::1::2/128 dev tun0
ip -6 route add 2002:dead:beef:1::/64 dev tun0
ip -6 route add default via 2002:dead:beef:1::1

As you can see, this just sets the tables "the other way around"; the notebook's tun0 gets IPv4 address 192.168.234.2 while the server end has IP 192.168.234.1 (this first command makes IPv4 packet routing possible between the server and the client). After that, the IPv6 address  2002:dead:beef::1::2 is assigned to the notebook's tunnel endpoint, the routing for the first /64-subnet is set up, and the last command tells the notebook to route all outgoing IPv6 traffic through the server at the other end of the tunnel.

After running these two scripts, I had a working IPv6 connection trough OpenVPN :)

In order to minimize the commands needed to type by hand I set up OpenVPN to start automatically at the server and extended both the server's and the client's configuration file to start the respective interface/routing-configuration-scripts on successful connection of the two OpenVPN endpoints. This however was just a quick and dirty solution as it only supports one connection at a time; if I wanted multiple concurrent connections I had to change the scripts to something more dynamically adjustable - but for my simple needs, this is sufficient to provide mw with IPv6 when at home.

Keine Kommentare:

Kommentar veröffentlichen