IPsec

From Segfault
Jump to: navigation, search

There are various IPsec implementations available:

Name Supported Platforms Status
FreeS/WAN Linux ceased operations in April 2004
Openswan FreeBSD, Linux forked from FreeS/WAN in 2004, active as of 2019
Libreswan Linux forked from Openswan in 2012, active as of 2019
strongSwan Linux, Android, Maemo, FreeBSD, MacOS X, Windows forked from FreeS/WAN in 2005, active as of 2019
IPsec-Tools Linux, FreeBSD, NetBSD Ported from KAME's IPsec utilities, last release from 2014-02-27


ipsec-tools

setkey

Although ipsec-tools have been deprecated[1], let's try to use it anyway and build a simple tunnel between two nodes.

apt-get install ipsec-tools                                                   # Debian, Ubuntu
dnf install ipsec-tools                                                       # Fedora

On both hosts, create a configuration file /etc/ipsec-tools.d/local.conf:

flush;
spdflush;

# AH  - Authentication Header
add 10.0.0.10 10.0.0.20 ah 0x200 -A hmac-sha256
       0xb11e3d9bf0daa34dd091e40fd36b5e1ebcbd3ffbeff8f94703efe8db93c681fe;
add 10.0.0.20 10.0.0.10 ah 0x300 -A hmac-sha256
       0x623b2c4b1db7240dd34dafd01b0f2e4e7fc688c781208c005547db1417ac756e;

# ESP - Encapsulating Security Payload
add 10.0.0.10 10.0.0.20 esp 0x201 -E aes-ctr
        0x61537b6f4596d6d27e6a83575adc84364a20956930240c20ac9ae4d6;
add 10.0.0.20 10.0.0.10 esp 0x301 -E aes-ctr
        0x4335a97be5781e954643a9e922c43d8faa28015031b941617c5f6089;

# SPD - Security Policy Database
spdadd 10.0.0.10 10.0.0.20 any -P out ipsec
        esp/transport//require
        ah/transport//require;

spdadd 10.0.0.20 10.0.0.10 any -P in  ipsec
        esp/transport//require
        ah/transport//require;
  • They key lengths are important here, see setkey(1) for details. For example, hmac-ripemd160 only accepts 160 bit keys, so we have to generate a key of that exact length:
dd if=/dev/random bs=1 count=$(expr 160 / 8) 2>/dev/null | xxd -c 256 -ps
openssl rand -hex $(expr 160 / 8)
  • Some modes may not be available until the correct kernel module is loaded. This may be tricky to find out. E.g. xcbc is not sufficient for cbc modes, and aes-x86_64 may be needed for aes ciphers, etc.

On the other node, the same configuration is needed, but the Security Policy entries need to be reversed:

# SPD - Security Policy Database
spdadd 10.0.0.10 10.0.0.20 any -P in  ipsec
        esp/transport//require
        ah/transport//require;

spdadd 10.0.0.20 10.0.0.10 any -P out ipsec
        esp/transport//require
        ah/transport//require;

Once both files are in place (and only readable by root), we can load the configuration into the kernel:

setkey -f /etc/ipsec-tools.d/local.conf                                       # Some init script may be available to do this during startup.


We can use tcpdump to see if our encrypted tunnel is working. Before the IPsec policy was loaded, a ping request and its reply would like this:

$ tcpdump -c2 -n host 10.0.0.10
16:03:34.388305 IP 10.0.0.10 > 10.0.0.20: ICMP echo request, id 9512, seq 4, length 64
16:03:34.388383 IP 10.0.0.20 > 10.0.0.10: ICMP echo reply, id 9512, seq 4, length 64

After only one side has its policy in place, the ping requests were still noted, but not replied to:

16:03:51.936657 IP 10.0.0.10 > 10.0.0.20: ICMP echo request, id 9513, seq 1, length 64
16:03:52.943532 IP 10.0.0.10 > 10.0.0.20: ICMP echo request, id 9513, seq 2, length 64

Once both sides have their policy loaded, all we see is the encrypted traffic:

16:04:07.832305 IP 10.0.0.10 > 10.0.0.20: AH(spi=0x00000200,seq=0x4): ESP(spi=0x00000201,seq=0x4), length 84
16:04:07.832592 IP 10.0.0.20 > 10.0.0.10: AH(spi=0x00000300,seq=0x4): ESP(spi=0x00000301,seq=0x4), length 84

