Search this blog ...

Monday, March 14, 2011

Quick & simple VPN setup guide: using OpenVPN on a ‘Tomato’ router

Before the advent of custom firmware on consumer-priced/graded routers (Linksys/Netgear etc), obtaining secure remote access to a network resource using stock firmware was somewhat an art.  Circa 2002, I remember geeking it up in front of my work colleagues by remotely accessing my home machine’s desktop to download mp3 over Napster. Back then, I was using a combination of port-forwarding, VNC, and SSH; amazingly this setup was able to be tunnelled through work’s HTTP proxy server.

Even today, many people still leverage such approaches.  The following links give a bit of a history and overview:

Modern custom router firmwares (DD-WRT / Tomato / etc) make it even simpler by bundling some of the required software.  Here is a link that describes an updated approach to the above:


A friend whom runs a small business on a tight technology budget recently asked me what I would suggest that would allow his employees remote access to the office “files”.  He and his colleagues are regularly travelling for days at a time and require access both at customer sites, and also from hotels.  Most of the sites he visits do allow him to connect his laptop to the customer network; but not all. Thus, we needed a solution that would wherever possible allow him to leverage the customer’s office internet connection. For those sites that prevent such access, he would leverage a 3G solution using either a USB dongle, or 3G Wi-Fi Hotspot.

Immediately, OpenVPN sprung to mind as a possible solution – mostly cause it was free, and also had client support on Windows, Mac, and Linux. The icing on the cake however was the fact that the DD-WRT and Tomato router firmwares happened to provide special VPN builds that bundled the OpenVPN server application.  Thus, assuming we could locate a router with sufficient grunt that could run such a firmware, there would be no need to have a dedicated separate OpenVPN server machine.

Basically, three main routers sprung to mind:

Asus RT-N16

Linksys (Cisco) E3000

Netgear WNDR3700

The first two routers (Asus & Linksys) are supported by Tomato.  All three routers are supported by DD-WRT.

I’ve had great success with Tomato in the past, so I suggested to my friend to purchase the Linksys E3000. This can be purchased here in Australia for about $170.

It just so happened that I had an Asus RT-N16 at home, hence I was able to test out the VPN configuration on my home environment, before messing with my friends office network. I liked it so much (having a VPN), that I decided to keep it and refine it!

The main decisions when leveraging OpenVPN are:

1) Does your router have a static WAN IP address?

If you router does not have a static IP address, then you are at the mercy of your internet provider whom may or may not reissue you the same IP address upon lease expiration/reboot etc. It is best to play it safe and configure your router to use a Dynamic DNS. Sign up for a free account at an appropriate provider (e.g., and then configure your router with the appropriate DDNS account details.  Your router will subsequently register your dynamic IP address with the provider whenever your WAN connection comes up/changes. This way, you can configure your OpenVPN client configuration with a static hostname (e.g.

2) Do I TUN (network TUNnel) or TAP (network tap)?

TAP > runs at layer 2 (OSI model)  - bridge mode ; effectively the VPN client will appear to be on the same network subnet as the destination. Packets broadcast on destination network will be received by client.

TUN > runs at layer 3 (OSI model) – router mode; effectively the VPN client is on a different network to the destination; routing rules are used to allow the client to access the destination network.  Packets broadcast on destination network won’t be received by VPN client.

3) What protocol: TCP or UDP?

SgtPepper (Tomato guru) over at the LinksysInfo forum clarified/corrected my understanding of what this option means. Initially I assumed incorrectly that the choice between TCP and UDP came down to what application layer network protocols you planned on using over the VPN.  Such that, if you wanted FTP/Telnet/SSH HTTP/HTTPS/SMTP/IMAP/SMB-over-TCP/NFS-over-TCP/ you chose TCP, whereas VoIP and Network Games, then UDP.  WRONG!

To Quote SgtPepper directly “Choosing TCP vs UDP should have nothing to do with the application type you're using. OpenVPN tunnels TCP and UDP traffic over whichever protocol you choose. Tunneling TCP over TCP is extremely inefficient, so TCP should only be chosen if you absolutely have to. That should only be if you have to go through an HTTP proxy, trick a firewall, or have a very flaky connection and have problems with UDP. If you have the option, you should absolutely 100% use UDP.

4) What port for the server to listen on?

The default port is 1194. But if 443 is available, I would suggest using this. The use of port 443 is particularly pertinent to TCP-based OpenVPN configurations whereby client access to the VPN may require tunnelling through a proxy server. Proxy servers are much more likely to accept traffic destined for port 443 versus something like 1194. Port 443 is the default port used by secure HTTP (aka HTTPS), and as such most proxy servers should allow it unimpeded.

