Richard Bejtlich's blog on digital security, strategic thought, and military history.

Monday, January 03, 2005

IPSec Tunnels with FreeBSD

Although the FreeBSD Handbook offers a VPN over IPSec section, it doesn't describe the scenario I face when deploying network security monitoring sensors. That document also references commands that no longer exist in FreeBSD 5.3, like 'gifconfig.' My architecture looks like this (all IP addresses are obfuscated):

I need to encrypt communications from the sensor to the monitoring backend. This can involve multiple individual sockets. I don't like to use OpenSSH port forwarding or Stunnel because I must set up a separate port forwarding or tunnel session for each channel. I would much rather use IPSec, since that can carry any communications between the sensor and the backend.

Complicating matters, I need to communicate between a sensor with a public management IP and a backend with an internal private IP address. That backend internal private IP address is transformed using NAT on the VPN concentrator and NAT gateway. All boxes in this scenario run FreeBSD 5.3 RELEASE.

One answer to this problem, and the approach I use, is to create a virtual tunnel from the sensor to the gateway, through which traffic to and from the backend can pass. I will use the gif facility in FreeBSD. This will create an IP-in-IP tunnel, which I will then wrap inside IPSec ESP.

The monitoring backend will communicate with 10.4.12.10 when it needs to talk to the sensor. The sensor will communicate with 192.168.1.10 when it needs to talk to the backend. The gateway will take care of connecting the two endpoints.

The first step is to recompile the kernels of the sensor and gateway to suit their roles. Here is what I add to the sensor's kernel config file before recompiling the kernel:

options FAST_IPSECdevice crypto

Here is what I add to the gateway's kernel config file before recompiling the kernel. The last two lines are completely optional, but the IPFIREWALL_DEFAULT_TO_ACCEPT means I don't need to add rules to permit later traffic:

The gifconfig statement defines the public IPs used as the tunnel endpoints. The ifconfig_gif0 statement sets up the tunnel, with 10.4.12.10 as the local endpoint and 10.4.12.1 as the remote endpoint. The static_routes and route_gif0_0 statements tell the sensor how to reach the backend network.

First I tell the gateway to act as a gateway, and I enable the firewall. I also enable NAT, with em0 being the Internet-facing interface with the external public IP address. I have commented out a natd_flags line showing how to do port forwarding. For example, a connection to port 8080 TCP on the gateway's external IP would be sent to the internal system 192.168.1.10.

Next I set up the gif interfaces for this end of the tunnel. They are mirror images of the entries for the sensor.

Note that in both cases I have commented out ipsec_enable="YES" for the moment. It is important to get a working IPSec configuration before one enables ipsec_enable="YES" in /etc/rc.conf. If you reboot a system with ipsec_enable="YES" uncommented, and your /etc/ipsec.conf configuration file is faulty, the system will not completely boot up. You will end up needing physical access to the system or remote serial access to fix the problem.

We have done enough at this point to try sending traffic without using IPSec, but with the gif tunnel. To create the gif interface manually on the sensor, use syntax like this:

Now that the traffic is being passed appropriately, we need to apply IPSec ESP to it. We create the following /etc/ipsec.conf file on the sensor. All of the spdadd statements should occupy a single unbroken line:

The first two lines flush IPSec Security Association Database (SAD) entries and Security Policy Database (SPD) entries. The first spdadd statement says traffic sent out from 10.4.12.10 to 10.4.12.1 should go via the IPSec tunnel from 18.235.153.37 to 78.172.25.27.

The second spdadd statement says traffic sent in from 10.4.12.1 to 10.4.12.10 should go via the IPSec tunnel from 78.172.25.27 to 18.235.153.37. These two entries are enough to protect traffic sent between the sensor and gateway.

The third spdadd statement says traffic sent out from 10.4.12.10 to the 192.168.1.0/24 network should go via the IPSec tunnel from 18.235.153.37 to 78.172.25.27.

The fourth spdadd statement says traffic sent in from the 192.168.1.0/24 network to 10.4.12.10 should go via the IPSec tunnel from 78.172.25.27 to 18.235.153.37. These two entries protect traffic sent between the sensor and the backend.

The /etc/ipsec.conf file on the gateway is a mirror image of the sensor's /etc/ipsec.conf:

Now that the /etc/ipsec.conf files are ready, the last step is to install a program to manage key negotiations. We'll use Racoon, which can be installed via the security/racoon port. First, I made this change to the /usr/local/etc/racoon/racoon.conf file on the sensor to tell Racoon where to listen for key exchange packets:

listen{ isakmp 18.235.153.37 [500];}

On the gateway, the modification looks like this:

listen{ isakmp 78.172.25.27 [500];}

Now, both public IP endpoints are listening on port 500 UDP for key exchange traffic.

Next I enabled a secret key. On the sensor, /usr/local/etc/racoon/psk.txt looks like this, which says use the specified key with the gateway 78.172.25.27:

78.172.25.27 thisisabadsecret

On the gatewat, /usr/local/etc/racoon/psk.txt looks like this, which says use the specified key with the sensor 18.235.153.37:

18.235.153.37 thisisabadsecret

Make sure the permissions for the /usr/local/etc/racoon/psk.txt file are 600, or the Racoon daemon will complain.

Now we are ready to start up Racoon, and enable IPSec. I recommend starting Racoon on the sensor and gateway in separate windows, using the 'racoon -F' syntax to show Racoon running in the foreground. Next enable IPSec via 'setkey -f /etc/ipsec.conf' on each system.

You can test your IPSec tunnel by pinging the gateway's gif IP address from the sensor:

That's it. You will also be able to ping the backend (192.168.1.10) from the sensor, or ping the sensor (10.4.12.10) from the backend. It will all be encrypted via IPSec.

If you've been trying to deploy IPSec on FreeBSD, and have followed certain threads, you'll see I encountered no issues with enabling FAST_IPSEC and INET6 in the kernel. I also did not have to exempt port 500 UDP key exchange traffic in my /etc/ipsec.conf file. Those two problems seem to have been ironed out with FreeBSD 5.3 RELEASE.

8 comments:

Anonymous
said...

I've tried IPSEC, but found it difficult to setup, especially when some IP addresses are dynamic or behind other private networks (which may block IPSEC).

Instead, OpenVPN (/usr/ports/security/openvpn) proved to be a much easier and reliable solution. It handles certificate verification, key exchange, authentication, encryption and tunneling. It uses UDP (or optionally TCP) for all communication.

I always wonder why people always initialize a gif-tunnel for ipsec traffic, while it functions perfectly without it.

There are only two reasons why you would use it: 1) FreeBSD does not support NAT-T (for example openbsd and linux both do) and you need NAT 2) routing, openbsd adds ipsec tunnels to its routing tables afaik