Configuring OpenVPN
for multiple clients

1/31/2006
Eric Low

We needed a solution to allow multiple staff members, running Windows, to establish VPNs into our private network. OpenVPN is set up to allow this without too much difficulty, and includes versions for both Linux and Windows.

We already had a CA set up on the OpenVPN server according to my previous entry. This leaves off from there (before the IPSec stuff though).

I also generated a tls-auth key, which causes OpenVPN to drop all packets that do not contain a signature, helping to protect against portscans, DOS, etc. See the OpenVPN HOWTO for more info. This is done by typing openvpn --genkey --secret ta.key. This will dump that key to a file called ta.key, which I then moved to the /usr/share/ssl/certs/ directory. You will distribute this file to the clients as well, and include it in both your and their OpenVPN config files.

On the client:

First, download OpenVPN GUI for Windows. I chose the latest stable release, which is 1.0.3 with OpenVPN 2.0.5 (released 11/4/2005). We already had OpenVPN 2.0.5 installed on the server, as a matter of fact, so this seemed like the perfect choice!

I ran the setup (on a Windows 2000 client), making sure to check "My Certificate Wizard" and leaving the default install location as C:\Program Files\OpenVPN. The installation created a new LAN adapter, named "TAP-Win32 Adapter V8."

Once installation was complete, I checked the My Certificate Wizard INI file (C:\Program Files\OpenVPN\bin\mycert.ini) and made sure that it would create a 2048-bit RSA key as we required on our CA, and this was, in fact, the default, as was a one-year expiration on the key. I then ran My Certificate Wizard to create a certificate signing request. The options that need to be filled in are straightforward and self-explanatory, but nonetheless, here is what my answers looked like:

Common Name: Eric Low
email: elow@datastat.com
Country Name: US
State: Michigan
Locality: Ann Arbor
Organization Name: DataStat Inc. (this field does not allow commas)
Organizational Unit: Eric Home

The default also requires a pass phrase of at least six characters, which is good. I typed all that in, then clicked Create Request. This will save a certificate request and a private key in the C:\Program Files\OpenVPN\config\ directory, named according the Common Name that you entered. For example, my CSR is named Eric_Low.req and my private key is named Eric_Low.key. The program will also display and highlight the CSR for you, which you can then copy to the clipboard if you'd like to send it in an email. This is identical to the .req file.

 

On the server:

Copy that .req file or text to /usr/share/ssl/certs/newreq.pem on the server (this file should not already exist. If it does, it means you forgot to delete an old CSR). Next, sign the request by typing the following:

cd /usr/share/ssl/certs
/usr/share/ssl/misc/CA.pl -sign


It will then ask you for the CA password, show you the details of the CSR, and ask you if you wish to commit. When you say yes, it will add the required info to the index.txt in your CA directory (in our case, /usr/share/ssl/certs/PRLinkCA/index.txt), as well as stick the signed certificate in your newcerts directory (in our case, /usr/share/ssl/certs/PRLinkCA/newcerts/). Finally, it will create a signed certificate called newcert.pem (in the /usr/share/ssl/certs/ directory), which you will need to send back to the client computer. After you do that, you may delete both newreq.pem and newcert.pem from the server.

 

On the client:

You must also copy the CA Server certificate to the client computer and stick it the same OpenVPN\config\ directory as all your other OpenVPN keys. On our CA (server), this file is named /usr/share/ssl/certs/PRLinkCA/cacert.pem. So obviously, all clients will use the same CA certificate. I copied ours to OpenVPN\config\DScert.pem.

Copy that signed certficate, newcert.pem, to the client computer and save it in the C:\Program Files\OpenVPN\config\ directory, naming it according to your Common Name (I named mine Eric_Low.pem).

Next, you will need an .opvn (OpenVPN session config) file. Each time OpenVPN GUI starts, it scans the C:\Program Files\OpenVPN\config\ directory for any files with this extension, and attempt to create a connection based upon its contents. You can modify one of the default config files for this purpose (take a look at C:\Program Files\OpenVPN\sample-config\client.ovpn).

We already had a bridged OpenVPN tunnel running, limited to one client. So for these clients, I needed to set up a second instance of openvpn, listening on a different port, as well as using tun (routing) rather than tap (bridging) interface for ease of configuration.

Most of the default client config file was exactly how I wanted it. However, there were a few options that required attention. Here is my client.ovpn file, with the important (changed) lines in bold:

client

dev tun

proto udp
remote ourvpn.datastat.com 1094
;remote-random
resolv-retry infinite

nobind

persist-key
persist-tun

;mute-replay-warnings

ca DScert.pem
cert Eric_Low.pem
key Eric_Low.key
tls-auth DSta.key 1

comp-lzo

verb 3
;mute 20

 

The server directive, of course, is needed to point to the openvpn server and port. Notice the tls-auth directive, which tells the client where to find the tls-auth keyfile. This will stick a signature on all packets, and then the server will drop any packets that do not contain that signature.

