My-Tiny.Net :: Networking with Virtual Machines



Virtual Private Networks



Stunnel and native capabilities are fine for per-service security, but what if we want to secure all communications between clients and servers? (Not everyone will remember https://). What if we need to provide secure remote access to a lot of different applications?

Best answer: a "Virtual Private Network" (VPN)

VPNs can be used as building blocks to construct anything from a small secure telecommuting solution to a large-scale secure WAN. VPNs tie together concepts from cryptography, networking, and firewalls. OpenVPN is a SSL VPN which can accomodate a wide range of configurations, including remote access, site-to-site VPNs, WiFi security, and remote access with load balancing, failover, and fine-grained access-controls.

In a VPN, the computers at each end of the tunnel encrypt the data entering the tunnel and decrypt it when it arrives. In this sense it is very much like stunnel. The big difference is that stunnel does this at layer 4 (port forwarding), while the VPN works at either layer 3 or layer 2.

A "kernel space" VPN uses the secure version of IP (IPSec) to encrypt the data in every packet before it is sent. This works for both IPv4 and IPv6 networks. IPSec actually consists of three main protocols:
  • IPsec Authentication Header (AH),
  • IPsec Encapsulating Security Payload (ESP), and the
  • IPsec Internet Key Exchange (IKE).
which are described in RFC4301 to RFC4309, plus 27 additional RFCs to clarify and update.
For an excellent explanation of the basics (from the older RFCs) see https://cdn.ttgtmedia.com/searchEnterpriseLinux/downloads/Kozierok_Ch29.pdf

A "user-space" application like OpenVPN essentially links a local tun or tap virtual network adapter with a remote one of the same type. IP packets are encrypted and then encapsulated in UDP, and sent over the internet to the corresponding device on the remote host, which receives, decrypts, authenticates, and de-encapsulates the packets.

A program uses a tun or tap virtual network adapter just like a file to read and write packets, similar to a named pipe (see Standard I/O::Redirection). When a program opens /dev/net/tun, the driver creates and registers a new virtual device with the kernel. When the program closes the device, the driver will automatically delete it and all routes corresponding to it. For background on how this works, see Device Drivers under Linux Notes on the menu.

So, the big question: What is the difference between a tun device and a tap device? Simple: TUN works with IP datagrams (layer 3), TAP works with Ethernet frames (layer 2). This makes the two major techniques for making a VPN routing (TUN) and bridging (TAP).

Bridging is good for software that depends on LAN broadcasts such as Windows NetBIOS file and printer sharing. However, bridging is less efficient than routing and does not scale well. Generally people recommend using routing unless there is a particular reason not to.

OpenVPN: TLS vs. Static Key mode

The big thing to watch out for with OpenVPN documentation is that the configuration files use the directives server network netmask (for TUN) and server-bridge gateway netmask pool-start pool-end (for TAP) to have the OpenVPN server manage its own IP address pool and allocate addresses to connecting clients.

The tricky part is that about a meter into the man page it says SSL/TLS authentication must be used in this mode To use TLS, each peer that runs OpenVPN must have its own local certificate/key pair (cert and key), signed with a ca root certificate. That creates some extra key management work.

An alternative is to use a pre-shared static key. Static key configurations are simple to setup (Good). There is no signature or feature (such as a header or protocol handshake sequence) that marks the ciphertext packets as being generated by OpenVPN (Good). However, the same secret key must exist in plaintext form on each VPN peer, so if an attacker manages to steal the key, everything that was ever encrypted with it is compromised (Bad). Static key configurations also have limited scalability: one client, one server (Never mind).

This means you need to be careful with configuration examples: server and server-bridge cannot be used with static key mode, and there is no need for the ca, cert, key, and dh parameters - the secret parameter is used to designate the shared key that must must be in plaintext form on each VPN peer.

Getting Started

In this example, we set up a VPN using Static Key mode between two hosts named mon.tn and fri.tn - it is probably better to use names that are in /etc/dnsmasq/cnames already, or the actual IP addresses of the machines you are connecting.

If there is not one already, make a directory to store OpenVPN keys and configuration files such as /etc/openvpn

The first step is to create the encryption key on one of our hosts with openvpn --genkey --secret static.key
and set proper permissions with chmod 770 /etc/openvpn/*.key

Then copy the .key file to the other host using [F9] and Shell Link in mc (see the section VM to VM with mc and SSH in the Shared Folders page under Orientation on the menu. This only needs to be done once.

Every time we want to use OpenVPN we need to make sure the proper device driver kernel module is loaded with

modprobe -v tun

Normally we put this into a startup file like /etc/rc.d/rc.inet2 or /etc/rc.d/rc.openvpn. The device driver kernel module handles both tun and tap devices, and using modprobe when it is already loaded will not cause any problems.

The essential configuration is actually quite simple. There are only four required directives for using OpenVPN in Static Key mode - let's put them into configuration files (one for tun and one for tap on each host):

On fri   On mon
  # OpenVPN tun-fri-mon.conf
    remote mon.tn
    secret static.key
    dev tun
    ifconfig 192.168.86.81 192.168.86.18
<tun>
  # OpenVPN tun-mon-fri.conf
    remote fri.tn
    secret static.key
    dev tun
    ifconfig 192.168.86.18 192.168.86.81
  # OpenVPN tap-fri-mon.conf
    remote mon.tn
    secret static.key
    dev tap
    ifconfig 192.168.86.81 255.255.255.0
<tap>
  # OpenVPN tap-mon-fri.conf
    remote fri.tn
    secret static.key
    dev tap
    ifconfig 192.168.86.18 255.255.255.0
  1. The remote directive takes one parameter, the IP address or DNS name of the remote VPN endpoint
  2. The secret directive is the full path to the shared encryption key file
  3. The dev directive is the device: tun or tap
  4. The ifconfig directive takes two parameters: first, the IP address or DNS name of the local VPN endpoint,
    and second
    • For TUN devices, the IP address of the remote VPN endpoint.
      Both local and remote should be private IP addresses which are not in any subnet being used.
    • For TAP devices, the subnet mask of the bridged ethernet segment defined in your
      bridge-setup script (see below). The local VPN endpoint address must belong to this subnet.
    (Simple, and so easy to get wrong ...)
Since we only have to type it once in the configuration file, add some additional directives to each one:
# Switch UID and GID to "nobody" after initialization for extra security.
  user nobody
  group nobody

# Message detail ('verb'osity) - this may or may not be the syslog level ...
# 0 = quiet except for fatal errors
# 1 = mostly quiet, but display non-fatal network errors (default)
# 3 or 4 = medium output, good for normal operation
# 5 = output R or W for each packet read or write
#     uppercase for TCP/UDP packets, lowercase for TUN/TAP packets
# 6 to 9 = verbose, good for troubleshooting
  verb 3
Now to start OpenVPN all we need to do is put the name of the configuration file on the command line, along with the cd directive to tell OpenVPN to change directory before reading any configuration files, key files, and scripts. cd takes an absolute path with a leading / and without any . or .. references to the current directory. This option lets us put all of the OpenVPN control files in one location.

openvpn --cd /etc/openvpn --config tun-mon-fri.conf

This (properly modified for our cnames) will be just enough to verify the tunnel is working: we should be able to ping the first IP in the ifconfig directive from the other host. The problem is that we need to do this very quickly on both hosts. When OpenVPN starts from the command line it tries to connect to the peer immediately and will time out if it cannot. In this bare-bones mode it will also time out if there is no traffic.

OpenVPN and xinetd

The ideal tool to solve the race condition that comes from starting two peers is a xinetd listener, just like the one in Utilities::Multitail on the menu: it will (patiently) listen and start the local side when it receives a connection request from a remote host.

The only thing we need to take care of is when we run multiple instances of OpenVPN we need a separate (tun/tap) adapter and a separate port for each instance. If we don't want them to talk to each other we also need to ensure each adapter has a unique, non-overlapping subnet.

Having a set of ports is no problem: looking in /etc/services we find there is only one tcp+udp port for OpenVPN (1194), but there are a couple of useful blocks of tcp+udp ports that are assigned to services we are highly unlikely to use ...
timbuktu-srv1 1417
timbuktu-srv2 1418
timbuktu-srv3 1419
timbuktu-srv4 1420
timbuktu was a remote desktop
(screen-sharing) client/server
application that was discontinued
in 2015.

netview-aix is network monitoring
software for the IBM version of Unix
(AIX), that runs mainly on IBM hardware.
netview-aix-1  1661
 ...2 through 8...
netview-aix-9  1669
netview-aix-10 1671
netview-aix-11 1672
netview-aix-12 1673
Get /etc/xinetd.d/ on both sides in mc. Use [F5] to copy and rename telnet (just type the new filename in the destination field). The filename needs to match the service and id directives, so be sure to decide in advance.

Just a few quick edits and we're ready.
    service timbuktu-srv1
    {
        id            = timbuktu-srv1	
    #    flags         = NAMEINARGS
        socket_type   = dgram
        protocol      = udp
        wait          = yes
    #    user          = root
        server        = /usr/sbin/openvpn
        server_args   = --inetd --cd /etc/openvpn --config tun-mon-fri.conf
    #    only_from     = 127.0.0.1
        disable       = no
    }
Restart xinetd and connect using this on the remote system from the command line or a shell script:

openvpn --port 1417 --daemon --cd /etc/openvpn --config tun-mon-fri.conf

The daemon directive frees the terminal after all initialization functions are completed, and sends all message and error output to syslog (rumour has it the syslog facility is 'daemon', but the level is undocumented). The port can be specified by number or its name in /etc/services

Remember, using UDP and Static Key mode you need a separate file in /etc/xinetd.d FOR EACH connection, the filename must match the service name, and you need to specify the port when you connect from the remote system. Other configurations using TLS mode and/or TCP are described in the OpenVPN documentation.

Getting Real

So far we have covered the very basics - just enough to get a tun-to-tun or tap-to-tap connection. But that's not quite enough to be really practical - to really use the VPN a bit more needs to be done with the startup files.
  • For tun, start ip forwarding between interfaces and set a static route
  • For tap, create a virtual ethernet bridge on the server side

Routed VPN

To talk with the networks behind your VPN server, the first step is to add a static route in the configuration file.
                xinetd                      script
   +-------------+   +-------+       +-------+   +-------------+
   | mon (net-b) |---| <tun>  [<< >>]  <tun> |---| fri (net-c) |
   +-------------+   +-------+       +-------+   +-------------+
   192.168.66.0/24       [192.168.86.0/24]       192.168.76.0/24
The OpenVPN route directive adds a route to the routing table after the connection is established. Multiple routes can be specified. There are two keywords we can use to make things simple.

On fri (client side), we derive the gateway from the
second parameter to ifconfig
On mon (xinetd side) we do the same for the
fri subnet, and read the default gateway
from the routing table for other subnets
  # OpenVPN tun-fri-mon.conf
    remote mon.tn
    secret static.key
    dev tun
    ifconfig 192.168.86.81 192.168.86.18
route 192.168.66.0 netmask 255.255.255.0 vpn_gateway

  # OpenVPN tun-mon-fri.conf
    remote fri.tn
    secret static.key
    dev tun
    ifconfig 192.168.86.18 192.168.86.81
route 192.168.76.0 netmask 255.255.255.0 vpn_gateway
route 192.168.56.0 netmask 255.255.255.0 net_gateway

With those directives in our OpenVPN configuration files, the last step is to turn on IP forwarding between the tun and the eth interfaces. This command needs to be in a shell script - put it in the same one that has the command to load the device driver (/etc/rc.d/rc.inet2 or /etc/rc.d/rc.openvpn)

# load the tun/tap device driver kernel module
modprobe -v tun
# turn on IPv4 forwarding between interfaces
echo 1 > /proc/sys/net/ipv4/ip_forward

Bridged VPN

Bridging is not as easy to set up as routing, but the scripts in /etc/openvpn/sample-scripts take care of the gnarly part.

An ethernet bridge is essentially the software equivalent of a physical ethernet switch. Each connected interface corresponds to one "switch port" for the bridge. Network traffic coming in on any of these ports will be forwarded to the other (layer 2) ports transparently, which is the same thing that turning on IP forwarding does for our (layer 3) TUN configuration.

The ethernet bridge interface must be created, and the relevant ethernet interfaces added to it, before OpenVPN is actually started. This requires the Linux bridge-utils package, which is part of the MyTyVM core. The IP address and subnet of the bridge interface must be set in a script; we cannot set these with an ifconfig directive in the OpenVPN config file, like we can for the TAP interface.

Usually the bridge is only configured on the server side, not the client side. The clients will be "multi-homed" when they connect to the server: they have their regular ethernet interface plus a TAP interface that is bridged with the server's ethernet interface when the OpenVPN connection is established.

This is the gnarly part: the scripts to set up the bridge interface and add the physical interface to the bridge.
#!/bin/bash
# Bridging setup script 
# ORDER IS IMPORTANT: run this BEFORE rc.inet1 and rc.xinetd
  # load the device driver kernel module
    modprobe tun
  # load the ethernet bridging kernel module
    modprobe bridge
  # create a bridge interface named br0
    brctl addbr br0
  # set the 'bridge forward delay' to [n seconds]
    brctl setfd br0 0
  # control participation in the IEEE 802.1d spanning tree protocol
  #   (please know what you are doing if you turn this off)
  #  brctl stp br0 off

  # bridge configuration - DO NOT use this IP in OpenVPN directives
    ifconfig br0 192.168.86.1 netmask 255.255.255.0 broadcast 192.168.86.255








A common mistake that people make is to add the primary ethernet adapter to the bridge before they have set the IP and netmask of the bridge interface. It is important to understand that only the TCP/IP settings for the bridge interface are relevent: each interface added to the bridge will lose its individual identity in terms of IP address and netmask.

IMPORTANT: the address used in the remote directive MUST be joined to the bridged interface but MUST NOT be part of the bridged subnet - otherwise we would end up with a routing loop.
#ADD THE PHYSICAL INTERFACE TO THE BRIDGE - order is important!

need a grep here from rc.inet1 // or $1

  brctl addif br0 $eth


Now set up the Linux firewall to permit packets to flow 
freely over the newly created tap0 and br0interfaces:
iptables -A INPUT -i tap0 -j ACCEPT
iptables -A INPUT -i br0 -j ACCEPT
iptables -A FORWARD -i br0 -j ACCEPT



The command brctl addif <brname> <ifname> 
will make the interface <ifname> a port of the bridge <brname>. 

This means that all frames received on <ifname> will be processed as if 
destined for the bridge. 

Also, when sending frames on <brname>, <ifname> will be considered as a 
potential output interface.


The OpenVPN dev directive  
specifies the TAP interface joined to the bridge, 
not the name of the bridge interface itself.



usage start|stop|status

The command brctl show shows all current instances of the ethernet bridge.

The command brctl show <brname> will show some information on the bridge 
and its attached ports.

The command brctl delbr <brname> deletes the instance <brname> of the ethernet 
bridge. The network interface corresponding to the bridge must be down before 
it can be deleted!
The command brctl delif <brname> <ifname> will detach the interface <ifname> 
from the bridge <brname>







On our server (xinetd) side, we need to modify the ifconfig directive in the tap configuration file to call an up script to join the tap interface to the bridge interface. So, the config file (on mon, using the diagram above) needs to be changed like this:
  # OpenVPN tap-mon-fri.conf
    remote fri.tn
    secret static.key
    dev tap
    ifconfig 192.168.86.18 255.255.255.0
    ifconfig-noexec
    up tap-server.up
The up script (permissions 755) looks like this:
#!/bin/bash
  # OpenVPN tap-server.up
  # add interface to bridge and activate
  brctl addif br0 $1
  ifconfig $1 up
(details of the positional parameters are hard to find in the documentation)
  # command line passed to the shell by ifconfig-noexec   
  #     %0  script_name
  #     %1  device (from dev directive)
  #     %2  device_mtu 
  #     %3  link_mtu
  #     %4  local_ip (from ifconfig directive)
  #     %5  remote_ip || netmask (from ifconfig directive)
  #     %6  init || restart (default: init)
















OpenVPN can now be started and stopped using this sequence::
on the server,
run bridge-setup - tun-setup BEFORE xinetd

on the client,
run openvpn
stop openvpn


# If you want to see everything that is actually being passed 
# add a line like this at the start of your script. 
# (echo "$*" ; export )> /tmp/blah 



A final security note: never bridge a TAP interface with the same ethernet interface you use to connect to the internet; be sure to only bridge TAP interfaces with private ethernet interfaces which are protected behind a firewall.















Useful Resources

See the Examples section of the man page at 
      https://linux.die.net/man/8/openvpn
      for a tunnel with full TLS-based security and IPtables firewall rules
Some nice tricks
      http://workaround.org/openvpn-faq
A very complete and extensively commented config file for multi-client server and for the clients too!
      http://www.commentcamarche.net/forum/affich-5348139-open-vpn
Three interesting scenarios
      http://www.wains.be/index.php/2008/07/18/openvpn-routing-all-traffic-through-the-vpn-tunnel/
NICE discussion of openvpn push v. dhcp
      http://hans.fugal.net/blog/2009/01/06/putting-openvpn-in-its-place/