IPSec

I have set up racoon and setkey to handle IPsec between VPN nodes. The certificate files do not exist yet, racoon would read them when the connection is used.

In /etc/ipsec-tools.conf

flush;
spdflush;

/etc/racoon/racoon.conf

path pre_shared_key "/etc/racoon/psk.txt";
path certificate "/usr/local/lib/ssl";

remote anonymous {
        exchange_mode main;
        certificate_type x509 "../../../../var/local/lib/vpn/vpn.crt" "../../../../var/local/lib/vpn/vpn.key";
        verify_cert off;
        my_identifier asn1dn;
        lifetime time 10 second;
        peers_identifier asn1dn;
        proposal {
                encryption_algorithm 3des;
                hash_algorithm md5;
                authentication_method rsasig;
                dh_group modp1024;
        }
}

sainfo anonymous {
        pfs_group modp768;
        encryption_algorithm 3des;
        authentication_algorithm hmac_md5;
        compression_algorithm deflate;
}

Startup

Now you have racoon and quagga running in the background, you can peer your nodes.

First, a sample OpenSSL configuration file, saved to /usr/local/etc/vpnssl.cnf

Other object identifiers may be added as well.

#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#

# This definition stops the following lines choking if HOME isn't
# defined.
HOME                    = .
RANDFILE                = $ENV::HOME/.rnd

# Extra OBJECT IDENTIFIER info:
oid_file                = $ENV::HOME/.oid
oid_section             = new_oids

# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions            = 
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)

[ new_oids ]

# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6

streetAddress=2.5.4.9
postalCode=2.5.4.17
favouriteDrink=0.9.2342.19200300.100.1.5

[ policy_match ]
#countryName            = match
#stateOrProvinceName    = match
#streetAddress          = optional
#postalCode             = optional
#organizationName       = optional
#organizationalUnitName = optional
commonName              = supplied
#emailAddress           = optional

# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
streetAddress           = optional
postalCode              = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ req ]
default_bits            = 1024
default_keyfile         = vpn.key

distinguished_name      = req_distinguished_name
attributes              = req_attributes

# Use extensions on requests and certificates
utf8                    = yes
x509_extensions         = v3_crt
req_extensions          = v3_req

# only want UTF-8 strings
string_mask = utf8only

# we can have many LDAP style attributes
# http://www.alvestrand.no/objectid/2.5.4.html

[ req_distinguished_name ]
commonName                      = Common Name (eg, YOUR name)
commonName_max                  = 64
commonName_default              = …

[ req_attributes ]

[ v3_req ]

# Extensions to add to a certificate requests

basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_crt ]

subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints = CA:true

Now, the main event, /usr/local/sbin/vpn

Adjust by identifying the interface with your public IPv6 address on it, so the number is copied to MYPUB.

The addresses of your peers go replace the example 2001:db8:d0be:f00d::1 2001:db8:d0be:f00d::2 2001:db8:d0be:f00d::3 addresses.

#!/bin/bash

MYPUB=$(ifconfig eth0 | grep Scope:Global | cut -f2- -d":"|cut -f1 -d"/")
MYADDR=2001:db8:d0be:f00d::1 2001:db8:d0be:f00d::2 2001:db8:d0be:f00d::3

if test "$1" == "init"
then
        CRT=/var/local/lib/vpn/vpn.crt
        KEY=/var/local/lib/vpn/vpn.key

        openssl req -config /usr/local/etc/vpnssl.cnf -new -utf8 -x509 -newkey rsa:1024 -out $CRT -keyout $KEY -nodes -days 10203 -set_serial 0 -batch
        #openssl x509 -text -noout -nameopt utf8,sep_multiline,oid -in $CRT

        function c() { echo fd${1:62:3}${1:65:2}${1:68:3}${1:71:2}${1:74:2}::/128; };
        MYPREFIX=$(c "$(openssl x509 -in $CRT -noout -sha1 -fingerprint)")

        I=0
        for N in $MYADDR
        do
                if test $N != $MYPUB
                then
                        ip -6 tunnel add free$I mode ip6ip6 remote $N local $MYPUB
                        ip -6 addr add $MYPREFIX dev free$I
                        ip link set dev free$I up

                        # want mDNS to work here as well…
                        ifconfig free$I multicast
                        I=$((0${I} + 1))
                fi
        done

        # Use IPsec to cipher all incoming and outgoing data…
        setkey -c <<<"
                spdadd $MYPREFIX fd00::/8 any -P out ipsec esp/transport//require ah/transport//require;
                spdadd fd00::/8 $MYPREFIX any -P in  ipsec esp/transport//require ah/transport//require;
        "
