OpenVPN + NAT How-To

Introduction

This document describes how to set up openvpn to create a secure link between a network and an external node, and to set up NAT so that such external node access the network as if it was part of it.

In this document, we refer to A as the external node (connected to the Internet but with no access to the protected network), and B as a node inside the network, with at least partial access to the Internet, and which we are allowed to configure. In our example, we'll suppose the network uses the address space 192.168.34.0/24, and the VPN links will use the space 10.8.0.0/24.

Requirements

  • Root access to the external node and to a node in the network (which will act as the other peer in the VPN).
  • Access to at least one external public port from within the network, besides the usual 80 or 443. 21 or 22 are good candidates.
  • Static IP or static name (via no-ip) for the the external node. In case of dynamic IP, make sure the client that updates the DNS name is working.
  • SSH and OpenVPN in both sides, and autossh and Shorewall in B.
  • TUN/TAP support in both kernels.

Step 1: Checking open ports

We need to be able to access the external node A, from the controlled node in the network B, via ssh. In the best case, you can access both external 22 (ssh) and 1194 (openvpn) ports.

nmap a-address

If you have open access to 1194 port, then probably there're some other open ports as well, even if ssh one is blocked. In that case, set up the ssh server in A to listen to a different port, so that you can connect to it from the protected network. Being able to access OpenVPN port simplifies the configuration (and improves performance).

Step 2: Basic access to the external node

You should be able to access to A from B via

[A]# ssh [-p a-ssh-port] a-user@a-address

If OpenVPN port is closed, then access via ssh is mandatory. Otherwise it will speed up the process of configuring the link (without it, you'll have to go physically to each location to set them up).

Step 3: Mapping OpenVPN port remotely via ssh

To bypass B network's firewall rules, with SSH you'll be able to map port 1194 in B to another port in A (such as 1195):

[B]# ssh [-p a-ssh-port] -N -R 1195:localhost:1194 a-user@a-address

We need this ssh connection always alive, so we wrap it with autossh:

[B]# autossh -M 20000 [-p a-ssh-port] -N -R 1195:localhost:1194 a-user@a-address

In order to make sure it's always running, I usually run it at start-up (in /etc/conf.d/local.start in Gentoo).

Step 4: Configuring OpenVPN

Now we need to configure OpenVPN. The special case here is that the client will see the server as A:1195, and ssh will manage the tunnel A:1195<->B:1194.

Step 4.1: OpenVPN server

It's recommended to review OpenVPN documentation and sample files. I'll show the basic stuff I use, for /etc/openvpn/openvpn.conf:

port 1194
proto tcp
dev tun
ca keys/ca.crt
cert keys/server.crt
key keys/server.key  # This file should be kept secret
dh keys/dh1024.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "route 192.168.34.0 255.255.255.0"
client-config-dir ccd
route 192.168.7.0 255.255.255.0
keepalive 10 120
comp-lzo
user nobody
group nobody
persist-key
persist-tun
status openvpn-status.log
log-append  /var/log/openvpn.log
verb 6
mute 20

We need also to create the certificates and keys. First, edit the /usr/sahre/openvpn*/easy-rsa/vars file:

export EASY_RSA="`pwd`"
export KEY_CONFIG="$EASY_RSA/openssl.cnf"
export KEY_DIR="$EASY_RSA/keys"
echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR
export KEY_SIZE=1024
export CA_EXPIRE=3650
export KEY_EXPIRE=3650
export KEY_COUNTRY="ES"
export KEY_PROVINCE="Madrid"
export KEY_CITY="Madrid"
export KEY_ORG="YourCompany"
export KEY_EMAIL="your.name@your.company"
 [B]# cd /usr/share/openvpn*/easy-rsa
 [B]# source ./vars
 [B]# ./clean-all
 [B]# ./build-ca
 [B]# ./build-key-server server
 [B]# ./build-key [a-hostname]
 [B]# ./build-dh
 [B]# mv keys /etc/openvpn

Now we can start the server and check that it's waiting for incoming requests:

 [B]# /etc/init.d/openvpn start
 [B]# tail /var/log/openvpn.log
Sat Dec  8 12:46:28 2007 us=31662 UDPv4 link remote: [undef]
Sat Dec  8 12:46:28 2007 us=31695 MULTI: multi_init called, r=256 v=256
Sat Dec  8 12:46:28 2007 us=31777 IFCONFIG POOL: base=10.8.0.4 size=62
Sat Dec  8 12:46:28 2007 us=31814 IFCONFIG POOL LIST
Sat Dec  8 12:46:28 2007 us=31866 Initialization Sequence Completed

Step 4.2: OpenVPN client

Now we need to transfer /etc/openvpn/keys/ca.crt, /etc/openvpn/keys/[a-hostname].crt and /etc/openvpn/keys/[a-hostname].key to A:

 [B]# scp [-P a-ssh-port] /etc/openvpn/keys/ca.crt a-user@a-address:/etc/openvpn
 [B]# scp [-P a-ssh-port] /etc/openvpn/keys/[a-hostname].crt a-user@a-address:/etc/openvpn
 [B]# scp [-P a-ssh-port] /etc/openvpn/keys/[a-hostname].key a-user@a-address:/etc/openvpn

To configure the client, review OpenVPN documentation. In our case, it would be like:

client
dev tun
proto tcp
remote 127.0.0.1 1195
persist-key
persist-tun
ca ca.crt
cert [a-hostname].crt
key [a-hostname].key
comp-lzo
log /var/log/openvpn.log
verb 7
mute 20

Let's start the client and check the log file.

 [A]# /etc/init.d/openvpn start
 [A]# tail /var/log/openvpn.log
Sat Dec  8 17:16:33 2007 us=598496 TUN/TAP device tun0 opened
Sat Dec  8 17:16:33 2007 us=598575 TUN/TAP TX queue length set to 100
Sat Dec  8 17:16:33 2007 us=598610 /sbin/ifconfig tun0 10.8.0.6 pointopoint 10.8.0.5 mtu 1500
Sat Dec  8 17:16:33 2007 us=606530 /sbin/route add -net 192.168.34.0 netmask 255.255.255.0 gw 10.8.0.5
Sat Dec  8 17:16:33 2007 us=608613 /sbin/route add -net 10.8.0.1 netmask 255.255.255.255 gw 10.8.0.5
Sat Dec  8 17:16:33 2007 us=610714 Initialization Sequence Completed

Step 5: Testing the VPN

So far so good. Now you should be able to ping B from A:

 [A]# ping [b-address]
PING 192.168.34.135 ([b-address]) 56(84) bytes of data.
64 bytes from [b-address]: icmp_seq=1 ttl=64 time=55.1 ms
64 bytes from [b-address]: icmp_seq=2 ttl=64 time=54.2 ms

The problem is that you'll not be able to access any other node in B's network. That's because B hasn't been told to route and NAT your packets. We'll do it in the following section.

Step 6: Configuring Shorewall

We'll proceed configuring iptables in B. For our purposes, we can use Shorewall, which makes things easier. First, we define our zones in /etc/shorewall/zones:

fw      firewall
offce
home

Next, we have to declare the interfaces in /etc/shorewall/interfaces:

offce  eth0  -
home   tun0  -

And their policies (/etc/shorewall/policy:

fw              all             ACCEPT
home            all             ACCEPT
offce           fw              ACCEPT
offce           home            DROP

We also have to define NAT. This is somewhat a risky step, since we'll take another IP address from the network space (without acknowledging the DHCP server). Find a spare IP and put it here. In our example, we suppose it's 192.168.34.13

192.168.34.13 eth0 tun0