FreeBSD Handbook
Prev | Chapter 15 Security | Next

# 15.9 VPN over IPsec

Written by Nik Clayton.

Creating a VPN between two networks, separated by the Internet, using FreeBSD gateways.

## 15.9.1 Understanding IPsec

Written by Hiten M. Pandya.

This section will guide you through the process of setting up IPsec. In order to set up IPsec, it is necessary that you are familiar with the concepts of building a custom kernel (see Chapter 9).

IPsec is a protocol which sits on top of the Internet Protocol (IP) layer. It allows two or more hosts to communicate in a secure manner (hence the name). The FreeBSD IPsec network stack is based on the KAME implementation, which has support for both protocol families, IPv4 and IPv6.

IPsec consists of two sub-protocols:

- Encapsulated Security Payload ESP), protects the IP packet data from third party interference, by encrypting the contents using symmetric cryptography algorithms (like Blowfish, 3DES).

- Authentication Header (AH), protects the IP packet header from third party interference and spoofing, by computing a cryptographic checksum and hashing the IP packet header fields with a secure hashing function. This is then followed by an additional header that contains the hash, to allow the information in the packet to be authenticated.

ESP and AH can either be used together or separately, depending on the environment.

IPsec can either be used to directly encrypt the traffic between two hosts (known as Transport Mode); or to build virtual tunnels between two subnets, which could be used for secure communication between two corporate networks (known as Tunnel Mode). The latter is more commonly known as a Virtual Private Network (VPN). The ipsec(4) manual page should be consulted for detailed information on the IPsec subsystem in FreeBSD.

To add IPsec support to your kernel, add the following options to your kernel configuration file:

    options   IPSEC        #IP security
    device    crypto

If IPsec debugging support is desired, the following kernel option should also be added:

    options   IPSEC_DEBUG  #debug for IP security

## 15.9.2 The Problem

There is no standard for what constitutes a VPN. VPNs can be implemented using a number of different technologies, each of which have their own strengths and weaknesses. This section presents a scenario, and the strategies used for implementing a VPN for this scenario.

## 15.9.3 The Scenario: Two networks, one home based and one corporate based. Both are connected to the Internet, and expected, via this VPN to behave as one.

The premise is as follows:

- You have at least two sites

- Both sites are using IP internally

- Both sites are connected to the Internet, through a gateway that is running FreeBSD.

- The gateway on each network has at least one public IP address.

- The internal addresses of the two networks can be public or private IP addresses, it does not matter. They just may not collide; e.g.: may not both use `192.168.1.x`.

## 15.9.4 Configuring IPsec on FreeBSD

Written by Tom Rhodes.

To begin, the `security/ipsec-tools` must be installed from the Ports Collection. This third party software package provides a number of applications which will help support the configuration.

The next requirement is to create two gif(4) pseudo-devices which will be used to tunnel packets and allow both networks to communicate properly. As `root`, run the following commands, replacing the `internal` and `external` items with the real internal and external gateways:

    # ifconfig gif0 create

    # ifconfig gif0 internal1 internal2

    # ifconfig gif0 tunnel external1 external2

For example, the corporate LAN's public IP is `172.16.5.4` having a private IP of `10.246.38.1`. The home LAN's public IP is `192.168.1.12` with an internal private IP of `10.0.0.5`.

This may seem confusing, so review the following example output from the ifconfig(8) command:

    Gateway 1:

    gif0: flags=8051 mtu 1280
    tunnel inet 172.16.5.4 --> 192.168.1.12
    inet6 fe80::2e0:81ff:fe02:5881%gif0 prefixlen 64 scopeid 0x6
    inet 10.246.38.1 --> 10.0.0.5 netmask 0xffffff00

    Gateway 2:

    gif0: flags=8051 mtu 1280
    tunnel inet 192.168.1.12 --> 172.16.5.4
    inet 10.0.0.5 --> 10.246.38.1 netmask 0xffffff00
    inet6 fe80::250:bfff:fe3a:c1f%gif0 prefixlen 64 scopeid 0x4

Once complete, both private IPs should be reachable using the ping(8) command like the following output suggests:

    priv-net# ping 10.0.0.5
    PING 10.0.0.5 (10.0.0.5): 56 data bytes
    64 bytes from 10.0.0.5: icmp_seq=0 ttl=64 time=42.786 ms
    64 bytes from 10.0.0.5: icmp_seq=1 ttl=64 time=19.255 ms
    64 bytes from 10.0.0.5: icmp_seq=2 ttl=64 time=20.440 ms
    64 bytes from 10.0.0.5: icmp_seq=3 ttl=64 time=21.036 ms
    --- 10.0.0.5 ping statistics ---
    4 packets transmitted, 4 packets received, 0% packet loss
    round-trip min/avg/max/stddev = 19.255/25.879/42.786/9.782 ms

    corp-net# ping 10.246.38.1
    PING 10.246.38.1 (10.246.38.1): 56 data bytes
    64 bytes from 10.246.38.1: icmp_seq=0 ttl=64 time=28.106 ms
    64 bytes from 10.246.38.1: icmp_seq=1 ttl=64 time=42.917 ms
    64 bytes from 10.246.38.1: icmp_seq=2 ttl=64 time=127.525 ms
    64 bytes from 10.246.38.1: icmp_seq=3 ttl=64 time=119.896 ms
    64 bytes from 10.246.38.1: icmp_seq=4 ttl=64 time=154.524 ms
    --- 10.246.38.1 ping statistics ---
    5 packets transmitted, 5 packets received, 0% packet loss
    round-trip min/avg/max/stddev = 28.106/94.594/154.524/49.814 ms

