IPsec is a protocol which sits on top of the Internet Protocol (IP) layer. IPsec allows communication between hosts in a secure manner. The FreeBSD IPsec based on the KAME implementation.

I’ll use FreeBSD 7.2 for this howto, before we start to configure IPsec we need to compile IPSEC module into FreeBSD kernel if you don’t know to recompile FreeBSD kernel then please follow this document. To enable IPsec support into your kernel, add the following options in kernel configuration file:

Options IPSEC           #IP security
Options IPSEC_DEBUG     #debug IP security
Device  crypto

Let’s draw the scenario which will be use throughout this tutorial

IPSec FreeBSD

In the above diagram two public IP addresses has been used (172.17.1.254, 172.18.1.254). We will use these IP addresses as a reference in the rest of this article. Anywhere you see those ip addresses, replace them with your own public IP address.

Note for the local network I used (192.168.1.x) for 172.17.1.254 and (192.168.2.x) for 172.18.1.254.

All the machines on (192.168.1.x) network side assigned 192.168.1.1 as a default gateway.

All machines on (192.168.2.x) network side assigned 192.168.2.1 as a default gateway.

Creating a “virtual” network link

We will use a “tunnel” between the two networks. The two “tunnel mouths” are the IP addresses 172.16.17.254 and 172.18.1.254, and the tunnel must be told the addresses of the private IP addresses that will be allowed to pass through it. The tunnel is used to transfer traffic with private IP addresses across the public Internet.

This tunnel is created by using the generic interface or gif devices on FreeBSD. As you can imagine, the gif interface on each gateway host must be configured with four IP addresses; two for the public IP addresses, and two for the private IP addresses.

FreeBSD 7.x already include gif support into kernel, but you can also do this by adding the line:

device gif

Now let’s create our first tunnel on the machine (172.17.1.254) you would run the following commands to configure the tunnel.

ifconfig gif0 create
ifconfig gif0 tunnel 172.17.1.254 172.18.1.254
ifconfig gif0 inet 192.168.1.1 192.168.2.1 netmask 255.255.255.0

On 2nd machine (172.18.1.254) you would run the following commands to configure the tunnel.

ifconfig gif0 create
ifconfig gif0 tunnel 172.18.1.254 172.17.1.254
ifconfig gif0 inet 192.168.2.1 192.168.1.1 netmask 255.255.255.0

Now check the gif0 interface either its up or not, you can run ifconfig command on both machines:

ifconfig gif0

On the machine(172.17.1.254) you would see this kind of information:

gif0: flags=8051< ,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1280
        tunnel inet 172.17.1.254 –> 172.18.1.254
        inet 192.168.1.1 –> 192.168.2.1 netmask 0xffffff00

In the above output you can see, a tunnel has been created between the two hosts 172.17.1.254 and 172.18.1.254. The traffic between 192.168.1.1 and 192.168.2.1 pass through this tunnel, latter on we will secure this tunnel through IPSec.

On the other end (172.18.1.254) you would see this:

gif0: flags=8051< ,POINTOPOINT,RUNNING,MULTICAST> metric 0 mtu 1280
        tunnel inet 172.18.1.254 –> 172.17.1.254
        inet 192.168.2.1 –> 192.168.1.1 netmask 0xffffff00

In the above output you can see, a tunnel has been created between the two hosts 172.18.1.254 and 172.17.1.254. The traffic between 192.168.2.1 and 192.168.1.1 pass through this tunnel.

A new entry will be added automatically in the routing table on both sides, which you can examine with the command netstat -rn. you would see a output like this.

netstat -rn
192.168.2.1    192.168.1.1   UH    1   1    gif0

Securing the link

We want to secure our both side tunnels for this we will be use IPsec. IPsec provides a mechanism for two hosts to agree on an encryption key to encrypt data between the two hosts.

Security associations and security policies are both maintained by the kernel, and can be modified by userland programs. However, before you can do this you must configure the kernel to support IPsec and the Encapsulated Security Payload (ESP) protocol. This is done by configuring a kernel with:

Options IPSEC           #IP security
Options IPSEC_DEBUG     #debug IP security
Device  crypto

We don’t need to do this again, as you know we already compiled these options into our kernel.

IPSec Configuration

There are a number of choices for daemons to manage security associations with FreeBSD. In this article we will describe how to use one of these, racoon – which is available from security/ipsec-tools in the FreeBSD Ports collection.

cd /usr/ports/security/ipsec-tools
make install clean

After the installation of ipsec-toosl create a directory /usr/local/etc/racoon

mkdir /usr/local/etc/racoon

