HOWTO: Opportunistic IPsec using LetsEncrypt

From Libreswan
Revision as of 06:20, 17 January 2017 by Paul Wouters (talk | contribs)
Jump to navigation Jump to search

The idea is to leverage the LetsEncrypt Certificate Agency to authenticate servers for IPsec. At the same time, we want our IPsec clients to remain anonymous. This allows the client configuration to be soimple since it does not need to have its own verifiable identity. This is similar to how TLS works. But with IPsec we get to encrypt every kind of traffic between the two hosts.

Client configuration

The client configuration is reasonable straightforward. What is needed is the Root Certificate Agency file for LetsEncrypt and libreswan-3.19 or higher.

If libreswan is not yet installed or has never started before, it must be started first so that it initializes the NSS certificate store. For example:

yum install libreswan
ipsec start

Next, we need to install the LetsEncrypt CA certificates into the NSS db. Note that for RHEL and Fedora, this store is located in /etc/ipsec.d and for Debian and Ubuntu this store is located in /var/lib/ipsec/nss/

mkdir letsencrypt
cd letsencrypt
wget https://letsencrypt.org/certs/lets-encrypt-x4-cross-signed.pem
wget https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem
wget https://letsencrypt.org/certs/isrgrootx1.pem
# the trustid root is missing the header / footer and is stupidly embedded on web page
# baesed on https://www.identrust.com/certificates/trustid/root-download-x3.html
wget https://nohats.ca/LE/identrust-x3.pem

# use the right NSS location!
certutil -A -i lets-encrypt-x3-cross-signed.pem -n lets-encrypt-x3 -t CT,, -d sql:/etc/ipsec.d
certutil -A -i lets-encrypt-x4-cross-signed.pem -n lets-encrypt-x4 -t CT,, -d sql:/etc/ipsec.d
certutil -A -i isrgrootx1.pem -n isrgrootx1 -t CT,, -d sql:/etc/ipsec.d
certutil -A -i identrust-x3.pem -n identrust-x3 -t CT,, -d sql:/etc/ipsec.d

Next, we need to configure libreswan to attempt to setup an IPsec tunnel for each new target IP address the kernel wants to send a packet to. This uses a special connection named "private-or-clear".

You can cut & paste the below configuration, or you can download it: https://raw.githubusercontent.com/libreswan/libreswan/master/docs/examples/oe-letsencrypt-client.conf oe-letsencrypt-client.conf] Place the file in /etc/ipsec.d/

# See https://libreswan.org/wiki/HOWTO:_Opportunistic_IPsec_using_LetsEncrypt
#
conn private-or-clear
	rightid=%fromcert
	rightrsasigkey=%cert
	rightauth=rsasig
	right=%opportunisticgroup
	rightmodecfgclient=yes
	rightcat=yes
	# Any CA will do because we only load the LetsEncrypt CA
	rightca=%any
	#
	left=%defaultroute
	leftid=%null
	leftauth=null
	leftmodecfgclient=yes
	leftcat=yes
	#
	narrowing=yes
	type=tunnel
	ikev2=insist
	negotiationshunt=drop
	failureshunt=passthrough
	keyingtries=1
	retransmit-timeout=3s
	auto=ondemand

Next, we need to tell when this kind of LetsEncrypt connection is attempted. We are planning to add some plugins and DNS record or other kind of information that will allow libreswan to detect which sites support this before trying to connect. For now, we will just always try and if it fails we remember this for a while (1h).

# /etc/ipsec.d/policies/private-or-clear
#
# A number of hosts within this /24 support LetsEncrypt (letsencrypt.libreswan.org, nohats.ca, mx.nohats.ca)
193.110.157.0/24
# If you just want to always try it to everyone in the world, enable the below line
0.0.0.0/0

That's it. Now you can restart libreswan to reload the configuration and test it.

paul@thinkpad:~$ sudo ipsec restart
Redirecting to: systemctl stop ipsec.service
Redirecting to: systemctl start ipsec.service
paul@thinkpad:~$ sudo ipsec whack --trafficstatus
paul@thinkpad:~$ ping letsencrypt.libreswan.org
PING letsencrypt.libreswan.org (193.110.157.131) 56(84) bytes of data.
64 bytes from letsencrypt.libreswan.org (193.110.157.131): icmp_seq=2 ttl=64 time=96.5 ms
64 bytes from letsencrypt.libreswan.org (193.110.157.131): icmp_seq=3 ttl=64 time=98.0 ms
^C
--- letsencrypt.libreswan.org ping statistics ---
3 packets transmitted, 2 received, 33% packet loss, time 2062ms
rtt min/avg/max/mdev = 96.564/97.306/98.049/0.805 ms
paul@thinkpad:~$ sudo ipsec whack --trafficstatus
006 #4: "private-or-clear#193.110.157.0/24"[2] ...193.110.157.131, type=ESP, add_time=1484626492, inBytes=168, outBytes=168, id='CN=letsencrypt.libreswan.org'

If a host does not support Opportunistic IPsec, you can see this in the bare shunt table.

paul@thinkpad:~$ ping 193.110.157.1
PING 193.110.157.1 (193.110.157.1) 56(84) bytes of data.
64 bytes from 193.110.157.1: icmp_seq=2 ttl=52 time=93.9 ms
64 bytes from 193.110.157.1: icmp_seq=3 ttl=52 time=93.9 ms
^C
--- 193.110.157.1 ping statistics ---
3 packets transmitted, 2 received, 33% packet loss, time 2001ms
rtt min/avg/max/mdev = 93.906/93.935/93.964/0.029 ms
paul@thinkpad:~$ sudo ipsec whack --trafficstatus
006 #2: "private-or-clear#193.110.157.0/24"[1] ...193.110.157.131, type=ESP, add_time=1484626698, inBytes=168, outBytes=168, id='CN=letsencrypt.libreswan.org'
paul@thinkpad:~$ sudo ipsec whack --shuntstatus
000 Bare Shunt list:
000  
000 76.10.157.68/32:0 -0-> 193.110.157.1/32:0 => %pass 0    oe-failing

(note the tunnel shown was already established above)


Server configuration