I dumped this file into the OpenVPN\config directory, then went down to my system tray, right-clicked on the OpenVPN GUI icon, and selected Connect. A dialogue box popped up to prompt for my key's password. After that, a status window popped up and showed me what was happening with the connection. A few seconds later, it told me that the virtual adapter had a connection, and voila! I could see our internal network. :)

 

On the server:

I modified the default /etc/openvpn/server.conf file (renaming it to /etc/openvpn/servingothers.conf) for our needs.

local 198.175.107.12
port 1094
proto udp
float

dev tun
tun-mtu 1500
#fragment 1300
mssfix 1300
#tcp-queue-limit 256
txqueuelen 15000

#mode server
tls-timeout 60
tls-server
ca /usr/share/ssl/certs/PuertoRicoLinkCA/cacert.pem
cert /usr/share/ssl/certs/PuertoRicoLinkCert.pem
key /usr/share/ssl/certs/PuertoRicoLinkKey.pem
tls-auth /usr/share/ssl/certs/servingothers-ta.key 0

dh /usr/share/ssl/certs/dh2048.pem

server 192.168.6.0 255.255.255.0

ifconfig-pool-persist /etc/openvpn/servingothers-ipcache.txt

push "route 192.168.5.0 255.255.255.0"
push "route 198.173.192.0 255.255.255.0"
push "dhcp-option DNS 198.173.192.250"

;client-to-client

# This directive is pushed to the client, although the ping-restart
# on the server will be DOUBLE what is pushed to the client.
# In this case, the server would get a ping-restart of 240.
keepalive 10 120

comp-lzo

max-clients 5

# It's a good idea to reduce the OpenVPN
# daemon's privileges after initialization.
#
# You can uncomment this out on
# non-Windows systems.
;user nobody
;group nobody

persist-key
persist-tun

status /etc/openvpn/servingothers-status.log
log-append /etc/openvpn/servingothers.log

verb 3
;mute 20

This config, set up for routing, will create one tun interface named tun0, through which all clients are routed (the IP of this interface is their gateway). The IP of this interface is defined by the server directive - the tun interface always gets the first IP on this subnet (in our case, .1) Notice the push directives, which send rules to the client when they log in. I push a route command for each internal subnet that the clients should see, as well as one so they see our internal DNS server.

Again, notice the tls-auth directive! This is very important because it causes packets that do not contain that signature to be dropped! See the beginning of this document for info about how to generate the file. The server gets an option of 0 at the end of this line, while the client, which has the same file specified, gets a 1.

As far as firewall rules, they are pretty straightforward:

iptables -A INPUT -p udp --dport 1195 -j ACCEPT

iptables -A FORWARD -i tun0 -m physdev --physdev-out eth1 -j ACCEPT
iptables -A FORWARD -o tun0 -m physdev --physdev-in eth1 -j ACCEPT
iptables -t nat -A POSTROUTING -m physdev --physdev-out ! eth0 -s 192.168.6.0/24 -d 198.173.192.0/24 -j SNAT --to 198.173.192.1
iptables -t nat -A POSTROUTING -m physdev --physdev-out ! eth0 -s 192.168.6.0/24 -d 192.168.5.0/24 -j SNAT --to 192.168.5.1

Our normal firewall setup is a bridge, and I am not making tun0 a part of it. Therefore, with ip_forwarding turned on, anything that comes in through tun0 will have an input interface of tun0 and an output interface of bridge. So basically, it gets forwarded to the bridge stack. Then, any of those packets that are destined for our internal network (most of it, anyhow) will go out through physdev eth1. Hence that first forwarding rule. The next two lines simply handle the SNAT so the clients can talk to the internal subnet.

 

=========================================================

Route the client's subnet back to the server

It's also pretty easy to add the client's subnet(s) to your intranet, so that you're sharing in both directions. And yes, this still involves the client running Windows, the server running Linux, and both using tun interfaces (I say that because this would be ridiculously easy with a bridge - hell, there would be no setup!). This simply involves setting Windows to do packet forwarding (routing) and then putting the appropriate routing entries in place.

Our purpose for doing this was actually to route VOIP through the tunnel so that users could make calls through our voip server from home, but only over a secure line. I have a feeling I made this overly complicated though, so bear with me!

First, I added a second network card, behind which I would stick the VOIP box (and only the voip box) and which would act as the VOIP box's gateway. The VOIP box was configured to have an IP address of 192.168.85.2 and use a gateway of 192.168.85.1, the IP address of which I statically assigned to the new network card (netmask of 24, in case you were wondering)

Next, I statically assigned that network card an IP address of 192.168.85.1, but told it to use a gateway of 192.168.80.6, which is the IP ADDRESS of the virtual tun interface.

In the OpenVPN server's configuration file, I added the following directives:

client-config-dir /etc/openvpn/ccd
route 192.168.85.0 255.255.255.0
push "route 192.168.85.0 255.255.255.0"

