Opportunistisches VPN mit IPSec

1 Einfache IPSec-VPNs mit LibreSWAN – opportunistisches VPN

1.1 per x509 Zertifikat von Let's Encypt

  • LibreSwan, die IPSEC/IKE2 Implementierung in RedHat 8, unterstützt opportunistisches VPN mit Let's Encrypt Zertifikaten
  • bei opportunistischem VPN wird erst eine VPN Verschlüsselung probiert, wenn diese nicht zustandekommt wird unverschlüsselt gesprochen
  • bei dem opportunistischem Modus wird der Server von Client authentisiert, aber nicht der Client vom Server (ähnlich wie bei TLS/HTTPS)
  • alternativ (aber nicht hier im Workshop gezeigt –> Hausaufgabe) unterstützt LibreSwan auch ein Mesh-VPN mit Let's Encrypt Zertifikaten. Bei diesem Modus authentisieren sich beide Seiten per x509 Zertifikat.

1.2 Infrastruktur

  • Virtuelle Server zum Test (Hetzner/Digital-Ocean/Vultr)
  • jeder Benutzer bekommt zwei Maschinen
    • aXX.dane.onl - Server
    • bXX.dane.onl - Client
  • Workshop Teilnehmer
    Name Teilnehmernummer
    Stephan 01
    Karsten 02
    jana 03
    Danny 04
    Michael 05
    Bernhard 06
    Oliver Germer 07
    Ulf 08
    Gert 09
    Bene 10
    Joerg 11
    Jürgen 12
    Max 13
    julian stacey 14
    Bjørn 15
    Stefan 16
    Ronald 17
    Andrè 18
    Carsten 19
       
  • Zugriff per SSH oder Cockpit unter https://aXX.dane.onl:9090 und https://bXX.dane.onl:9090

1.3 Server (clear-or-private)

  • wir arbeiten im Internet auf der VM aXX.dane.onl via Cockpit (Port 9090) oder ssh
  • Benutzername user, Passwort IPSecOE
  • Installation der benötigten Softwarepakete
dnf install git socat libreswan bind-utils
  • das ACME Zertifikat wird mit dem Shell-Skript acme.sh angefordert
  • acme.sh per git aus dem Repository laden
git clone https://github.com/acmesh-official/acme.sh
  • acme.sh installieren
cd acme.sh
./acme.sh --install --cert-home /etc/tls --home /etc/acme
  • Port 80 für die Kommunikation mit Let's Encrypt freischalten
firewall-cmd --add-service="http" --permanent
firewall-cmd --reload
  • Test: ein Zertifikat für den Namen aXX.dane.onl per standalone Webservice anfordern (Port 80 auf dem Server muss frei sein)
