...making Linux just a little more fun!

<-- prev | next -->

Digging More Secure Tunnels with IPsec

By René Pfeiffer

Introduction

In my last article about IPsec we learned about the building blocks of IPsec in the Linux kernel. We took a look at the tools needed to build encrypted and secured data paths between two hosts. Now we'll use this knowledge and move a step further on.

IPsec is often used to connect two or more different networks by using tunnels. If you have a complex setup, then exchanging keys manually and keeping track of them can get very challenging. IPsec offers a way of handling keys automatically. Let's take a look how this works.

Entering the Tunnels

We already know how to encrypt all data between two hosts. We created the keys and put them into the setkey.conf file, where we also configured our security policy that tells the kernel to use IPsec between the IP addresses of our hosts. When using IPsec for remote access purposes, it is far more useful to reach a whole network of hosts than only one IP address. You can do this by using IPsec's tunnel mode. Let's assume that we have two networks named A and B. Both are "behind" two gateways named after the nearest connected network. The classical way to connect both networks is to install a route on both gateways. This means that every packet between network A and B travels unencrypted through the direct connection.

Illustration of IPsec tunnel between two networks

We want to use IPsec instead. This means that both gateways still need to see each other, and we still have the direct connection. In addition to that, we get a "second" way our packets can travel. We call this connection a virtual connection or tunnel. Only the tunnel holds our packets travelling between network A and B. They are encapsulated into ESP or AH packets. The direct connection holds the corresponding IPsec packets (i.e. the AH and ESP packets itself). You have to keep this in mind when creating configurations. Compared to the two single host scenario we have more addresses involved. We need the gateway and the network we want to connect to. If you know this information, then you can begin to create an appropriate entry for the gateways' Security Policy Databases (SPD).
#!/usr/sbin/setkey -f
#
# SPD for gateway A (172.16.72.1)
#
spdadd 192.168.1.0/24 10.42.23.0/24 any -P out ipsec
       esp/tunnel/172.16.72.1-172.16.72.254/require
       ah/tunnel/172.16.72.1-172.16.72.254/require;

spdadd 10.42.23.0/24 192.168.1.0/24 any -P in ipsec
       esp/tunnel/172.16.72.254-172.16.72.1/require
       ah/tunnel/172.16.72.254-172.16.72.1/require;
Let's start with the first spdadd line. It tells the kernel the following: if you see a outbound packet going from our network 192.168.1.0/24 to the network 10.42.23.0/24, then use IPsec encapsulation and transport the encapsulated data from our external address 172.16.72.1 to the machine with the address 172.16.72.254. The keyword require tells the kernel that IPsec encapsulation is mandatory. The second line defines how to handle the return traffic. Rephrased it says: if you see an IPsec encapsulated inbound packet coming from the network 10.42.23.0/24 and going to our network 192.168.1.0/24 and this packet is coming from the gateway 172.16.72.254 to our external address, then undo the IPsec encapsulation. These are the policies we need for our tunnel. It sounds complicated, but if you take the diagram and trace the packet flow, you will see that it is just a description of what the kernel should do. We now need the keys. We'll reuse the ones from the last article.
# AH SAD entries with 160 bit keys
add 172.16.72.254 172.16.72.1 ah 0x200 -A hmac-sha1 0x46915c30ed7e2465b42861b6ab19f2772813020c;
add 172.16.72.1 172.16.72.254 ah 0x300 -A hmac-sha1 0xc4dac594f8228e0b94a54758f7fbf2fdf4e37f3e;

# ESP SAD entries with 192 bit keys
add 172.16.72.254 172.16.72.1 esp 0x201 -E rijndael-cbc 0xa3993b3dfc41ef0a1aa8d168a8bf2c27e48249ac17b61e09;
add 172.16.72.1 172.16.72.254 esp 0x301 -E rijndael-cbc 0x8f6498928ba354bd45cfad147f54c67b3b742896b3bafc02;
Again this tells the kernel which keys to use when to go from one gateway to another. You have one line for outbound traffic and one for inbound traffic. Now go ahead and create the configuration on both gateways. You can create one setkey.conf and mirror source and destination for the other gateway since packet flows are symmetrically reversed. Enter the command
setkey -f /path/to/setkey.conf
on both gateways and try pinging, telnetting or tracerouting to the network "on the other side".