Racoon

Additionally, we can also use racoon for key and IP management, a common configuration.

First, let's generate our pre-shared key:

dd if=/dev/random bs=1 count=$(expr 192 / 8) 2>/dev/null | xxd -c 256 -ps     # Adjust key length as needed
openssl rand -hex $(expr 192 / 8)

Add the key to /etc/racoon/psk.txt on both nodes, each pointing to the other remote. So, on 10.0.0.10 we would add:

10.0.0.20   acca3d446a1c0e9e28469f936bb2c3817df4bb3c69233753

And on 10.0.0.20 we would add:

10.0.0.10   acca3d446a1c0e9e28469f936bb2c3817df4bb3c69233753

With that in place, we can configure /etc/racoon/racoon.conf on both nodes, again each referencing the other remote node. Note that this time we will create a seprate network for our VPN. So, on 10.0.0.10 we have:

log notify;
path pre_shared_key "/etc/racoon/psk.txt";

# Remotes
remote 10.0.0.20 {
       exchange_mode main,aggressive;
       proposal {
               encryption_algorithm blowfish;
               hash_algorithm sha256;
               authentication_method pre_shared_key;
               dh_group modp2048;
       }
}

# SA, with internal networks (local=>remote)
sainfo address 172.16.1.0/24 any address 172.16.2.0/24 any {
       pfs_group modp2048;
       encryption_algorithm blowfish;
       authentication_algorithm hmac_sha256;
       compression_algorithm deflate;
}

And on the other node, 10.0.0.20:

log notify;
path pre_shared_key "/etc/racoon/psk.txt";

# Remotes
remote 10.0.0.10 {
       exchange_mode main,aggressive;
       proposal {
               encryption_algorithm blowfish;
               hash_algorithm sha256;
               authentication_method pre_shared_key;
               dh_group modp2048;
       }
}

# SA, with internal networks (local=>remote)
sainfo address 172.16.2.0/24 any address 172.16.1.0/24 any {
       pfs_group modp2048;
       encryption_algorithm blowfish;
       authentication_algorithm hmac_sha256;
       compression_algorithm deflate;
}

Because the PSK has been configured with racoon, our ipsec-tools configuration can be simplified, but we have to pay attention to the new SA entries, this time in tunnel mode:

spdadd 172.16.1.0/24 172.16.2.0/24 any -P out ipsec
          esp/tunnel/10.0.0.10-10.0.0.20/require;

spdadd 172.16.2.0/24 172.16.1.0/24 any -P in  ipsec
          esp/tunnel/10.0.0.20-10.0.0.10/require;

And on the remote node:

spdadd 172.16.1.0/24 172.16.2.0/24 any -P in  ipsec
          esp/tunnel/10.0.0.10-10.0.0.20/require;

spdadd 172.16.2.0/24 172.16.1.0/24 any -P out ipsec
          esp/tunnel/10.0.0.20-10.0.0.10/require;

After starting both setkey and racoon, we still have to adjust our routing:

ip addr add 172.16.1.1/24 dev eth0 && ip route add to 172.16.2.0/24 via 172.16.1.1 src 172.16.1.1

And on the remote node:

ip addr add 172.16.2.1/24 dev eth0 && ip route add to 172.16.1.0/24 via 172.16.2.1 src 172.16.2.1

This all needs to go into network scripts, but here's how to tear it all down again:

ip route del 172.16.2.0/24 dev eth0 && ip addr del 172.16.1.1/24 dev eth0
ip route del 172.16.1.0/24 dev eth0 && ip addr del 172.16.2.1/24 dev eth0     # For the remote node

iproute2

We can also build a VPN with the standard iproute2 toolset.[2] Once configured, we should arrive at something like this:

$ ip xfrm state && ip xfrm policy
src 192.168.56.144 dst 192.168.56.130
       proto esp spi 0x865f38ae reqid 2254387374 mode tunnel
       replay-window 0 
       auth-trunc hmac(sha256) 0x182b3d4674e8387ddcd3df82185568d87ab33db6b07cbe53d97e6e13f60c376b 96
       enc cbc(blowfish) 0x3ac0d5c10202087bbe8ff043a7922914f7c44fd6e694b0023e0996745baf98ae
       anti-replay context: seq 0x0, oseq 0x0, bitmap 0x00000000
       sel src 0.0.0.0/0 dst 0.0.0.0/0 
