termal.in

← Blog

SSH-Portweiterleitung erklärt: lokale, remote und dynamische Tunnel

· Termalin Team sshport-forwardingtunnelingtutorial

SSH-Portweiterleitung ist eines dieser Features, die man von Stack Overflow kopiert, einmal zum Laufen bringt und nie ganz versteht – und beim nächsten Mal rät man wieder, welches von -L, -R und -D dran ist. Die drei Flags sind nicht schwer; sie sind nur schlecht benannt. Hier das eine Denkmodell, das sie offensichtlich macht, ein Beispiel für jedes, und die Flags und Fallen, die entscheiden, ob der Tunnel den Nachmittag überlebt.

Die eine Frage, die das Flag wählt

Jeder SSH-Tunnel öffnet einen lauschenden Port auf der einen Maschine und schickt alles, was dort auftrifft, still an einen Ort, der von der anderen Maschine aus erreichbar ist. Die einzige Frage lautet also: Welche Seite öffnet den Port, zu dem du dich verbindest?

  • -L (lokal) – der Port öffnet auf deiner Maschine. Du verbindest lokal; der Verkehr tritt auf der Serverseite aus.
  • -R (remote) – der Port öffnet auf dem Server. Jemand verbindet dort; der Verkehr tritt auf deiner Seite aus.
  • -D (dynamisch) – der Port öffnet auf deiner Maschine, aber es ist ein SOCKS-Proxy: Das Ziel wird pro Verbindung entschieden, nicht vorab festgelegt.

Alles andere ist Detail. Behalt „welche Seite öffnet den Port“ im Kopf, und du greifst nie wieder zum falschen Flag.

Lokale Weiterleitung (-L) – einen entfernten Dienst auf localhost holen

Die häufigste. Eine Datenbank, ein internes Dashboard, eine Metrik-Oberfläche – etwas, das auf dem Server an 127.0.0.1 gebunden ist oder auf einer Maschine, die nur der Server erreicht. Du willst es auf deinem Laptop, als wäre es lokal.

ssh -L 5432:localhost:5432 deploy@db.example.com

Lies es von links nach rechts: öffne 5432 auf meiner Maschine und leite alles, was ankommt, an localhost:5432 aus Sicht des Servers weiter. Jetzt spricht psql -h 127.0.0.1 auf deinem Laptop mit dem Postgres des Servers (auf Englisch) – durch die verschlüsselte SSH-Verbindung, ohne die Datenbank überhaupt dem Netz auszusetzen.

Der mittlere Host wird auf der Serverseite aufgelöst, und das macht es mächtig:

# reach a database that only the bastion can see
ssh -L 5432:10.0.0.9:5432 deploy@bastion.example.com

Hier ist 10.0.0.9 eine private Adresse, zu der dein Laptop nicht routen kann – der Bastion-Host aber schon. Der Tunnel überbrückt die Lücke.

Remote-Weiterleitung (-R) – deine Maschine der Serverseite bereitstellen

Das Spiegelbild, und das, was den meisten verkehrt herum vorkommt (es hat seine eigene Tiefenbetrachtung – SSH-Reverse-Tunnel). Der Port öffnet auf dem Server; Verbindungen dorthin treten auf deinem Ende wieder aus. Zwei klassische Einsätze:

# let the server (or its network) reach a dev server running on your laptop
ssh -R 8080:localhost:3000 deploy@app.example.com

Jetzt wird alles, was localhost:8080 auf dem Server trifft, an localhost:3000 auf deinem Laptop geleitet – praktisch, um einem Teamkollegen auf der Kiste deine laufende Arbeit zu zeigen oder einen entfernten Dienst in etwas zurückrufen zu lassen, das nur du betreibst.

Das ist auch der Trick, um eine Maschine zu erreichen, die ohne eingehenden Zugriff hinter NAT feststeckt: Lass sie einen Reverse-Tunnel hinaus zu einem Server öffnen, den du kontrollierst, und du verbindest dich durch den Server. Die Maschine, die keine Verbindungen annehmen kann, baut stattdessen die ausgehende auf.

Ein Haken: Standardmäßig bindet der Server den weitergeleiteten Port an localhost, sodass nur der Server selbst ihn nutzen kann. Damit andere Maschinen im Netz des Servers ihn erreichen, braucht die sshd_config des Servers GatewayPorts yes (oder clientspecified) – eine serverseitige Einstellung, nichts, was du vom Client erzwingen kannst.

Dynamische Weiterleitung (-D) – ein SOCKS-Proxy in einem Flag

-L leitet an ein festes Ziel weiter. -D öffnet stattdessen einen SOCKS-Proxy, sodass das Ziel pro Anfrage gewählt wird:

ssh -D 1080 deploy@jump.example.com

Richte deinen Browser (oder curl --socks5 127.0.0.1:1080) auf 127.0.0.1:1080, und jede Anfrage wird vom Server aus geroutet. Das ist der schnelle Weg, ein internes Netz zu durchstöbern, zu testen, wie ein Dienst von der IP eines Rechenzentrums aussieht, oder eine Handvoll interner Hosts zu erreichen, ohne für jeden ein -L zu brauchen.

Die Flags, die es benutzbar machen

Ein nackter Tunnel-Befehl öffnet außerdem eine Shell, die du nicht willst, und stirbt in dem Moment, in dem du sie schließt. In der Praxis:

