After it was requested multiple times on my post about using pfSense as a Cisco AnyConnect VPN Client, I finally found some time to document the setup I ended up with as a replacement, the purpose of which was to not clutter up the pfSense box. pfSense, after all, acts as the main company router and is integral to the operation of the business network. I don’t want to modify it beyond what some future software update would be able to handle.
The solution to this issue, and several others that have popped up since then, is a set of very small virtual machines. I run each of these, one for each non-pfSense supported client VPN solution (there are quite a few unusual ones out there), with 64 megs of RAM and a mere 3GB of harddrive space.
VM Setup
The virtual machines I use run Debian, as that’s what I’m most familiar with and when setting up things like these, I want them to consume as little of my time as possible. The OpenConnect VPN client is readily available in the Debian package repositories. No mucking about with the apt sources. I also use shorewall to do the routing and firewall rules for me. Thus:
- Debian (minimal install)
- OpenConnect (from repositories)
- Shorewall
- A cup of coffee
Scripts
The script I use to keep it all going is pretty much copy-pasted from the post about using pfSense to do this, but here it is in its current form:
#!/bin/sh # settings user="user_name" pass="secret_password" host="vpn.remote.company.com" test="nc -v -w 10 -z 172.16.30.2 3389" tmpif="tun69" iface="tun69" pidfile="/tmp/${iface}.pid" script="/usr/share/vpnc-scripts/vpnc-script" # env openconnect="/usr/sbin/openconnect" ifconfig="/sbin/ifconfig" # func ifkill() { $ifconfig "$1" down 2>/dev/null || : $ifconfig "$1" destroy 2>/dev/null || : } # check if we're already running if [ -n "$test" ] && $test 2>/dev/null; then echo "Connection is already up" exit 0 fi # scream echo "Connection is not up! Attempting restart..." >&2 # clean up previous instance, if any if [ -e "$pidfile" ]; then read pid <"$pidfile" echo "Killing previous pid: $pid" kill -TERM "$pid" rm "$pidfile" fi ifkill "$tmpif" ifkill "$iface" # open vpn connection echo "$pass" |\ $openconnect \ --background \ --pid-file="$pidfile" \ --interface="$tmpif" \ --user="$user" \ --passwd-on-stdin \ --script="$script" \ "$host" # rename the interface if [ "$iface" != "$tmpif" ]; then echo "Renaming $tmpif to $iface" $ifconfig "$tmpif" name "$iface" fi
I run the above script every minute using the following line in root’s crontab:
* * * * * /root/bin/cron/vpnscript >/dev/null
Shorewall
To do the routing and firewall, there are a few files in /etc/shorewall that are essential for this setup. The first is the interfaces file, in which I define the virtual machine’s regular network interface and the “tun69” interface created by the VPN connection.
# ZONE INTERFACE BROADCAST OPTIONS net eth0 detect dhcp,logmartians=1,nosmurfs,routefilter,tcpflags,routeback vpn tun69 detect dhcp,logmartians=1,nosmurfs,routefilter,tcpflags
Then there’s the zones file. This is pretty basic, but matches the interfaces.
# ZONE TYPE fw firewall net ipv4 vpn ipv4
For the policy file, we want to allow traffic to flow from the net zone to the vpn zone.
# SOURCE DEST POLICY LOG_LEVEL $FW vpn ACCEPT $FW net ACCEPT net vpn ACCEPT all all REJECT info
The rules file allows SSH to control the VPN VM from the net zone, which is still in the company’s internal network, mind you.
#ACTION SOURCE DEST PROTO DEST SOURCE ORIGINAL RATE USER/ MARK CONNLIMIT TIME HEADERS SWITCH HELPER # PORT PORT(S) DEST LIMIT GROUP #?SECTION ALL #?SECTION ESTABLISHED #?SECTION RELATED #?SECTION INVALID #?SECTION UNTRACKED ?SECTION NEW SSH(ACCEPT+) net $FW
We need a bit of magic to do outbound NAT, with the masq file. This makes the connections the policy file allows to flow from the net zone to the vpn zone masquerade as whatever IP address the VPN client got for its tun69 interface. Without this, the connections would get relayed using their original IP, which the VPN client network probably wouldn’t appreciate. This simply covers all RFC1918 IPv4 addresses, so it doesn’t rely in any way on the rest of the company’s network.
#INTERFACE:DEST SOURCE ADDRESS PROTO PORT(S) IPSEC MARK USER/ SWITCH ORIGINAL # GROUP DEST tun69 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
Finally, in shorewall.conf, I change a single line to ensure IP_FORWARDING is enabled
IP_FORWARDING=Yes
pfSense setup
Now we have a VPN routing machine, which in my case is situated on a dedicated VLAN to isolate it from the rest of the company network. You don’t have to do this, but if you don’t, anyone on the internal network can configure a route through the VPN machine and access whatever is on the other end.
In pfSense, I add a static IP for the OpenConnect client, then add a Gateway in System=>Routing=>Gateways. The interface should be the one the OpenConnect client is at, the IP should be the static IP of the client. Then, on the “Static Routes” page, add a route to the network at the end of the VPN and set the gateway to be the one you just created with the OpenConnect client IP. Now it’s up to your rules which machines you want to allow.
Any questions?
If you have any questions about this setup, let me know 🙂
1 Comment
I love this solution, and have been looking for something similar myself. I’m contracting and currently have something like 7 different VPN clients clogging up my PC. Even if I don’t have more than one running at the time, it still effs up routing, my own LAN and certainly DNS. So I will give this baby a try.
Have you had any setups with overlapping IP-scopes? Like multiple small customers running stock routers on 192.168.1.1/24…… and there I saw the link at the bottom of the screen to your next article. Thanks ^_^