Ilija Matoski

    ← Articles

    Site to site with OpenVPN with TLS and FirewallD on Debian

    So I needed to setup a site to site transport between our two datacenters, so our internal network will be able to communicate between the two datacenters easily.

    There are a lot of ways to setup site to site VPN link between two networks, in our case let’s take a look at one way using OpenVPN.

    This is quite long tutorial so get yourself a bit comfortable so we can get started.

    Our network looks like this

    Site to site VPN

    Datacenter at location A has internal network 10.5.200.20 on the interface eth1 and 94.x.x.x.x on eth0, we will configure the openvpn server on this location. Datacenter at location B has internal network 10.0.0.5 on the interface eth1 and 93.x.x.x.x on eth0.

    Both machine have two network interfaces eth0 which is connected to the internet, and eth1 which is connected the the LAN

    The IP subnet has to be different for this to work, if they are on the same subnet this won’t work.

    VPN gateway at location A

    So to begin login to the machine in the location A

    Forwarding

    $ cat <<EOF > /etc/sysctl.d/30-openvpn-forward.conf
    net.ipv4.ip_forward = 1
    net.ipv6.conf.all.forwarding = 1
    EOF
    $ sysctl -p /etc/sysctl.d/30-openvpn-forward.conf
    net.ipv4.ip_forward = 1
    net.ipv6.conf.all.forwarding = 1

    Installation

    apt-get install openvpn easy-rsa firewalld

    Firewall

    Let’s configure the firewall to allow us to connect to the machine and all the necessary configuration needed for it

    $ firewall-cmd --permanent --zone=public --add-source=93.x.x.x/32
    $ firewall-cmd --permanent --zone=trusted --add-interface=eth1
    $ firewall-cmd --permanent --zone=trusted --add-interface=tun0
    $ firewall-cmd --permanent --zone=trusted --add-masquerade
    $ DEV=$(ip route get 8.8.8.8 | awk 'NR==1 {print $(NF-2)}')
    $ firewall-cmd --permanent --direct --passthrough ipv4 -t nat -A POSTROUTING -s 172.16.1.0/24 -o $DEV -j MASQUERADE
    $ firewall-cmd --reload

    Generating OpenVPN keys

    After we have installed all this we need to generate some configuration and tls keys for us to use.

    $ make-cadir ~/ca
    $ cd ~/ca
    $ ls -lah
    drwx------ 2 root root 4.0K Feb 5 21:23 .
    drwxr-xr-x 4 root root 4.0K Feb 5 21:23 ..
    lrwxrwxrwx 1 root root 28 Feb 5 21:23 build-ca -> /usr/share/easy-rsa/build-ca
    lrwxrwxrwx 1 root root 28 Feb 5 21:23 build-dh -> /usr/share/easy-rsa/build-dh
    lrwxrwxrwx 1 root root 31 Feb 5 21:23 build-inter -> /usr/share/easy-rsa/build-inter
    lrwxrwxrwx 1 root root 29 Feb 5 21:23 build-key -> /usr/share/easy-rsa/build-key
    lrwxrwxrwx 1 root root 34 Feb 5 21:23 build-key-pass -> /usr/share/easy-rsa/build-key-pass
    lrwxrwxrwx 1 root root 36 Feb 5 21:23 build-key-pkcs12 -> /usr/share/easy-rsa/build-key-pkcs12
    lrwxrwxrwx 1 root root 36 Feb 5 21:23 build-key-server -> /usr/share/easy-rsa/build-key-server
    lrwxrwxrwx 1 root root 29 Feb 5 21:23 build-req -> /usr/share/easy-rsa/build-req
    lrwxrwxrwx 1 root root 34 Feb 5 21:23 build-req-pass -> /usr/share/easy-rsa/build-req-pass
    lrwxrwxrwx 1 root root 29 Feb 5 21:23 clean-all -> /usr/share/easy-rsa/clean-all
    lrwxrwxrwx 1 root root 33 Feb 5 21:23 inherit-inter -> /usr/share/easy-rsa/inherit-inter
    lrwxrwxrwx 1 root root 24 Feb 5 21:23 keys -> /usr/share/easy-rsa/keys
    lrwxrwxrwx 1 root root 28 Feb 5 21:23 list-crl -> /usr/share/easy-rsa/list-crl
    -rw-r--r-- 1 root root 7.7K Feb 5 21:23 openssl-0.9.6.cnf
    -rw-r--r-- 1 root root 8.3K Feb 5 21:23 openssl-0.9.8.cnf
    -rw-r--r-- 1 root root 8.2K Feb 5 21:23 openssl-1.0.0.cnf
    -rw-r--r-- 1 root root 8.2K Feb 5 21:23 openssl.cnf
    lrwxrwxrwx 1 root root 27 Feb 5 21:23 pkitool -> /usr/share/easy-rsa/pkitool
    lrwxrwxrwx 1 root root 31 Feb 5 21:23 revoke-full -> /usr/share/easy-rsa/revoke-full
    lrwxrwxrwx 1 root root 28 Feb 5 21:23 sign-req -> /usr/share/easy-rsa/sign-req
    -rw-r--r-- 1 root root 2.1K Feb 5 21:23 vars
    lrwxrwxrwx 1 root root 35 Feb 5 21:23 whichopensslcnf -> /usr/share/easy-rsa/whichopensslcnf

    You can replace the openssl.cnf with whichever version you want to use for generation, I decided to go with openssl-1.0.0.cnf

    Open the file ~/ca/vars and update accordingly, you can customize it however you want but you should at least modify the following to suit your organization.

    export KEY_COUNTRY="US"
    export KEY_PROVINCE="CA"
    export KEY_CITY="SanFrancisco"
    export KEY_ORG="Fort-Funston"
    export KEY_EMAIL="me@myhost.mydomain"
    export KEY_OU="MyOrganizationalUnit"

    After you have edited the file you need to source it so we have the environment variables ready, to do so you can just run

    $ cd ~/ca
    $ . ./vars
    $ env | grep ^KEY
    KEY_OU=MyOrganizationalUnit
    KEY_SIZE=2048
    KEY_NAME=EasyRSA
    KEY_EXPIRE=3650
    KEY_EMAIL=me@myhost.mydomain
    KEY_ORG=Fort-Funston
    KEY_CITY=SanFrancisco
    KEY_COUNTRY=US
    KEY_PROVINCE=CA
    KEY_CONFIG=/root/ca/openssl.cnf
    KEY_DIR=/root/ca/keys
    $ ./clean-all

    Now you are ready to build your ca key and tls-crypt key for OpenVPN

    $ cd ~/ca
    $ ./build-ca
    $ openvpn --genkey --secret ~/ca/keys/ta.key

    You CA is ready now we have to generate a server key, make sure you sign the certificate when asked

    $ cd ~/ca
    $ ./build-key-server server
    $ ./build-dh

    And we also need to generate a client key for each client that will connect, make sure you sign the certificates when asked

    $ cd ~/ca
    $ ./build-key locb

    Now let’s see what have we generated your keys folder should have the following files

    $ ls -lh ~/ca/keys
    total 84K
    -rw-r--r-- 1 root root 5.4K Feb 5 21:31 01.pem
    -rw-r--r-- 1 root root 5.3K Feb 5 21:35 02.pem
    -rw-r--r-- 1 root root 1.7K Feb 5 21:30 ca.crt
    -rw------- 1 root root 1.7K Feb 5 21:30 ca.key
    -rw-r--r-- 1 root root 424 Feb 5 21:33 dh2048.pem
    -rw-r--r-- 1 root root 238 Feb 5 21:35 index.txt
    -rw-r--r-- 1 root root 21 Feb 5 21:35 index.txt.attr
    -rw-r--r-- 1 root root 21 Feb 5 21:31 index.txt.attr.old
    -rw-r--r-- 1 root root 120 Feb 5 21:31 index.txt.old
    -rw-r--r-- 1 root root 5.3K Feb 5 21:35 locb.crt
    -rw-r--r-- 1 root root 1.1K Feb 5 21:35 locb.csr
    -rw------- 1 root root 1.7K Feb 5 21:35 locb.key
    -rw-r--r-- 1 root root 3 Feb 5 21:35 serial
    -rw-r--r-- 1 root root 3 Feb 5 21:31 serial.old
    -rw-r--r-- 1 root root 5.4K Feb 5 21:31 server.crt
    -rw-r--r-- 1 root root 1.1K Feb 5 21:31 server.csr
    -rw------- 1 root root 1.7K Feb 5 21:31 server.key
    -rw------- 1 root root 636 Feb 5 21:30 ta.key

    Now we have everything so we can send finally do the connection between our datacenters.

    Configuration

    So let’s prepare some things before we start

    $ mkdir -p /etc/openvpn/ccd
    $ cp -v ~/ca/keys/{ca.crt,server.*,dh2048.pem,ta.key} /etc/openvpn/server
    '/root/ca/keys/ca.crt' -> '/etc/openvpn/server/ca.crt'
    '/root/ca/keys/server.crt' -> '/etc/openvpn/server/server.crt'
    '/root/ca/keys/server.csr' -> '/etc/openvpn/server/server.csr'
    '/root/ca/keys/server.key' -> '/etc/openvpn/server/server.key'
    '/root/ca/keys/dh2048.pem' -> '/etc/openvpn/server/dh2048.pem'
    '/root/ca/keys/ta.key' -> '/etc/openvpn/server/ta.key'

    And now lets make our configuration for the server. We need to create a separate user for our process to run in, so it’s isolated from the system.

    $ addgroup ovpn
    $ useradd --shell=/bin/false -g ovpn ovpn
    $ cat /etc/openvpn/server.conf
    dev tun
    persist-key
    persist-local-ip
    persist-remote-ip
    persist-tun
    topology subnet
    port 1194
    proto udp
    keepalive 10 60
    ca /etc/openvpn/server/ca.crt
    cert /etc/openvpn/server/server.crt
    key /etc/openvpn/server/server.key
    dh /etc/openvpn/server/dh2048.pem
    server 172.16.1.0 255.255.255.0
    client-to-client
    client-config-dir ccd
    explicit-exit-notify 10
    user ovpn
    group ovpn
    tls-crypt /etc/openvpn/server/ta.key
    auth SHA512
    tls-version-min 1.2
    tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256
    ncp-ciphers AES-256-GCM:AES-256-CBC
    ifconfig-pool-persist ipp.txt
    status openvpn-status.log
    log /var/log/openvpn.log
    verb 4
    route 10.0.0.0 255.255.255.0
    tun-mtu 1500
    tun-mtu-extra 32
    mssfix 1450
    reneg-sec 0
    cipher AES-128-CBC

    All the configuration in here is self explanatory, the only thing worth of not is route 10.0.0.0 255.255.255.0, which needs to be in the main file so the server knows that it needs to route that subnet to the connected client, if you don’t add that line then we will be missing the required routes to access the server.

    Now we need to setup the vm in the location b, we still have to install the same packages as for location a, but before we do that we need to create a configuration file for the client

    $ cd /etc/openvpn/ccd
    cat <<EOF > /etc/openvpn/ccd/locb
    push "route 10.5.100.0 255.255.252.0"
    iroute 10.0.0.0 255.255.255.0
    EOF

    This will tell that the client locb when connected that it needs to add the route for accessing the subnet in loca and for the server to setup an internal route for the locb

    Service

    Let’s enable the server

    $ systemctl disable openvpn
    $ systemctl enable openvpn@server
    $ systemctl restart openvpn@server
    $ systemctl status openvpn@server.service

    VPN gateway at location B

    Now we are ready to connect we need to transfer the files ca.crt,locb.*,dh2048.pem,ta.key in the keys folder from the server to the client in /etc/openvpn/client folder.

    Forwarding

    $ cat <<EOF > /etc/sysctl.d/30-openvpn-forward.conf
    net.ipv4.ip_forward = 1
    net.ipv6.conf.all.forwarding = 1
    EOF
    $ sysctl -p /etc/sysctl.d/30-openvpn-forward.conf
    net.ipv4.ip_forward = 1
    net.ipv6.conf.all.forwarding = 1

    Installation

    Let’s install the prerequisites

    $ apt-get install openvpn firewalld
    $ addgroup ovpn
    $ useradd --shell=/bin/false -g ovpn ovpn

    Firewall

    Let’s configure the firewall to allow us to connect to the machine and all the necessary configuration needed for it

    $ firewall-cmd --permanent --zone=public --add-source=94.x.x.x/32
    $ firewall-cmd --permanent --zone=trusted --add-interface=eth1
    $ firewall-cmd --permanent --zone=trusted --add-interface=tun0
    $ firewall-cmd --permanent --zone=trusted --add-masquerade
    $ DEV=$(ip route get 8.8.8.8 | awk 'NR==1 {print $(NF-2)}')
    $ firewall-cmd --permanent --direct --passthrough ipv4 -t nat -A POSTROUTING -s 172.16.1.0/24 -o $DEV -j MASQUERADE
    $ firewall-cmd --reload

    Configuration

    $ cat /etc/openvpn/client.conf
    client
    dev tun
    persist-key
    persist-tun
    persist-local-ip
    persist-remote-ip
    proto udp
    nobind
    user ovpn
    group ovpn
    remote-cert-tls server
    auth SHA512
    verb 4
    remote s2s.example.com 1194
    ca /etc/openvpn/client/ca.crt
    cert /etc/openvpn/client/locb.crt
    key /etc/openvpn/client/locb.key
    tls-crypt /etc/openvpn/client/ta.key
    log /var/log/openvpn.log
    keepalive 10 60
    $ systemctl disable openvpn
    $ systemctl enable openvpn@client
    $ systemctl restart openvpn@client
    $ systemctl status openvpn@client.service

    Routes

    The only thing missing now is setting up the routes required so we know where we need to go for a given subnet

    /etc/network/interfaces

    $ cat /etc/network/interfaces
    source /etc/network/interfaces.d/*
    auto lo
    iface lo inet loopback
    auto ens18
    iface ens18 inet dhcp
    auto ens19
    iface ens19 inet static
    address 10.0.0.3
    netmask 255.255.255.0
    up route add -net 10.5.100.0 netmask 255.255.252.0 gw 10.0.0.5
    down route add -net 10.5.100.0 netmask 255.255.252.0 gw 10.0.0.5

    So now everytime our interface is configured our route will be set up so we can access the servers on the other side of the VPN

    ip route

    We need to add a static route on the internal network interface so we can route the subnet.

    $ ip route
    default via 95.x.x.x dev ens18 onlink
    10.0.0.0/24 dev ens19 proto kernel scope link src 10.0.0.3
    95.x.x.x/25 dev ens18 proto kernel scope link src 95.x.x.x
    $ ping 10.5.100.10 -c1
    PING 10.5.100.10 (10.5.100.10) 56(84) bytes of data.
    --- 10.5.100.10 ping statistics ---
    1 packets transmitted, 0 received, 100% packet loss, time 0ms
    $ ip route add 10.5.100.0/22 via 10.0.0.5 dev ens19
    $ ip route
    default via 95.x.x.x dev ens18 onlink
    10.0.0.0/24 dev ens19 proto kernel scope link src 10.0.0.3
    10.5.100.0/22 via 10.0.0.5 dev ens19
    95.x.x.x/25 dev ens18 proto kernel scope link src 95.x.x.x
    $ ping 10.5.100.10 -c1
    PING 10.5.100.10 (10.5.100.10) 56(84) bytes of data.
    64 bytes from 10.5.100.10: icmp_seq=1 ttl=62 time=2.17 ms
    --- 10.5.100.10 ping statistics ---
    1 packets transmitted, 1 received, 0% packet loss, time 0ms
    rtt min/avg/max/mdev = 2.171/2.171/2.171/0.000 ms

    DHCP

    If we use DHCP we can easily put a stateless route so all machines automatically get the correct route.

    dnsmasq

    We just need to add this to the DHCP server in location A and we are done, the route will be automatically setup when the client requests an IP address.

    dhcp-option=121,10.5.100.0/22,10.0.0.5

    Testing

    And now let’s see the result of our process finally

    VPN gateway in location A

    $ traceroute 10.0.0.3 -n
    traceroute to 10.0.0.3 (10.0.0.3), 30 hops max, 60 byte packets
    1 172.16.1.2 2.050 ms 2.038 ms 2.033 ms
    2 10.0.0.3 2.239 ms 2.242 ms 2.315 ms

    VPN gateway in location B

    $ traceroute 10.5.100.10 -n
    traceroute to 10.5.100.10 (10.5.100.10), 30 hops max, 60 byte packets
    1 172.16.1.1 2.023 ms 2.007 ms 2.000 ms
    2 10.5.100.10 1.991 ms 1.986 ms 1.979 ms

    Machine in location B to a machine in location A

    $ traceroute 10.5.100.10 -n
    traceroute to 10.5.100.10 (10.5.100.10), 30 hops max, 60 byte packets
    1 10.0.0.5 0.514 ms 0.497 ms 0.486 ms
    2 * * *
    3 10.5.100.10 2.238 ms 2.234 ms 2.219 ms

    The 2nd hop is our VPN gateway doing the forwarding

    Speed

    The speed we got it’s not that bad, it’s 281 Mbits/sec which is around 35 MB/s. We can make it even faster by using Elliptic curve crypto (ECC) for the keys

    Machine on location A

    We start an iperf server on any machine on location A by logging into it and running

    $ apt-get install iperf
    $ iperf -s
    ------------------------------------------------------------
    Server listening on TCP port 5001
    TCP window size: 85.3 KByte (default)
    ------------------------------------------------------------
    [ 4] local 10.5.100.10 port 5001 connected with 10.5.100.10 port 42076
    [ ID] Interval Transfer Bandwidth
    [ 4] 0.0-10.0 sec 335 MBytes 280 Mbits/sec

    Machine on location B

    We start an iperf client on any machine on location B by logging into it and running

    $ apt-get install iperf
    $ iperf -c 10.5.100.10
    ------------------------------------------------------------
    Client connecting to 10.5.100.20, TCP port 5001
    TCP window size: 45.0 KByte (default)
    ------------------------------------------------------------
    [ 3] local 10.0.0.3 port 42076 connected with 10.5.100.20 port 5001
    [ ID] Interval Transfer Bandwidth
    [ 3] 0.0-10.0 sec 335 MBytes 281 Mbits/sec