Dienstag, 19. September 2017

IPSec VPN mit StrongSwan auf Debian Stretch (auch für Android 6+)

Nachdem ich Jahrelang Racoon aus den IPSec-Tools als IPSec VPN verwendet habe, musste ich mir für die Debian Stretch Installation etwas Neues suchen.

Seit Racoon 0.8.x funktionierte die PrivilegeSeparation nicht unter Linux - d.h. der Prozess kann nicht als non-root Benutzer laufen (Versuche enden immer in einem Segfault).
Daher hatte ich immer ein altes Lenny-Racoon  Paket im Einsatz; unter Sicherheitsaspekten ebenfalls ein Alptraum...

StrongSwan ist einer der Nachfolger des FreeS/WAN-Projekts, wird mit Debian Stretch geliefert und soll Racoon nun ersetzen.

Andriod VPN Dialog

Vom Protokollstapel her kann IPSec ziemlich unübersichtlich werden, zumal viele Anleitungen einen Transport über das Layer 2 Tunneling Protocol (L2TP) beschreiben. Dafür müsste man aber auch (virtuelle) Interfaces auf dem Gateway einrichten - mit nativem IPSec (transport über IP in ESP Paketen) geht es deutlich einfacher.
IPSec wird oft eine enorme Komplexität nachgesagt, da es mit größter Sicherheit betrieben auf Zertifikate angewiesen ist - sowohl für den Server als auch für den Client.

Es existieren zumindest zwei verbreitete Szenarien die zumindest nur ein Server Zertifikat benutzen und die Clients anhand von Benutzernamen/Passwort authentifizieren:
  • "IPSec Xauth PSK" gilt inzwischen als veraltet und unsischer, da der Hash mit dem PSK (PreSharedKey) geknackt werden kann. Wollte man es dennoch verwenden, muss in der
    /etc/strongswan.d/charon.conf
    den sogenannten Agressive Mode entgegen besseren Wissens freischalten:
    i_dont_care_about_security_and_use_aggressive_mode_psk = yes
  • "IPSec Hybrid RSA" ist mind. seit Android 5 verfügbar und wird im Folgenden eingerichtet

Installation

apt-get install libstrongswan strongswan libcharon-extra-plugins

Da ich Systembenutzer  zur Authentifizierung verwenden möchte, installiere ich ebenfalls das Paket libcharon-extra-plugins welches das "xauth-pam" Plugin enthält.

Die Grundkonfiguration füge ich in einer separaten Datei hinzu. Debian liest diese bei jedem Start zusätzlich ein:
# /etc/strongswan.d/user.conf
#
# User Options for the charon IKE daemon.
#

charon {

        # DNS server assigned to peer via configuration payload (CP).
        dns1 = 8.8.8.8
        dns2 = 8.8.4.4

        # Number of worker threads in charon.
        threads = 8

        # Name of the user the daemon changes to after startup.
        user = strongswan
} 
Die DNS werden den Clients übermittelt.
Die "user" Konfiguration sorgt dafür, dass der IPSec Server nicht mit Root-Rechen läuft.

Die eigentlichen Verbindungen (conn) werden nun konfiguriert:
# /etc/ipsec.conf
#
# ipsec.conf - strongSwan IPsec configuration file
#
# basic configuration
#
config setup
        # strictcrlpolicy=yes
        # uniqueids = no

conn %default
        ikelifetime=60m
        keylife=20m
        rekeymargin=3m
        keyingtries=1
        keyexchange=ikev1

# Add connections here.

conn android_vpn
        left=
        #Common Name des Server Zertifikats
        leftid=@vpn.example.org 
        leftauth=pubkey
        # Von letsencrypt.org erzeugtes Zertifikat
        leftcert=/etc/ssl/certs/vpn.example.org/fullchain.pem
        leftsendcert=always
        leftsubnet=0.0.0.0/0
        # Änderungen an der Firewall funktioniern nur wenn charon als root laeuft
#       leftfirewall=yes
        right=%any
        rightauth=xauth-pam
        # sollen Benutzer dierekt ueber ipsec.secrets authentifiziert werden,
        # dann muss der Eintrag so lauten:
        #rightauth=xauth

        # der Adresspool fuer die Clients
        rightsourceip=192.168.123.0/24

        # Erzwinge schwachen Integritaetscheck mit SHA1 fuer Android 6+
        esp=aes-sha1!
        auto=add

Die Zeile "esp=aes-sha1!" erzwingt für die Integritätsprüfung das schwache SHA1, da Android 6+ bei SHA256 eine Verkürzung auf 96 Bit vornimmt. Ab strongSwan 5.5.3 gibt es eine Option "sha256_96=yes", die diese "falsche" Verkürzung für alle sha2/sha256 Aushandlungen anwendet. Debian 9.1 bringt allerdings nur die Version 5.5.1 mit, weshalb ich vollständig auf das schwächere SHA1 gehe.

Die Authentifizierung des Servers (left) erfolgt per PublicKey bzw. Serverzertifikat.
Verwendet man kein letsencrypt Zertifikat, so muss ggfs. das Zertifikat der Stammzertifizierungsstelle (CA) noch in den Ordner /etc/ipsec.d/cacerts/ kopiert werden.

Der Private Key wird folgendermaßen eingetragen:
# /etc/ipsec.secrets
#
# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.
: RSA /etc/ssl/certs/vpn.example.org/privkey.pem

# alternativ Benutzer Authentifizierung per XAUTH
#alex : XAUTH "Super geheimes Passwort!"

Da der charon Prozess nicht als root läuft, kann er selbst keine IPTABLES Änderungen vornehmen, diese erledigt mein rc.local Script:
#!/bin/sh
# /etc/rc.local
#
iptables -t nat -A POSTROUTING -s 192.168.123.0/24 -j MASQUERADE
iptables -t mangle -A FORWARD -m policy --pol ipsec --dir in -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
iptables -t mangle -A FORWARD -m policy --pol ipsec --dir out -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360

Damit der Kernel überhaupt Pakete Routet muss noch ein SysCTL Eintrag vorgenommen werden:
# /etc/sysctl.d/forward.conf

net.ipv4.ip_forward=1

 Authentifizierung lokaler Benutzer per PAM

Neben der Einstellung "rightauth=xauth-pam" muss die PAM Authentifizierung noch konfiguriert werden:
# /etc/strongswan.d/charon/xauth-pam.conf
#
xauth-pam {
    load = yes

    # PAM service to be used for authentication.
    pam_service = ipsec
}

Den PAM Authentifizierungsdienst habe ich mit "ipsec" vorgegeben, weil ich die Authorisierung an eine bestimmte Gruppenmitgliedschaft knüpfen möchte. Sonst könnte auch einfach der bereits existierende "login" Dienst verwendet werden.

Der Vollständigkeit halber noch die PAM Konfiguration:
# /etc/pam.d/ipsec
#
# Allow only members of listed group
auth    required        pam_listfile.so onerr=fail item=group sense=allow file=/etc/ipsec.group.allow

@include common-auth
@include common-account

# /etc/ipsec.group.allow
#
# List of groups allowd to do IPSec
vpn

In der /etc/group bekommen die VPN Benutzer die Gruppe "vpn" entsprechend als "Secondary Group"
...
vpn:x:999:alex
...