cd ~
source ~/.bashrc
acme.sh  --issue  -d aXX.dane.onl  --standalone --cert-home /etc/tls
[Sat Feb 29 10:54:21 UTC 2020] Standalone mode.
[Sat Feb 29 10:54:21 UTC 2020] Single domain='aXX.dane.onl'
[Sat Feb 29 10:54:22 UTC 2020] Getting domain auth token for each domain
[Sat Feb 29 10:54:24 UTC 2020] Getting webroot for domain='aXX.dane.onl'
[Sat Feb 29 10:54:24 UTC 2020] Verifying: oe1.dane.onl
[Sat Feb 29 10:54:24 UTC 2020] Standalone mode server
[Sat Feb 29 10:54:28 UTC 2020] Success
[Sat Feb 29 10:54:28 UTC 2020] Verify finished, start to sign.
[Sat Feb 29 10:54:28 UTC 2020] Lets finalize the order, Le_OrderFinalize: https://acme-v02.api.letsencrypt.org/acme/finalize/79357257/2482192265
[Sat Feb 29 10:54:29 UTC 2020] Download cert, Le_LinkCert: https://acme-v02.api.letsencrypt.org/acme/cert/03053523225ecddb768abe8381a834ef13e0
[Sat Feb 29 10:54:30 UTC 2020] Cert success.
-----BEGIN CERTIFICATE-----
MIIFTzCCBDegAwIBAgISAwU1IyJezdt2ir6Dgag07xPgMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0yMDAyMjkwOTU0MjlaFw0y
[...]
VniqleXfkuSZFUzyLlQxmt1pjq8HERHQRGYzdy3LX/Rj35yJ/71gfy+K06BoL+sx
5pJdehKSelTtiOkZ+bP5kBCTs+DHbAs20x098bIBB1XWhyv/8vDKLb01GPJnArPL
YKBCbB00oKMfMpxKYRXVa6ejzAKNDhYmIgTYESZ8rKBtakHvRQI6WnLB0zunfHBE
66rLAVn7OKupPXRouN5e0IFRKA==
-----END CERTIFICATE-----
[Sat Feb 29 10:54:30 UTC 2020] Your cert is in  /etc/tls/aXX.dane.onl/aXX.dane.onl.cer
[Sat Feb 29 10:54:30 UTC 2020] Your cert key is in  /etc/tls/aXx.dane.onl/aXX.dane.onl.key
[Sat Feb 29 10:54:30 UTC 2020] The intermediate CA cert is in  /etc/tls/aXX.dane.onl/ca.cer
[Sat Feb 29 10:54:30 UTC 2020] And the full chain certs is there:  /etc/tls/aXX.dane.onl/fullchain.cer
  • x509-Zertifikat in eine p12 Zertifikatsdatei wandeln
# openssl pkcs12 -export -inkey /etc/tls/aXX.dane.onl/aXX.dane.onl.key \
    -in /etc/tls/aXX.dane.onl/aXX.dane.onl.cer \
    -name aXX.dane.onl \
    -certfile /etc/tls/aXX.dane.onl/fullchain.cer \
    -out aXX.dane.onl.p12
  • IPSec (NSS) Zertifikatsdatenbank erstellen
# ipsec initnss
Initializing NSS database
  • die p12 Zertifikatsdatei in die Zertifikatsdatenbank von LibreSwan importieren
# ipsec import aXX.dane.onl.p12
Enter password for PKCS12 file:
pk12util: PKCS12 IMPORT SUCCESSFUL
  • die Vorlage der Server-Konfigurationsdatei für opportunistische Verschlüsselung (OE = opportunistic encryption) kopieren
# cp /usr/share/doc/libreswan/examples/oe-letsencrypt-server.conf /etc/ipsec.d/
  • die Konfigurationsdatei anpassen, den Namen des eigenen Servers eintragen
[...]
	leftcert=aXX.dane.onl
[...]
  • IPSec Policy anpassen. Wir wollen das jede beliebige IP-Adresse (0.0.0.0/0) die Verbindung starten kann. Policy ist clear-or-private, d.h. wenn die Gegenstelle IPSec startet, dann wird verschlüsselt, ansonsten wird unverschlüssel kommuniziert
echo "0.0.0.0/0" >> /etc/ipsec.d/policies/clear-or-private
  • IPSec in der Firewall erlauben
firewall-cmd --add-service="ipsec" --permanent
firewall-cmd --reload
  • IPSec (neu-) starten um die neue Konfiguration zu aktivieren
# systemctl enable --now ipsec
  • IPSec Statusmeldungen anzeigen
journalctl -fu ipsec

1.4 Client (private-or-clear)

  • wir arbeiten im Internet auf der VM bXX.dane.onl via Cockpit (Port 9090) oder SSH.
  • Pakete auf der Client-Seite installieren. Wir benutzen den DNS-Resolver Unbound, um lokale DNSSEC-Validierung zu bekommen
dnf install libreswan unbound
  • Unbound aktivieren und starten