That route directive will cause OpenVPN to add a route to the kernel's routing table, sending that subnet to OpenVPN, once a tunnel is established (it sends it to OpenVPN, not to the virtual interface - OpenVPN takes it from there, sending it to the correct client, which is configured by the iroute directive). (Typing ip route will show 192.168.85.0/24 via 192.168.80.1 dev tun0). The push route directive tells the client to add an entry to its routing table, sending that subnet back in the other direction.

Next, you must add an iroute directive. Because that route command only tells the kernel to route that subnet to OpenVPN, OpenVPN must know where to send it once it gets it. An iroute directive can only go in either a client-connect script or a client instance config file. We are going to put it in a client instance config file, which is basically a small config that is added when a given client connects, based off of the common name on their certificate (this is what the client-config-dir directive up above is for - OpenVPN compares the files in the specified directory with the user's common name each time a connection is made).

I created a client instance config file named /etc/openvpn/ccd/Eric_Low which contained the following line:

iroute 192.168.85.0 255.255.255.0

So, now the server and the client both know where to route packets. Finally, you must turn on IP forwarding on the Windows client. Believe it or not, almost every version of Windows is capable of this! Here is a very good document describing how. On Windows 2000/XP, you simply have to set the HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\IPEnableRouter to 1 (reg_dword).

Voila! Windows now forwarded all packets from the VOIP box at 192.168.85.2 to the second NIC on the Windows computer, 192.168.85.2, which then forwarded them on to the OpenVPN server's tun interface at 192.168.80.1, which then forwarded them to our internal network (they will have their original source IP the entire way). And, of course, vice-versa! It's just that simple. Keep in mind, however, that you may need to SNAT any packets (on the OpenVPN server/firewall) to be on the correct subnet.

A word of caution about turning on IP forwarding on the Windows client: Should the tunnel get disconnected, causing the VOIP box's gateway to no longer exist, the Windows box will still forward the packets - since its normal route is gone, it will now forward them to its own default gateway! My router (which is obviously configured wrong) was then NAT'ing the packets so they all had a real live source IP. And, at least with my Windows firewall (Tiny Personal Firewall), it was unable to see these packets! Apparently it does not listen at a low enough level, or these packets somehow pass through below what anything in the userspace can see. So, be careful! Luckily, our VOIP boxes are only trying to hit one location, which is on our private intranet and therefore blocked by our firewall unless the packets come through the tunnel. But I can see the packets trying to come in nonetheless!

 

Routing a client's subnet back to the server can also be done with just one network card.

In my test, I plugged my VOIP box into the same hub that my computer's external interface was connected (both behind my router, but it should work even if it's plugged directly into a cable modem or whatever). I assigned the VOIP box a static IP address of 192.168.85.2/32 with a gateway of 192.168.85.1. I then assigned my Windows NIC an alias IP address of 192.168.5.1/32 (this is easily done by going to Local Area Connection -> Properties -> TCP/IP -> Advanced -> Add IP address). So now the Windows NIC was the gateway for the VOIP box.

I once again turned IP forwarding on in Windows, as described above. I added a route to the VOIP box in Windows by typing ROUTE ADD 192.168.85.2 MASK 255.255.255.255 192.168.85.1.

On the server, I put the following in the OpenVPN config file:

client-config-dir /etc/openvpn/ccd
route 192.168.85.2 255.255.255.255

I did not add a push route. In the Common Name script in the ccd directory (/etc/openvpn/ccd/Eric_Low), I put the following line:

iroute 192.168.85.2 255.255.255.255

When I connected to the server, voila! They could both see each other. The VOIP box sent all packets to its gateway (these will have the MAC address of the gateway but the correct destination IP), which then forwarded them through the tunnel to its gateway, the OpenVPN server. This worked whether the OpenVPN tunnel was on the same subnet as the VOIP box or not! The reason is that the VOIP box was trying to communicate with IP addresses on the server's subnet(s), the routes of which were pushed to the client.

Enjoy!

 

===========================================================

 

 

 

 

 

*** Should the OpenVPN GUI installation program freeze, it is most likely having difficulty installing the TUN interface. The user will need to reboot Windows to abort the installation, then install the TUN interface manually (the program will actually still be installed at this point). After rebooting, simply click on Start -> Run and type CMD to open a command prompt. Then, type the following commands:

cd c:\Program Files\OpenVPN\driver
tapinstall install OemWin2k.inf TAP0801

You should then see the following success message:

Device node created. Install is complete when drivers are updated...
Updating drivers for TAP0801 from C:\Program Files\OpenVPN\driver\OemWin2k.inf.
Drivers updated successfully.

Now, run OpenVPN manually by clicking on Start -> Programs -> Openvpn -> OpenVPN GUI. This will cause openVPN to add the correct registry keys and complete the installation. If necessary, drag the OpenVPN GUI icon to the Startup Folder.

 

Downloads:

openvpn-2.0.5-gui-1.0.3-install.exe