5) What LAN subnet/segment should the router be leveraging?

Most routers ship with a default IP address of, and subnet mask Chances are, one of the destinations you visit will also be leveraging such a topology. The problem occurs when the destination network is the same as the actual client network you are connecting from. For example, if my network at home is 192.168.1.x, and I’m at a client site that is using 192.168.1.x, and I attempt to make VPN connection to home, the routing tables get completely messed up. There are probably some crazy network mask and routing rule/metric options you can leverage to work around such a situation, but my advice is to avoid it in the first place.



For my home VPN, I decided to leverage TAP (interface type), TCP (protocol), 443 (port), and (CIDR Notation).

The reason I chose TAP is to theoretically support broadcast traffic, but mostly so as to appear on the same network as my destination. If your client has no business to receive the broadcast traffic, and you are expecting large amounts of broadcast traffic on the destination network subnet, then it is best to switch to TUN.

The reason I chose TCP is that OpenVPN natively supports tunnelling of the TCP traffic over a HTTP proxy. This means that if we are stuck behind a HTTP proxy without direct internet access, we can still hopefully access the VPN by tunnelling through the proxy server. Refer to SgtPepper’s quote from above however regarding efficiency and only choose this option if you have to!

I chose port 443 likewise to increase my odds of a proxy server connection working, and also so that it looks legitimate from an auditing perspective.

I chose 192.168.192.x/ network for my LAN, as this network has less chance of being leveraged on a client site (unlike for example 192.168.1.x).

One interesting point to make is that Tomato natively supports two OpenVPN server processes running at the same time.  Thus there is nothing stopping you running TCP and TAP for one instance, and UDP with TUN/TAP on the other instance.


Now… on to the VPN setup:

If you are running a Linksys E3000, I suggest obtaining and installing the following firmware (or newer):


the above firmware can be installed directly over the top of the factory firmware.


If you are running an Asus RT-N16, I suggest obtaining and installing the following firmware (or newer):


Note: With the RT-N16, to upgrade from a stock firmware you must first flash the router with the following:


Next, you must download and install the OpenVPN software specific for your OS (client)

For Windows, this is currently:

As we only need the client components and RSA certificate management scripts, be sure to DESELECT “OpenVPN Service”.



Next, we will generate the various keypairs…

CD /D "%ProgramFiles%\OpenVPN\easy-rsa"

##   create vars.bat and openssl.cnf from templates

##   Update the values in vars.bat as appropriate, specifically:
##   If requiring stronger keys, change KEY_SIZE from 1024 to 2048
##   Use WordPad to edit the file, as it leverages UNIX line breaks
"%ProgramFiles%\Windows NT\Accessories\wordpad.exe" "%ProgramFiles%\OpenVPN\easy-rsa\vars.bat"


##   Invoke vars.bat to set environment

##   CAUTION - deletes any existing keys / reset SERIAL and INDEX.TXT

##   Construct Certificate Authority keypair, set commonName to be CA


##   Construct server keypair, set commonName to VPNServer
build-key-server server


##   Construct client keypair for user 'matt', set commonName as appropriate (e.g. matt)
build-key client_matt



##   Construct client keypair for user 'louise', set commonName as appropriate (e.g. louise)
## build-key client_louise

##   NOTE - if you have specified a challenge password in your certificate when you created it,
##   you will be required to provide that password should you ever want to request to revoke the certificate

##   The keys directory will contain private keys / certificates / index file stating
##   issued certificates / serial file containing next serial number to leverage etc.
##   !!! KEEP THEM IN A SAFE PLACE. !!!!


##   Generate Diffie Hellman parameters
##   This will generate dh1024.pem in keys folder (or dh2048.pem files, depending on KEY-SIZE variable)



Next, we will configure Tomato…

From the Tomato Administration UI, choose “VPN Tunnelling” > "Server”

Refer to screenshots for detailed settings.

Basic Settings:
Start with WAN: <checked>
Interface Type: TAP
Protocol: TCP
Port: 443
Firewall: Automatic
Authorization Mode: TLS
Extra HMAC authorization (tls-auth): Disabled
Client address pool: DHCP <checked>


Advanced Settings:
Poll Interval: 0
Direct clients to redirect internet traffic: <NOT checked>
Respond to DNS: <NOT checked>
Encryption cipher: Use Default
Compression: Adaptive
TLS Renegotiation Time: -1
Manage Client-Specific Options: <checked>
Allow Client<->Client: <checked>
Allow Only These Clients: <NOT checked>

Custom Configuration**:
script-security 3
auth-user-pass-verify /etc/ via-env