One word about the example setup: I set the gateways' IP addresses so that they're near each other in IP space; this is something you need to do when routing with directly-connected gateways. When using IPsec tunnels, the gateways don't have to be physically connected, and you can create IPsec tunnels between any hosts and networks that "see" each other on layer 3 (IP in our case).

Routing and the Kernel Policy

Maybe you have noticed that we didn't set any routes to the networks we connected. We don't need to. We told the kernel already what to do with the packets. The Security Policy Database takes care of the packets' path. This behaviour has some implications you have to consider. First, whenever using IPsec tunnels your networks can be "contaminated" by packets with an origin IP outside your network. This is actually what you usually want, but it is very important to consider it in order to implement good access control. If a server in network B only expects and allows connections to be from 10.42.23.0/24, then clients in network A cannot access these services. This can be either good or bad. In any case you have to be aware (and possibly take care) of that. This leads to the second consideration - security. When building one or lots of VPN tunnels, you have to be careful where your endpoints are and what networks they can see.

Automatic Keying and X.509 Certificates

Digging multiple tunnels, dealing with many IPsec clients and keeping track of the keys is a big problem. Consider a gateway that expects IPsec connections from 10 other systems. Then the setkey.conf gets a bit crowded and no one will want to maintain long hexadecimal numbers. In addition to that, pre-shared keys are best used with fixed IP addresses. If you obtain IP addresses dynamically, then you have to think of something else.

Fortunately there is a solution for this problem. The Internet Security Association and Key Management Protocol (ISAKMP) is part of IPsec. It was designed "for establishing Security Associations (SA) and cryptographic keys in an Internet environment", to quote the RFC. It can help us with exchanging keys and creating security policies for the Linux kernel. Key exchange and creation of an IPsec connection are broken up into different phases. During phase 1 the two IPsec partners also check whether they have the right keys to talk to each other. The real data transmission starts after phase 2 is completed.

In order to use ISAKMP you have to configure the racoon daemon. It is also part of the ipsec-tools package. Its configuration file is typically found at /etc/racoon/racoon.conf. We will recreate the tunnel above with racoon in order to get to know the most important configuration directives.

While we are at it, we will swap the pre-shared keys for X.509 certificates. This makes life easier when maintaining multiple keys or issuing VPN access to clients. The certificates are used in the same familiar way as SSL certificates on the World Wide Web, a combination also known as HTTPS (encrypted HTTP). Instead of creating long strings, you simply create a self-signed SSL certificate. Your IPsec gateway(s) check these certificates against the public key of your own Certificate Authority (CA). Every certificate signed by your own CA opens a IPsec tunnel, just like a key would. You can now give these certified keys to every host that is allowed to talk IPsec to you. While this sounds more complicated, it really gets easier when dealing with a lot of IPsec clients.

Automatic Keying in Action

Let's rebuild the last example with racoon. It starts like this:

# racoon.conf file for gateway A
#
path certificate "/etc/racoon/certs";
path pre_shared_key "/etc/racoon/psk.txt";
log notify;
listen {
	isakmp 172.16.72.1 [500];
};
The first directive tells racoon where to look for certificates and certified keys. In our case, this is the directory /etc/racoon/certs. Then we tell the daemon where to look for a collection of pre-shared keys (PSKs). You can use PSKs as well, you don't need to use certificates. The file /etc/racoon/psk.txt holds a list of unique identifiers (such as hostnames or IP addresses) and their corresponding keys.

The next line sets the log level. The log level can be either notify, debug or debug2. Increase the log level if you want to see how racoon creates the IPsec connection. All logging goes to syslog. The listen directive tells racoon where to listen for ISAKMP requests. By default it listens on all devices and on port 500/UDP (the default port for ISAKMP). So far, so good. Now we define the path to gateway B.

