HOWTO: Opportunistic IPsec using LetsEncrypt

From Libreswan
Revision as of 07:24, 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)

You can confirm with tcpdump:

23:21:25.682769 IP 76.10.157.68 > 193.110.157.131: ESP(spi=0x63c0c45a,seq=0x5), length 120
23:21:25.778733 IP 193.110.157.131 > 76.10.157.68: ESP(spi=0x43f7f488,seq=0x5), length 120
23:21:25.778733 IP 193.110.157.131 > 76.10.157.68: ICMP echo reply, id 18348, seq 3, length 64
23:21:26.683790 IP 76.10.157.68 > 193.110.157.131: ESP(spi=0x63c0c45a,seq=0x6), length 120
23:21:26.782588 IP 193.110.157.131 > 76.10.157.68: ESP(spi=0x43f7f488,seq=0x6), length 120
23:21:26.782588 IP 193.110.157.131 > 76.10.157.68: ICMP echo reply, id 18348, seq 4, length 64
23:21:27.685652 IP 76.10.157.68 > 193.110.157.131: ESP(spi=0x63c0c45a,seq=0x7), length 120
23:21:27.785603 IP 193.110.157.131 > 76.10.157.68: ESP(spi=0x43f7f488,seq=0x7), length 120
23:21:27.785603 IP 193.110.157.131 > 76.10.157.68: ICMP echo reply, id 18348, seq 5, length 64

Note that the way tcpdump and IPsec hook into the kernel, you see both the encrypted outgoing, encrypted incoming and decrypted incoming traffic, but not the outgoing pre-encrypt traffic. Work is underway to integrate OE with VTI interfaces where you will see all cleartext on the ipsec0 interface and all encrypted packets on the physical interface.


Server configuration