Libreswan Documentation

Logo

Libreswan's Online Documentation

View the Project on GitHub libreswan/libreswan.github.io

Support
FAQ Common Error Messages
History
Implemented Standards
Kernel Support
HOWTO Additional ipsec.conf documentation
AWS Mesh
brendans Road Warrior Setup at Home
Configuration examples
Confuse !?@: github wiki
Enterprise cloud encryption
Entropy matters
EoIP shared ethernet LAN using IPsec
High Availability Fallover VPN in AWS
Host to host VPN
Host to host VPN with PSK
Libreswan as client to a Cisco ASA or VPN3000 server
Microsoft Azure configuration
Migrate from IKEv1 DPD to IKEv2 LIVENESS
Opportunistic IPsec
Opportunistic IPsec using LetsEncrypt
Pluto and DNSSEC
Read status output
Route based VPN
SElinux and Labeled IPsec VPN
Subnet extrusion
Subnet to subnet using NAT
Subnet to subnet VPN
Subnet to subnet VPN with PSK
Unauthenticated Opportunistic IPsec
Using Apache to serve PKCS
Using NSS Hardware Tokens
Using NSS with libreswan
VPN server for remote clients using IKEv2
VPN server for remote clients using IKEv2 split VPN
GSoC 2027 Code Project Ideas DRAFT
Contributor Guidance DRAFT
Completed Projects 2026 RFC 9593 Announcing Supported Authentication Methods in IKEv2
2026 Improve the ACQUIRE to IKE policy lookup
2026 Add HOST TO HOST Support on BSD
2021 RFC 8420 Add EdDSA Signature Authentication Support to IKEv2
2020 Session Resumption
2020 IKEv2 Interop testing with OpenBSD
2020 IKE Intermediate Exchange
2019 Libreswan Opportunistic IPsec using LetsEncrypt
2018 RSA PSS Support in compliance with RFC 7427 and RFC 8247
2018 RFC 7427 Add ECDSA Signature Authentication Support to IKEv2
2018 RFC 5685 Redirect Mechanism
2018 Managing Interface
2017 TCP encapsulation of IKE and IPsec
2017 RFC 7427 Add Signature Authentication Support to IKEv2
2017 Postquantum Preshared Keys
Presentations
IRC
Hacking Documentation
Git, GitHub, and Pull Requests
Merging GitHub Pull Requests
Programming Conventions
Testing Docker
KVM 1. Setup The Host
2. Configure Testing
3. Compile Libreswan
4. Test Libreswan
5. Accessing The Console
6. Maintenance and Internals
Bisecting
Debugging Pluto
Logging In Using SSH
Performance
Running A Custom Kernel
Running A Single Test
Running In The Background
Setup a Web Server
Testing Old Branches
Updating Test Results
Namespace Magic
Namespaces
Topology
Internals 3.14 X509
Benchmarking and Performance testing
Cipher suites and algorithm support
Cloud OE ideas
Compiling with AddressSanitizer
Compliance of RFC 7427 Signature Authentication in IKEv2
Coverity
Cryptographic Acceleration
Developer links strongswan android
Discouraged or forbidden C functions
IKEv2 Child SA
IKEv2 CP and EAP support
Introduction
Libreswan xfrm kernel support
Logging cleanup
New OE
Pluto
Pluto packet processing
Proposed ipsec ca command
Retransmit timings
Road Map
SAref code
Setting up system for debug logging
stf status
Testing 2017 Next Generation
Unbound
Uncrustify
Use Cases and Requirements document for ECC ECDSA support
Use Cases and Requirements document
XFRM Interface Development Notes
XFRM pCPU
XFRM pCPU RSS
Security Crypto boundary and certification
Libreswan and Heartbleed
Libreswan and TunnelCrack
Reporting a Vulnerability
Vulnerabilities
Meetups 2013 Helsinki
2014 San Francisco
2014 Toronto
2018 Toronto
Obsolete HOWTOs IKEv1 XAUTH with FreeOTP and FreeIPA
IKEv1 XAUTH with Google Authenticator One Time Passwords
Route based VPN using VTI
Testing Namespace
VPN server for remote clients using IKEv1 with L2TP
VPN server for remote clients using IKEv1 XAUTH with Certificates
VPN server for remote clients using IKEv1 XAUTH with PSK

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 simple since it does not need to have its own verifiable identity. And it also offers the best privacy for the client. This is similar to how TLS works. But with IPsec we get to encrypt every kind of traffic between the two hosts and not just those applications that support SSL/TLS.