As expected, both sides have the ability to send and receive ICMP packets from the privately configured addresses. Next, both gateways must be told how to route packets in order to correctly send traffic from either network. The following command will achieve this goal:

    # corp-net# route add 10.0.0.0 10.0.0.5 255.255.255.0

    # corp-net# route add net 10.0.0.0: gateway 10.0.0.5

    # priv-net# route add 10.246.38.0 10.246.38.1 255.255.255.0

    # priv-net# route add host 10.246.38.0: gateway 10.246.38.1

At this point, internal machines should be reachable from each gateway as well as from machines behind the gateways. This is easily determined from the following example:

    corp-net# ping 10.0.0.8
    PING 10.0.0.8 (10.0.0.8): 56 data bytes
    64 bytes from 10.0.0.8: icmp_seq=0 ttl=63 time=92.391 ms
    64 bytes from 10.0.0.8: icmp_seq=1 ttl=63 time=21.870 ms
    64 bytes from 10.0.0.8: icmp_seq=2 ttl=63 time=198.022 ms
    64 bytes from 10.0.0.8: icmp_seq=3 ttl=63 time=22.241 ms
    64 bytes from 10.0.0.8: icmp_seq=4 ttl=63 time=174.705 ms
    --- 10.0.0.8 ping statistics ---
    5 packets transmitted, 5 packets received, 0% packet loss
    round-trip min/avg/max/stddev = 21.870/101.846/198.022/74.001 ms

    priv-net# ping 10.246.38.107
    PING 10.246.38.1 (10.246.38.107): 56 data bytes
    64 bytes from 10.246.38.107: icmp_seq=0 ttl=64 time=53.491 ms
    64 bytes from 10.246.38.107: icmp_seq=1 ttl=64 time=23.395 ms
    64 bytes from 10.246.38.107: icmp_seq=2 ttl=64 time=23.865 ms
    64 bytes from 10.246.38.107: icmp_seq=3 ttl=64 time=21.145 ms
    64 bytes from 10.246.38.107: icmp_seq=4 ttl=64 time=36.708 ms
    --- 10.246.38.107 ping statistics ---
    5 packets transmitted, 5 packets received, 0% packet loss
    round-trip min/avg/max/stddev = 21.145/31.721/53.491/12.179 ms

Setting up the tunnels is the easy part. Configuring a secure link is a much more in depth process. The following configuration uses pre-shared (PSK) RSA keys. Aside from the IP addresses, both `/usr/local/etc/racoon/racoon.conf` files will be identical and look similar to

    path    pre_shared_key  "/usr/local/etc/racoon/psk.txt"; #location of pre-shared key file
    log     debug;	#log verbosity setting: set to 'notify' when testing and debugging is complete

    padding	# options are not to be changed
    {
            maximum_length  20;
            randomize       off;
            strict_check    off;
            exclusive_tail  off;
    }

    timer	# timing options. change as needed
    {
            counter         5;
            interval        20 sec;
            persend         1;
    #       natt_keepalive  15 sec;
            phase1          30 sec;
            phase2          15 sec;
    }

    listen	# address [port] that racoon will listening on
    {
            isakmp          172.16.5.4 [500];
            isakmp_natt     172.16.5.4 [4500];
    }

    remote  192.168.1.12 [500]
    {
            exchange_mode   main,aggressive;
            doi             ipsec_doi;
            situation       identity_only;
            my_identifier   address 172.16.5.4;
            peers_identifier        address 192.168.1.12;
            lifetime        time 8 hour;
            passive         off;
            proposal_check  obey;
    #       nat_traversal   off;
            generate_policy off;

                            proposal {
                                    encryption_algorithm    blowfish;
                                    hash_algorithm          md5;
                                    authentication_method   pre_shared_key;
                                    lifetime time           30 sec;
                                    dh_group                1;
                            }
    }

    sainfo  (address 10.246.38.0/24 any address 10.0.0.0/24 any)	# address $network/$netmask $type address $network/$netmask $type ( $type being any or esp)
    {								# $network must be the two internal networks you are joining.
            pfs_group       1;
            lifetime        time    36000 sec;
            encryption_algorithm    blowfish,3des,des;
            authentication_algorithm        hmac_md5,hmac_sha1;
            compression_algorithm   deflate;
    }

