HOWTO: Opportunistic IPsec using LetsEncrypt
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)