src 192.168.56.130 dst 192.168.56.144
       proto esp spi 0x865f38ae reqid 2254387374 mode tunnel
       replay-window 0 
       auth-trunc hmac(sha256) 0x182b3d4674e8387ddcd3df82185568d87ab33db6b07cbe53d97e6e13f60c376b 96
       enc cbc(blowfish) 0x3ac0d5c10202087bbe8ff043a7922914f7c44fd6e694b0023e0996745baf98ae
       anti-replay context: seq 0x0, oseq 0x7f3e5, bitmap 0x00000000
       sel src 0.0.0.0/0 dst 0.0.0.0/0 

src 172.16.2.1/32 dst 172.16.1.1/32 
       dir in priority 0 ptype main 
       tmpl src 192.168.56.144 dst 192.168.56.130
               proto esp reqid 2254387374 mode tunnel
src 172.16.1.1/32 dst 172.16.2.1/32 
       dir out priority 0 ptype main 
       tmpl src 192.168.56.130 dst 192.168.56.144
               proto esp reqid 2254387374 mode tunnel

And on the remote side:

$ ip xfrm state && ip xfrm policy
ip xfrm state
src 192.168.56.144 dst 192.168.56.130
       proto esp spi 0x865f38ae reqid 2254387374 mode tunnel
       replay-window 0 
       auth-trunc hmac(sha256) 0x182b3d4674e8387ddcd3df82185568d87ab33db6b07cbe53d97e6e13f60c376b 96
       enc cbc(blowfish) 0x3ac0d5c10202087bbe8ff043a7922914f7c44fd6e694b0023e0996745baf98ae
       anti-replay context: seq 0x0, oseq 0x1cc8b, bitmap 0x00000000
       sel src 0.0.0.0/0 dst 0.0.0.0/0 
src 192.168.56.130 dst 192.168.56.144
       proto esp spi 0x865f38ae reqid 2254387374 mode tunnel
       replay-window 0 
       auth-trunc hmac(sha256) 0x182b3d4674e8387ddcd3df82185568d87ab33db6b07cbe53d97e6e13f60c376b 96
       enc cbc(blowfish) 0x3ac0d5c10202087bbe8ff043a7922914f7c44fd6e694b0023e0996745baf98ae
       anti-replay context: seq 0x0, oseq 0x0, bitmap 0x00000000
       sel src 0.0.0.0/0 dst 0.0.0.0/0 
src 172.16.1.1/32 dst 172.16.2.1/32 
       dir in priority 0 
       tmpl src 192.168.56.130 dst 192.168.56.144
               proto esp reqid 2254387374 mode tunnel
src 172.16.2.1/32 dst 172.16.1.1/32 
       dir out priority 0 
       tmpl src 192.168.56.144 dst 192.168.56.130
               proto esp reqid 2254387374 mode tunnel

Libreswan

Install the libreswan package and generate a secret on each side:

$ ipsec initnss --nssdir /var/lib/ipsec/nss
Initializing NSS database

$ ipsec newhostkey --bits 3072 --output /etc/ipsec.d/test.secrets
Generated RSA key pair with CKAID f25fa16922b06aaa2cd8f828049cb8098472a084 was stored in the NSS database

On the remote side:

$ ipsec initnss --nssdir /var/lib/ipsec/nss
Initializing NSS database

$ ipsec newhostkey --bits 3072 --output /etc/ipsec.d/test.secrets
Generated RSA key pair with CKAID bcdfa3e6210e83d59c54dac35989018aa7b96c08 was stored in the NSS database

The CKAID can be used to lookup and show the actual secrets:

$ ipsec showhostkey --list
< 1> RSA keyid: AwEAAcN28 ckaid: f25fa16922b06aaa2cd8f828049cb8098472a084

$ ipsec showhostkey --left --ckaid f25fa16922b06aaa2cd8f828049cb8098472a084
       # rsakey AwEAAcN28
       leftrsasigkey=0sAwEAAcN28aOnHIhmUaZPrhZRmeD04PUuXQ/lql/Ac2EffrVfLnijc93duYdfGfgFEtk[...]

Example configuration for a simple host-to-host VPN:

config setup
       protostack=netkey