Explaining every available option, along with those listed in these examples is beyond the scope of this document. There is plenty of relevant information in the racoon configuration manual page.

The SPD policies need to be configured so FreeBSD and racoon is able to encrypt and decrypt network traffic between hosts.

This task may be undertaken with a simple shell script similar to the following which is on the corporate gateway. This file will be used during system initialization and should be saved as `/usr/local/etc/racoon/setkey.conf`.

    flush;
    spdflush;
    # To the home network
    spdadd 10.246.38.0/24 10.0.0.0/24 any -P out ipsec esp/tunnel/172.16.5.4-192.168.1.12/use;
    spdadd 10.0.0.0/24 10.246.38.0/24 any -P in ipsec esp/tunnel/192.168.1.12-172.16.5.4/use;

Once in place, racoon may be started on both gateways using the following command:

    # /usr/local/sbin/racoon -F -f /usr/local/etc/racoon/racoon.conf -l /var/log/racoon.log

The output should be similar to the following:

    corp-net# /usr/local/sbin/racoon -F -f /usr/local/etc/racoon/racoon.conf
    Foreground mode.
    2006-01-30 01:35:47: INFO: begin Identity Protection mode.
    2006-01-30 01:35:48: INFO: received Vendor ID: KAME/racoon
    2006-01-30 01:35:55: INFO: received Vendor ID: KAME/racoon
    2006-01-30 01:36:04: INFO: ISAKMP-SA established 172.16.5.4[500]-192.168.1.12[500] spi:623b9b3bd2492452:7deab82d54ff704a
    2006-01-30 01:36:05: INFO: initiate new phase 2 negotiation: 172.16.5.4[0]192.168.1.12[0]
    2006-01-30 01:36:09: INFO: IPsec-SA established: ESP/Tunnel 192.168.1.12[0]->172.16.5.4[0] spi=28496098(0x1b2d0e2)
    2006-01-30 01:36:09: INFO: IPsec-SA established: ESP/Tunnel 172.16.5.4[0]->192.168.1.12[0] spi=47784998(0x2d92426)
    2006-01-30 01:36:13: INFO: respond new phase 2 negotiation: 172.16.5.4[0]192.168.1.12[0]
    2006-01-30 01:36:18: INFO: IPsec-SA established: ESP/Tunnel 192.168.1.12[0]->172.16.5.4[0] spi=124397467(0x76a279b)
    2006-01-30 01:36:18: INFO: IPsec-SA established: ESP/Tunnel 172.16.5.4[0]->192.168.1.12[0] spi=175852902(0xa7b4d66)

To ensure the tunnel is working properly, switch to another console and use tcpdump(1) to view network traffic using the following command. Replace `em0` with the network interface card as required.

    # tcpdump -i em0 host 172.16.5.4 and dst 192.168.1.12

Data similar to the following should appear on the console. If not, there is an issue, and debugging the returned data will be required.

    01:47:32.021683 IP corporatenetwork.com > 192.168.1.12.privatenetwork.com: ESP(spi=0x02acbf9f,seq=0xa)
    01:47:33.022442 IP corporatenetwork.com > 192.168.1.12.privatenetwork.com: ESP(spi=0x02acbf9f,seq=0xb)
    01:47:34.024218 IP corporatenetwork.com > 192.168.1.12.privatenetwork.com: ESP(spi=0x02acbf9f,seq=0xc)

At this point, both networks should be available and seem to be part of the same network. Most likely both networks are protected by a firewall, as they should be. To allow traffic to flow between them, rules need to be added to pass packets back and forth. For the ipfw(8) firewall, add the following lines to the firewall configuration file:

    ipfw add 00201 allow log esp from any to any
    ipfw add 00202 allow log ah from any to any
    ipfw add 00203 allow log ipencap from any to any
    ipfw add 00204 allow log udp from any 500 to any

> Note: The rule numbers may need to be altered depending on the current host configuration.

For users of pf(4) or ipf(8), the following rules should do the trick:

    pass in quick proto esp from any to any
    pass in quick proto ah from any to any
    pass in quick proto ipencap from any to any
    pass in quick proto udp from any port = 500 to any port = 500
    pass in quick on gif0 from any to any
    pass out quick proto esp from any to any
    pass out quick proto ah from any to any
    pass out quick proto ipencap from any to any
    pass out quick proto udp from any port = 500 to any port = 500
    pass out quick on gif0 from any to any

Finally, to allow the machine to start support for the VPN during system initialization, add the following lines to `/etc/rc.conf`:

    ipsec_enable="YES"
    ipsec_program="/usr/local/sbin/setkey"
    ipsec_file="/usr/local/etc/racoon/setkey.conf" # allows setting up spd policies on boot
    racoon_enable="yes"

Prev | Home | Next
OpenSSL | Up | OpenSSH