in der Praxis
SSH-Tunneling verwandelt eine einzige verschlüsselte Verbindung in einen sicheren Kanal für beliebige TCP-Dienste, die sonst nur lokal auf einem Server erreichbar sind. Dieser Artikel erklärt lokale, entfernte und dynamische Portweiterleitung an konkreten Beispielen, zeigt den Tunnel zu einer nur serverintern erreichbaren Datenbank, den Einsatz von autossh für dauerhaft stabile Verbindungen und die Sicherheitsrisiken offen gelassener Tunnel.
Inhaltsverzeichnis
- 1. Was SSH-Tunneling technisch bedeutet
- 2. Lokale Portweiterleitung mit ssh -L
- 3. Entfernte Portweiterleitung mit ssh -R
- 4. Dynamische Portweiterleitung und SOCKS-Proxy mit ssh -D
- 5. Praxisfall: Tunnel zu einer nur serverintern erreichbaren Datenbank
- 6. autossh für dauerhaft stabile Tunnel
- 7. SSH-Config: Tunnel dauerhaft und wiederverwendbar konfigurieren
- 8. Sicherheitsaspekte offen gelassener Tunnel
- 9. SSH-Tunneling-Varianten im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Was SSH-Tunneling technisch bedeutet
SSH wird meist auf die Remote-Shell reduziert, doch das zugrunde liegende Protokoll ist ein generischer, verschlüsselter Transportkanal für beliebige TCP-Verbindungen. Über eine einzige authentifizierte Verbindung auf Port 22 kann der SSH-Client mehrere logische Kanäle multiplexen, darunter auch Kanäle vom Typ direct-tcpip und forwarded-tcpip. Genau diese Kanaltypen sind die technische Grundlage von Port-Forwarding: Statt Daten für eine Shell-Sitzung zu übertragen, transportiert der Kanal rohe TCP-Nutzdaten einer beliebigen Anwendung, unsichtbar für alles zwischen Client und Server.
Daraus ergeben sich drei fundamental unterschiedliche Weiterleitungsrichtungen. Lokale Portweiterleitung (-L) holt einen entfernten Dienst zu dir. Entfernte Portweiterleitung (-R) macht einen lokalen Dienst auf dem entfernten Server sichtbar. Dynamische Portweiterleitung (-D) baut aus derselben Verbindung einen vollwertigen SOCKS5-Proxy. Alle drei nutzen dieselbe verschlüsselte Verbindung und dieselbe Authentifizierung, unterscheiden sich aber grundlegend darin, wer auf wen zugreifen darf.
2. Lokale Portweiterleitung mit ssh -L
Das lokale Forwarding ist die häufigste Variante: Ein Port auf dem eigenen Rechner wird transparent zu einem Zielport hinter dem SSH-Server durchgereicht. Die Syntax ssh -L lokaler_port:zielhost:zielport user@sshserver öffnet auf dem lokalen Rechner einen Listener. Jede Verbindung dorthin wird über den SSH-Server zum zielhost:zielport weitergeleitet, wobei zielhost aus Sicht des SSH-Servers aufgelöst wird, nicht aus Sicht des Client-Rechners. Das ist der Punkt, an dem viele Einsteiger stolpern: localhost als Zielhost bezieht sich auf den Server, nicht auf den eigenen Laptop.
Typischer Einsatz ist der Zugriff auf ein internes Admin-Interface, das nur auf dem Serverhost selbst lauscht, etwa ein Monitoring-Dashboard auf Port 9090. Mit -N unterdrückt man die Remote-Shell komplett und nutzt die Verbindung ausschließlich als Tunnel, mit -f schickt man den Prozess in den Hintergrund. Diese Kombination ist die Grundlage fast aller produktiven Tunnel-Setups.
# Local forwarding: reach an internal dashboard bound to the server itself
$ ssh -L 9090:127.0.0.1:9090 -N -f deploy@app-server.example.com
# Verify: local listener now exists on the client
$ ss -tlnp | grep 9090
LISTEN 0 128 127.0.0.1:9090 0.0.0.0:* users:(("ssh",pid=41210,fd=6))
# Access the internal dashboard through the tunnel
$ curl -s http://127.0.0.1:9090/metrics | head -3
# Close the backgrounded tunnel again
$ pkill -f "9090:127.0.0.1:9090"
3. Entfernte Portweiterleitung mit ssh -R
Die entfernte Portweiterleitung kehrt die Richtung um: Ein Dienst, der lokal auf deinem Rechner läuft, wird über den SSH-Server nach außen sichtbar gemacht. Die Syntax ssh -R remote_port:localhost:lokaler_port user@sshserver öffnet auf dem entfernten Server einen Listener, der Verbindungen zurück zum lokalen Zielport tunnelt. Standardmäßig bindet der Server diesen Listener nur an 127.0.0.1, sodass er ausschließlich vom Server selbst erreichbar ist, nicht von außen. Erst die Server-Option GatewayPorts ändert dieses Verhalten.
Praktisch relevant ist das etwa, um einen lokal laufenden Webhook-Empfänger während der Entwicklung kurzfristig über einen erreichbaren Server testen zu lassen, ohne einen öffentlichen Tunneldienst eines Drittanbieters zu nutzen. Ein weiteres Szenario: Support-Zugriff auf ein Gerät hinter einer restriktiven Firewall, das selbst eine ausgehende Verbindung zu einem erreichbaren Jump-Host aufbaut und darüber eine Rückverbindung anbietet, ganz ohne eingehende Firewall-Regel am Zielgerät.
Konkret sieht der Aufbau so aus: ssh -R 8443:localhost:3000 -N -f deploy@jump-host.example.com öffnet auf dem Jump-Host einen an 127.0.0.1:8443 gebundenen Listener, sichtbar mit ss -tlnp | grep 8443 direkt auf dem Server. Beim Support-Szenario dreht sich die Rolle um: Das Gerät selbst führt ssh -R 2222:localhost:22 -N -f support@jump-host.example.com aus und macht seine eigene SSH-Shell über den Jump-Host per ssh -p 2222 localhost erreichbar, ganz ohne eingehende Verbindung von außen zum Gerät selbst.
4. Dynamische Portweiterleitung und SOCKS-Proxy mit ssh -D
Bei mehr als ein oder zwei Zielen wird das Anlegen einzelner lokaler Weiterleitungen schnell unübersichtlich. Die dynamische Portweiterleitung löst dieses Problem, indem sie aus der SSH-Verbindung einen vollwertigen SOCKS5-Proxy macht. Der Befehl ssh -D 1080 -N -f user@sshserver öffnet lokal Port 1080 als SOCKS5-Endpunkt. Jede Anwendung, die SOCKS5 unterstützt, kann darüber beliebige Ziele erreichen, die vom SSH-Server aus routbar sind, ohne dass für jedes Ziel ein eigener -L-Eintrag nötig wäre.
In der Praxis konfiguriert man Browser, curl oder ganze Kommandozeilen-Toolchains über proxychains so, dass ihr Traffic durch den SOCKS-Proxy läuft. Damit lässt sich zum Beispiel ein komplettes internes Subnetz erreichen, das nur vom Jump-Host aus geroutet wird, etwa um mehrere interne Webanwendungen im selben VPC-Segment zu testen, ohne für jede einzeln einen Tunnel aufzubauen. Wichtig: DNS-Auflösung läuft standardmäßig lokal, nicht über den Proxy, sofern man nicht explizit --socks5-hostname oder die entsprechende Browser-Einstellung für Remote-DNS aktiviert.
# Dynamic forwarding: turn the SSH connection into a SOCKS5 proxy
$ ssh -D 1080 -N -f deploy@jump-host.example.com
# Route a single curl request through the proxy, resolving DNS remotely
$ curl --socks5-hostname 127.0.0.1:1080 http://internal-app.local/health
# Route an arbitrary CLI tool through the same proxy via proxychains
$ echo "socks5 127.0.0.1 1080" >> /etc/proxychains.conf
$ proxychains mysql -h db-internal.local -u reporting -p
# Verify the proxy listener is up
$ ss -tlnp | grep 1080
5. Praxisfall: Tunnel zu einer nur serverintern erreichbaren Datenbank
Ein sehr häufiges Szenario in Magento- und PHP-Deployments: MySQL oder MariaDB läuft auf dem Applikationsserver, ist aber bewusst nur an 127.0.0.1 gebunden, um jeden direkten Zugriff aus dem Netz von vornherein auszuschließen. Für Reporting, Migrations-Debugging oder den Anschluss eines GUI-Clients wie DBeaver oder Sequel Ace braucht man dennoch Zugriff von der eigenen Maschine aus. Genau hierfür ist lokale Portweiterleitung das richtige Werkzeug, ohne die Datenbank jemals über das öffentliche Netz erreichbar zu machen.
Der Tunnel ssh -L 3307:127.0.0.1:3306 -N -f deploy@app-server.example.com bindet lokal Port 3307, weil 3306 auf dem eigenen Rechner oft bereits von einer lokalen MySQL-Instanz belegt ist. Der GUI-Client verbindet sich dann schlicht gegen 127.0.0.1:3307, als wäre die Datenbank lokal installiert. Der SSH-Server sieht dabei ausschließlich verschlüsselten SSH-Traffic auf Port 22, niemals unverschlüsselten MySQL-Traffic auf der Leitung nach außen, was diesen Weg auch für sensible Produktivdaten deutlich sicherer macht als ein direkt exponierter Datenbank-Port.
#!/usr/bin/env bash
# db-tunnel.sh: open a persistent local tunnel to the app server's database
set -euo pipefail
REMOTE_HOST="app-server.example.com"
REMOTE_USER="deploy"
LOCAL_PORT=3307
DB_PORT=3306
# Skip if a tunnel on this port is already running
if ss -tln | grep -q ":${LOCAL_PORT} "; then
echo "[INFO] Tunnel on port ${LOCAL_PORT} already active"
exit 0
fi
autossh -M 0 -f -N \
-o "ServerAliveInterval=30" \
-o "ServerAliveCountMax=3" \
-o "ExitOnForwardFailure=yes" \
-L "${LOCAL_PORT}:127.0.0.1:${DB_PORT}" \
"${REMOTE_USER}@${REMOTE_HOST}"
echo "[OK] Database reachable at 127.0.0.1:${LOCAL_PORT}"
mysql -h 127.0.0.1 -P "${LOCAL_PORT}" -u reporting -p -e "SELECT 1"
6. autossh für dauerhaft stabile Tunnel
Ein manuell gestarteter Tunnel überlebt weder einen WLAN-Wechsel noch einen kurzen Netzwerkausfall noch einen Server-Neustart. Genau dieses Problem löst autossh: Es startet den eigentlichen ssh-Prozess, überwacht die Verbindung fortlaufend und baut sie bei einem Abbruch automatisch neu auf, ohne manuelles Eingreifen. In Kombination mit den Optionen ServerAliveInterval und ServerAliveCountMax erkennt SSH selbst tote Verbindungen zuverlässig, noch bevor ein TCP-Timeout überhaupt greift, und signalisiert autossh den nötigen Neustart.
Die Option -M 0 deaktiviert den älteren Monitoring-Port-Mechanismus von autossh zugunsten der SSH-eigenen Keepalive-Prüfung, was in modernen OpenSSH-Versionen der robustere Ansatz ist und keinen zusätzlichen offenen Port erfordert. Für dauerhaften Betrieb gehört ein solcher Tunnel in eine systemd-Unit statt in eine manuell gestartete Hintergrundsitzung: Nur so übersteht er auch einen Server-Reboot automatisch und lässt sich mit systemctl status wie jeder andere Dienst überwachen und im Fehlerfall per Journal nachvollziehen.
7. SSH-Config: Tunnel dauerhaft und wiederverwendbar konfigurieren
Lange ssh-Befehlszeilen mit mehreren Optionen sind fehleranfällig und schlecht wiederverwendbar. Die Datei ~/.ssh/config löst das, indem sie Host-Aliase mit vorkonfigurierten Optionen definiert. Ein Host-Block kann LocalForward, RemoteForward und DynamicForward direkt enthalten, sodass ein einfaches ssh db-tunnel genügt, um exakt denselben Tunnel wie in einer langen Kommandozeile aufzubauen. Das reduziert Tippfehler und macht Tunnel-Definitionen versionierbar, wenn die Config-Datei Teil eines dotfiles-Repositories ist.
Die Option ExitOnForwardFailure=yes ist für Automatisierung entscheidend: Ohne sie startet SSH auch dann erfolgreich, wenn die Portweiterleitung selbst fehlschlägt, etwa weil der lokale Port bereits belegt ist, und Skripte gehen fälschlich von einem funktionierenden Tunnel aus. Mit dieser Option bricht die Verbindung sofort ab, wenn das Forwarding nicht zustande kommt, was Fehler in CI-Pipelines und Deploy-Skripten früh sichtbar macht statt sie stillschweigend zu verschlucken.
# ~/.ssh/config: reusable tunnel definitions as host aliases
Host db-tunnel
HostName app-server.example.com
User deploy
LocalForward 3307 127.0.0.1:3306
ServerAliveInterval 30
ServerAliveCountMax 3
ExitOnForwardFailure yes
# no shell needed, tunnel only
RequestTTY no
RemoteCommand none
Host webhook-relay
HostName jump-host.example.com
User deploy
RemoteForward 8443 localhost:3000
ExitOnForwardFailure yes
Host socks-proxy
HostName jump-host.example.com
User deploy
DynamicForward 1080
ServerAliveInterval 30
8. Sicherheitsaspekte offen gelassener Tunnel
Ein SSH-Tunnel ist nur so sicher wie der zugrunde liegende Schlüssel und die Konfiguration des Servers. Standardmäßig ist die Server-Option GatewayPorts no aktiv, das heißt entfernte Weiterleitungen binden nur an 127.0.0.1 und sind nicht von außen erreichbar. Wird diese Option versehentlich auf yes gesetzt, macht jede entfernte Weiterleitung den weitergeleiteten Dienst potenziell für das gesamte Netz sichtbar, in dem der SSH-Server steht, oft ohne dass der Nutzer, der den Tunnel öffnet, sich dessen bewusst ist.
Ein zweites, häufig übersehenes Risiko: Wer nur Tunnel-Zugriff gewähren will, sollte einen eigenen SSH-Key mit eingeschränkten Optionen in authorized_keys hinterlegen, etwa command="echo restricted",no-pty,permitopen="127.0.0.1:3306". So kann der Key ausschließlich zum genannten Ziel tunneln, aber keine Shell öffnen und auch keine beliebigen anderen Ports erreichen. Vergessene, dauerhaft offene Tunnel sind zudem ein Monitoring-Thema: Mit ss -tlnp | grep ssh und regelmäßiger Auswertung lassen sich verwaiste Tunnel identifizieren, bevor sie zu unbemerkten Zugriffspfaden in produktive Systeme werden.
{
"audit_run": "2026-07-12T07:40:00+02:00",
"host": "app-server.example.com",
"open_ssh_forward_listeners": [
{
"local_bind": "127.0.0.1:8443",
"type": "remote_forward",
"owning_pid": 9021,
"connected_since": "2026-06-02T11:12:04+02:00",
"flag": "stale_over_30_days"
},
{
"local_bind": "127.0.0.1:3307",
"type": "local_forward",
"owning_pid": 41210,
"connected_since": "2026-07-12T06:58:11+02:00",
"flag": "active_expected"
}
],
"recommended_action": "terminate stale_over_30_days entries and rotate the associated key"
}
9. SSH-Tunneling-Varianten im direkten Vergleich
Ob lokale, entfernte oder dynamische Weiterleitung die richtige Wahl ist, hängt von der Zugriffsrichtung und der Anzahl der Ziele ab. Genauso wichtig ist die Wahl zwischen einem fragilen Ad-hoc-Setup und einem robusten, dauerhaften Pattern, gerade bei Datenbank-Tunneln, die im Alltag ständig gebraucht werden.
| Szenario | Unsicherer / fragiler Ansatz | Empfohlenes SSH-Tunneling-Pattern | Vorteil |
|---|---|---|---|
| Datenbank-Zugriff aus der Ferne | Datenbank-Port an 0.0.0.0 binden und in der Firewall öffnen | ssh -L Tunnel zu Datenbank auf 127.0.0.1 | Datenbank bleibt von außen komplett unerreichbar |
| Tunnel dauerhaft am Laufen halten | Terminal mit ssh -L manuell offen lassen | autossh mit ServerAliveInterval als systemd-Service | Automatischer Reconnect nach Netzwerkausfall oder Reboot |
| Internen Webservice testen | Firewall-Regel für öffentlichen Zugriff anlegen | ssh -R Reverse-Tunnel nur für den Testzeitraum | Kein dauerhaft offener Port am Zielsystem |
| Mehrere interne Anwendungen erreichen | Für jede Anwendung einen eigenen Tunnel oder VPN-Client | ssh -D dynamischer SOCKS5-Proxy für alle Ziele | Ein einziger Tunnel für beliebig viele Ziele |
| Tunnel-Zugriff einschränken | Vollzugriff über den normalen persönlichen SSH-Key | Eigener Key mit permitopen und no-pty in authorized_keys | Key kann nicht für Shell-Zugriff missbraucht werden |
Mironsoft
Server-Zugriff, Netzwerksicherheit und Deployment-Infrastruktur
Sicheren Zugriff auf interne Dienste ohne offene Ports?
Wir richten SSH-Tunnel-Setups mit autossh und systemd ein, schränken Tunnel-Keys auf das Nötigste ein und sorgen dafür, dass eure Datenbanken und internen Dienste niemals direkt exponiert werden müssen.
Tunnel-Setup
Lokale, entfernte und dynamische Weiterleitung sauber über ~/.ssh/config verwaltet
Persistenz
autossh als systemd-Service, übersteht Reboots und Netzwerkausfälle automatisch
Zugriffshärtung
Eingeschränkte Tunnel-Keys mit permitopen und regelmäßiges Monitoring offener Tunnel
10. Zusammenfassung
SSH-Tunneling und Port-Forwarding lösen drei unterschiedliche Zugriffsprobleme mit derselben verschlüsselten Grundlage. Lokale Weiterleitung (-L) holt einen entfernten Dienst zu dir, etwa eine Datenbank, die bewusst nur an 127.0.0.1 auf dem Server gebunden ist. Entfernte Weiterleitung (-R) macht einen lokalen Dienst über den Server erreichbar, ohne eingehende Firewall-Regeln am eigentlichen Zielgerät. Dynamische Weiterleitung (-D) bündelt beliebig viele Ziele über einen einzigen SOCKS5-Proxy.
Für den produktiven Einsatz reicht ein manuell gestarteter Tunnel nicht aus. autossh mit ServerAliveInterval, verpackt in einer systemd-Unit, hält Tunnel dauerhaft stabil und übersteht Netzwerkausfälle und Reboots automatisch. Genauso wichtig ist die Sicherheitsseite: GatewayPorts no, eingeschränkte Tunnel-Keys mit permitopen und regelmäßiges Monitoring offener Weiterleitungen verhindern, dass vergessene Tunnel zu unbemerkten Zugriffspfaden in produktive Systeme werden.
SSH-Tunneling und Port-Forwarding: Das Wichtigste auf einen Blick
Lokal vs. entfernt
ssh -L holt einen entfernten Dienst zu dir, ssh -R macht einen lokalen Dienst über den Server erreichbar.
Dynamisch mit SOCKS
ssh -D baut einen SOCKS5-Proxy für beliebig viele Ziele, ohne für jedes einen eigenen Tunnel anzulegen.
Datenbank-Tunnel in der Praxis
ssh -L 3307:127.0.0.1:3306 erreicht eine Datenbank, die nur serverintern lauscht, ohne sie je öffentlich zu exponieren.
Persistenz & Sicherheit
autossh als systemd-Service für Stabilität, permitopen-Keys und Monitoring gegen vergessene offene Tunnel.