TOC

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

The server-side configuration consists of two parts. Getting a LetsEncrypt certificate and configurating libreswan. In this example, we will setup nssec.nohats.ca as an Opportunistic IPsec server for use with LetsEncrypt.

There are different methods and software available to get a LetsEncrypt certificate. While most people seem to use certbot, I found dehydrated much nicer to use. To proof ownership of the server, you need to answer an ACME challenge using either DNS or HTTP. We will use HTTP. Note that you only briefly need to run an HTTP server while getting or renewing the certificate. In our example we will use a standard apache install on RHEL/CENTOS server. It should work on version 6 or 7 of these distributions.

At the time of writing, those repositories do not yet have libreswan-3.19 or later. The Libreswan Project offers a repository for RHEL/CentoOS 6 and 7 that does have a new enough version:

cd /etc/pki/rpm-gpg
wget https://download.libreswan.org/RPM-GPG-KEY-libreswan
cd /etc/yum.repos.d
wget https://download.libreswan.org/rhel-libreswan.repo
</pre?

Install libreswan and apache:

<pre>
yum install libreswan httpd
ipsec start
systemctl start httpd

Install and run “dehydrated” to get (and renew) a LetsEncrypt certificate. If your host has multiple DNS names, add all of them on a single line in the domains.txt file:

mkdir /etc/dehydrated
cd /etc/dehydrated
wget https://raw.githubusercontent.com/lukas2511/dehydrated/master/dehydrated
chmod 755 dehydrated
echo "nssec.nohats.ca" > domains.txt
echo "WELLKNOWN=/var/www/html/.well-known/acme-challenge" >>config
echo "CONTACT=your@email.com" >>config
mkdir -p /var/www/html/.well-known/acme-challenge
restorecon -R /var/www/html/.well-known
ln -s  /etc/dehydrated/dehydrated /usr/local/sbin/dehydrated
dehydrated -c
<pre>

If everything worked, it will look like:

<pre>
[root@nssec dehydrated]# dehydrated -c
# INFO: Using main config file /etc/dehydrated/config
Processing nssec.nohats.ca
 + Signing domains...
 + Generating private key...
 + Generating signing request...
 + Requesting challenge for nssec.nohats.ca...
 + Already validated!
 + Requesting certificate...
 + Checking certificate...
 + Done!
 + Creating fullchain.pem...
 + Done!

Now we need to import the certificate into libreswan. It will require an “export password”. It is not really used because we will immediately import it without password into libreswan. You should set the name (-name) to the DNs name of your host. We will use this later in the libreswan configuration.

cd /etc/dehydrated/certs/nssec.nohats.ca/
openssl pkcs12 -export -inkey privkey.pem -in cert.pem -name nssec.nohats.ca -certfile fullchain.pem -out nssec.nohats.ca.p12
Enter Export Password:
Verifying - Enter Export Password:

The .p12 file can now be imported into libreswan:

ipsec import nssec.nohats.ca.p12
Enter password for PKCS12 file:
pk12util: PKCS12 IMPORT SUCCESSFUL
correcting trust bits for Let's Encrypt Authority X3 - Digital Signature Trust Co.

Now similar to what we had done on the client, we need to create a connection. You can cut & paste the below configuration, or you can download it: https://raw.githubusercontent.com/libreswan/libreswan/master/docs/examples/oe-letsencrypt-server.conf] Place the file in /etc/ipsec.d/

# See https://libreswan.org/wiki/HOWTO:_Opportunistic_IPsec_using_LetsEncrypt
conn clear-or-private
        leftid=%fromcert
        leftrsasigkey=%cert
        # Use the nickname (DNS name) of YOUR server
        leftcert=nssec.nohats.ca
        leftauth=rsasig
        left=%defaultroute
        leftaddresspool=100.64.0.1-100.64.255.254
        leftmodecfgclient=yes
        #
        rightid=%null
        rightauth=null
        right=%opportunisticgroup
        #
        negotiationshunt=passthrough
        failureshunt=passthrough
        type=tunnel
        ikev2=insist
        sendca=issuer
        auto=add

Since with this connection you want to respond to everyone who tries to connect to you, you should add 0.0.0.0/0 to the clear-or-private policy file:

# /etc/ipsec.d/policies/clear-or-private
# The world
0.0.0.0/0

And now you are read to restart libreswan and test it all out!

ipsec restart

List of servers that are known to support Opportunistic IPsec

Monitoring and Debugging

Monitoring and debugging can be done using:

# to see all IPsec tunnels currently active (and the amount of traffic encrypted)
sudo ipsec whack --trafficstatus

# to see all the IP addresses that were tried, but did not offer Opportunistic IPsec:
sudo ipsec whack --shuntstatus

# to see all the gory inside state of the libreswan pluto daemon
sudo ipsec status

Libreswan also logs via syslog (or to a file if specified using logfile=/var/log/pluto.log in /etc/ipsec.conf)

It can be useful to do a manual debug version of an OE request. Since you might already have an outstanding request, and there cannot be two requests, the easiest is to restart libreswan and then test. Note that you need to know your local IP and the remote IP. There is no DNS resolution using this debug command.

sudo ipsec restart
sleep 3
sudo ipsec whack --oppohere YOURIP --oppothere REMOTEIP

It will look something like:

paul@thinkpad:~$ sudo ipsec restart
Redirecting to: systemctl stop ipsec.service
Redirecting to: systemctl start ipsec.service
paul@thinkpad:~$ dig +short nssec.nohats.ca
193.110.157.123
paul@thinkpad:~$ sudo ipsec whack --oppohere 76.10.157.68 --oppothere 193.110.157.123
002 initiate on demand from 76.10.157.68:0 to 193.110.157.123:0 proto=0 because: whack
133 "private-or-clear#193.110.157.0/24"[1] ...193.110.157.123 #1: STATE_PARENT_I1: initiate
002 "private-or-clear#193.110.157.0/24"[1] ...193.110.157.123 #1: private-or-clear#193.110.157.0/24 IKE proposals for initial initiator (selecting KE): 1:IKE:ENCR=AES_GCM_C_256;PRF=HMAC_SHA2_512,HMAC_SHA2_256,HMAC_SHA1;INTEG=NONE;DH=MODP2048,MODP3072,MODP4096,MODP8192 2:IKE:ENCR=AES_GCM_C_128;PRF=HMAC_SHA2_512,HMAC_SHA2_256,HMAC_SHA1;INTEG=NONE;DH=MODP2048,MODP3072,MODP4096,MODP8192 3:IKE:ENCR=AES_CBC_256;PRF=HMAC_SHA2_512,HMAC_SHA2_256,HMAC_SHA1;INTEG=HMAC_SHA2_512_256,HMAC_SHA2_256_128,HMAC_SHA1_96;DH=MODP2048,MODP3072,MODP1536 4:IKE:ENCR=AES_CBC_128;PRF=HMAC_SHA2_512,HMAC_SHA2_256,HMAC_SHA1;INTEG=HMAC_SHA2_512_256,HMAC_SHA2_256_128,HMAC_SHA1_96;DH=MODP2048,MODP3072,MODP1536 (default)
002 "private-or-clear#193.110.157.0/24"[1] ...193.110.157.123 #1: private-or-clear#193.110.157.0/24 ESP/AH proposals for initiator: 1:ESP:ENCR=AES_GCM_C_256;INTEG=NONE;ESN=DISABLED 2:ESP:ENCR=AES_GCM_C_128;INTEG=NONE;ESN=DISABLED 3:ESP:ENCR=AES_CBC_256;INTEG=HMAC_SHA2_512_256,HMAC_SHA2_256_128;ESN=DISABLED 4:ESP:ENCR=AES_CBC_128;INTEG=HMAC_SHA2_512_256,HMAC_SHA2_256_128;ESN=DISABLED 5:ESP:ENCR=AES_CBC_128;INTEG=HMAC_SHA1_96;ESN=DISABLED (default)
002 "private-or-clear#193.110.157.0/24"[1] ...193.110.157.123 #2: certificate CN=nssec.nohats.ca OK
002 "private-or-clear#193.110.157.0/24"[1] ...193.110.157.123 #2: negotiated connection [76.10.157.68,76.10.157.68:0-65535 0] -> [193.110.157.123,193.110.157.123:0-65535 0]

Common Errors

If you are trying to perform a manual OE operation for a target IP that is not covered by /etc/ipsec.d/policies/private* you will receive this error:

paul@thinkpad:~$ sudo ipsec whack --oppohere 76.10.157.68 --oppothere 1.2.3.4
002 initiate on demand from 76.10.157.68:0 to 1.2.3.4:0 proto=0 because: whack
002 Cannot opportunistically initiate for 76.10.157.68 to 1.2.3.4: no routed template covers this pair
033 Cannot opportunistically initiate for 76.10.157.68 to 1.2.3.4: no routed template covers this pair