Certificate Authority: <paste contents of ca.crt>
Server Certificate: <paste contents of server.crt from -----BEGIN CERTIFICATE----- through-----END CERTIFICATE----- inclusive>
Server Key: <paste contents of server.key>
Diffie Hellman parameters: <paste contents of dh1024.pem>


All the above OpenVPN configuration options ultimately get stored in NVRAM. Tomato will dynamically generate the appropriate configuration file and keys/certificates in the “/tmp/etc/openvpn”  area based on these NVRAM values.

If you were to SSH/Telnet in to the router, and issue an “nvram show | grep vpn_server1” command, you should see the various configuration values stored in NVRAM from above.

The dynamically constructed files are as follows:

/tmp/etc/openvpn/server1/ [ca.crt | config.ovpn | dh.pem | server.crt | server.key]

The file contains the iptables entries.

tomato generated vpn files

** You will notice the “Advanced” tab > “Custom Configuration” option for the Server VPN Tunnelling is populated with a script-security and auth-user-pass-verify entry.  These options are adding an additional level of security by requiring the VPN client not only hold a valid keypair, but also present a valid username/password.  The client supplied username/password is provided to a custom script (that we must create) named  This script must return exit status 0 in order for the VPN client connection to be successful (assuming they had a valid keypair in the first place).

Unfortunately the /tmp folder is erased and recreated every time the router is rebooted. (/etc is a symbolic link to /tmp/etc).  We need a mechanism to ensure shell scripts for custom authentication survive reboot. There are three options:

1) enable the JFFS feature and essentially leverage the unused portion of the router's NVRAM and turn it in to a mountable and writable space 

2) leverage init scripts in the tomato UI to recreate the various shell scripts required in the /tmp directory at boot time.

3) use the "nvram setfile2nvram <filename>" command to save small files in nvram.  The files will be automatically restored on start-up.