conn test
       leftid=@left
       left=192.168.56.130
       leftrsasigkey=0sAwEAAcN28aOnHIhmUaZPrhZRmeD04PUuXQ/lql/Ac2EffrVfLnijc93duYdfGfgFEtkGA[...]

       rightid=@right
       right=192.168.56.144
       rightrsasigkey=0sAwEAAcHr+05UAVJwnFOA12jhpM2z5hQ5kfklJ4084UXjSAaTG+cwYJXPGd640Ng1oxQr[...]

       authby=rsasig
       ikev2=insist
       ike=aes_gcm256-sha2
       auto=add

The same configuration can be used on the remote side too.

Start the IPsec framework, on both nodes:

$ ipsec setup --start
Redirecting to: systemctl start ipsec.service

Add and start the connection:

$ ipsec auto --add test
002 "test": deleting non-instance connection
002 "test" #2: deleting state (STATE_V2_IPSEC_I) and sending notification
005 "test" #2: ESP traffic information: in=0B out=0B
002 "test" #1: deleting state (STATE_PARENT_I3) and sending notification
002 added connection description "test"
$ ipsec auto --up test
002 "test" #3: initiating v2 parent SA
133 "test" #3: initiate
002 "test" #3: constructed local IKE proposals for test (IKE SA initiator selecting KE): 1:IKE:ENCR=AES_GCM_C_256;PRF=HMAC_SHA2_256;INTEG=NONE;DH=MODP2048
133 "test" #3: STATE_PARENT_I1: sent v2I1, expected v2R1
002 "test" #3: constructed local ESP/AH proposals for test (IKE SA initiator emitting ESP/AH proposals): 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)
134 "test" #4: STATE_PARENT_I2: sent v2I2, expected v2R2 {auth=IKEv2 cipher=AES_GCM_16_256 integ=n/a prf=HMAC_SHA2_256 group=MODP2048}
002 "test" #4: IKEv2 mode peer ID is ID_FQDN: '@right'
003 "test" #4: Authenticated using RSA
002 "test" #4: negotiated connection [192.168.56.130-192.168.56.130:0-65535 0] -> [192.168.56.144-192.168.56.144:0-65535 0]
004 "test" #4: STATE_V2_IPSEC_I: IPsec SA established tunnel mode {ESP=>0x91463e52 <0x68ec030f xfrm=AES_GCM_16_256-NONE NATOA=none NATD=none DPD=passive}

Stop the connection with:

$ ipsec auto --down test
002 "test": terminating SAs using this connection
002 "test" #4: deleting state (STATE_V2_IPSEC_I) and sending notification
005 "test" #4: ESP traffic information: in=0B out=0B
002 "test" #3: deleting state (STATE_PARENT_I3) and sending notification

$ ipsec auto --delete test
002 "test": deleting non-instance connection

Stop the whole IPsec framework:

$ ipsec setup --stop
Redirecting to: systemctl stop ipsec.service

strongSwan

strongSwan can be quite difficult to configure and appears to focus on bigger setups, where authentication keys are managed by a PKI. But let's skip all that and just create a simple host-to-host configuration.

sudo apt-get install strongswan libstrongswan-standard-plugins strongswan-pki
  • libstrongswan-standard-plugins is needed to handle ECDSA or ed25519 keys.[3]. And since we're messing with ike and esp settings[4], we need to install this.
  • strongswan-pki would be needed to provide the ipsec pki command.

Example configuration:

$ cat /etc/ipsec.conf
conn test
       authby=psk
       keyexchange=ike
       left=192.168.56.130
       right=192.168.56.144
       type=transport
       auto=route
       ike=aes128gcm16-prfsha256-ecp256!
       esp=aes128gcm16!

The same configuration can be used on the remote side too.

Generate a PSK:

$ tail -2 /etc/ipsec.secrets
# openssl rand -hex $(expr 192 / 8)
192.168.56.130 192.168.56.144 : PSK "69b9b50297bdfd9cbb87a1c990dc811f203c3046265ed638"

Once both the configuration and ipsec.secrets have been copied to the remote side, the ipsec framework (namely the charon daemon) need to be started, on both hosts.

$ ipsec restart
Stopping strongSwan IPsec...
Starting strongSwan 5.7.2 IPsec [starter]...

IPsec is now active, but we don't have a running SA yet:

$ ipsec status
Routed Connections:
       test{1}:  ROUTED, TRANSPORT, reqid 1
       test{1}:   192.168.56.130/32 === 192.168.56.144/32
Security Associations (0 up, 0 connecting):
 none

The first packet will be used to initiate the SA:

$ ping -c2 192.168.56.144
PING 192.168.56.144 (192.168.56.144) 56(84) bytes of data.
64 bytes from 192.168.56.144: icmp_seq=2 ttl=64 time=1.58 ms
--- 192.168.56.144 ping statistics ---
2 packets transmitted, 1 received, 50% packet loss, time 28ms
rtt min/avg/max/mdev = 1.584/1.584/1.584/0.000 ms

And now we have something like this:

$ ipsec status
Routed Connections:
       test{1}:  ROUTED, TRANSPORT, reqid 1
       test{1}:   192.168.56.130/32 === 192.168.56.144/32
Security Associations (1 up, 0 connecting):
       test[1]: ESTABLISHED 13 seconds ago, 192.168.56.130[192.168.56.130]...192.168.56.144[192.168.56.144]
       test[1]: IKE proposal: AES_GCM_16_128/PRF_HMAC_SHA2_256/ECP_256
       test{2}:  INSTALLED, TRANSPORT, reqid 1, ESP SPIs: ce9cb6bc_i c7aa0fe0_o
       test{2}:   192.168.56.130/32 === 192.168.56.144/32

WireGuard

WireGuard is all the rage these days, and its setup is quite straightforward too. Let's build a host-to-host VPN for testing purposes. Install as needed:

apt-get install linux-headers-$(uname -r)                          # Debian
dnf install wireguard                                              # Fedora

For this Ubuntu system, the following was needed:[5]

$ cat /etc/apt/sources.list.d/wireguard.list 
deb http://ppa.launchpad.net/wireguard/wireguard/ubuntu bionic main 

$ gpg --keyserver hkp://keys.gnupg.net --recv-keys AE33835F504A1A25
$ gpg --armour --export AE33835F504A1A25 | apt-key add -

Generate a key pair on each side:

cd /etc/wireguard/
umask 0266
wg genkey | tee privatekey | wg pubkey > publickey

Create a configuration on each side:

[Interface]
Address = 172.16.1.1/16
ListenPort = 51820
PrivateKey = <privatekey1>

[Peer]
PublicKey  = <publickey2>
AllowedIPs = 172.16.2.1/32
Endpoint   = 192.168.56.144:51820

And on the remote side:

[Interface]
Address = 172.16.2.1/16
ListenPort = 51820
PrivateKey = <privatekey2>

[Peer]
PublicKey = <publickey1>
AllowedIPs = 172.16.1.1/32
Endpoint = 192.168.56.130:51820

Activate the new interface on both nodes:

wg-quick up wg0

And we should be good to go:

# wg show wg0 
interface: wg0
 public key: U65vBZ01dvtHI4vX1RZ7wJ9tEI2UvC9ztBhZJ8h5QyE=
 private key: (hidden)
 listening port: 51820

peer: Y61kB3RUVsaAeREwwk0/pBXUOLb9ZtRfvw3ZXMkgXC0=
 endpoint: 192.168.56.144:51820
 allowed ips: 172.16.2.1/32
 latest handshake: 3 minutes, 11 seconds ago
 transfer: 604 B received, 724 B sent

Tinc

TBD
How to Set up tinc, a Peer-to-Peer VPN

SSH

We can also use SSH to create a tunnel between two hosts.[6] For this to work, PermitTunnel must be enabled on the remote side's sshd. First we have to configure our tun device on both sides:

sudo ip tuntap add dev tun1 mode tun user $(id -n -u) group $(id -n -g)
sudo ip addr add 10.0.0.10/32 peer 10.0.0.20 dev tun1

And on the remote side:

sudo ip tuntap add dev tun0 mode tun user $(id -n -u) group $(id -n -g)
sudo ip addr add 10.0.0.20/32 peer 10.0.0.10 dev tun0

Note: with ifconfig, the same would look like this for the remote side:

sudo ifconfig tun0 up 
sudo ifconfig tun0 10.0.0.20 pointopoint 10.0.0.10 netmask 255.255.255.255                             # pointopoint can be omitted in NetBSD

With that in place, we can use SSH to establish a tunnel:

ssh -NTf -w 1:0 192.168.56.144                                                                         # Maybe add -C for compression?

The command should go into background and now we should have a working connection. performance may not be great though, in part because TCP Over TCP Is A Bad Idea

VTUN

TBD
vtun

fastd

TBD
Fast and Secure Tunnelling Daemon (fastd)
fastd documentation

QuickTun

TBD
QuickTun

Links


References