SSH VPN
SSH-VPN mit tun-Device
Beschreibung
OpenSSH kann mehr als Portweiterleitung: Mit der Option -w baut es auf beiden
Seiten ein tun-Interface auf. Damit entsteht ein echter Layer-3-Tunnel, durch den
sich beliebige IP-Pakete routen lassen — ein vollwertiges, wenn auch einfaches VPN über Port 22.
Typischer Einsatz: Von aussen ist nur der SSH-Port einer Maschine erreichbar. Über diesen einen Zugang soll das gesamte dahinterliegende interne Netz erreichbar werden.
Szenario
- Client (aussen): Notebook des Administrators
- Server (innen):
it2XX, per SSH von aussen erreichbar, Gateway ins interne Netz - Internes Netz:
192.168.2XX.0/24 - Tunnelnetz:
10.9.0.0/30
| Rolle | Interface | Adresse |
|---|---|---|
| Client | tun0 | 10.9.0.1/30 |
| Server it2XX | tun0 | 10.9.0.2/30 |
| Server it2XX | ens18 | 192.168.2XX.10/24 |
Voraussetzungen
Das Anlegen eines tun-Devices ist eine privilegierte Operation. Die SSH-Sitzung muss deshalb auf beiden Seiten mit Root-Rechten laufen. Ein unprivilegierter Benutzer kann den Tunnel nicht aufbauen.
Konfiguration des sshd
- Konfigurationsdatei öffnen
- sudo vim /etc/ssh/sshd_config
- Folgende Optionen setzen
PermitTunnel yes PermitRootLogin prohibit-password
- Dienst neu starten
- sudo systemctl restart ssh
- Konfiguration prüfen
- sudo sshd -T | grep -i permittunnel
Tunnel aufbauen
- Verbindung mit tun-Device herstellen
- sudo ssh -w 0:0 root@it2XX.example.net
Die Angabe 0:0 bedeutet: lokal tun0, entfernt tun0. Solange
diese Sitzung besteht, existiert der Tunnel. Die Interfaces sind nach dem Verbindungsaufbau
vorhanden, aber noch ohne Adresse und im Zustand DOWN.
- Interfaces kontrollieren (auf beiden Seiten)
- ip link show tun0
Adressvergabe
Auf dem Client
- Adresse zuweisen und Interface aktivieren
- sudo ip addr add 10.9.0.1/30 dev tun0
- sudo ip link set tun0 up
Auf dem Server it2XX
- Adresse zuweisen und Interface aktivieren
- sudo ip addr add 10.9.0.2/30 dev tun0
- sudo ip link set tun0 up
Test der Punkt-zu-Punkt-Verbindung
- Gegenstelle anpingen
- ping -c 3 10.9.0.2
Erst wenn dieser Ping funktioniert, machen Routing und NAT Sinn.
Routing und NAT auf dem Server
- IP-Forwarding dauerhaft aktivieren
- echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-forward.conf
- sudo sysctl --system
- NAT-Tabelle und Postrouting-Chain anlegen
- sudo nft add table ip nat
- sudo nft 'add chain ip nat postrouting { type nat hook postrouting priority srcnat ; }'
- Masquerading für das Tunnelnetz
- sudo nft add rule ip nat postrouting ip saddr 10.9.0.0/30 oifname "ens18" masquerade
- Filter-Regeln für den Forward-Weg
- sudo nft 'add chain ip filter forward { type filter hook forward priority filter ; policy drop ; }'
- sudo nft add rule ip filter forward ct state established,related accept
- sudo nft add rule ip filter forward ct state new iifname "tun0" oifname "ens18" accept
- Regelwerk anzeigen
- sudo nft list ruleset
Ohne Masquerading würden die Antwortpakete aus dem internen Netz an 10.9.0.1
gerichtet — eine Adresse, die dort niemand kennt. Alternativ liesse sich auf den internen
Hosts eine statische Route setzen; Masquerading ist der Weg mit dem geringsten Eingriff.
Route auf dem Client
- Internes Netz über den Tunnel routen
- sudo ip route add 192.168.2XX.0/24 via 10.9.0.2
- Route prüfen
- ip route get 192.168.2XX.20
- Erreichbarkeit testen
- ping -c 3 192.168.2XX.20
Automatisierung über die ssh-config
OpenSSH kann Adressvergabe und Routing selbst anstossen.
- Datei anlegen
- vim ~/.ssh/config
Host vpn-it2XX
HostName it2XX.example.net
User root
Tunnel point-to-point
TunnelDevice 0:0
PermitLocalCommand yes
LocalCommand ip addr add 10.9.0.1/30 dev tun0 && ip link set tun0 up && ip route add 192.168.2XX.0/24 via 10.9.0.2
ServerAliveInterval 30
ServerAliveCountMax 3
Auf der Serverseite übernimmt ein Skript die Gegenseite:
- Skript anlegen
- sudo vim /usr/local/sbin/tun-up.sh
#!/bin/bash ip addr add 10.9.0.2/30 dev tun0 ip link set tun0 up
- Skript ausführbar machen
- sudo chmod 755 /usr/local/sbin/tun-up.sh
- Verbindung starten
- sudo ssh vpn-it2XX
Persistenz mit autossh
Bricht die SSH-Sitzung ab, verschwinden Interface und Route ersatzlos. autossh
überwacht die Verbindung und baut sie automatisch neu auf.
- Paket installieren
- sudo apt install autossh
- Tunnel dauerhaft betreiben
- sudo autossh -M 0 -f -N vpn-it2XX
Grenzen des Verfahrens
- TCP over TCP
Der Tunnel läuft über eine TCP-Verbindung. Die darin transportierten TCP-Verbindungen bringen ihre eigene Fehlerkorrektur mit. Bei Paketverlust schaukeln sich beide Retransmission-Timer gegenseitig hoch, der Durchsatz bricht ein — der sogenannte TCP meltdown. Für Administrationszugriffe ist das unkritisch, für Durchsatz oder instabile Leitungen ungeeignet.
- Root auf beiden Seiten
PermitRootLogin und PermitTunnel sind Zugeständnisse, die in
produktiven Umgebungen gut begründet sein wollen.
- Kein Ersatz für ein richtiges VPN
Für den Dauerbetrieb sind WireGuard, strongSwan oder OpenVPN die richtige Wahl. Das SSH-VPN ist das Werkzeug für den Fall, dass genau ein Port offen ist und nichts weiter installiert werden darf.
Alternative: sshuttle
sshuttle erreicht ein ähnliches Ergebnis ohne Änderung am sshd und ohne
Root-Rechte auf der Gegenseite. Es setzt die Regeln lokal selbst und leitet den Verkehr über
eine gewöhnliche SSH-Sitzung.
- Paket installieren
- sudo apt install sshuttle
- Internes Netz tunneln
- sudo sshuttle -r benutzer@it2XX.example.net 192.168.2XX.0/24
- Zusätzlich DNS über den Tunnel leiten
- sudo sshuttle --dns -r benutzer@it2XX.example.net 192.168.2XX.0/24
Für das Verständnis ist der Weg über -w der lehrreichere: Tunnel-Interface,
Forwarding und Masquerading sind dort einzeln sichtbar und einzeln zu prüfen. sshuttle ist
die Variante für den Arbeitsalltag.
Übung
- Aktivieren Sie
PermitTunnelaufit2XXund prüfen Sie die effektive Konfiguration. - Bauen Sie den Tunnel auf und vergeben Sie die Adressen des Tunnelnetzes.
- Prüfen Sie mit
ping, dass die Punkt-zu-Punkt-Strecke steht. - Aktivieren Sie IP-Forwarding und richten Sie Masquerading ein.
- Setzen Sie die Route auf dem Client und erreichen Sie einen internen Host.
- Beobachten Sie den Tunnel mit
tcpdump -i tun0in einer zweiten Sitzung, während Sie den internen Host anpingen. - Beenden Sie die SSH-Sitzung und beobachten Sie, was mit Interface und Route geschieht.
- Bauen Sie den Tunnel über
~/.ssh/configerneut auf.