expand all
Projects Research Private Contact

Michael Pfeiffer
Imprint, Disclaimer


Getting secure WLAN by using OpenVPN on a WRT54G under OpenWRT

If you want to use OpenVPN just to get your WLAN secure so that nobody can snoop your packet contents and nobody can use your system to get access to the internet, this is probably the right page for you. If you decide to configure your system like it is described here you have to do it carefully without leaving one or more steps out. This is important because everything is working very closely together. This configuration is based on OpenVPN version 2.0-beta...! All connected machines (clients) must have a 2.0 version of OpenVPN.

I have spend a lot of time to find the configuration I am describing here. I think this is one of the standard configurations of an OpenVPN server on the WRT54G and therefore it should be documented. Hopefully, it will help save some of your time! But, I have to mention one additional point: "This configuration has only been tested with my WRT54G-Ver1.1!" Because everything is done by using the nvram variables you should only have to check/change the nvram settings.

Changes/History

08/09/2006 S45firewall
Removed the destination IP address WANIP from PREROUTING rule in case that it's a pppoe connection of the WAN interface. This is necessary because if the ISP is cancelling the connection you normally get a different IP address. In this case you have to rerun the S45firewall script to update that specific rule. The destination address of this rule isn't necessary on a interface which is running in P-to-P mode.
08/02/2006 S45firewall
Different way to get the ip address of the WAN interface.
07/03/2006 S45firewall
Changed input policy to drop: Unwanted access to the router e.g. dns service from wlan is rejected now. New commented section for ssh port forwarding.
07/03/2006 Client configuration
Some remarks for window clients.

The system layout

My intention for this system configuration was to get the system behavior just as it was before the installation. The new security aspect should be totally transparent to the user of the system. The system should be able to handle Linux clients as well as Windows clients.

This image depicts the configuration provided by the normal OpenWRT distribution. There are three interfaces handled by the system. vlan1 is the WAN interface. This is obviously an insecure line. vlan2 is connected to a 4-port switch where you can connect devices via TP 10/100. This could be the only secure part of this configuration, but, it isn't. WLAN is represented by the eth2 interface. The traffic of all wireless clients is handled by this interface. vlan2 and eth2 are bridged. This results in the new logical interface br0. Because of this fact, vlan2 isn't safe anymore, nothing is safe. All WLAN clients have access to the devices connected to vlan2. This configuration is working with two different IP nets. One is the net where vlan1 belongs to. There are different policies to obtain the IP address. The second is the local IP net e.g. 192.168.1.0/24 which is masqueraded by the WRT. br0 represents this private IP net.

So what can be done? The trick is to block everything coming from eth2 which isn't certificated. Here OpenVPN joins the game.

This image shows the OpenVPN configuration. Every trusted WLAN client which is handled by its own secure tunnel is represented by a new interface (tapX) in the WRT. eth2 is removed from the bridge and all tap interfaces are added. In this configuration only some specified requests on eth2 are let in the WRT. These are the OpenVPN channels and DHCP requests. Concerning the IP nets we also have a different configuration. To get the same user view of the system we have the same local IP net for each TP and authenticated WLAN client (tapX interfaces). In addition, we have a third IP net, the insecure wireless net, e.g. 192.168.2.0/24, which consists of the eth2 interface and all connected WLAN client network cards.

Creating the keys and certificates needed by OpenVPN in tls-server mode

This description is taken from the OpenVPN home page.

# create your main key/certificate pair
openssl req -nodes -new -x509 -keyout my-ca.key -out my-ca.crt -days 3650
# put these two files to the locations defined in the openssl.cnf

Now the openssl is configured to produce a key/certificate pair for the openvpn server and each client you want to give access to your private network represented by the bridge interface br0.

for each key/certificate pair you have to specify several parameters e.g. company name etc. Because the OpenVPN server takes the client names for individual configurations, the name entry of the key/certificate should be different and mappable. If the clients are uniform in configuration you can take generic names like clientX.

# first for the server
openssl req -nodes -new -keyout server.key -out server.csr
openssl ca -out server.crt -in server.csr
openssl dhparam -out dh1024.pem 1024

# do this for each client (replace the X by a number or 
#                          chose a different name)
openssl req -nodes -new -keyout clientX.key -out clientX.csr
openssl ca -out clientX.crt -in clientX.csr