ssh -N -L 5432:localhost:5432 deploy@db.example.com
  • -N – führe keinen Remote-Befehl aus. Du willst nur den Tunnel, keine Shell.
  • -f – schick den Prozess nach dem Verbinden in den Hintergrund (mit -N kombinieren).
  • -o ExitOnForwardFailure=yes – wenn der Port nicht gebunden werden kann (schon belegt, permission denied), scheitere laut, statt dich trotzdem zu verbinden und still nichts weiterzuleiten.
  • Keepalives – ein langlebiger Tunnel stirbt ohne ServerAliveInterval 30 an Idle-Timeouts. Füge es hinzu, sonst reißt der Tunnel ab, sobald du ihn nicht mehr nutzt.

Dauerhaft machen in ssh_config

Alles, was du mit -L/-R/-D übergeben würdest, hat eine Konfigurationsdirektive, sodass ein gespeicherter Host seine Tunnel mitträgt:

Host db-tunnel
    HostName db.example.com
    User deploy
    LocalForward 5432 localhost:5432
    ServerAliveInterval 30
    RequestTTY no

Jetzt bringt ssh -N db-tunnel die Datenbank auf 127.0.0.1:5432, ohne dass du dir Flags merken musst. RemoteForward und DynamicForward funktionieren genauso. (Mehr zur Konfigurationsdatei selbst in dem ~/.ssh/config-Leitfaden (auf Englisch).)

Drei Fallen

  • Die Bind-Adresse zählt. -L 5432:... lauscht nur auf 127.0.0.1. Damit andere Geräte in deinem LAN den Tunnel nutzen, stell eine Bind-Adresse voran: -L 0.0.0.0:5432:... – und sei dir bewusst, dass du ihn damit deinem lokalen Netz ausgesetzt hast.
  • „Address already in use“ heißt, der lokale Port ist belegt (oft ein vorheriger Tunnel, der nicht beendet wurde). Wähl einen anderen lokalen Port oder beende den alten; ExitOnForwardFailure macht aus dem stillen Scheitern einen Fehler, den du auch bemerkst.
  • Der mittlere Host wird auf der Gegenseite aufgelöst. localhost in -L a:localhost:b meint das localhost des Servers, nicht deins. Das verkehrt herum zu verstehen ist der mit Abstand häufigste Portweiterleitungs-Fehler.

Wenn der Tunnel nicht bindet

Wenn der Befehl mit bind: Address already in use oder bind: Permission denied scheitert, hat der Listener nie geöffnet – arbeite die kurze Liste durch. Already in use: Etwas hält den Port, oft der vergessene Tunnel von gestern – ss -tlnp nennt den Prozess, dann wähl einen anderen Port oder beende den alten. Permission denied lokal bedeutet meist einen privilegierten Port: Alles unter 1024 braucht root, lausche also auf 8443 statt 443. Auf der Gegenseite (-R) hat der Server ein Veto: AllowTcpForwarding no in der sshd_config blockiert die Weiterleitung rundweg, und ohne GatewayPorts bindet der Remote-Listener nur an localhost, egal welche Bind-Adresse du angefordert hast. Und auf gehärteten Hosts der RHEL-Familie kann SELinux sshd die Bindung verweigern, selbst wenn die Konfiguration sie erlaubt – prüf das Audit-Log, bevor du deinem Befehl die Schuld gibst.

Wenn das Jonglieren mit Tunneln von Hand alt wird

Weiterleitung ist ein großartiges Primitiv, und jeder Entwickler sollte die drei Flags im Schlaf können. Die Reibung zeigt sich später: Du führst einen Zettel mit ssh -N -L ...-Befehlen (auf Englisch), du führst sie nach jedem Ruhezustand des Laptops neu aus, du vergisst, welcher lokale Port auf welchen Dienst zeigt, und du siehst nicht auf einen Blick, ob ein Tunnel überhaupt noch läuft.

Hier hilft ein Client mit einem richtigen Tunnel-Manager. Termalin hält lokale, remote und dynamische Weiterleitungen als gespeicherte Konfiguration am Host – starte sie mit einem Klick, sieh, welche live sind, und lass sie sich mit der Verbindung neu aufbauen, statt den Befehl neu zu tippen. Dieselbe Host-Liste (mit ihren Schlüsseln, Jump-Hosts und Keepalives) synchronisiert sich Ende-zu-Ende-verschlüsselt über deine Maschinen, und jeder Server wird beim ersten Verbinden verifiziert. Die Kommandozeile bringt dir bei, wie Tunnel funktionieren; ein Client bewahrt dich davor, sie jeden Morgen von Hand neu zu bauen.

So oder so ist das Modell der ganze Trick: entscheide, welche Seite den Port öffnet, und der Rest des Flags schreibt sich von selbst.


Termalin ist ein kostenloser, plattformübergreifender SSH-Client mit integrierten Tunneln, SFTP und Schlüsselverwaltung – lade es herunter oder sieh, wie es deine Schlüssel sicher verwahrt.

Probieren Sie es auf einem Host aus.

Termalin ist ein schneller SSH-Client für Sie – und Ihre Agenten.

Kostenloser Tarif · 14 Tage Pro gratis testen · Preise