1. Einen Schlüssel für den Tunnel erzeugen
Legen Sie auf dem Rechner, der den Tunnel startet, ein lokales Konto und einen Schlüssel ohne Passphrase an, weil der Dienst unbeaufsichtigt startet. Speichern Sie dann den Host-Schlüssel des Servers, damit die erste Verbindung nicht zum Nachfragen anhält:
sudo apt install autossh
sudo useradd -m -s /usr/sbin/nologin tunnel
sudo -u tunnel mkdir -m 700 /home/tunnel/.ssh
sudo -u tunnel ssh-keygen -t ed25519 -N "" -C tunnel -f /home/tunnel/.ssh/id_ed25519
ssh-keyscan -t ed25519 server | sudo -u tunnel tee /home/tunnel/.ssh/known_hosts
Vergleichen Sie diesen Host-Schlüssel mit dem echten Fingerabdruck des Servers, bevor Sie sich darauf verlassen. Siehe known_hosts erklärt.
2. Das Konto auf dem Server einschränken
Ein Schlüssel ohne Passphrase auf einem entfernten Rechner sollte nur diesen Tunnel öffnen. Legen Sie auf dem Server ein Konto namens tunnel an und tragen Sie den öffentlichen Schlüssel in dessen ~/.ssh/authorized_keys ein:
restrict,port-forwarding ssh-ed25519 AAAA... tunnel
Fügen Sie dann Folgendes am Ende von /etc/ssh/sshd_config hinzu:
Match User tunnel
AllowTcpForwarding remote
PermitListen localhost:2222
X11Forwarding no
ForceCommand /usr/sbin/nologin
sudo sshd -t && sudo systemctl reload ssh
Das Ergebnis haben wir mit diesem Schlüssel getestet:
| Versuch | Ergebnis |
|---|---|
-R 2222:localhost:22 | Funktioniert |
-R 2223:localhost:22 | remote port forwarding failed for listen port 2223 |
| Einen Befehl oder eine Shell ausführen | This account is currently not available. |
-L 9000:localhost:22 | Verbindung geschlossen, nichts kommt durch |
Die letzte Zeile braucht den Match-Block. Mit nur permitlisten="localhost:2222" in der authorized_keys funktionierte in unserem Test eine -L-Weiterleitung weiterhin, sodass der Schlüssel Dienste auf dem Loopback des Servers erreichen konnte. Mehr zu Schlüsseloptionen steht unter SSH-Schlüssel einschränken.
3. Den Dienst anlegen
Speichern Sie dies als /etc/systemd/system/autossh-tunnel.service:
[Unit]
Description=Reverse SSH tunnel to server
Wants=network-online.target
After=network-online.target
[Service]
User=tunnel
Environment=AUTOSSH_GATETIME=0
ExecStart=/usr/bin/autossh -M 0 -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -i /home/tunnel/.ssh/id_ed25519 -R 2222:localhost:22 tunnel@server
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now autossh-tunnel.service
systemctl status autossh-tunnel
Was die einzelnen Teile tun
-M 0schaltet den eigenen Monitor-Port von autossh aus, sodass essshnur dann neu startet, wennsshsich beendet. Die autossh-README schlägt die Keepalive-Optionen als Ersatz für den Monitor-Port vor.ServerAliveInterval=30undServerAliveCountMax=3lassensshsich nach etwa 90 bis 120 Sekunden Stille vom Server beenden. In unserem Test dauerte es bei 5 Sekunden Intervall mit gleicher Anzahl 20 Sekunden, und ein 100 Sekunden langes Einfrieren mit diesen Werten löste es nicht aus.ExitOnForwardFailure=yeslässtsshsich beenden, wenn der Port nicht geöffnet werden kann. Ohne die Option können Sie eine lebende Verbindung ohne Tunnel bekommen.AUTOSSH_GATETIME=0lässt autossh weiter versuchen, auch wenn der erste Versuch scheitert, was beim Booten wichtig ist.Restart=alwaysbringt den Dienst zurück, wenn autossh selbst stirbt.- Verwenden Sie hier nicht das
-fvon autossh, denn systemd erwartet, dass der Prozess im Vordergrund bleibt.
4. Die Wiederherstellung beobachten
| Was wir taten | Was geschah |
|---|---|
| Sitzung auf dem Server beendet | autossh startete sofort ein neues ssh |
| Server 28 Sekunden eingefroren, bei 5 Sekunden Intervall | Timeout, server server not responding. nach 20 Sekunden, dann eine neue Verbindung, sobald der Server zurück war |
autossh mit kill -9 beendet, und sein ssh | systemd startete den Dienst neu, und NRestarts stieg auf 1 |
Das können Sie selbst mit journalctl -u autossh-tunnel -f beobachten. Auf dem Server zeigt ss -ltn | grep 2222 den Port.
Wenn der Port nach einem Netzwerkausfall belegt bleibt
Nach einem echten Ausfall kann der Server die alte Verbindung und ihren Port noch halten, sodass die neue mit remote port forwarding failed scheitert, bis die alte ein Timeout erreicht. Fügen Sie im Match-Block auf dem Server hinzu:
ClientAliveInterval 30
ClientAliveCountMax 3
Der Server trennt dann tote Clients nach etwa 90 Sekunden, und die Wiederholungen gelingen.
Weitere Gründe für einen belegten Port finden Sie unter SSH-Tunnel: Address already in use und remote port forwarding failed.
Brauchen Sie autossh überhaupt
Mit systemd und den Keepalive-Optionen funktioniert auch einfaches ssh. ssh beendet sich, und Restart=always startet es neu. Ersetzen Sie den Befehl in der Unit durch:
ExecStart=/usr/bin/ssh -N -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -o ExitOnForwardFailure=yes -i /home/tunnel/.ssh/id_ed25519 -R 2222:localhost:22 tunnel@server
In unserem Test erholte sich das von einer beendeten Sitzung mit einem systemd-Neustart, nach den 10 Sekunden von RestartSec. autossh startet früher neu und ist die Option, wenn Sie kein systemd haben.
Ein Unterschied fiel uns auf: systemctl restart protokollierte status=1/FAILURE für autossh beim Beenden. Der Dienst startete danach trotzdem normal wieder.
Fragen
Was bewirkt autossh -M 0?
Es schaltet den Überwachungsport ab. autossh startet ssh dann nur neu, wenn ssh sich beendet, und ServerAliveInterval und ServerAliveCountMax sorgen dafür, dass das bei einer toten Verbindung passiert.
Brauche ich autossh, wenn ich systemd nutze?
Nicht unbedingt. Auch ein normales ssh mit den Keepalive-Optionen und Restart=always kommt zurück. autossh startet schneller neu und läuft auch ohne systemd.
Warum scheitert mein Reverse-Tunnel nach einem Netzausfall?
Der Server hält eventuell noch die alte Verbindung und ihren Port. Setzen Sie auf dem Server ClientAliveInterval 30 und ClientAliveCountMax 3, dann trennt er tote Clients nach etwa 90 Sekunden.
Quellen
- autossh README
- ssh_config(5), OpenBSD-Handbuch
- sshd_config(5), OpenBSD-Handbuch
- systemd.service(5), Handbuch