OpenVPN Server configuration

Getting the correct time

The usage of OpenVPN requires a correct time setting on all machines that want to build a tunnel between them. That means you have to get the right time at the startup procedure of the router. With the ntpclient application you can obtain the correct time. I have inserted the following line into the S50services startup script:

ntpclient -s -h ntp1.ptb.de

You should take one ntp server which is located close to your router. In Germany where the time is made ;-)) the PTB Braunschweig is one server you can trust. This configuration leads to the effect that every time the router is turned on a connection to your ISP is made to get the right time.

Server configuration file "server.conf"

First of all, the OpenVPN server has to get the information on what to do and where to find additional information. This information is located in the file server.conf, which looks like this:

# main behavior
mode server
tls-server

# use tap devices because of the windows clients
dev tap

# execute the following script for each client which is started
up /etc/openvpn/server-tun.up

# use the following encryption method
cipher BF-CBC
auth SHA1

# certificates an the private key
ca /etc/openvpn/my-ca.crt
dh /etc/openvpn/dh1024.pem
cert /etc/openvpn/server.crt
key /etc/openvpn/server.key

# the servers ip on eth2
local 192.168.2.1

# information which is evaluated by the clients
#  this is: take the tap interface as default gateway
push "route-gateway 192.168.1.1"
push "redirect-gateway local"
# additional windows stuff which is ignored by linux clients
#  take the router as dns server
push "ip-win32 dynamic"
push "dhcp-option DNS 192.168.1.1"

# directory contains the client specific configurations
#  the ip address and netmask
client-config-dir /etc/openvpn/server-config-dir

# standard configuration: keep keys in mind and don't reopen devices
keepalive 10 60
persist-key
persist-tun
# ping only on active connection
ping-timer-rem

# emit nothing more than fatals 
verb 0

This configuration requires two additional scripts/configurations which are executed/evaluated while the server starts/the connection comes up.

The first one is a script which is used to add the interface to the lan bridge and to add firewall rules to get access from the WLAN to the OpenVPN server which is about to start. To understand what is done in detail have a look at the description of the startup scripts S41openvpn and S45firewall. The following frame consists of the important stuff in this script:

#!/bin/ash
LAN=$(nvram get lan_ifname)
WLAN=$(nvram get wlan_ifname)

# bring up the tap interface
$DEBUG ifconfig $1 0.0.0.0 up
# and add that to the bridge
$DEBUG brctl addif $LAN $1

# prepare a rule to accept incoming requests for the OpenVPN server
echo "iptables -t filter -A INPUT -i $WLAN -p udp --dport $local_port\
               -m state --state NEW -j ACCEPT" >> /etc/tun-iptable-rules

# and prohibit all requests coming from non wireless internal interfaces 
#     for the OpenVPN server (avoid multiple interfaces in one net)
for interf in $(nvram get vpnlan_ifnames); do
  echo "iptables -t filter -A INPUT -i $interf -p udp --dport $local_port\
                 -m state --state NEW -j DROP" >> /etc/tun-iptable-rules
done