fi

if test "$1" == "away"
then
	I=0
	for N in $MYADDR
	do
                if test $N != $MYPUB
		then
		        ip -6 tunnel del free${I}
			I=$((0${I} + 1))
		fi
	done
fi

Run as vpn init to connect, and vpn away to drop connection. Quagga should detect that links have gone up and add routes as needed. When ping6ing across the VPN, racoon should detect it, and they will then have some protection with IPSec.

As SSL files are generated randomly, deriving the IP address from the key fingerprint, which is hash of the public key, gives some degree of decentralised claim over the address selected.

MTU setting

The ospf6 router requires that MTU match on both ends of the vpn links in the mesh, though it can be different on different links.

It is desirable for the mtu to be large so enabling jumbograms on a network can be useful even if that does not have mesh wide coverage.

It is possible to use ping6 to find out what that is.

while true
do
	echo try size $N
	if ! ping6 -Mdo -c1 -s $N $1
	then
		exit
	fi
	N=$(($N+1))
done

Testing

It is possible to feed the key material to Wireshark to see inside the resulting encrypted IPsec

#!/bin/bash

IF=0
sudo /usr/sbin/setkey -D | while read
do
	if test "${REPLY:0:1}" != "	"
	then
		IF=$((${IF} + 1))
		IN=0
		read SRC DST <<<"${REPLY}"
	fi
	if test "${IN}" = 1
	then
		read MODE TUNNEL SPI REQ ETC <<<"${REPLY}"
	fi
	if test "${IN}" = 2
	then
		read CRYPT EALGO EKEY <<<"${REPLY}"
	fi
	if test "${IN}" = 3
	then
		read HASH HALGO HKEY <<<"${REPLY}"
		if test "$MODE" = "esp" -a "$TUNNEL" = "mode=transport"
		then
			HHALGO=$'ANY'
			EEALGO=$'NULL'
			case $EALGO in
			des-cbc)
			EEALGO=$'DES-CBC [RFC2405]'
			;;
			3des-cbc)
			EEALGO=$'TripleDES-CBC [RFC2451]'
			;;
			blowfish-cbc)
			EEALGO=$'BLOWFISH-CBC [RFC2451]'
			;;
			cast128-cbc)
			EEALGO=$'CAST5-CBC [RFC2144]'
			;;
			twofish-cbc)
			EEALGO=$'TWOFISH-CBC'
			;;
			aes-ctr)
			EEALGO=$'AES-CTR [RFC3686]'
			;;
			esac
			case $HALGO in
			hmac-md5)
			HHALGO=$'HMAC-MD5-96 [RFC2403]'
			;;
			hmac-sha1)
			HHALGO=$'HMAC-SHA1-96 [RFC2404]'
			;;
			hmac-sha256)
			HHALGO=$'HMAC-SHA256'
			;;
			hmac-ripemd160)
			HHALGO=$'HMAC-RIPEMD'
			;;
			esac

			EEKEY=$'0x'"${EKEY:0:8}""${EKEY:9:8}""${EKEY:18:8}""${EKEY:27:8}""${EKEY:36:8}""${EKEY:45:8}"
			HHKEY=$'0x'"${HKEY:0:8}""${HKEY:9:8}""${HKEY:18:8}""${HKEY:27:8}"
			sed -i $'
s/^esp.sa_'"${IF}"$': .*$/esp.sa_'"${IF}"$': IPv6|'"${SRC}"$'|'${DST}$'|*/
s/^esp.encryption_key_'"${IF}"$': .*$/esp.encryption_key_'"${IF}"$': '"${EEKEY}"$'/
s/^esp.encryption_algorithm_'"${IF}"$': .*$/esp.encryption_algorithm_'"${IF}"$': '"${EEALGO}"$'/
s/^esp.authentication_key_'"${IF}"$': .*$/esp.authentication_key_'"${IF}"$': '"${HHKEY}"$'/
s/^esp.authentication_algorithm_'"${IF}"$': .*$/esp.authentication_algorithm_'"${IF}"$': '"${HHALGO}"$'/
' ~/.wireshark/preferences
		fi
	fi


	IN=$((${IN} + 1))
done