The purpose of this howto is to explain how to get OpenVPN working with a routed tun setup. The goal is to have a permanent point-to-point link connecting several networks together through a central VPN server that manages the routing table propagation. We don't want to NAT the routes, either, so this post explains how to disable that.
I used dd-wrt build 12533 (vpn generic) on a Linksys WRT310N v1.0.
Go here, and find your model: http://www.dd-wrt.com/dd-wrtv3/dd-wrt/hardware.html
Download the mini and vpn versions. You *must* install the mini version first, then upgrade from that to the vpn build. It has to do with how Linksys' firmware loader handles loading images over 3MB. The mini build overcomes this restriction, so get it installed first. There are plenty of documents on the dd-wrt site and on the net explaining this issue in detail, so I won't discuss it any further.
I'm going to assume that you're familiar enough with linksys and/or dd-wrt to get it installed and running, so I won't explain the process in detail, but here are the steps I generally take to prep my hardware:
Upgrade to latest Linksys firmware, then reset to factory defaults.
Install dd-wrt mini via web interface, then reset to factory defaults. (I use the 30-30-30 method here).
Install dd-wrt vpn via web interface, then reset to factory defaults.
The dd-wrt boards generally recommend doing a 30-30-30 reset on the system after each update, but I only do it after the initial dd-wrt load, then I use the factory reset software option when upgrading my firmwares.
If you don't know what the 30-30-30 reset method is, go google it. It's basically just holding down the reset button to do a hard reset (though diehards might disagree with me), so if you've ever restored a linksys to factory state with that little reset button in the back, that's pretty much it.
Ok, on with the configuration. At this point I'm going to assume that you have the vpn build installed and are ready to configure it.
For my configuration, the server is servicing 192.168.0.0/20. My network is 192.168.16.0/20 and there are other 192.168.x.0/20 networks hosted by other clients which will be connecting to the VPN as well. For simplicity sake I will only show how to configure a single client, but I'll touch on the points that are relevant to consider when handling multiple permanent VPN links in this scenario.
First, I configured my router (LAN-side) for the appropriate local subnet (192.168.16.0/20). Since my router gets a DHCP address on the WAN-side, I won't go into configuration here and will assume that your router is configured to reach the Internet.
Since OpenVPN is an SSL-based VPN we'll need to generate certificates. This is the most complicated part of the process. I'm not going to go into how to generate the server certificate, but I will explain how I generate my client certificates.
OpenVPN is going to be running on embedded hardware, and modifying the dd-wrt installation post-install is a bit of a pain in the butt, we're going to use certificates that don't have a passphrase so that the setup will be simpler. As always, keep your private key private.
I've named my router Crete, so let's generate a certificate:
> openssl genrsa -out crete.key
> openssl req -new -key crete.key -out crete.csr -days 730
> openssl ca -policy policy_anything -out crete.pem -infiles crete.csr
The data in the certificate doesn't matter *EXCEPT* for the CN field. This matters very much for reasons we'll get into later. Since my router is named Crete, I'm going to put "Crete" in my CN field.
Also, remember to *NOT* put a passphrase on your certificate, but you will need to provide the CA's passphrase to sign the certificate.
Here are the details for my signed certificate:
Check that the request matches the signature
Signature ok
Certificate Details:
Serial Number: 5 (0x5)
Validity
Not Before: Sep 4 10:44:22 2009 GMT
Not After : Sep 4 10:44:22 2010 GMT
Subject:
countryName = US
stateOrProvinceName = UT
localityName = Murray
organizationName = OpenSEA
organizationalUnitName = Open1X
commonName = Crete
emailAddress = terry.simons@gmail.com
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier:
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
X509v3 Authority Key Identifier:
keyid:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
Certificate is to be certified until Sep 4 10:44:22 2010 GMT (365 days)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
Ok, we're ready to configure the VPN.
Go to Services->VPN and enable the OpenVPN Client.
Enter in the relevant information. For my purposes I'm using UDP TUN with LZO compression. Everything else is default.
You'll need to copy and paste in the certificates now. The CA certificate which was used to sign your client certificate, your client certificate, and your private key. My files are named cacert.pem, crete.pem, and crete.key. When you paste them into the UI, you *only* need the data in between (and including) the BEGIN and END lines.
Also, I include an extra line after the END just because I've seen some things get picky about certificates that way. I don't know if it's strictly necessary on this setup.
Go ahead and save this and head over to the Administrations->Commands window.
Here's the most simple setup:
# Accept any tunnel traffic destined for Crete
iptables -I INPUT 1 -i tun+ -j ACCEPT
# Accept any traffic leaving Crete and heading for any tunnel
iptables -I OUTPUT 1 -i tun+ -j ACCEPT
# Accept any traffic that needs to be forwarded to or from the tunnel on behalf of another host.
iptables -I FORWARD 1 -i tun+ -j ACCEPT
The above lines make it so we'll accept any input or output from the tunnel, and we'll forward anything that we need to that is tunnel related. The above rules are very insecure, but you can use them to troubleshoot if you have issues.
I'm going to go off of a stricter set of rules for my configuration:
Here's the most simple setup:
# Accept any tunnel traffic destined for Crete
iptables -I INPUT 1 -i tun+ -j ACCEPT
# Accept any traffic leaving Crete and heading for any tunnel
iptables -I OUTPUT 1 -i tun+ -j ACCEPT
# Accept any traffic that needs to be forwarded to or from the tunnel on behalf of another host.
iptables -I FORWARD 1 -i tun+ -j ACCEPT
The above lines make it so we'll accept any input or output from the tunnel, and we'll forward anything that we need to that is tunnel related. The above rules are very insecure, but you can use them to troubleshoot if you have issues.
I'm going to go off of a stricter set of rules for my configuration:
Paste this in the Commands box:
# DROP any traffic coming from any tunnel that purports to be us.
iptables -I INPUT 1 -i tun+ -s 192.168.16.0/20 -j DROP# DROP any traffic coming from any tunnel that purports to be us.
# Accept any other tunnel traffic.
iptables -I INPUT 2 -i tun+ -j ACCEPT
# Forward traffic through the tunnel for 172.16.16.0/24 hosts (this is our tunnel address space).
iptables -I FORWARD 1 -o tun0 -s 172.16.16.0/24 -j ACCEPT
# Forward traffic through the tunnel for any of our subnet.
iptables -I FORWARD 2 -o tun0 -s 192.168.16.0/20 -j ACCEPT
# Drop any other traffic trying to go through the tunnel.
iptables -I FORWARD 3 -o tun0 -j DROP
# Turn off NAT on the tunnel, since we want to route directly.
iptables -D POSTROUTING -t nat -o tun0 -j MASQUERADE
Note that if we add more tunnels to different places in the future, the above rules will prevent the other tunnels from talking to each other.
iptables -D POSTROUTING -t nat -o tun0 -j MASQUERADE
Note that if we add more tunnels to different places in the future, the above rules will prevent the other tunnels from talking to each other.
Click Save Firewall
The INPUT chain configuration says to accept packets inbound from the tunnel interface that are destined for the router (Crete), but to drop any other traffic. If you'd rather not have people poking at your router remotely you can leave this line out, but you'll probably still want to block anyone claiming to be 192.168.16.0/20 (or whatever your subnet is).
The FORWARD chain configuration says to accept packets that are destined to be forwarded on beyond Crete. This means the systems behind the router on 192.168.16.0/20, but not including Crete. We also explicitly include the tunnel interfaces (172.16.16.0/24) but exclude everything else. This prevents addresses we don't trust from using our tunnel.
Strictly speaking, at this point if your server is configured you should be able to establish a tunnel and pass traffic, but this wasn't good enough for me. The problem is that the default OpenVPN configuration on dd-wrt brings up a NAT that tunnels all traffic out the tunnel interface, but I want only traffic destined for systems on the other side of the tunnel to traverse the tunnel. Everything else should be routed out my WAN interface.
In order to accomplish this we turn off NAT with the final iptables line.
That's it. Now it's time to discuss the server configuration.
Here it is:
port 1194
proto udp
dev tun
ca /etc/ssl/CA/cacert.pem
cert /etc/ssl/CA/server_cert.pem
key /etc/ssl/CA/server_key.pem
dh /etc/ssl/CA/dh1024.pem
server 172.16.16.0 255.255.255.0
ifconfig-pool-persist ipp.txt 60
keepalive 10 30
comp-lzo
persist-key
persist-tun
status status.log
verb 3
client-to-client
client-config-dir ccd
route 192.168.16.0 255.255.240.0
route 192.168.32.0 255.255.240.0
route 192.168.64.0 255.255.240.0
route 192.168.96.0 255.255.240.0
route 192.168.128.0 255.255.240.0
route 192.168.192.0 255.255.240.0
route 192.168.224.0 255.255.240.0
route 192.168.240.0 255.255.240.0
push "route 192.168.0.0 255.255.240.0"
push "route 192.168.16.0 255.255.240.0"
push "route 192.168.32.0 255.255.240.0"
push "route 192.168.64.0 255.255.240.0"
push "route 192.168.96.0 255.255.240.0"
push "route 192.168.128.0 255.255.240.0"
push "route 192.168.192.0 255.255.240.0"
push "route 192.168.224.0 255.255.240.0"
push "route 192.168.240.0 255.255.240.0"
Let's break this down into manageable chunks:
Pretty self explanatory. These options mirror the client settings. UDP port 1194 with a tun interface.
port 1194
proto udp
dev tun
Server certificate configuration, again you'll need to figure this step out on your own:
ca /etc/ssl/CA/cacert.pem
cert /etc/ssl/CA/server_cert.pem
key /etc/ssl/CA/server_key.pem
dh /etc/ssl/CA/dh1024.pem
Next up we configure the tun server subnet. This is arbitrary. It's just a subnet used by the server to establish the point-to-point links. Just make sure it's an RFC 1918 address space (unless you want to use real space) that you aren't using for your clients.
server 172.16.16.0 255.255.255.0
Set up a persistent pool:
ifconfig-pool-persist ipp.txt 60
This just causes everybody to get the same tunnel addresses every time they connect. It's not strictly required for the setup unless you think you have a need.
The keepalive command dictates how a client handles a dropped connection. This means they'll try pinging the server every 10 seconds and if the connection is down for 30 seconds they will try to re-establish the link.
keepalive 10 30
Now we turn on compression. Again this mirrors the client configuration.
comp-lzo
I'm not sure what these persistent options do.
persist-key
persist-tun
Configure the status log and verbosity level:
status status.log
verb 3
And the rest of this is purely routing magic for our clients and server to be able to communicate with each other.
This is where the interesting things happen.
First, we need to let other clients communicate with each other:
client-to-client
And we also need to set up a client configuration directory:
client-config-dir ccd
The client configuration directory is where the CN that you configured in your certificate becomes relevant.
You need to create a file that is named whatever is in your certificate's CN field. My router is named Crete, so I'll create a file named Crete in the ccd directory. Inside the file is the following configuration line:
iroute 192.168.16.0/20
NOTE WELL: The iroute command *MUST* be in the client configuration directory file. I'll explain why in a moment, and yes, the route appears to be redundant because of the route 192.168.16.0/20 directive in the main server configuration, but it's not. I'll explain why.
Finally the route entries.
The normal "route" entries are route directives for the server. This tells the server to create routing entries for these networks. You might be wondering how the server knows where the other side of the route is. That will become clear in a moment.
route 192.168.16.0 255.255.240.0
route 192.168.32.0 255.255.240.0
route 192.168.64.0 255.255.240.0
route 192.168.96.0 255.255.240.0
route 192.168.128.0 255.255.240.0
route 192.168.192.0 255.255.240.0
route 192.168.224.0 255.255.240.0
route 192.168.240.0 255.255.240.0
Next, we push the routes out to all of the clients that connect.
What's actually going on here is that the openvpn server uses the client configuration directory entry to determine who owns which subnet. So in my case Crete owns the 192.168.16.0/20 subnet. The server uses that information to construct its local route. It also uses this information to know *NOT* to push the 192.168.16.0/20 route out to Crete when it does the push. This is important because if the route does accidentally get pushed to the client it will appear to have died. I thought I had found a bug in OpenVPN on dd-wrt when I first experienced this, but I realized that it was a misconfiguration on my part… so be careful here. If you've managed to lose connectivity to your router and you think this might be the cause, unplug it from the WAN, reboot it, and you should be able to talk to it again. You can disable the OpenVPN client (your certificates and configuration won't be purged) and you can plug the router back. Once you have your client configurations set up properly you should be able to connect.
Verify you can ping the remote tunnel endpoint and that they can ping you.
One other note about routes. You *MUST* ensure that any routers behind the respective networks have routes back to the VPN client for this to work. If your VPN client is your router, then you don't have to do anything extra. If you have enterprise-grade equipment servicing routes that are being pushed by the VPN server then the routers managing those networks need to route the various remote networks back to the VPN router.