dcsimg

Casting Your Net with OpenVPN

A virtual private network (VPN) extends the resources of your local area network to telecommuter’s home, satellite offices, and far-flung sales warriors. OpenVPN is a fast, scalable, secure, and free VPN solution for Linux. And best of all, it’s easy to configure and deploy. Here’s a hands-on primer.

OpenVPN (http://openvpn.net/) is a fast, open, free, and scalable SSL/TLS- based virtual private network (VPN) solution. OpenVPN can route, bridge, and scale to hundreds of clients, tunnel over a single port (UDP or TCP, even through HTTP and SOCKS5 proxies), traverse NAT with ease, use static or public key-based encryption, and authenticate via PAM or any other scriptable authentication mechanism. Best of all, OpenVPN is incredibly simple to configure, and it runs in most common operating systems, including *BSD (FreeBSD, NetBSD, and OpenBSD), Linux, Mac OS X, Solaris, and yes, even Windows.

Let’s set up a PKI-based, routing VPN in Linux for Fancy Sprockets, Incorporated, manufacturers of the world’s finest sprockets, widgets, and doodads (it’s a growth industry). More about Fancy Sprockets later; first, you must install OpenVPN.

OpenVPN requires a kernel with tuntap support, so in practice, that means either a 2.4 or 2.6 kernel with the tuntap module. OpenVPN packages are available for most popular Linux distributions. If you use Debian or one of the many Debian-based distributions, installing OpenVPN couldn’t be easier: Just type apt-get install openvpn as root.

RPM s for Red Hat and Fedora are available from the DAG RPM Repository (http://dag.wieers.com/packages/openvpn/). If you’re using an RPM-based distribution other than Fedora or Red Hat, the OpenVPN source tarball includes a spec file and instructions on building your own RPM. If you’re using Gentoo, there’s an OpenVPN ebuild available through Portage.

If you want to build from source, you need OpenSSL (http://openssl.org/), and if you want to use the recommended adaptive link compression, you also need the LZO compression library

(http://www.oberhumer.com/opensource/lzo/).

Once you have OpenSSL (and, optionally, the LZO compression library) installed, grab the latest stable version of OpenVPN (version 2.0.5, as of this writing) from the OpenVPN download page (http://openvpn.net/download.html), uncompress the source tarball, cd into the source directory, and read both the INSTALL and README files for any architecture- or distribution-specific notes. As soon as everything is in order, run ./configure&&make&&su –c ‘make install’, spend a few minutes reading Fark.com (http://www.fark.com/), and you should be up and running.

Simple VPN Authentication

Let’s start with the simplest possible VPN authentication configuration: static, pre-shared, keys. As the description implies, static keys are a secret key shared between the OpenVPN server and the client. While this configuration has a few disadvantages — a lack of perfect forward secrecy (more about that in a bit), no key exchange mechanism, and, worst of all, only one client per server — tailoring OpenVPN to use static keys is fast and simple.

First, generate a static key:

# openvpn ––genkey ––secret static.key

Next, copy the static key to both the client and the server. This step is entirely dependent on your server setup, but it’s best to use SneakerNet (an advanced, high-latency, high-bandwidth, jumbo-frame network infrastructure that consists of a floppy disk or USB memory stick, walking, and, you guessed it, a pair of sneakers), SSH, or some other equally secure channel to copy the key to both machine.

Next, create the server and client configuration file. In the examples below, communication is via UDP port 1194 (the default, IANA-assigned OpenVPN port number), the server endpoint is 10.55.55.1, and the client endpoint is 10.55.55.2. The OpenVPN documentation recommends that you not use the 192.168.0.0/24 subnet, since it’s the most popular subnet for wireless access points and other NAT devices. Also, don’t forget to replace the imaginary server (vpn.fancysprockets.com in the example below) with the hostname or IP address of your actual OpenVPN server, and, for pete’s sake, don’t forget to open up UDP port 1194 on your firewall.

Here’s the server configuration using a static key:

# static key config (server)
dev tun
ifconfig 10.55.55.1 10.55.55.2
secret static.key

And here’s the equally sparse client configuration file:

# static key config (client)
remote vpn.fancysprockets.com
dev tun
ifconfig 10.55.55.2 10.55.55.1
secret static.key

With the files in hand, launch OpenVPN on both the client and server (as root), like so:

# openvpn ––config /path/to/config.conf

After running the command on both machines, you should be able to ping the client from the server, and vice versa. If you get stuck here, make sure your firewall allows traffic on UDP port 1194 — that’s usually the culprit for connection-related problems. (If you’re using Netfilter (iptables) with connection tracking, try adding the lines in Listing One to your firewall rules. Working now? Good.)

LISTING ONE: Netfilter rules to allow traffic via UDP port 1194

# allow input on all interfaces for port 1194/udp (OpenVPN)
iptables –A INPUT –p udp ––dport 1194 –j ACCEPT

# for testing, allow connections from all tunnel interfaces
# (you’ll probably want to restrict this more later)
iptables –A INPUT –i tun+ –j ACCEPT

# allow forwarding from all tunnel interfaces
iptables –A FORWARD –i tun+ –j ACCEPT

# enable IPv4 forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

Now, let’s get OpenVPN working with public keys.

Certified: OpenVPN With Public Keys

OpenVPN also supports client authentication via X509 PKI certificates. In OpenVPN, authenticating clients with public keys has several advantages over authenticating with static keys:

*Multiple Clients. A single VPN daemon can support an arbitrary number of clients, limited only by bandwidth and processor power.

*Perfect Forward Security(PFS). The symmetric key used to encrypt the connection is established during the initial public key exchange, is re-negotiated hourly, and isn’t based on previous keys. This means that an attacker cannot derive the current or future connection keys based on prior ones.

*Protection against man-in-the-middle(MITM)attacks. OpenVPN certificate validation is bi-directional, meaning each side verifies the the validity and issuer of their counterpart’s certificate. This protection is not absolute, however; in particular, it doesn’t protect a specific form of MITM attack from another client with a valid, signed certificate. Fortunately there are two solutions to this problem, both covered later in this article.

Creating and managing a properly configured OpenSSL-based Certificate Authority (CA) is a fairly elaborate process. Fortunately, there are a couple of alternatives. The OpenVPN package includes a set of scripts called easy-rsa that simplifies the process — it’s usually located in either /usr/share/doc/openvpn-2.0/ or /usr/share/packages/openvpn/ for RPM-based Linux distributions, or /usr/share/doc/openvpn/ in Debian.

First things first: Copy the easy-rsa directory to a working directory (this example uses $HOME/openvpn) and create a subdirectory in the working directory named keys:

$ cp –av /usr/share/doc/openvpn/examples/easy-rsa \
~/proj/fancysprockets_ca
$ mkdir ~/proj/fancysprockets_ca/keys

Next, cd into your working easy-rsa directory and edit the file vars, changing the KEY_SIZE, KEY_COUNTRY, KEY_PROVINCE, KEY_CITY, KEY_ORG, and KEY_EMAIL parameters. Here’s what the file looked like for the example CA:

# key length (in bits) for our
# Diffie-Hellman parameters (you
# probably don’t want anything
# less than 2048 here)
export KEY_SIZE=2048

# Our default certificate parameters
export KEY_COUNTRY=US
export KEY_PROVINCE=”Virginia”
export KEY_CITY=”Annandale”
export KEY_ORG=”Fancy Sprockets, Inc.”
export KEY_EMAIL=”ca@fancysprockets.com”

Now, load your configuration values, create the Diffie-Hellman (DH) parameters, and create the root CA certificate and key:

. ./vars
./clean-all

# create DH parameters. This will probably
# take a couple of minutes, even on your
# fancy Opteron, so fire up minesweeper or
# go get a cup of coffee.
./build-dh

# create root CA cert and key
./build-ca

The output from build-ca should look something like Figure One.

FIGURE ONE: The results of creating the root certificate authority

Generating a 2048 bit RSA private key
….+++
……………..+++
writing new private key to ’ca.key’
—–
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter ’.’, the field will be left blank.
—–
Country Name (2 letter code) [US]:
State or Province Name (full name) [Virginia]:
Locality Name (eg, city) [Annandale]:
Organization Name (eg, company) [Fancy Sprockets, Inc.]:
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server’s hostname) []:Fancy Sprockets Root CA
Email Address [ca@fancysprockets.com]:

Of all the prompts shown in Figure One, the only option that’s required is the Common Name; every other value defaults to the value you saved in ./vars.

Creating the server and client certificates works the same way. Here’s the command to create the Fancy Sprockets VPN server certificate:

# create server cert and key
# (./keys/server.{crt,key})
./build-key-server server

Next, let’s create certificates for each of Fancy Sprockets’ four employees — Alice, Bob, Carol, and Mallory. You can use whatever naming convention and unique values you’d like for the Common Name (CN) field, but choosing path-friendly CNs, such as “client1,” “client2,” and “client3”, or “alice_smith,” “bob_jones,” and “carol_franklin,” makes things easier if you decide to do any client-specific configuration in the future. To keep things simple, let’s use first names, since Fancy Sprockets only has a few employees. Listing Two shows how to create the client certificates.

LISTING TWO: Creating the client certificates

# create client certificates for each employee (keys/$user.{crt,key}),
# and default to the corrent email address for each employee, too
for user in alice bob carol mallory; do
KEY_EMAIL=”$user@fancysprockets.com” ./build-key $user
done

The directory ./keys contains the results, the data files for an OpenSSL CA and a handful of of certificates and private keys. Figure Two depicts the certificate hierarchy.

FIGURE TWO: The certificate hierarchy used by the clients and server



(If command-line CA scripts aren’t your cup of tea, you might be interested in TinyCA (http://tinyca.sm-zone.net/). TinyCA, pictured in Figure Three, is a simple, graphical, OpenSSL front-end for managing certificate authorities, creating certificate requests, and issuing and revoking certificates.)

FIGURE THREE: The TinyCA graphical certificate management tool



Configuring the PKI-Enabled OpenVPN Server

Configuring the OpenVPN server to use your shiny new certificates is a relatively straightforward process. To begin, create the OpenVPN configuration directory (if it doesn’t already exist) and copy all of the necessary files from the working directory to the OpenVPN configuration directory:

$ su –c ’mkdir –p /etc/openvpn && \
cp –v keys/ca.crt keys/server.{crt,key} \
keys/dh2048.pem /etc/openvpn/’

Now make sure the server private key really is private:

$ su –c ’chmod 0600 /etc/openvpn/server.key’

Finally, adjust the configuration to enable PKI. Listing Three shows the new config file for the Fancy Sprockets VPN server.

LISTING THREE: Enabling PKI on the Fancy Sprockets VPN server

# use tun device
dev tun

# CA certificate, server certificate, and server private key
ca ca.crt
cert server.crt
key server.key

# Diffie-Hellman parameters
dh dh2048.pem

# run as a server on 10.55.55.1, and supply the rest of the IPs in
# the 10.55.55.0/24 subnet for clients.
server 10.55.55.0 255.255.255.0

# maintain a pool of client-IP associations (e.g. clients keep
# the same IP between OpenVPN resets)
ifconfig-pool-persist ipp.txt

# keep link alive by sending ping-like traffic through the link every
# 10 seconds, and assume the link is down if no ping is received for
# 120 seconds.
#
# Note: this directive is particularly important if you’re tunneling
# UDP through a stateful firewall (Netfilter, for example). Since UDP
# is, by definition, a stateless protocol, a stateful firewall will
# “forget” about a UDP connection if there isn’t any traffic going
# through the link at regular intervals.
keepalive 10 120

# enable real-time adaptive compression
comp-lzo

# maximum number of simultaneously connected clients
# (defaults to 100, but Fancy Sprockets doesn’t need that many, so
# let’s lower it a bit)
max-clients 50

# enable adaptive link compression
# (omit this line if you compiled OpenVPN without the LZO support)
comp-lzo

# short log of active connections and the internal routing table
# (truncated and re-written every minute)
status openvpn-status.log

# set log verbosity (0 = silent, ~4 = normal, 9 = extremely verbose)
verb 3

# silence repeating messages (no more than 20 of the same message
# category will be logged at a time)
mute 20

And that’s it for the server configuration. Save the configuration to the file server.conf, copy it to the OpenVPN configuration directory, then fire up OpenVPN like so:

$ su –c ’cp server.conf /etc/openvpn/ && \
chmod 0600 /etc/openvpn/server.conf && \
/etc/init.d/openvpn start’

Be sure to check your system logs to make sure there are no problems. OpenVPN logs to /var/log/daemon.log in Debian, although this is distribution-specific. Now let’s configure the VPN clients.

Configuring the PKI-Enabled Client

Configuring an OpenVPN client with PKI is even easier than configuring the server. Using Alice as an example, you’d copy Alice’s certificate (keys/alice.crt), Alice’s private key (keys/alice.key), and The root CA certificate (keys/ca.crt) to Alice’s machine (again, via SneakerNet, SSH, or some other secure method):

(By the way, the proper way to do things is to generate a certificate request from Alice’s machine, then sign and issue a certificate from the CA. More about that in the next section, but for now, just copy the private key over by hand).

Once the files are on Alice’s machine, copy them to the OpenVPN configuration directory, just like you did with the server. To allow the configuration file to work across all clients without modification, Alice’s uniquely-named files are renamed from alice.* to client.*.

su –c ’mkdir –p /etc/openvpn &&
cp –v ca.crt alice.{crt,key} /etc/openvpn/ &&
rename –v “s/alice/client/” /etc/openvpn/alice.*’

Next, create the client configuration file, as shown in Listing Four.

LISTING FOUR: A “generic” OpenVPN client configuration file

# we’re a client (e.g. pull some config directives from the server)
client

# use a tun device
dev tun

# hostname/IP and port of VPN server
remote vpn.fancysprockets.com 1194

# keep trying indefinitely to resolve the OpenVPN server (nice
# for laptops, dialup, or other machines with intermittent internet
# connections)
resolv-retry infinite

# don’t bind to a specific client-side port
nobind

# Root CA certificate, client certificate, and client private key
ca ca.crt
cert client.crt
key client.key

# enable adaptive link compression
# (omit this line if you compiled OpenVPN without the LZO support)
comp-lzo

# set log verbosity (0 = none, ~4 = normal, 9 = really verbose)
verb 3

# silence repeating messages
mute 20

Save this file as” client.conf”, copy it to the OpenVPN configuration folder, lock down the permissions, then start up OpenVPN:

su –c ’cp –v client.conf /etc/openvpn/ &&
chmod 0600 /etc/openvpn/client.conf &&
/etc/init.d/openvpn start’

At this point, you should be able to ping the server through the VPN from Alice’s machine with $ ping 10.55.55.1.

If the VPN isn’t up, make sure that forwarding is enabled, your firewall isn’t blocking traffic on UDP port 1194, and forwarding tun devices is enabled. Failing that, increase the logging verbosity on both the client and the server (try changing the verbosity level to 6 on each side), restart each side of the VPN, and keep an eye on your system logs, cross-referencing any errors with the OpenVPN FAQ (http://openvpn.net/faq.html).

Got everything squared away? Great! Now let’s lock down OpenVPN to protect it against attacks.

Lockdown: Securing OpenVPN

The saga at Fancy Sprockets takes a startling twist: Mallory, a former employee of the month, has been fired for stealing paper clips and sabotaging the office water cooler! Who knows what malice Mallory has in store for the burgeoning Fancy Sprockets corporate network? Hence, it’s necessary to disable Mallory’s VPN access and lock down the OpenVPN server.

Fortunately, securing OpenVPN is relatively simple. You can disable Mallory’s VPN access by enabling certificate revocation list (CRL) verification on the OpenVPN server, using the easy-rsa scripts to revoke her certificate, and then installing the CRL.

To revoke Mallory’s certificate, go back to the CA working directory (~/proj/fancysprockets_ca) and run the following commands:

# load CA configuration
. ./vars

# revoke mallory’s certificate
./revoke-full mallory

Here’s what the output looks like:

Using configuration from /home/pabs/proj/fancysprockets_ca/openssl.cnf
Revoking Certificate 04.
Data Base Updated
Using configuration from /home/pabs/proj/fancysprockets_ca/openssl.cnf
mallory.crt: /C=US/ST=Virginia/O=Fancy Sprockets, Inc./CN=mallory/emailAddress=mallory@fancysprockets.com
error 23 at 0 depth lookup:certificate revoked

(Don’t worry about that last line: error 23… means certificate validation failed, which makes sense, since you just revoked it.)

The revocation process also created a CRL file for the server, located in keys/crl.pem. Take keys/crl.pem and copy it to the OpenVPN configuration directory, then add the following lines to the server.conf file in your OpenVPN configuration directory:

# validate client certificate during
# TLS negotiation (also during
# key renegotiation, which, by
# default, happens once an hour)
crl-verify crl.pem

Now restart the OpenVPN daemon (using /etc/init.d/openvpn restart or killall –HUP openvpn). OpenVPN verifies the client certificate of each new connection against the CRL, which means Mallory won’t be able to connect to the Fancy Sprockets VPN any more. Revoking future certificates in the future is even easier; just use the revoke-full script and copy the generated CRL into the OpenVPN configuration directory. The change takes effect immediately, without restarting the OpenVPN daemon.

By the way, you can use OpenSSL to examine the contents of a CRL, like this:

$ openssl crl –in /etc/openvpn/crl.pem –noout –text

Although it’s beyond the scope of this article, you can also embed a URL directly into issued client certificates, then use the crl-verify directive in the client configuration as well. This is really only useful if you want to be able to revoke server certificates; clients don’t (and shouldn’t) care whether other client certificates have been revoked.

Keeping the Man in the Middle Far, Far Away

Unfortunately, you’re not done yet. Remember that Man in the Middle (MITM) attack mentioned earlier? Even though Mallory can’t connect directly to the Fancy Sprockets VPN any more, she can still wreak havok with her revoked certificate by tricking other clients into connecting to her. By exploiting a vulnerability in Bob’s woefully unpatched BIND 4 installation, for example, Mallory could use a spoofed DNS entry and trick Bob’s OpenVPN client into connecting to rogue-vpn.malicious-mallory.com instead of vpn.fancysprockets.com. Fortunately, this kind of chicanery can be prevented by adding a single directive to the client config file:

# verify server certificate type
ns-cert-type server

Now Mallory can’t connect to the VPN server because her certificate is revoked, and she can’t trick clients with her certificate because her certificate wasn’t issued as a server certificate. Incidentally, the other way to protect against this type of MITM attack is to issue client and server certificates with different CAs. Figure Four show what the client certificate hierarchies would look in that scenario. Figure Five shows what the associated server certificate hierarchy would look:

FIGURE FOUR: A client certificate hierarchy to prevent” man in the middle” attacks



FIGURE FIVE: A server certificate hierrchy to prevent” man in the middle” attacks



What’s this, multiple OpenVPN servers? That’s right, you can specify multiple remote directives in the OpenVPN client configuration, like this:

# try connecting to the following servers, in order, until one
# succeeds:
# * vpn.fancysprockets.com, port 1194
# * vpn.fancysprockets.com, port 1195
# * vpn-2.fancysprockets.com, port 1194
# * vpn-3.fancysprockets.com, port 1194
remote vpn.fancysprockets.com 1194
remote vpn.fancysprockets.com 1195
remote vpn-2.fancysprockets.com 1194
remote vpn-3.fancysprockets.com 1194

Given the configuration above, an OpenVPN client tries each server, in order, until it connects. This can be used to bind multiple OpenVPN daemons to different ports on the same server or multiple OpenVPN daemons on separate servers. In fact, running multiple OpenVPN daemons on separate servers can function for VPN failover or, if used in conjunction with the remote-random directive, even for VPN load balancing. Fancy Sprockets doesn’t need failover (for now, anyway), so let’s get back to securing OpenVPN.

More Bullets to Dodge

There are only a few remaining attack vectors that Mallory has at her disposal. She can use a TLS Denial of Service (DoS), which attempts flood the OpenVPN server with bogus TLS connections; she can exploit an OpenSSL vulnerability; or exploit an OpenVPN vulnerability.

You can prevent a TLS DoS and mitigate against OpenSSL vulnerabilities by using the tls-auth directive. tls-auth directive adds an additional Hashed Message Authentication Code (HMAC) to the TLS handshake packets. Conceptually, this is similar to the initial static key configuration, except that the HMAC signature verification happens at a much lower level in the connection process, and it’s used in with existing X509 certificates.

To use tls-auth, generate a shared tls-auth key using the following command:

$ /usr/sbin/openvpn ––genkey ––secret ta.key

Next, copy the the file ta.key to the respective client and server OpenVPN configuration directories (again, using a secure channel, such as the venerable SneakerNet or SSH). Then, add the following directive to the OpenVPN server configuration file:

# server-side tls-auth
tls-auth ta.key 0

The client-side counterpart looks like this:

# client-side tls-auth
tls-auth ta.key 1

And that’s all there is to tls-auth.

Mallory’s last attack vector is possible vulnerabilities in the OpenVPN server. Of course, a vulnerability in the OpenVPN server can’t cause nearly as much trouble if OpenVPN isn’t running as root, so let’s configure OpenVPN to drop privileges, and run the OpenVPN server in a chroot() jail.

Add the following directives to the OpenVPN server configuration file:

# drop privileges after initialization
user nobody
group nobody

# we can’t access the private key or the tun device without root
# access, so keep those around between server restarts so we don’t
# lose access
persist-key
persist-tun

# run in a chroot jail
chroot /var/run/openvpn

(The user, group, persist-tun, and persist-key configuration directives work fine on the client side, too).

OpenVPN has several additional security-related configuration directives that you can research and deploy:

*cipher specifies a different symmetric cipher. OpenVPN defaults to Blowfish in Cipher Block Chaining (CBC) mode, but you can use any symmetric cipher compiled in to OpenSSL (yes, that includes AES and Triple-DES). Unfortunately, there are speed and security ramifications associated with each cipher, and covering those in detail is beyond the scope of this article.

*auth-user-pass and auth-user-pass-verify provide username and password authentication via a script.

*plugin isn’t a security option per-se, but the plugin directive allows OpenVPN to authenticate users via the included openvpn-auth-pam module, which in turn, can authenticate via LDAP, Kerberos, two-factor authentication, or any other wacky authentication scheme you can imagine.

To recap, Listing Five shows the complete server configuration file (without comments). Listing Six shows the complete client configuration file.

LISTING FIVE: A complete OpenVPN server configuration file

dev tun

ca ca.crt
cert server.crt
key server.key
dh dh2048.pem

crl-verify crl.pem
tls-auth ta.key 0

server 10.55.55.0 255.255.255.0
max-clients 50
keepalive 10 120
ifconfig-pool-persist ipp.txt
status openvpn-status.log

comp-lzo
verb 3
mute 20

user nobody
group nobody
persist-key
persist-tun

chroot /var/run/openvpn

LISTING SIX: A complete OpenVPN client configuration file

client
dev tun

ca ca.crt
cert client.crt
key client.key
tls-auth ta.key 1
ns-cert-type server

remote vpn.fancysprockets.com 1194

resolv-retry infinite
nobind

comp-lzo
verb 3
mute 20

user nobody
group nobody
persist-key
persist-tun

Flying Bits: Routing with OpenVPN

Fancy Sprockets has decided to provide full internal network access to VPN users. In OpenVPN, a client-side options are handled with the “push” directive. The Fancy Sprockets internal subnet is 10.33.33.0/24, and here’s how we specify this route in the OpenVPN server configuration:

# push a route to VPN clients for the private subnet 10.33.33.0/24
# (note: if the default gateway and the VPN server are not one the
# same machine, then the default gateway and/or machines in the
# private subnet will have to have a return route to the VPN pool
# subnet)
push “route 10.33.33.0 255.255.255.0″

Restart the OpenVPN server, and, firewall rules permitting, VPN clients should be able to ping internal IPs and access internal services.

Of course, even with routing working properly, VPN clients still won’t have access to internal DNS. There are a two ways to fix this:

*If you’ve only got a few servers, the easiest solution is probably to manually add entries for each one to /etc/hosts on each client. For example, Fancy Sprockets could add the following entries to /etc/hosts on client machines:

# internal fancy sprockets machines
# (hard-coded entries in clients’ /etc/hosts files)
10.33.33.2 gw.vpn.fancysprockets.com
10.33.33.2 ns.vpn.fancysprockets.com
10.33.33.3 ns2.vpn.fancysprockets.com
10.33.33.4 mail.vpn.fancysprockets.com
10.33.33.5 db.vpn.fancysprockets.com

*Send a DNS server as a DHCP option via the” push” directive, like so:

# push DNS servers as dhcp option to client
push “dhcp-option DNS 10.33.33.1″
push “dhcp-option DNS 10.33.33.2″

This seems easy enough, except that OpenVPN under Linux doesn’t honor dhcp-option DNS (not directly, anyway). Fortunately, you can emulate this behavior using a pair of client-side scripts and a plugin called down-root. The scripts — which are included with the OpenVPN tarball under contrib/pull-resolv-conf/ — allow you to add and remove the necessary resolv.conf entries, while the plugin preserves root permissions to allow the client.down script to work properly and allow OpenVPN to drop permissions. After combining all this magic, the additions to the client configuration look like this:

# pull dhcp options from server
pull dhcp-options

# run “client.up” to add necessary
# DNS entries to resolv.conf
up client.up

# run “client.down” to remove
# resolv.conf entries when VPN
# is disconnected
plugin “openvpn-down-root.so” “client.down”

Then, copy the client scripts to the OpenVPN configuration directory and make sure they’re marked executable. The scripts are located under contrib/pull-resolv-conf/client.{up,down} in the source tarball:

# copy resolv.conf scripts from OpenVPN
# source tarball to configuration directory
# and make sure they’re marked executable
su –c ’cd openvpn-2.0.5/contrib/pull-resolv-conf/ &&
cp –av client.{up,down} /etc/openvpn/ &&
chmod 750 /etc/openvpn/client.{up,down}’

Finally, restart both the OpenVPN server and client, and see if the new DNS entries take effect.

There’s have one final ace to play: dealing with subnet overlaps. For example, let’s say Carol does most of her Fancy Sprockets work remotely from another company, Wonderful Sprockets. Both Fancy Sprockets and Wonderful Sprockets use 10.33.33.0/24 as their respective internal subnets, so pushing a direct route over the tunnel won’t work.

Enter NETMAP. NETMAP is a Netfilter (iptables) DNAT target module that allows you to selectively remap traffic based on destination subnet. Assuming you’ve got Netfilter and connection tracking set up, here’s what the NETMAP rule would look like:

# nat tunnel traffic from destined for
# 10.98.76.0/24 to 10.33.33.0/24
iptables –t nat –A PREROUTING –i tun+ \
–d 10.98.76.0/24 –j NETMAP \
––to 10.33.33.0/24

And of course, in your OpenVPN server configuration file, you must change the route you push to clients to something like this:

# push imaginary client route, then
# rely on server-side DNAT to remap
# to the actual internal subnet
push “route 10.98.76.0 255.255.255.0″

After a server restart, traffic coming over the VPN destined for the[ Ed: sentence ends abruptly.]

If you’re using the DNS via DHCP method to push DNS to clients, you’ll need to update those push directives as well, like so:

# push DNS servers as dhcp option to client
push “dhcp-option DNS 10.98.76.1″
push “dhcp-option DNS 10.98.76.2″

Of course, this breaks actual DNS queries, since the DNS server will still merrily return internal addresses in the 10.33.33.0/24 subnet. To fix this, you can either set up a separate DNS server to handle VPN DNS requests, or, if you’re using BIND 9, use views to provide a separate, VPN-specific DNS namespace. Either will work, and both, unfortunately, are beyond the scope of this article.

Post Mortem

There are many OpenVPN topics that we don’t have space to discuss: per-client configuration, authenticating via PAM, routing between clients and to client-side subnets, bridging mode, tunneling through an HTTP or SOCKS5 proxy, the management interface, and more. The upcoming OpenVPN 2.1 — in beta as of this writing, but it may be available by the time you read this — adds support for PKCS# 11 (smart cards) and multihoming.

Comments are closed.