I’ve recently re-vamped my OpenVPN remote access setup at home. I have always had two separate VPN instances: a “trusted” instance that was not firewalled, and a “normal user” instance that has limited access – just to email, internal websites, etc (but not complete unfettered access). Previously, I had accomplished this by running two separate instances of OpenVPN, and used iptables firewall rules on the VPN endpoint to enforce the access limitations. I use OpenVPN in routed mode, with a separate subnet for VPN users. This limits the spread of broadcasts, and is cleaner / easier to manage in my opinion.
Since I originally deployed that setup four or five years ago, I’ve moved to using zone based firewalls (Vyatta) for my internal routers. I now try to do all of my firewall access control on those devices for simplicity. The VPN endpoint at one location also runs VMware Server. I’ve been trying to move services off of that host so I can convert it to ESXi. In re-designing my OpenVPN configuration, I came up with the following requirements:
- Should take advantage of the Vyatta router / firewalls for access control (as opposed to maintaining iptables rules on the VPN endpoint)
- Should still work with OpenVPN in routed mode
- Should support multiple instances of OpenVPN on a single host
The design I came up with to meet those requirements uses VRF’s on the Linux host through iproute2. VRF support on Linux is a little kludgier than what you would find on a Cisco router for instance, but it is usable and works well for this purpose. The VRF’s are necessary to isolate the “trusted” and “user” traffic on the VPN endpoint. Without them, all of the traffic would use the same routing table, and I wouldn’t be able to separate the traffic out into separate zones on my firewall. So, I have a separate routing VLAN between my firewall and VPN endpoint for each of the VPN server instances. Instead of having routing VLAN’s, you could probably also use GRE tunnels if you wanted. On the firewall though, each of the routing VLAN’s are put into the appropriate firewall zones. I have five main firewall zones – Trusted, Normal User, Guest, Untrust (Internet), and DMZ. The routing network for the “trusted” VPN instance is included in the Trusted zone, and the normal user VPN instance is placed into the Normal User zone. So, the VPN connections seemlessly have the same firewall ACL’s applied as the client would if they were directly connected inside of the network.
To use VRF’s on Linux for this purpose, there were three files I had to modify:
First, I had to actually add the new routing table in /etc/iproute2/rt_tables:
[RT ID] [RT NAME]
Here’s an example of the complete file from one of my VPN servers:
# # reserved values # 255 local 254 main 253 default 0 unspec # # local # #1 inr.ruhep 10 trust-RT 20 normaluser-RT
I also had to add a static route in each of the new routing tables attached to the physical interface for the routing network. This is needed so the VPN endpoint will know how to route the traffic from the VPN clients to the rest of the network. It’s configured in /etc/sysconfig/network-scripts/route-ethX (where ethX is the physical interface on the VPN server connected to the routing network).
0.0.0.0/0 table [RT NAME] via [next-hop] dev ethX
And again, here’s an example from one of my VPN endpoints (eth1 in my case is connected to the routing network for the “trusted” VPN instance):
0.0.0.0/0 table trust-RT via 192.168.0.193 dev eth1
Lastly, I needed to configure a rule to map traffic to a particular VRF. We want to associate traffic for each of the OpenVPN tunnel interfaces to the appropriate VRF. This is done in /etc/sysconfig/network-scripts/rule-ethX
dev table [RT NAME]
And here’s what my rule-eth1 file looks like:
dev tun0 table trust-RT
This project ended up taking a while. It took a little while to come up with the design, and to test how the VRF support on Linux worked. I also wanted to manage this as much as possible through puppet. That took some thought.
There was some PKI work involved to get this up and running. I ended up creating a new internal root certificate authority, and an intermediate to actually sign the client certs. Previously, I was just using a stand-alone CA for OpenVPN that was unrelated to the internal root CA I use. I wanted to create an intermediate CA to sign the VPN client certs, but my internal root CA expires somewhat soon. So, I went ahead and stood up a new root CA (which I was going to have to do soon anyway).
One cool feature of OpenVPN that I wasn’t using previously is the ability to dynamically add a route to the client to ensure that the VPN server itself is reachable. In addition to using OpenVPN for remote access, I also occasionally used it to access the “Trusted” firewall zone while I’m connected through the Normal User zone. ( I only provide access to the “Normal User” zone via wireless, so for instance, if I need to SSH to some internal server from my laptop while on wireless, I would either need to connect to the VPN, or SSH into one of the jump servers that I specifically allowed access to from the normal user networks ). Previously, I ran a separate OpenVPN instance that injected different routes to make that work. I needed to do this because OpenVPN sends routes to the client for all of my internal networks (which include the internal network that the VPN server itself is on). So, this would break access to the VPN server if you’re on one of the internal networks. Now, I can have the OpenVPN server push a /32 route to the client to ensure that traffic to the VPN server itself is always routed through the original default gateway for the physical network that the client is connected to:
push “route remote_host 255.255.255.255 net_gateway”
This project has really highlighted the reasons I like OpenVPN so much. It’s incredibly flexible in terms of deployment models. Despite that though, it’s relatively easy to utilize that complexity since it builds on other tools. I’ve also recently noticed that there is now an free iOS app from OpenVPN in Apple’s App Store. Previously, you had to jail break an iOS device, and then pay for an OpenVPN client through Cydia. This, combined with the several decent Android OpenVPN clients eliminates the one scenario that OpenVPN previously was not as usable for (remote access via mobile devices).