The racoon software must be run on both sides. Each host it is configured with the IP address of the other end VPN, and a secret key (which we will create in /usr/local/etc/racoon/psk.txt.

Before configuring racoon.conf file we should create /etc/ipsec.conf keys on both machines.

Now let’s create a key on 172.17.1.254 machine.

vi /etc/ipsec.conf
spdadd 172.17.1.254/32 172.18.1.254/32 ipencap -P out ipsec
esp/tunnel/192.168.1.1-192.168.2.1/require;
spdadd 172.18.1.254/32 172.17.1.254/32 ipencap -P in ipsec
esp/tunnel/192.168.2.1-192.168.1.1/require;

On the 172.18.1.254 side.

vi /etc/ipsec.conf
spdadd 172.18.1.254/32 172.17.1.254/32 ipencap -P out ipsec
esp/tunnel/192.168.2.1-192.168.1.1/require;
spdadd 172.17.1.254/32 172.18.1.254/32 ipencap -P in ipsec
esp/tunnel/192.168.1.1-192.168.2.1/require;

The above racoon’s policies will encrypt all traffic following between the following hosts 172.17.1.254 & 172.18.1.254 and by using tunnel mode it will also provide protection(“authentication”) of the IP packet.

We need to enable ipsec in /etc/rc.conf on both sides so ipsec automatically run when machine is boot.

ipsec_enable="YES"
ipsec_file="/etc/ipsec.conf"

Now let’s configure racoon.conf on the machine A side (172.17.1.254).

vi /usr/local/etc/racoon/racoon.conf

path include "/usr/local/etc/racoon";
path pre_shared_key "/usr/local/etc/racoon/psk.txt";
# "padding" defines some padding parameters.
{
        maximum_length 20;      # maximum padding length.
        randomize off;          # enable randomize length.
        strict_check off;       # enable strict check.
        exclusive_tail off;     # extract last one octet.
}
listen
{
        isakmp 172.17.1.254 [500];
}
# Specify various default timers.
timer
{
        # These value can be changed per remote node.
        counter 5;              # maximum trying count to send.
        interval 20 sec;        # maximum interval to resend.
        persend 1;              # the number of packets per send.
        phase1 30 sec;
        phase2 15 sec;
}
remote anonymous
{
        exchange_mode main,aggressive;
        doi ipsec_doi;
        situation identity_only;
        my_identifier asn1dn;
        certificate_type x509 "my.cert.pem" "my.key.pem";
        nonce_size 16;
        initial_contact on;
        proposal_check obey;    # obey, strict, or claim
        proposal {
                encryption_algorithm 3des;
                hash_algorithm sha1;
                authentication_method rsasig;
                dh_group 2;
        }
}
remote 172.18.1.254 [500]
{
        exchange_mode aggressive,main;
        doi ipsec_doi;
        situation identity_only;
        nonce_size 16;
        lifetime time 1 min;    # sec,min,hour
        initial_contact on;
        proposal_check obey;
        proposal {
                encryption_algorithm blowfish;
                hash_algorithm sha1;
                authentication_method pre_shared_key;
                dh_group 2;
        }
}
sainfo anonymous
{
        pfs_group 2;
        encryption_algorithm 3des;
        authentication_algorithm hmac_sha1;
        compression_algorithm deflate;
}
sainfo address 172.17.1.254 any address 172.18.1.254 any
{
        pfs_group 1;
        lifetime time 3600 sec;
        encryption_algorithm blowfish;
        authentication_algorithm hmac_md5;
        compression_algorithm deflate;
}

As you can see I used isakmp 172.17.1.254 [500] on A side machine.

Thes listen section tells the demon what ip address and port to bind to. If you don’t specify anything under the Listen section, the daemon will attempt to bind port 500 on all available interfaces.

In the remote section I have defined Side B ip address (172.18.1.254) and tell the daemon to use shared-key authentication on both sides.

The remote section is for IKE phase 1 & the sainfo is for IKE phase 2.

Now we need to create psk.txt file for shared key. In this file we will define Side B ip address with shared password. The file permission should be 644 on both sides.

cd /usr/local/etc/racoon/
echo "172.18.1.254 shared_password" > psk.txt
chmod 644 psk.txt

Side A is configured we need to edit racoon.conf on Side B

Let’s configure /usr/local/etc/racoon/racoon.conf on the machine (172.18.1.254).

vi /usr/local/etc/racoon/racoon.conf

path include "/usr/local/etc/racoon";
path pre_shared_key "/usr/local/etc/racoon/psk.txt";
# "padding" defines some padding parameters.
{
        maximum_length 20;      # maximum padding length.
        randomize off;          # enable randomize length.
        strict_check off;       # enable strict check.
        exclusive_tail off;     # extract last one octet.
}
listen
{
        isakmp 172.18.1.254 [500];
}
# Specify various default timers.
timer
{
        # These value can be changed per remote node.
        counter 5;              # maximum trying count to send.
        interval 20 sec;        # maximum interval to resend.
        persend 1;              # the number of packets per send.
        phase1 30 sec;
        phase2 15 sec;
}
remote anonymous
{
        exchange_mode main,aggressive;
        doi ipsec_doi;
        situation identity_only;
        my_identifier asn1dn;
        certificate_type x509 "my.cert.pem" "my.key.pem";
        nonce_size 16;
        initial_contact on;
        proposal_check obey;    # obey, strict, or claim
        proposal {
                encryption_algorithm 3des;
                hash_algorithm sha1;
                authentication_method rsasig;
                dh_group 2;
        }
}
remote 172.17.1.254 [500]
{
        exchange_mode aggressive,main;
        doi ipsec_doi;
        situation identity_only;
        nonce_size 16;
        lifetime time 1 min;    # sec,min,hour
        initial_contact on;
        proposal_check obey;
        proposal {
                encryption_algorithm blowfish;
                hash_algorithm sha1;
                authentication_method pre_shared_key;
                dh_group 2;
        }
}
sainfo anonymous
{
        pfs_group 2;
        encryption_algorithm 3des;
        authentication_algorithm hmac_sha1;
        compression_algorithm deflate;
}
sainfo address 172.18.1.254 any address 172.17.1.254 any
{
        pfs_group 1;
        lifetime time 3600 sec;
        encryption_algorithm blowfish;
        authentication_algorithm hmac_md5;
        compression_algorithm deflate;
}

As you can see I used isakmp 172.18.1.254 [500] on 172.18.1.254 machine.

In the remote section I have defined Side A ip address (172.17.1.254)

Let’s create shared key authentication file on Side B machine. In this file we will define other Side A ip address and shared password

cd /usr/local/etc/racoon/
echo "172.17.1.254 shared_password" > psk.txt
chmod 644 psk.txt

Everything is configured and placed, we need to enable racoon in /etc/rc.conf on both sides and then start racoon daemon.

racoon_enable="YES"
racoon_flags="-l /var/log/racoon.log && echo -n ‘ racoon’"

Let’s start racoon on both machines.

/usr/local/etc/rc.d/racoon start

After starting the racoon daemon you can test your encrypted tunnel with setkey -DP, Let’s run this command on Side A (172.17.1.254)

setkey -DP

The ouput of above command should be like this.

172.18.1.254[any] 172.17.1.254[any] ip4
        in ipsec
        esp/tunnel/172.18.1.254-172.17.1.254/require
        spid=2 seq=1 pid=1144
        refcnt=1
172.17.1.254[any] 172.18.1.254[any] ip4
        out ipsec
        esp/tunnel/172.17.1.254-172.18.1.254/require
        spid=1 seq=0 pid=1144
        refcnt=1

Above output means you have successfully configred your IPSec on FreeBSD.

Let’s test our IPSec tunnel to ensure that everything is running fine. On Side A we will ping Side B internal ip address e.g.

ping 192.168.2.1

And get a response, you should be able to do the same thing on the other Side B machine.

However, you will not be able to reach other internal machines on both sides. This is because of the routing — although the gateway machines know how to reach one another, but they do not know how to reach the entire network.

To solve this problem we have to add a static route on each Sides. The command to do this on the first Side A (172.17.1.254)

route add 192.168.2.0 192.168.2.1

This says “In order to reach on the entire network 192.168.2.0, send the packets to the host 192.168.2.1”.

On Side B (17.18.1.254) would be:

route add 192.168.1.0 192.168.1.1

This says “In order to reach on the entire network 192.168.1.0, send the packets to the host 192.168.1.1”.

We need to set these entries in /etc/rc.conf so whenever computer is rebooting it’s automatically up and running.

On machine 172.17.1.254 add these entries in /etc/rc.conf

gif_interfaces="gif0"
gifconfig_gif0="172.17.1.254 17.18.1.254"
ifconfig_gif0="192.168.1.1 192.168.2.1 netmask 255.255.255.0"
static_routes="vpn"
route_vpn="192.168.2.0/24 192.168.2.1"

And machine 172.18.1.254 add these entries in /etc/rc.conf

gif_interfaces="gif0"
gifconfig_gif0="172.18.1.254 17.17.1.254"
ifconfig_gif0="192.168.2.1 192.168.1.1 netmask 255.255.255.0"
static_routes="vpn"
route_vpn="192.168.2.0/24 192.168.2.1"

Firewall Setup:

It is likely that you are running a firewall on both machines. In this regards you might want to allow all traffic between both networks, or you might want to include firewall rules that protect both ends of the VPN from one another.

If you are using pf then you will need to run this command on both gateway hosts.

pass in quick on gif0 from any to any keep state

It will allow all traffic between the two end points of the VPN

This solution was configured and test successfully on FreeBSD 7.2. But We do not assume any responsibility or we are not liable for any damage which may have been occurred tu to implementation of this solution.

Your comments on this post helpful for us to make more competent howtos for you.