I like a fixed mapping of IP addresses to my clients. This mapping is stored in the client specific configuration files located in <client-config-dir>. These files are named by the entry of the keys/certificates. In my system the contents are like (with different IP's):

ifconfig-push 192.168.1.100 255.255.255.0

The configuration of the DHCP server of the WRT must exclude these IP's which are reserved for WLAN clients (see Configuration of DHCP and DNS).

Additional nvram entries

To keep the configuration consistent some configuration details should be set in the nvram to get global access from all configuration files. We have two additional things to configure, the WLAN part and the new base configuration for the bridge. The following frame contains all entries to set the configuration files.

# settings for the insecure wlan interface
nvram set wlan_ifname eth2
nvram set wlan_ipaddr  192.168.2.1
nvram set wlan_proto none

# settings for the new and secure lan (vpnlan)
nvram set vpnlan_ifname=br0
nvram set vpnlan_ifnames=vlan2
nvram set vpnlan_hwaddr=$(nvram get lan_hwaddr)
nvram set vpnlan_ipaddr=192.168.1.1
nvram set vpnlan_netmask=255.255.255.0
nvram set vpnlan_proto=static
nvram commit

Startup file S41openvpn

The intent of my configuration was to make it very simple to reactivate the original configuration. This will probably conform with your interests. This means the original interface's configuration is built by the S40network script. After that work is done the S41openvpn script brings up the interfaces for the OpenVPN configuration and starts the desired OpenVPN server processes. This is shown in the following frame:

#!/bin/sh

. /etc/functions.sh

# load the kernel module
$DEBUG insmod tun

# reconfigure the lan
ifup vpnlan
# bring up the wlan and set the static ip (by hand because there isn't a gateway)
ifup wlan
$DEBUG ifconfig $(nvram get wlan_ifname) $(nvram get wlan_ipaddr) up

# prepare the iptable settings
rm -f /etc/tun-iptable-rules

# start the openvpn servers for the fixed ports
#  /etc/tun-iptable-rules is filled and tun interfaces are added to the bridge
$DEBUG openvpn --config /etc/openvpn/server.conf --port 5000 --daemon
$DEBUG openvpn --config /etc/openvpn/server.conf --port 5001 --daemon

Startup file S45firewall

At the moment when this script is executed all of the interfaces are setup correctly. The only part missing is prohibiting the access to the router from the WLAN, except for services needed to connect the OpenVPN server and get an IP address. There are too much changes to the original version of the firewall script so that my startup script replaces the original one. The original script is allways available in /rom. My script looks like this:

#!/bin/sh
. /etc/functions.sh

WAN=$(nvram get wan_ifname)
WANIP=$(ifconfig $WAN | awk 'BEGIN {FS=" [^:]*:";} (NR==2) { print $2 ; exit; }')
LAN=$(nvram get vpnlan_ifname)
WLAN=$(nvram get wlan_ifname)

IPT=/usr/sbin/iptables

for T in filter nat mangle ; do
  $IPT -t $T -F
  $IPT -t $T -X
done

# set the new default action -> we have to allow explicitly
$IPT -P INPUT DROP 

$IPT -t filter -A INPUT -m state --state INVALID -j DROP
$IPT -t filter -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 

# enable the OpenVPN server ports (script is created in S41openvpn and
#                                  filled by the configuration scripts)
if [ -f /etc/tun-iptable-rules ]; then
  . /etc/tun-iptable-rules
fi

# accept all from secure lan
$IPT -t filter -A INPUT -i $LAN -j ACCEPT 

# enable DHCP and SSH access from wlan (uncomment if desired)
$IPT -t filter -A INPUT -i $WLAN -p udp --dport 67 -m state --state NEW -j ACCEPT
#$IPT -t filter -A INPUT -i $WLAN -p tcp --dport 22 -m state --state NEW -j ACCEPT
# drop everything else from wlan
$IPT -t filter -A INPUT -i $WLAN -m state --state NEW -j DROP

# accept icmps from wan and lan
$IPT -t filter -A INPUT -p icmp -j ACCEPT
# block all access from wan
$IPT -t filter -A INPUT -i $WAN -p tcp -j REJECT --reject-with tcp-reset 
$IPT -t filter -A INPUT -i $WAN -j REJECT --reject-with icmp-port-unreachable 

# pass through
$IPT -t filter -A FORWARD -m state --state INVALID -j DROP 
$IPT -t filter -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
# block everything else wlan->wan
$IPT -t filter -A FORWARD -i $WLAN -j DROP

# forwarding ssh from outside (uncomment following block if desired 
#  and define SSHHost variable above)
#if [ "$(nvram get wan_proto)" = "pppoe" ]
#then
#  $IPT -t nat    -A PREROUTING -p tcp -i $WAN --dport 22 \
#                               -j DNAT --to-destination $SSHHost:22
#else
#  $IPT -t nat    -A PREROUTING -p tcp -i $WAN -d $WANIP --dport 22 \
#                               -j DNAT --to-destination $SSHHost:22
#fi
#$IPT -t filter -A FORWARD    -p tcp -i $WAN -d $SSHHost --dport 22 \
#                             -m state --state NEW -j ACCEPT

$IPT -t filter -A FORWARD -i $WAN -m state --state NEW,INVALID -j DROP
$IPT -t filter -A FORWARD -o $WAN -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

$IPT -t nat -A POSTROUTING -o $WAN -j MASQUERADE

Configure dnsmasq

Finally, we have to startup a DHCP service for the new and separate WLAN net. The request for an IP address will be the first contact a wireless client makes to the router.

Therefore, you have to alter your /etc/dnsmasq.conf file. You have to add a dhcp-range line for the new net. In addition, you have to change the lan entry to separate the dhcp range from the addresses which are set fixed by the OpenVPN client-config files (that is why the upper bound is set to 99 in the first entry).

  .
  .
# enable dhcp (start,end,netmask,leasetime)
# one rage for the vpn lan (already existing entry)
dhcp-range=192.168.1.10,192.168.1.99,255.255.255.0,24h
# new range for the wlan net
dhcp-range=192.168.2.2,192.168.2.254,255.255.255.0,24h
  .
  .

First try and installation

The frame below shows where to put the mentioned files. To transfer the files it's easiest to have a ssh daemon installed, e.g. dropbear. Because it's always dangerous to change the startup configuration of the system, the system might be in an unusable state afterwards. I suggest to make a test of the configuration first. Rename the startup scripts so that their names start with something different than S, e.g. TS41opnvpn and TS45firewall.

Checklist: Transfer all files to their intended locations. Make the mentioned configurations in the files on your router. Create all missing nvram entries. Test the installation and fix the configuration by renaming the startup scripts.

File location table

Files to install

/etc/init.d/TS41openvpn
/etc/openvpn/server.conf
/etc/openvpn/my-ca.crt
/etc/openvpn/dh1024.pem
/etc/openvpn/server.crt
/etc/openvpn/server.key
/etc/openvpn/server-tun.up
/etc/openvpn/server-config-dir/Client1
/etc/openvpn/server-config-dir/Client2

# copy S45firewall to TS45firewall

Files to change/configure

/etc/init.d/TS45firewall
/etc/init.d/S50services
/etc/dnsmasq.conf

File attributes
There are some file attributes to change:

chmod rg+x /etc/init.d/TS41openvpn
chmod go-rwx /etc/openvpn/server.key
chmod rg+x /etc/openvpn/server-tun.up

Test the configuration

Restart the router and check if the time is set and if everything is working as before. The best way to test the configuration in this state is to connect your computer to a TP connector at the router. Start /etc/init.d/TS41openvpn. After that is done, your shell should be unusable, because this script is closing the interface that you are currently using. Start a new ssh/telnet connection to the router. Check the state of the bridge:

#> brctl show
bridge name     bridge id               STP enabled     interfaces
br0             8000.000xxxxxxxxx       no              vlan2
                                                        tap0
                                                        tap1

If eth2 is utilizing the bridge the script wasn't executed correctly. Check the execution permissions of the script. If the tap entries are missing, then the OpenVPN server couldn't start as expected. In this case, we can start it by hand and check if the server can say what's wrong. Check the process table and kill all openvpn processes (if there are some) and start one by hand:

openvpn --config /etc/openvpn/server.conf --port 5000 --verb 3

Some additional checks should also be made:

#> ifconfig eth2 
eth0      Link encap:Ethernet  HWaddr XX:XX:XX:XX:XX:XX 
          inet addr:192.168.2.1  Bcast:192.168.2.255 Mask:255.255.255.0
          UP BROADCAST .....
#> ifconfig br0
br0       Link encap:Ethernet  HWaddr XX:XX:XX:XX:XX:XX 
          inet addr:192.168.1.1  Bcast:192.168.1.255 Mask:255.255.255.0
          UP BROADCAST .....

If you connect a wireless client to the router, the client should get an IP address of the insecure range 192.168.2.0/24 set in dnsmasq.conf.

OpenVPN Client configuration

The configuration of windows and linux clients is nearly the same. The difference is the configuration of the DNS service which must be switched to the secure net, this is a very unusual behavior. This is necessary because the DNS service is an outgoing service and might initiate a call to your ISP that cannot be admitted from the insecure wireless net. On windows clients, this configuration is done in the server.conf of the router, but this is a windows specific configuration. On linux clients, we have to use two scripts to do that work, "client-tun.up" and "client-tun.down".

The common client configuration "client.conf"

The following frame shows the common client configuration. Only the server port to be used, the location/names of the keys and certificates must be set, depending on the installation of OpenVPN.

# the main behavior
tls-client

# the device to take for the connection
dev tap

# OpenVPN peer (server)
remote 192.168.2.1
# and port (this port should only be used by a unique client machine)
port 5000

# activate the device only if connected to the server
up-delay
# once the VPN is alive. (comment next two lines if you use this on a windows machine)
up /etc/openvpn/linux-tun.up
down /etc/openvpn/linux-tun.down
down-pre


# set the data obtained by the server
pull
# this is: route-gateway 
#          redirect-gateway local
#          ping 10
#          ip-win32 dynamic
#          dhcp-option DNS 
# Certificate Authority file
ca /etc/openvpn/my-ca.crt
# Our certificate/public key
cert /etc/openvpn/client1.crt
# Our private key
key /etc/openvpn/client1.key


# encryption options
cipher BF-CBC
auth SHA1

# Verbosity level.
# 0 -- quiet except for fatal errors.
# 1 -- mostly quiet, but display non-fatal network errors.
# 3 -- medium output, good for normal operation.
# 9 -- verbose, good for troubleshooting
verb 1
# and the logging options
mute-replay-warnings
mute 2

Setting DNS service on the client machines

The change of the DNS service location of the linux machine is done by the linux-tun.up script which is shown below. This script saves the original resolv.conf and builds a new one where the router is set as a DNS server and the search entry is taken from the original resolv.conf:


#!/bin/bash
echo "###### linux-tun.up: $*"

PATH="/bin:/usr/bin"
RESOLV="/etc/resolv.conf"

if [ -f $RESOLV ] ; then
  cp $RESOLV ${RESOLV}.openvpn
  gawk '
BEGIN {
  printf("nameserver %s\n", ARGV[2]);
  ARGC = 2;
}

/^search/ {
  print;
} ' ${RESOLV}.openvpn $route_vpn_gateway > $RESOLV
else
  echo "nameserver $route_vpn_gateway"  > $RESOLV
  chmod a+r $RESOLV
fi

If the OpenVPN tunnel is going down. the original resolv.conf should be restored. This is done by the following script:

#!/bin/bash
echo "###### linux-tun.down: $*"

PATH="/bin:/usr/bin"
RESOLV="/etc/resolv.conf"

if [ -f ${RESOLV}.openvpn ] ; then
  cat ${RESOLV}.openvpn > $RESOLV
  rm ${RESOLV}.openvpn
fi

Don't forget to set the execution bit of the up/down scripts!

Windows Clients

To avoid that windows is using the insecure wlan for netbios services you have to disable these services for your wlan network adapter. These services should be enabled only for the new virtual tap network adapter.

Final OpenVPN test

Now everything should be configured correctly. Test if everything works together. First start one server OpenVPN by hand. Now start the OpenVPN client on a wireless connected machine which uses the port provided by the server you just started. You should see the process of building the connection. Hopefully, the connection can be established ;-)) Generally, it should not be possible to build up an OpenVPN tunnel from the TP LAN. The firewall will block these requests, but this firewall configuration isn't activated yet. That's the reason why you should use a WLAN connection to check the building of the connection! I don't know what would happen if you try to connect from TP LAN.

