Using racoon with IPSECKEY records on FreeBSD

Introduction

This is a HOWTO type of document accompanying my project Using DNS for IPsec authentication and the related patches to the racoon IKE server and its management command, racoonctl. Here I describe how you get it to work to the point of two FreeBSD nodes automatically authenticating each other and encrypting all IP traffic between them.

Please note that this is mostly a description on how to test the current state of the patches and for development work, not for actual use in production. I will continue to update this document to describe the current state of the patches.

Feel free to test and report back to Michael Cardell Widerkrantz.

As an extra service to readers this text also includes some early notes on how to compile under Linux. This is, if possible, even more experimental and may not ever work, pending my time and the continuation of this project.

Scenarios

There are three scenarios.

Scenario 1

Both peers are known by name but not by IP address nor by any keys before any traffic takes place.

A program, autosp, is started before any traffic takes place on both hosts.

autosp is given the names of both peers, queries DNS for A/AAAA records and the public keys involved, loads the keys into racoon and configures security policies.

Scenario 2

The host intending to initiate traffic queries for A/AAAA records and the query is trapped by a specially crafted DNS resolver which also queries for the peer's public key, loads the key into racoon and configures a security policy.

The other end is handed the initiating host's name as identification in the IKE dialogue and queries DNS for its public key.

Scenario 3

Both peers receive the other's name as identification during the IKE dialogue and queries DNS for the public key associated with that name.

Kernel support

Before you do anything else you need a kernel with IPsec support.

On FreeBSD this typically means you have to recompile your kernel. On the Linux system I have tried I didn't have to do anything to the kernel.

On my machine, the amd64 architecture, I did it like this:

# cd /usr/src/sys/amd64/conf
# cp GENERIC TOTORO
# mg TOTORO

and changed the defaults:

--- GENERIC 2012-01-03 04:27:06.000000000 +0100
+++ TOTORO  2012-02-07 09:10:37.000000000 +0100
@@ -19,7 +19,7 @@
 # $FreeBSD: release/9.0.0/sys/amd64/conf/GENERIC 227305 2011-11-07 13:40:54Z marius $

 cpu        HAMMER
-ident      GENERIC
+ident      TOTORO

 makeoptions    DEBUG=-g        # Build kernel with gdb(1) debug symbols

@@ -27,6 +27,7 @@
 options    PREEMPTION      # Enable kernel thread preemption
 options    INET            # InterNETworking
 options    INET6           # IPv6 communications protocols
+options    IPSEC           # IP security (requires device crypto)
 options    SCTP            # Stream Control Transmission Protocol
 options    FFS         # Berkeley Fast Filesystem
 options    SOFTUPDATES     # Enable FFS soft updates support
@@ -336,3 +337,6 @@
 device     snd_ich     # Intel, NVidia and other ICH AC'97 Audio
 device     snd_uaudio  # USB Audio
 device     snd_via8233 # VIA VT8233x Audio
+device     crypto      # core crypto support
+device     cryptodev   # /dev/crypto for access to h/w

The cryptodev line means I get access to my machine's nice AES-NI instructions.

Then I rebuilt and installed the kernel:

   # cd /usr/src
   # make buildkernel KERNCONF=TOTORO
   ...
   # make installkernel KERNCONF=TOTORO
   ...

and rebooted.

Build racoon

Clone my repository of ipsec-tools:

% git clone git://hack.org/ipsec-tools

If you prefer you can download ipsec-tools 0.8.0 and apply the latest released patch. See the project page for a link to the patch file.

Build ipsec-tools with configure --enable-adminport;make and, optionally, install it. That's it. Go on to Generating Keys.

Linux

You need the Linux kernel sources installed somewhere where the configure script can find it. This is usually done automatically with a little help from uname but can be specified with:

--with-kernel-headers=/lib/modules/<uname>/build/include

Other than that you run configure with

--disable-security-context --enable-adminport

Note that the kernel sources I tried, 3.2.12-1-ARCH from Arch Linux, are sprinkled with #warning lines and with the default -Werror in ipsec-tools this made the build fail. The easy way around this until I have figured out what to do with Linux support is to temporaraly disable -Werror.

The right thing in Linux apparently not to depend on the kernel source and its ever-changing API and instead isolate the interface and include the files in ipsec-tools, just like the warning message says.