remote 172.16.72.254 {
	exchange_mode main;
	generate_policy off;
	passive off;
	certificate_type x509 "gateway.a.example.net.cert" "gateway.a.example.net.key";
	ca_type x509 "ca-cert.pem";
	my_identifier asn1dn;
	peers_identifier asn1dn;
	verify_identifier on;
	proposal {
		encryption_algorithm 3des;
		hash_algorithm sha1;
		authentication_method rsasig;
		dh_group modp1024;
	}
}
Let's walk through the options and see what they mean. The man page of racoon.conf has a full list of options and parameters. My example boils down to the bare bones. Keep in mind that we need the configuration above for phase 1 of our connection. We still need to define the security policy for phase 2. This is done in a separate block.
sainfo address 192.168.1.0/24 any address 10.42.23.0/24 any {
        pfs_group modp1024;
        encryption_algorithm aes;
        authentication_algorithm hmac_sha1;
        lifetime time 28800 sec;
        compression_algorithm deflate;
}
The first line announces that the policy is valid for all packets originating in the 192.168.1.0/24 network and going to the 10.42.23.0/24 network. We don't need to define the return path policy since our peer already encapsulates inbound packets with IPsec. That is the whole racoon.conf configuration. You need to have another on gateway B. Make sure that the IP address in the remote section corresponds to the correct peer and that the networks in the sainfo section are reversed. As soon as you have everything in place you can test the setup. Start the racoon daemon on both gateways. On most systems you can do this by issueing the command:
/etc/init.d/racoon start
Check the logs. Most probably not much will happen. The IPsec connection will be initiated by the gateway with the option passive off enabled. Use a client on this gateway's network and create some traffic to the network you wish to connect to via the tunnel. After the first packets are sent, the racoon daemon will start ISAKMP and negotiate through phase 1 and 2. If everything goes well, you can send your first ping packets through the tunnel. The tunnel may needs some seconds to come up. You will get messages such as "resource temporarily unavailable" if the tunnel is not yet ready.

Mobile Tunnels for Roadwarriors

IPsec tunnels are frequently used to connect mobile clients "on the road". They connect to a central gateway, sometimes called a VPN or an IPsec server, and create a secure tunnel. You can use our racoon.conf files with some modifications. Your IPsec server needs to be in passive mode since it's waiting for incoming connections. The connections can originate from anywhere, so your peer will be anonymous.

remote anonymous {
	exchange_mode main;
	generate_policy on;
	passive on;
	certificate_type x509 "vpnserf.example.net.cert" "vpnserf.example.net.key";
	ca_type x509 "ca-cert.pem";
	my_identifier asn1dn;
	peers_identifier asn1dn;
	verify_identifier on;
	proposal {
		encryption_algorithm 3des;
		hash_algorithm sha1;
		authentication_method rsasig;
		dh_group modp1024;
	}
}
The rest of the options can stay the same except for generate_policy and passive. Both need to be enabled. generate_policy tells racoon to create new policies for new connections; this makes sense because our peer varies. The same considerations need to be applied to the security policy in phase 2.
sainfo anonymous {
        pfs_group modp1024;
        encryption_algorithm aes;
        authentication_algorithm hmac_sha1;
        lifetime time 1 hour;
        compression_algorithm deflate;
}
It is a good idea to reduce the lifetime for mobile peers. Again, the parameter anonymous lets racoon accept varying policies. The configuration of the mobile client has to specify the IPsec server's address and the security policy to our internal networks.

Next time we will take a look at filtering IPsec traffic and protecting exposed IPsec servers. Happy digging!

Further Reading

Talkback: Discuss this article with The Answer Gang


[BIO]

René was born in the year of Atari's founding and the release of the game Pong. Since his early youth he started taking things apart to see how they work. He couldn't even pass construction sites without looking for electrical wires that might seem interesting. The interest in computing began when his grandfather bought him a 4-bit microcontroller with 256 byte RAM and a 4096 byte operating system, forcing him to learn assembler before any other language.

After finishing school he went to university in order to study physics. He then collected experiences with a C64, a C128, two Amigas, DEC's Ultrix, OpenVMS and finally GNU/Linux on a PC in 1997. He is using Linux since this day and still likes to take things apart und put them together again. Freedom of tinkering brought him close to the Free Software movement, where he puts some effort into the right to understand how things work. He is also involved with civil liberty groups focusing on digital rights.

Since 1999 he is offering his skills as a freelancer. His main activities include system/network administration, scripting and consulting. In 2001 he started to give lectures on computer security at the Technikum Wien. Apart from staring into computer monitors, inspecting hardware and talking to network equipment he is fond of scuba diving, writing, or photographing with his digital camera. He would like to have a go at storytelling and roleplaying again as soon as he finds some more spare time on his backup devices.


Copyright © 2006, René Pfeiffer. Released under the Open Publication license unless otherwise noted in the body of the article. Linux Gazette is not produced, sponsored, or endorsed by its prior host, SSC, Inc.

Published in Issue 126 of Linux Gazette, May 2006

<-- prev | next -->
Tux