openvpn --config /etc/openvpn/server.conf --verb 3

If everything works fine rename the TS41openvpn to S41openvpn. After a reboot of the router everything should work as desired. Only blocking the WRT access, from the wireless lan, isn't activated yet.

Activating the firewall

After activating the firewall script by starting TS45firewall everything should proceed as it was. Try to connect to the router by using telnet or ftp on the insecure net (192.168.2.0/24). There shouldn't be any answer from the router because the request is eaten up by the firewall, i.e. the client is waiting and waiting for the answer and finally responding "Connection timed out". You should notice a difference if you try these services on the secure net (192.168.1.0/24). You should immediately get an answer even if it is "Connection refused". Finally check if it's still possible to get an external connection.

If everything works fine move TS45firewall to S45firewall. This replaces the original startup script.

Download

I have tared everything together. The first file contains all configurations to be made on the WRT54G except certificates and keys. The second file is the linux client configuration. Follow the comments in the configuration file to transform it to a windows configuration file. Have a look at the changes section at the beginning of the page, in that case I have forgotten to update the archive files you have to do that by your own! ;-))

Disclaimer

Although I have made and documented everything with care there are probably some bugs on this page or in the downloads. This is the reason why I don't give any warranty of any kind!

If you find a bug or if you have any suggestions for improvement or changes of the described configuration feel free to contact me!

Cheers,
Pfeiffer

Special thanks

To David Theriault for correcting the grammar of this document to make it more readable!

OpenWrt