Up until recently, I've been using RRAS (built into Windows Server) to handle my NAT/router needs on my VMware ESXi host. I had a couple of problems previously, and annoyingly, one of the things I had set up previously (subnet-to-subnet VPN) stopped working and I was unable to fix it, even after several days of kicking it around. So, I opted to replace it with a Linux option, using Libreswan (over Openswan due to the history) as my IPsec implemntation. One of the advantages (other than it being somewhat easier to debug odd issues) is that it's quite a bit simpler to add additional Linux systems to the subnet-to-subnet VPN, which I'm planning for later use.
The eventual plan is to combine the following into one system:
- NAT/router
- Subnet-to-subnet VPN (site-to-site)
- Host-to-subnet VPN (client)
This post will address the first two, and I'll cover the third a later time.
NAT/router
Bits and pieces taken from this page.
Naturally, if you'd like to set up your system as a NAT/router, you'll want to set your firewall software up; in this case, I'm operating under the assumption that you're using iptables. First off, you'll need to make sure that you enable IP forwarding. This entry should be in your /etc/sysctl.conf, so that it's set on each boot:
And then, on the shell:
$ sysctl -p
# Check that it's set correctly with:
$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
This page assumes the following definitions:
You'll want entries similar to the following in your iptables configuration in the appropriate location (comments optional, of course):
iptables -A INPUT -i eth1 -j ACCEPT
iptables -A FORWARD -i eth1 -j ACCEPT
# Only forward connections we know about from the "outside" to the "inside," using built-in connection tracking.
iptables -A FORWARD -i eth0 -o eth1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT--
# Tag all outgoing packets as though they're coming from the NAT/router's external IP.
iptables -A POSTROUTING -s 192.168.0.0/24 -o eth0 -j SNAT --to-source 1.2.3.4
At this point, you should be able to set up 192.168.0.1 as your gateway for the devices you'd like to put behind the NAT/router.
Subnet-to-subnet
Bits and pieces taken from this page and this page (particularly for the iptables setup).
Things get more interesting once you'd like to connect multiple private subnets. In my situation, I'm connecting my ESXi's virtual network to my home internal network so that I'm always on the "inside." Not surprisingly, I'd like to do this in a reasonably secure manner. As noted above, this setup uses Libreswan as its IPsec implementation. The following instructions apply to the systems on both sides of the tunnel.
Our example uses the following setup ("left" and "right" are essentially arbitrary, but should be consistent):
Installing Libreswan takes a little bit of effort on CentOS 6, as it's not in the default repositories. As a result, you'll need to grab Libreswan's repository file and put it in /etc/yum.repos.d (you'll also need to download their GPG key to /etc/pki/rpm-gpg), along with installing the Fedora Extra Packages for Enterprise Linux 6 RPM. At this point, you should be able to install Libreswan:
At this point, you now need to configure Libreswan. Starting with /etc/ipsec.conf, I only touch a couple of lines:
config setup
.
.
.
#
# NAT-TRAVERSAL support
# exclude networks used on server side by adding %v4:!a.b.c.0/24
# It seems that T-Mobile in the US and Rogers/Fido in Canada are
# using 25/8 as "private" address space on their wireless networks.
# This range has not been announced via BGP (at least upto 2010-12-21)
nat_traversal=yes
virtual_private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:25.0.0.0/8,%v4:100.64.0.0/10,%v6:fd00::/8,%v6:fe80::/10,%v4:!192.168.0.0/24
.
.
.
# You may put your configuration (.conf) file in the "/etc/ipsec.d/" directory
# by uncommenting this line
include /etc/ipsec.d/*.conf
The virtual_private entry denotes which private subnets to allow/disallow; in this case, we're leaving the default except for explicitly disallowing the local subnet. This particular entry is for the 192.168.0.0 side; the 192.168.1.0 side should be updated appropriately. The last line is a personal preference; I like having configurations separated out on a per-file basis.
Next up is setting up the key for the connection:
$ ipsec newhostkey --output /etc/ipsec.d/vpn.secrets
# Print out the RSA key signature - adjust for the side you're on.
$ ipsec showhostkey --left
ipsec showhostkey loading secrets from "/etc/ipsec.secrets"
ipsec showhostkey loading secrets from "/etc/ipsec.d/vpn.secrets"
ipsec showhostkey loaded private key for keyid: PPK_RSA:ABCDEFGHI
# rsakey ABCDEFGHI
leftrsasigkey=......
You'll need the RSA key signature in the next step (your RSA key's key ID is obviously going to be different). The directory /etc/ipsec.d should have its permissions locked down, but if you're paranoid, you can lock down the signature file as well:
$ chmod 0600 /etc/ipsec.d/*.secrets
After this, you should set up your VPN configuration file, /etc/ipsec.d/vpn.conf:
authby=rsasig
# Automatically start the connection when the service starts. Set to "add" if you don't want it
# automatically starting.
auto=start
# Use more aggressive dead connection detection.
dpdaction=restart
dpddelay=5
dpdtimeout=120
# What we're calling the left side.
leftid=@left.internal
# See note below regarding left/right.
left=1.2.3.4
leftsourceip=192.168.0.1
leftsubnet=192.168.0.0/24
# From the showhostkey command above.
leftrsasigkey=......
# What we're calling the right side.
rightid=@right.internal
# See note below regarding left/right.
right=5.6.7.8
rightsourceip=192.168.1.1
rightsubnet=192.168.1.0/24
# From the showhostkey command above.
rightrsasigkey=......
# NOTE: The "left" and "right" entries are how each side sees its own IP and the other side's IP.
# If the system is behind its own NAT, you'll need to put its assigned IP instead. This is
# one instance in which the configuration files will not match.
You'll also need additional lines to your /etc/sysctl.conf:
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
And then, on the shell:
$ sysctl -p
# If you want to the values on all of your current network interfaces without rebooting, replacing
# $interface with the appropriate interface name:
$ echo 0 > /proc/sys/net/ipv4/conf/$interface/accept_redirects
$ echo 0 > /proc/sys/net/ipv4/conf/$interface/send_redirects
$ echo 0 > /proc/sys/net/ipv4/conf/$interface/rp_filter
At this point, we can let Libreswan verify itself (everything should come up looking good):
Before we can start the VPN, however, we'll need to set up our iptables configuration appropriately (once again, comments optional):
iptables -N ICMPALL
# Set up a rejection bucket.
iptables -N ZREJ
# Using connection tracking, accept packets we already know about.
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Treat ICMP packets specially.
iptables -A INPUT -p icmp -m icmp --icmp-type any -j ICMPALL
# Allow new connections to be made to ports 500 (IKE) and 4500 (IKE NAT-Traversal), both for Libreswan.
iptables -A INPUT -p udp -m conntrack --ctstate NEW -m multiport --dports 500,4500 -j ACCEPT
# Allow connections to port 1701 (L2TP) for Libreswan.
iptables -A INPUT -p udp -m udp --dport 1701 -m policy --dir in --pol ipsec -j ACCEPT
iptables -A INPUT -p udp -m udp --dport 1701 -j DROP
# Goes at the bottom of the INPUT section: reject all other packets.
iptables -A INPUT -j ZREJ
# Forward valid IPsec packets.
iptables -A FORWARD -m policy --dir in --pol ipsec -j ACCEPT
# Goes at the bottom of the FORWARD section: reject all other packets.
iptables -A FORWARD -j ZREJ
# Reject fragmented packets.
iptables -A ICMPALL -p icmp -f -j DROP
# Accept: echo reply
iptables -A ICMPALL -p icmp -m icmp --icmp-type 0 -j ACCEPT
# Accept: destination unreachable
iptables -A ICMPALL -p icmp -m icmp --icmp-type 3 -j ACCEPT
# Accept: source quench
iptables -A ICMPALL -p icmp -m icmp --icmp-type 4 -j ACCEPT
# Accept: echo
iptables -A ICMPALL -p icmp -m icmp --icmp-type 8 -j ACCEPT
# Accept: time exceeded
iptables -A ICMPALL -p icmp -m icmp --icmp-type 11 -j ACCEPT
# Drop all other ICMP packets.
iptables -A ICMPALL -p icmp -j DROP
# Reject packet types as appropriate.
iptables -A ZREJ -p tcp -j REJECT --reject-with tcp-reset
iptables -A ZREJ -p udp -j REJECT --reject-with icmp-port-unreachable
iptables -A ZREJ -j REJECT --reject-with icmp-proto-unreachable
At this point, you can start up the IPsec service.
# If you set auto=add, bring up the VPN by hand:
$ ipsec auto --up vpn
You should now be able to ping from one subnet to systems on the other subnet.