We will leverage the latter option (#3) for our custom authentication script; SSH/telnet in to the router as root and issue the following:

cd /etc

cat > /etc/ <<EOF

HASHPASS=\`echo -n "\$1\$2" | md5sum | sed s'/\  -//'\`
while [ \$i -lt 10 ]; do
  HASHPASS=\`echo -n \$HASHPASS\$HASHPASS | md5sum | sed s'/\  -//'\`
  i=\`expr \$i + 1\`
echo \$1:\$HASHPASS
exit 1

chmod 755 /etc/

nvram setfile2nvram /etc/

cat > /etc/ <<EOF
# echo "\${username}"
hash=\`/etc/ "\${username}" "\${password}"\`

USERS=\`cat /etc/vpnusers\`
for u in \$USERS; do
  test "\${hash}" == "\${u}" && exit 0
exit 1

chmod 755 /etc/

nvram setfile2nvram /etc/

To generate password hashes for the users (first argument is username, second argument is password):

/etc/ matt test1234 >> /etc/vpnusers

Persist the vpnusers file:

nvram setfile2nvram /etc/vpnusers


To test the verify script:

export username=matt
export password=test1234
echo $?

If the output is 0 from the above command, the matt/test1234 credential was found in the vpnusers file.  If the output is 1, something is broken!

The hard part is now done. Reboot the router!


The final task is creation of the client configuration file and adding the client keypair and CA:

1) Make a directory "config" under "%ProgramFiles%\OpenVPN" if not already present.

2) Within the "config" directory, make a subdirectory, e.g "homevpn"

3) Copy to the "homevpn" directory ca.crt, and the appropriate client keypair (e.g. client_matt.key / client_matt.crt).

4) Within the "config" directory, create an openvpn client config file, e.g. "homevpn.ovpn"; The contents of "homevpn.ovpn" based on the above server configuration above are as follows:

# The hostname/IP and port of the server. You can have multiple remote entries to load balance between the servers.
remote 443

# Specify that we are a client and that we will be pulling certain config file directives from the server.

ns-cert-type server

# On most systems, the VPN will not function unless you partially or fully disable the firewall for the TUN/TAP interface.
dev tap21

# Are we connecting to a TCP or UDP server?
proto tcp

# Keep trying indefinitely to resolve the host name of the OpenVPN server.  Useful for machines which are not permanently connected to the internet such as laptops.
resolv-retry infinite

# Most clients don't need to bind to a specific local port number.

# Try to preserve some state across restarts.

# --float tells OpenVPN to accept authenticated packets from any address, not only the address which was specified in the --remote option.
# Useful if you're using round-robin DNS.  Also useful if your server has a dynamic IP address which the ISP could change.
# I use float so I can connect from inside AND outside my router.

# If the pushed routes appear not to be added on windows hosts, add the following:
# route-delay 30

# SSL/TLS parms.
ca "C:\\Program Files\\OpenVPN\\config\\homevpn\\ca.crt"
cert "C:\\Program Files\\OpenVPN\\config\\homevpn\\client_matt.crt"
key "C:\\Program Files\\OpenVPN\\config\\homevpn\\client_matt.key"

# Enable compression on the VPN link.
# Don't enable this unless it is also
# enabled in the server config file.

# Set log file verbosity.
verb 3

# Silence repeating messages
mute 20

# prompt for username and password

You should hopefully now be able to establish a VPN connection!  Good luck.


  1. excellent guide !!! I followed it and did some modification. Got error connecting to VPN server if custom script as follow:
    Custom Configuration**:
    script-security 3
    auth-user-pass-verify /etc/ via-env

    Result of
    "To test the verify script:
    export username=matt
    export password=test1234
    echo $?" is 0

    Emptying custom configuration allows me to connect my win7 locally to tomato server successfully.

    Im doing BT while connected to OPENVPN server and it is doing fine. My only concern, does my ISP still sees my transactions, meaning secured by doing this on local connection?

  2. Hi Leandro; Check /var/log/messages

    Do you see any messages indicating failure cause?

    Also, try adding a line such as the following to your script:

    echo "${username}" "${password}" > /tmp/loginrequest

    and then attempt login, and do a cat /tmp/loginrequest

    it should contain the username and password you supplied.

    You should then be able to manually try out the script with these credentials.

    If I provide an invalid password, I will see:

    Mar 16 22:24:21 asus daemon.err openvpn[3000]: TLS Auth Error: Auth Username/Password verification failed for peer

    Whereas, a successful password will result in for example:
    Mar 16 08:30:48 asus daemon.notice openvpn[645]: TLS: Username/Password authentication succeeded for username 'matt'

  3. for Version 1.28 of tomato, after you've created all the scripts and tested everything, and before you reboot the router, you have to enter

    nvram commit

    or the files will not persist past reboot. Thanks for the tutorial, everything is working now that I did that.

  4. this is a fantastic walk-through! Thank you so much for creating this.

    I figured out after some trial and error that you're missing one essential step/command that must be issued via the CLI to get the NVRAM to stay after a reboot (so that the added security of the user/password combo work): "NVRAM commit"

  5. This is very good.
    Thanks so much.

  6. Awsome guide man! Worked like a charm on my Netgear WNR3500L v2 running Shibbys build 108. However, I found that open OpenVPN GUI refused to parse the ovpn file unless the port is declared on a separate row.

    Like so:

    port 443

    Also consider using TUN instead, if anyone wants to connect using Android, as OpenConnect currently only supports TUN connections.


  7. THANK YOU for this guide! ... I'm really impressed and appreciate the work you do .. Thanks for taking the time to discuss this, I feel strongly about it and love learning more on this topic.


  8. Excellent tutorial. Many thanks.

  9. This comment has been removed by a blog administrator.

  10. Note if you get an error along the lines of "openssl not a recognized command" when running build-ca, the solution is to copy the files from the 'bin' folder in the OpenVPN installation directory to the 'easy-rsa' folder in the installation directory (copy the files FROM bin, not the folder itself). My guess is it's probably a minor bug in the x64 bat which they may not have tested after some update.

  11. I had a ton of problems doing this on Windows 7 x64. It seems the OpenVPN devs really aren't particularly good at the Windows side of things, as it seems the windows versions of OpenVPN have been plagued with issues and vague instructions in the easy-rsa side of things. I was completely unable to generate keys properly when OpenVPN was installed to the C:\Program Files (x86)" directory. I narrowed the problem down to the path, it seems that having spaces in it screws up some of the scripts. Note that I tried every way of writing the paths in my var.bat (writing the full paths, using double slashes, forward slashes, backward slashes, enclosing the path in double quotes), to no avail. The solution was to install OpenVPN to the C:\ root (so C:\OpenVPN), and define the full paths in var.bat.

    Also, I ended up manually creating the "keys" folder in the easy-rsa directory, as it seems the scripts do not, and then freak out when they can't find it. The clean-all script is also broken, I ended up manually cleaning (delete the "keys" folder, create a new one). Don't forget to not only set the HOME path in var.bat, but the KEY_DIR path as well. It should point to your keys folder.

    I finally got things working using OpenVPN version 2.1.4, as that was one suggestion I found while browsing the net.

    All in all the OpenVPN devs need to read up on their Windows scripting, as the scripts are attrocious. They're not commented well, they do things in ways that can easily backfire (for example making "serial" a protected operating system file, which means that it's invisible in Explorer, so anyone trying to manually clean things can miss it. There's literally no reason to hide it like that :P), and just do not work well.