With GCC 4.6.3 under Linux GCC seems to optimize away some fairly important data to the RSA key lookup functions. If you compile racoon without optimization it seems to work better. That is, remove -O2 from CFLAGS in src/racoon/Makefile. If anyone has any comment on what's happening here I would appreciate it. Please note that this occurs in the generic list handling that's been in ipsec-tools forever.

If you make it here the entire ipsec-tools should build with a make.

Racoon and racoonctl works under Linux. Setting policies with setkey works. The IKE dialogue works fine. SAs are established. ESP traffic is generated but then nothing happens. The other end doesn't recognize the traffic although the SPIs are correct. I don't know why yet. Any information you can find out about this is welcome.

Note that I so far have only experimented with one Arch Linux node speaking to a FreeBSD node, not Linux to Linux.

Generating keys

ipsec-tools comes with a tool called plainrsa-gen. If you didn't install ipsec-tools you can find the binary as src/racoon/plainrsa-gen.

Generate a key pair for each node:

% plainrsa-gen -b 4096 -f ipsec1.rsa
% plainrsa-gen -b 4096 -f ipsec2.rsa

Move or copy the keys for each peer into the directory indicated by the racoon configuration file, typically /usr/local/etc/racoon/certs on that node.

Configuring DNS

In the .rsa files you produced you will find the public key in the commented out "pubkey" line and duplicated in the commented out line beginning with "# : PUB". Insert the public key into your DNS zone without the first "0s" (which just marks it as BASE64).

If you don't want to cut and paste or want to include this in a script you can do something like:

head -1 foo.hack.org.rsa | cut -c 11-

In BIND's zone format the records should look like this:

ipsec1                  IN      IPSECKEY ( 10 0 2
                                           .
                                           AQOqVjnhO/eQdETVCH7r2am7yUixEcWUgyffNhdmTigiLS7VZODtxC2ii3ySKEOD6ADxykvgn6KVotSYmSq2kNAdmbElkMl3stAHE+E327vSkX3vKR3/l23Wrrv0iUlJFm+5WK1TJC6jTkjH8Fc0j77mdATGfmWYC4MdONuPUDMeEaMu9/XJm+X0ZWfGG/q1158smfkwsRvKh1KZCJLuGYv+Y85kRnMwnejtQRpDMZbjNr8dtS90MR7kGgQIJ/ctgc//MRjpTo8NTWncDp9KKvuNcBxsPPL08YTF/BkcG60SeeKD1jqjdnY+VRFkrv3BO0WyFEib/fuxeA2FJLYMYOuGG3n1KKSLckD7B5AfNOKKME94f+glY65M8njVVAIKahufUzp+DIW9AgURxuAwNoCiRpS3i+sh7lY/dT5AzbZ8Ewl2jgsdX/neAXCiOWrSMUZGyARNcvAsOleRk93pb0y43FEU8f8OcS027p+ZbBHVbHYbLFEX6eg3NbJso3LTL6Gx8SxWX8W6KvBjFf7sWO4ehEcXPJXJ+lsY/UQfAbeFlUTicQd83+rVuuHfJvbxYWx0Sia6bERlKRSYTWT4FBwH3AwMTLQRskWasyBTbLbk1KHkUBZv4Ts5J+pGzAVsbPLINLnb5/lifjZdPGca6yn+5mbkKdjPSyV0ZHYfqiO4qQ==

Of course, you must also have a corresponding A or AAAA record:

ipsec1                  IN      AAAA    2001:16d8:ffff:1::3

Do the same thing for ipsec2.

Dynamic DNS updates

If you don't have the ability to edit zone files of a DNS server you might be able to use dynamic DNS updates using DNS Update (RFC 2136) to publish both your IP address in A or AAAA records and your public key. The nsupdate program from BIND can be used.

Note that the syntax for the IPSECKEY record is slightly different compared to the BIND zone files. Here's a complete example how to update the IPSECKEY record for the host ipsec.d.hack.org:

nsupdate -v -k /path/to/the/secret.private <<EOF
server ns1.hack.org
zone d.hack.org
update delete ipsec.d.hack.org. IPSECKEY
update add ipsec.d.hack.org. 86400 IN IPSECKEY 10 0 2 \
                                          .         \
                                           AQOqVjnhO/eQdETVCH7r2am7yUixEcWUgyffNhdmTigiLS7VZODtxC2ii3ySKEOD6ADxykvgn6KVotSYmSq2kNAdmbElkMl3stAHE+E327vSkX3vKR3/l23Wrrv0iUlJFm+5WK1TJC6jTkjH8Fc0j77mdATGfmWYC4MdONuPUDMeEaMu9/XJm+X0ZWfGG/q1158smfkwsRvKh1KZCJLuGYv+Y85kRnMwnejtQRpDMZbjNr8dtS90MR7kGgQIJ/ctgc//MRjpTo8NTWncDp9KKvuNcBxsPPL08YTF/BkcG60SeeKD1jqjdnY+VRFkrv3BO0WyFEib/fuxeA2FJLYMYOuGG3n1KKSLckD7B5AfNOKKME94f+glY65M8njVVAIKahufUzp+DIW9AgURxuAwNoCiRpS3i+sh7lY/dT5AzbZ8Ewl2jgsdX/neAXCiOWrSMUZGyARNcvAsOleRk93pb0y43FEU8f8OcS027p+ZbBHVbHYbLFEX6eg3NbJso3LTL6Gx8SxWX8W6KvBjFf7sWO4ehEcXPJXJ+lsY/UQfAbeFlUTicQd83+rVuuHfJvbxYWx0Sia6bERlKRSYTWT4FBwH3AwMTLQRskWasyBTbLbk1KHkUBZv4Ts5J+pGzAVsbPLINLnb5/lifjZdPGca6yn+5mbkKdjPSyV0ZHYfqiO4qQ==
send
EOF

To make this work you need to have the DNS server, here ns1.hack.org, configured to accept dynamic updates and configured to authenticate the client.

Typically it looks something like this in named.conf:

key "ipsec.d.hack.org." {
  algorithm HMAC-MD5;
  secret "something rather secret"
}

// ...

zone "d.hack.org" {
  type master;
  file "dynamic/dynamic-hack.zone";
  notify yes;

  update-policy {
    grant * self * A AAAA IPSECKEY;
  };
};

This means that the host using the key called "ipsec.d.hack.org" can update A, AAAA and IPSECKEY records matching the key's name.

The keys used for authentication can either be symmetric or a public/private key pair.

For more abot this, see the Secure Dynamic DNS HOWTO.

Scenario 1

(Peer's with known names.)

Use this racoon configuration: racoon-ipseckey1.conf

The secret is really just this:

peers_certfile plain_rsa;

which configures racoon to accept bare RSA public keys as the peer's key but doesn't configure any keys to load. That is done later by autosp.pl.

Start racoon (you need to run as root):

# ./racoon -F -f racoon-ipseckey1.conf

You also need autosp.pl. You will find it in this repository:

% git clone git://hack.org/ns

Note that autosp.pl needs to have racoonctl in $PATH.

Run it with the names of the peers as arguments, your local peer first:

# ./autosp.pl ipsec2 ipsec1

Do the same thing on the peer.

Try initiating traffic. An IKE dialogue should take place and you should have authenticated nodes encrypting traffic between them.

Scenario 2

(Magic resolver trapping A/AAAA queries on initiating side.)

Use this racoon configuration: racoon-ipseckey.conf. Edit it to fit the host.

Clone the repository with the DNS resolver, ns.pl:

% git clone git://hack.org/ns

Out of the box the ns.pl program supposes that your node's hostname is set to an FQDN and has an A and/or AAAA record in DNS. If not, check the $hostname variable in ns.pl.

It also requires that you edit /etc/ns-resolv.conf to point to your full resolver.

If you want to run it for real, change $port to 53.

Nota bene: To be able to set security policies and to execute racoonctl to talk to racoon you have to run ns.pl as root.

You can run the IKE daemon manually in the foreground (to see the pretty debug messages) like this:

# ./racoon -F -f ~mc/racoon-ipseckey.conf

If you want racoon to be extremely chatty about what it does, try -d and repeat it for even more verbosity.

Then start the resolver, given that you have configured it to listen to port 53:

# ./ns.pl

Note that you need to run all this as root.

Then try to connect to the peer by using something that queries DNS, for instance:

% ping6 ipsec1

if you're running ns.pl on ipsec2.

This should trigger a lot of action in ns.pl and then in racoon.

Linux users: Note that using ping6 (ICMPv6 Echo) doesn't seem to trigger the IKE dialouge like it does in FreeBSD. Try sending some TCP instead.

You should see debug messages that ns.pl queried for an IPSECKEY and that it loaded the key into racoon. Racoon should report that the key was loaded and that an IKE dialogue is taking place. The nodes should authenticate each other and setting up a Security Association.

Scenario 3

(Peer name sent as identication, then racoon queries for public key.)

Use this configuration: racoon-ipseckey.conf. Edit it to fit the host. Note that this is the same configuration as in Scenario 2.

You must also set a local security policy to tell the IP stack when to trigger the IKE dialogue.

This is only be necessary on the initiating node. If the other end is a server that never initiates traffic racoon will automatically generate a fitting security policy when the Security Association is established.

You can use an all-encompassing policy, that is, always try to establish an IKE dialogue with any node. Save this text in a file and run it:

 #!/sbin/setkey -f 

 spdflush ; 

 spdadd ::/0 ::/0 any -P in ipsec esp/transport//use ; 
 spdadd ::/0 ::/0 any -P out ipsec esp/transport//use ; 

 spdadd 0.0.0.0 0.0.0.0  any -P out ipsec esp/transport//use ;
 spdadd 0.0.0.0 0.0.0.0 any -P in ipsec esp/transport//use ;

This means anytime a process on your node to communicate it will trigger the IKE daemon into action. If it fails to establish a Security Association everything will still work but won't be authenticated nor encrypted.

If you don't want just any node to be hit by IKE, try adding the nodes involved specifically, for instance:

 #!/sbin/setkey -f 

 spdflush ; 

 spdadd 2001:16d8:ffff:1::3 2001:16d8:ffff:1::4 any -P out ipsec esp/transport//use ; 
 spdadd 2001:16d8:ffff:1::4 2001:16d8:ffff:1::3 any -P in ipsec esp/transport//use ; 

 spdadd 10.0.0.1 10.0.0.2  any -P out ipsec esp/transport//require ;
 spdadd 10.0.0.2 10.0.0.1 any -P in ipsec esp/transport//require ;

Store the file as, say, /root/setkey.sh and chmod it to 755.

Note that I have used "require" only on IPv4. For some reason it didn't work on IPv6 in my test network. Perhaps a Neighbor Discovery issue?

No scenario

Without my patches you can still use raw RSA public key authentication. Try this configuration: racoon-public-key.conf

Move or copy the keys into the directory indicated by the configuration file, typically /usr/local/etc/racoon/certs.

Verifiying

If everything looks good you can verify that all is OK by checking the SAD:

# setkey -D

You should see something like this:

# setkey -D
2001:16d8:ffff:1::3 2001:16d8:ffff:1::4
        esp mode=transport spi=14639167(0x00df603f) reqid=0(0x00000000)
        E: rijndael-cbc  9179e6bd 9dcb21a6 13250501 3bec456d
        A: hmac-sha2-256  78e741c4 2e397b80 31123038 c82b0083 2c4f12ce 633867ad 444e5964 acb81edf
        seq=0x000000c3 replay=4 flags=0x00000000 state=mature
        created: Feb  9 14:14:07 2012   current: Feb  9 14:59:07 2012
        diff: 2700(s)   hard: 3600(s)   soft: 2880(s)
        last: Feb  9 14:59:07 2012      hard: 0(s)      soft: 0(s)
        current: 39952(bytes)   hard: 0(bytes)  soft: 0(bytes)
        allocated: 195  hard: 0 soft: 0
        sadb_seq=1 pid=1275 refcnt=2
2001:16d8:ffff:1::4 2001:16d8:ffff:1::3
        esp mode=transport spi=230853419(0x0dc28b2b) reqid=0(0x00000000)
        E: rijndael-cbc  cbc8768c 4c155c90 ed0694aa 9da363d3
        A: hmac-sha2-256  f9cae53b 615f82f8 b03215dd 234304f3 6c97de6b 9ca96d2d eaa9c5ad 145b9d5e
        seq=0x000000f8 replay=4 flags=0x00000000 state=mature
        created: Feb  9 14:14:07 2012   current: Feb  9 14:59:07 2012
        diff: 2700(s)   hard: 3600(s)   soft: 2880(s)
        last: Feb  9 14:59:07 2012      hard: 0(s)      soft: 0(s)
        current: 23840(bytes)   hard: 0(bytes)  soft: 0(bytes)
        allocated: 248  hard: 0 soft: 0
        sadb_seq=0 pid=1275 refcnt=1

You can see the configured security policies with

# setkey -DP

This is perhaps more interesting if you rely on the automatically generated policies by racoon on the responding side.

You can also verify by listening on the network that the traffic is encrypted. Typically you will see something like this with tcpdump:

14:22:08.703831 IP6 2001:16d8:ffff:1::4 > 2001:16d8:ffff:1::3: ESP(spi=0x0dc28b2b,seq=0x31), length 136
14:22:08.708406 IP6 2001:16d8:ffff:1::3 > 2001:16d8:ffff:1::4: ESP(spi=0x00df603f,seq=0x1b), length 136
14:22:08.808077 IP6 2001:16d8:ffff:1::4 > 2001:16d8:ffff:1::3: ESP(spi=0x0dc28b2b,seq=0x32), length 88
14:22:08.871815 IP6 2001:16d8:ffff:1::4 > 2001:16d8:ffff:1::3: ESP(spi=0x0dc28b2b,seq=0x33), length 136
14:22:08.876476 IP6 2001:16d8:ffff:1::3 > 2001:16d8:ffff:1::4: ESP(spi=0x00df603f,seq=0x1c), length 136
14:22:08.976078 IP6 2001:16d8:ffff:1::4 > 2001:16d8:ffff:1::3: ESP(spi=0x0dc28b2b,seq=0x34), length 88

Linux users: Right now, I only see ESP traffic in one direction and nothing else in the other direction on your platform even though racoon on both sides say it was succesful in setting up Security Associations.

Manual configuration

If you want, you can do what the ns.pl resolver and the autosp.pl programs does manually.

The instructions given below assume that you are logged in to the host ipsec2.hack.org and trying to communicate with ipsec1.hack.org.

First get hold of the public key for the peer, ipsec1.hack.org. Convert it to binary.

% host -t ipseckey ipsec1
ipsec1.hack.org has IPSECKEY record 10 0 2 . AQOqVjnhO/eQdETVCH7r2am7yUixEcWUgyffNhdmTigiLS7VZODtxC2i i3ySKEOD6ADxykvgn6KVotSYmSq2kNAdmbElkMl3stAHE+E327vSkX3v KR3/l23Wrrv0iUlJFm+5WK1TJC6jTkjH8Fc0j77mdATGfmWYC4MdONuP UDMeEaMu9/XJm+X0ZWfGG/q1158smfkwsRvKh1KZCJLuGYv+Y85kRnMw nejtQRpDMZbjNr8dtS90MR7kGgQIJ/ctgc//MRjpTo8NTWncDp9KKvuN cBxsPPL08YTF/BkcG60SeeKD1jqjdnY+VRFkrv3BO0WyFEib/fuxeA2F JLYMYOuGG3n1KKSLckD7B5AfNOKKME94f+glY65M8njVVAIKahufUzp+ DIW9AgURxuAwNoCiRpS3i+sh7lY/dT5AzbZ8Ewl2jgsdX/neAXCiOWrS MUZGyARNcvAsOleRk93pb0y43FEU8f8OcS027p+ZbBHVbHYbLFEX6eg3 NbJso3LTL6Gx8SxWX8W6KvBjFf7sWO4ehEcXPJXJ+lsY/UQfAbeFlUTi cQd83+rVuuHfJvbxYWx0Sia6bERlKRSYTWT4FBwH3AwMTLQRskWasyBT bLbk1KHkUBZv4Ts5J+pGzAVsbPLINLnb5/lifjZdPGca6yn+5mbkKdjP SyV0ZHYfqiO4qQ==

Save the BASE64 encoded answer to a file, say ipsec1.b64, and run

% uudecode -m -r ipsec1.b64 > ipsec1.pub

Load the key into your running racoon by invoking racoonctl:

# ./racoonctl -s /var/run/racoon.sock load-key inet6 2001:16d8:ffff:1::4 2001:16d8:ffff:1::3 /home/mc/ipsec-keys/ipsec1.pub

The arguments are the socket where racoon is listening, then the command "load-key", the address family, source and destination addresses and the filename where it will find the RSA public key for the destination address.

Set the security policy to trigger the IKE dialogue. Save and run this file:

 #!/sbin/setkey -f 

 spdflush ; 

 spdadd 2001:16d8:ffff:1::4 2001:16d8:ffff:1::3 any -P out ipsec esp/transport//use ; 
 spdadd 2001:16d8:ffff:1::3 2001:16d8:ffff:1::4 any -P in ipsec esp/transport//use ;

Then try to access something on the other node, for instance by using ping. An IKE dialogue should start and security associations should be established.


Last updated: <2012-05-23 16:03:08 MEST>