# systemctl enable --now unbound
  1. Lokale DNS-Resolver Konfiguration
    • Netzwerkkonfigurationen des Netzwerkmanagers auflisten
    nmcli con show
    NAME         UUID                                  TYPE      DEVICE
    System eth0  5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03  ethernet  eth0
    
    • DNS Konfiguration manuell auf die Loopback-Adressen (Unbound DNS-Resolver) festlegen
    nmcli con mod "System eth0" ipv4.ignore-auto-dns yes
    nmcli con mod "System eth0" ipv6.ignore-auto-dns yes
    nmcli con mod "System eth0" ipv4.dns "127.0.0.1"
    nmcli con mod "System eth0" ipv6.dns "::1"
    
    • Networkmanager Konfiguration neu ausrollen
    nmcli con down "System eth0" && nmcli con up "System eth0"
    
    • prüfen, das die Konfiguration erfolgreich übernommen wurde
    cat /etc/resolv.conf
    
    • Ausgabe
    nameserver 127.0.0.1
    nameserver ::1
    
    • wenn die Änderung der DNS-Konfiguration nicht funktioniert (nicht für Produktions-Umgebungen empfohlen!):
    echo "nameserver 127.0.0.1" > /etc/resolv.conf
    chattr +i /etc/resolv.conf
    
    • DNS Abfrage mit DNSSEC testen (AD-Flag!)
    dig dnssec.works +adflag +multi
    
    • Ausgabe:
    ; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el8 <<>> dnssec.works +adflag +multi
    ;; global options: +cmd
    ;; Got answer:
    ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 25162
    ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
    
    ;; OPT PSEUDOSECTION:
    ; EDNS: version: 0, flags:; udp: 4096
    ;; QUESTION SECTION:
    ;dnssec.works.          IN A
    
    ;; ANSWER SECTION:
    dnssec.works.           3600 IN A 5.45.107.88
    
    ;; Query time: 17 msec
    ;; SERVER: 127.0.0.1#53(127.0.0.1)
    ;; WHEN: Sat Feb 29 04:04:58 UTC 2020
    ;; MSG SIZE  rcvd: 57
    
  2. IPSec/LibreSwan Konfiguration
    • IPSec (NSS) Zertifikatsdatenbank erstellen
    ipsec initnss
    Initializing NSS database
    
    dnf install wget
    wget https://letsencrypt.org/certs/isrgrootx1.pem.txt
    wget https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt
    wget https://letsencrypt.org/certs/lets-encrypt-x4-cross-signed.pem.txt
    wget https://letsencrypt.org/certs/trustid-x3-root.pem.txt
    
    • die Zertifikate in die IPSEC Datenbank importieren, damit den Let's Encrypt Zertifikaten vertraut wird. LibreSwan benutzt nicht die Zertifikatsdatenbank des Betriebssystems (welche z.B. der Browser benutzt), sondern hat eine eigene Datenbank die manuell verwaltet wird.
    certutil -A -i lets-encrypt-x3-cross-signed.pem.txt -n lets-encrypt-x3 -t CT,, -d sql:/etc/ipsec.d
    certutil -A -i lets-encrypt-x4-cross-signed.pem.txt -n lets-encrypt-x4 -t CT,, -d sql:/etc/ipsec.d
    certutil -A -i isrgrootx1.pem.txt -n isrgrootx1 -t CT,, -d sql:/etc/ipsec.d
    certutil -A -i trustid-x3-root.pem.txt -n trustid-x3 -t CT,, -d sql:/etc/ipsec.d
    
    • IP-Adresse der Gegenstelle in die Richtliniendatei private-or-clear eintragen. Bei dieser Richtlinie wird erst versucht eine VPN-Verbindung aufzubauen, wenn die nicht klappt, wird unverschlüsselt kommuniziert (Fail-Open):
    echo "w.x.y.z/32" >> /etc/ipsec.d/policies/private-or-clear
    
    • die Konfigurations-Datei /etc/ipsec.d/oe-le-client.conf für LibreSwan erstellen:
    # 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
    
    • IPSec in der Firewall erlauben
    firewall-cmd --add-service="ipsec" --permanent
    firewall-cmd --reload
    
    • IPSec starten
    systemctl enable --now ipsec
    
    • Test der Verbindung
    ping -4 -c4 aXX.dane.onl
    
    • Log-Meldungen auf der Server Seite
    Feb 29 11:55:04 oe1 pluto[13677]: "clear-or-private#0.0.0.0/0"[1] ...209.250.233.90 #1: processing IKE_SA_INIT request: SA,KE,Ni,N,N,N (message arrived 0 seconds ago)
    Feb 29 11:55:04 oe1 pluto[13677]: "clear-or-private#0.0.0.0/0"[1] ...209.250.233.90: constructed local IKE proposals for clear-or-private#0.0.0.0/0 (IKE SA responder matching remote proposals): 1:IKE:ENCR=AES_GCM_C_256;PRF=HMAC_SHA2_512,HMAC_SHA2_256;INTEG=NONE;DH=ECP_256,ECP_384,ECP_521,MODP2048,MODP3072,MODP4096,MODP8192 2:IKE:ENCR=CHACHA20_POLY1305;PRF=HMAC_SHA2_512,HMAC_SHA2_256;INTEG=NONE;DH=ECP_256,ECP_384,ECP_521,MODP2048,MODP3072,MODP4096,MODP8192 3:IKE:ENCR=AES_CBC_256;PRF=HMAC_SHA2_512,HMAC_SHA2_256;INTEG=HMAC_SHA2_512_256,HMAC_SHA2_256_128;DH=ECP_256,ECP_384,ECP_521,MODP2048,MODP3072,MODP4096,MODP8192 4:IKE:ENCR=AES_GCM_C_128;PRF=HMAC_SHA2_512,HMAC_SHA2_256;INTEG=NONE;DH=ECP_256,ECP_384,ECP_521,MODP2048,MODP3072,MODP4096,MODP8192 5:IKE:ENCR=AES_CBC_128;PRF=HMAC_SHA2_256;INTEG=HMAC_SHA2_256_128;DH=ECP_256,ECP_384,ECP_521,MODP2048,MODP3072,MODP4096,MODP8192
    Feb 29 11:55:04 oe1 pluto[13677]: "clear-or-private#0.0.0.0/0"[1] ...209.250.233.90 #1: processing encrypted IKE_AUTH request: SK (message arrived 0 seconds ago)
    Feb 29 11:55:04 oe1 pluto[13677]: "clear-or-private#0.0.0.0/0"[1] ...209.250.233.90 #1: processing decrypted IKE_AUTH request: SK{IDi,AUTH,SA,TSi,TSr}
    Feb 29 11:55:04 oe1 pluto[13677]: "clear-or-private#0.0.0.0/0"[1] ...209.250.233.90 #1: Authenticated using authby=null
    Feb 29 11:55:04 oe1 pluto[13677]: "clear-or-private#0.0.0.0/0"[1] ...209.250.233.90: constructed local ESP/AH proposals for clear-or-private#0.0.0.0/0 (IKE_AUTH responder matching remote ESP/AH proposals): 1:ESP:ENCR=AES_GCM_C_256;INTEG=NONE;DH=NONE;ESN=DISABLED 2:ESP:ENCR=CHACHA20_POLY1305;INTEG=NONE;DH=NONE;ESN=DISABLED 3:ESP:ENCR=AES_CBC_256;INTEG=HMAC_SHA2_512_256,HMAC_SHA1_96,HMAC_SHA2_256_128;DH=NONE;ESN=DISABLED 4:ESP:ENCR=AES_GCM_C_128;INTEG=NONE;DH=NONE;ESN=DISABLED 5:ESP:ENCR=AES_CBC_128;INTEG=HMAC_SHA1_96,HMAC_SHA2_256_128;DH=NONE;ESN=DISABLED
    Feb 29 11:55:04 oe1 pluto[13677]: "clear-or-private#0.0.0.0/0"[1] ...209.250.233.90 #2: negotiated connection [108.61.189.206-108.61.189.206:0-65535 0] -> [209.250.233.90-209.250.233.90:0-65535 0]
    
    • Log-Meldungen auf der Client Seite
    # journalctl -fu ipsec
    Feb 29 11:55:04 oe2 pluto[4957]: "private-or-clear#108.61.189.206/32"[2] ...108.61.189.206: constructed local IKE proposals for private-or-clear#108.61.189.206/32 (IKE SA initiator selecting KE): 1:IKE:ENCR=AES_GCM_C_256;PRF=HMAC_SHA2_512,HMAC_SHA2_256;INTEG=NONE;DH=ECP_256,ECP_384,ECP_521,MODP2048,MODP3072,MODP4096,MODP8192 2:IKE:ENCR=CHACHA20_POLY1305;PRF=HMAC_SHA2_512,HMAC_SHA2_256;INTEG=NONE;DH=ECP_256,ECP_384,ECP_521,MODP2048,MODP3072,MODP4096,MODP8192 3:IKE:ENCR=AES_CBC_256;PRF=HMAC_SHA2_512,HMAC_SHA2_256;INTEG=HMAC_SHA2_512_256,HMAC_SHA2_256_128;DH=ECP_256,ECP_384,ECP_521,MODP2048,MODP3072,MODP4096,MODP8192 4:IKE:ENCR=AES_GCM_C_128;PRF=HMAC_SHA2_512,HMAC_SHA2_256;INTEG=NONE;DH=ECP_256,ECP_384,ECP_521,MODP2048,MODP3072,MODP4096,MODP8192 5:IKE:ENCR=AES_CBC_128;PRF=HMAC_SHA2_256;INTEG=HMAC_SHA2_256_128;DH=ECP_256,ECP_384,ECP_521,MODP2048,MODP3072,MODP4096,MODP8192
    Feb 29 11:55:04 oe2 pluto[4957]: "private-or-clear#108.61.189.206/32"[2] ...108.61.189.206: constructed local ESP/AH proposals for private-or-clear#108.61.189.206/32 (IKE SA initiator emitting ESP/AH proposals): 1:ESP:ENCR=AES_GCM_C_256;INTEG=NONE;DH=NONE;ESN=DISABLED 2:ESP:ENCR=CHACHA20_POLY1305;INTEG=NONE;DH=NONE;ESN=DISABLED 3:ESP:ENCR=AES_CBC_256;INTEG=HMAC_SHA2_512_256,HMAC_SHA1_96,HMAC_SHA2_256_128;DH=NONE;ESN=DISABLED 4:ESP:ENCR=AES_GCM_C_128;INTEG=NONE;DH=NONE;ESN=DISABLED 5:ESP:ENCR=AES_CBC_128;INTEG=HMAC_SHA1_96,HMAC_SHA2_256_128;DH=NONE;ESN=DISABLED
    Feb 29 11:55:04 oe2 pluto[4957]: "private-or-clear#108.61.189.206/32"[2] ...108.61.189.206 #3: loading root certificate cache
    Feb 29 11:55:04 oe2 pluto[4957]: "private-or-clear#108.61.189.206/32"[2] ...108.61.189.206 #3: certificate verified OK: CN=oe1.dane.onl
    Feb 29 11:55:04 oe2 pluto[4957]: "private-or-clear#108.61.189.206/32"[2] ...108.61.189.206 #3: Authenticated using RSA
    Feb 29 11:55:04 oe2 pluto[4957]: "private-or-clear#108.61.189.206/32"[2] ...108.61.189.206 #3: negotiated connection [209.250.233.90-209.250.233.90:0-65535 0] -> [108.61.189.206-108.61.189.206:0-65535 0]
    
    • IPSec Kommunikation kann per tcpdump betrachtet werden
    tcpdump -n -i eth0 esp or udp port 500 or udp port 4500