
Grundlagen im Tunnelbau mit OpenSSH
Autor:in: Benedikt Waldvogel
Portweiterleitungen mit SSH sind ein sehr mächtiges Werkzeug. Diese Grundlagen sollte jede Entwicklerin und jeder DevOps-Engineer kennen.
In der Software-Entwicklung oder System-Administration möchte man manchmal auf Dienste zugreifen, die nicht direkt aus dem lokalen Netzwerk erreichbar sind.
In diesem Artikel zeigen wir die drei üblichsten Varianten, wie man mit OpenSSH1 Tunnels baut, um eine Verbindung zwischen Systemen herzustellen, die eigentlich nicht direkt miteinander sprechen können.
Local Port Forwarding
Ein häufiger Anwendungsfall in der Software-Entwicklung bei der cronn GmbH ist der Zugriff auf eine Datenbank oder ein Web-Service in einer Testumgebung.
In der Regel sind die Dienste in der Testumgebung nicht direkt vom eigenen Computer erreichbar, sondern erfordern den Zugriff über einen Server in der Testumgebung, auf den man sich per SSH anmelden kann.
In unserem Beispiel wollen wir eine SQL-Abfrage auf einer PostgreSQL-Datenbank in der Testumgebung ausführen. Man könnte sich dazu per SSH auf dem Datenbank-Server anmelden und in der Kommandozeile mit psql die SQL-Abfrage laufen lassen. Dieses Vorgehen ist in der Praxis oft zu unhandlich und man möchte viel lieber mit einem Datenbank-Client seiner Wahl (z.B. DBeaver oder DataGrip) arbeiten.
OpenSSH bietet mit „Local Port Forwarding“ ein nützliches Mittel, um genau das zu erreichen. Einige Datenbank-Clients bieten bereits eine grafische Konfigurationsmöglichkeit, um die Verbindung über eine Portweiterleitung (auch SSH-Tunnel genannt) herzustellen. In diesem Artikel wollen wir jedoch beleuchten, was eigentlich dahinter steckt und wie man die Verbindung auch dann herstellen kann, wenn z.B. der Datenbank-Client diese Möglichkeit nicht anbietet.

Angenommen die Datenbank lauscht auf Port 5432 auf dem Datenbank-Server in unserer Testumgebung.
Mit dem Befehl
ssh -L 5432:localhost:5432 db.example.comkann eine Weiterleitung des Ports 5432 auf den eigenen Computer eingerichtet werden. Wenn die Verbindung steht, dann kann man sich mit dem Datenbank-Client seiner Wahl ganz bequem auf den lokalen Port 5432 verbinden und die SQL-Abfragen ausführen.
Wenn auf dem eigenen Computer bereits eine lokale Datenbank auf Port 5432 läuft, dann würde der ssh-Befehl mit dem Fehler
bind [127.0.0.1]:5432: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 5432
Could not request local forwarding.fehlschlagen.
In der Praxis sieht man lokale Portweiterleitungen daher häufig mit einem anderen, freien, lokalen Port wie z.B. 54321:
ssh -L 54321:localhost:5432 db.example.comWer den Befehl ausführt, wird bemerken, dass OpenSSH neben der Portweiterleitung auch eine interaktive Sitzung inklusive Shell startet. In der Regel möchte man allerdings nur den Port weiterleiten. Das Aufbauen der interaktiven Sitzung kann mit der Option -N abgeschaltet werden:
ssh -N -L 54321:localhost:5432 db.example.comIn manchen Fällen ist der Datenbank-Server allerdings selbst nicht per SSH erreichbar oder es fehlen einem die Berechtigungen. Wenn es einen anderen Server in der Testumgebung gibt, auf den man sich per SSH anmelden kann und der sich zur Datenbank verbinden kann, dann können wir die Portweiterleitung über diesen Server herstellen. Wird dieser Server hauptsächlich dazu verwendet, um zu anderen Zielen zu „springen“, dann bezeichnet man ihn üblicherweise als „Jump-Host“ oder „Jump-Server“.

In diesem Fall baut man die Verbindung zur Datenbank mit dem Befehl
ssh -L 54321:db.example.com:5432 jump-host.example.comauf.
Möchte man die Verbindung zu zwei Datenbanken herstellen (z.B. einer primary und secondary Instanz), dann könnte man ssh zweimal starten mit unterschiedlichen lokalen Ports:
ssh -N -L 54321:primary-db.example.com:5432 jump-host.example.comssh -N -L 54322:secondary-db.example.com:5432 jump-host.example.comAlternativ dazu kann man beide Portweiterleitungen mit nur einem Befehl erzeugen:
ssh -L 54321:primary-db.example.com:5432 \
-L 54322:secondary-db.example.com:5432 \
-N jump-host.example.comAlle Details und Optionen zum Kommandozeilenprogramm ssh können auf der ssh man page nachgelesen werden.
Dynamic Port Forwarding
Möchte man sich in der Zielumgebung auf eine Vielzahl von Diensten verbinden, dann kann das Erzeugen von lokalen Portweiterleitungen schnell unübersichtlich werden. OpenSSH bietet für diesen Anwendungsfall die Möglichkeit der dynamischen Portweiterleitung. Anstatt eine Liste von Zielsystemen inklusive Ports zu definieren, gibt man stattdessen einen freien lokalen Port an (z.B. 2000), auf dem OpenSSH einen SOCKS-Proxy öffnet:
ssh -N -D 2000 jump-host.example.comÜber diesen lokalen SOCKS-Proxy können alle Systeme erreicht werden, als ob man sich selbst auf dem Jump-Host befinden würde. Eine Voraussetzung ist allerdings, dass der Client den Verbindungsaufbau über einen SOCKS-Proxy explizit unterstützt. Alle gängigen Browser unterstützen SOCKS-Proxies, sodass eine dynamische Port-Weiterleitung sehr praktisch sein kann, wenn man sich beispielsweise auf eine Vielzahl verschiedener Webapplikationen verbinden möchte, auf die man nur über einen Jump-Host zugreifen kann.
Remote Port Forwarding
In etwas selteneren Fällen möchte man die Portweiterleitungen in der umgekehrten Richtung einrichten.

Ein Anwendungsfall wäre beispielsweise die Portweiterleitung eines Service mit REST-API, den man bei einem regelmäßigen Integrationstest in einer Continuous-Integration-Umgebung verwenden möchte. Für das Beispiel nehmen wir an, dass man aus Sicherheitsgründen keinen direkten Zugriff ausgehend vom Continuous-Integration-Server in die Testumgebung erlauben möchte. Stattdessen ist allerdings der SSH-Zugriff ausgehend von einem vertrauenswürdigen Server ( hier: gateway.cloud.com) aus der Testumgebung auf den Continuous-Integration-Server erlaubt.
In diesem Fall kann eine umgekehrte Portweiterleitung („Remote Port Forwarding“) eingerichtet werden, indem man die Weiterleitung auf gateway.cloud.com mit dem Befehl
ssh -N -R 8080:service.cloud.com:8080 continuous-integration.example.comerzeugt.
Unsere Software, die auf dem Continuous-Integration-Server getestet wird, kann in diesem Fall über http://localhost:8080 auf den REST-Service zugreifen, obwohl dieser entfernt in der isolierten Testumgebung betrieben wird.
OpenSSH Konfigurationsdateien
In einigen Fällen ist der Jump-Host, über den man eine Port-Weiterleitung einrichten möchte, selbst nicht direkt per SSH erreichbar, sondern nur mit einem „Sprung“ über einen anderen Server. Mit der ProxyJump oder ProxyCommand Option in der lokalen SSH-Konfiguration (~/.ssh/config) können auch komplexere Szenarien elegant eingerichtet werden. In unserem Artikel „Grundlagen im Tunnelbau – Teil II: OpenSSH Konfigurationsdateien“ beleuchten wir diese Konfigurationsmöglichkeit etwas genauer. Dabei stellen wir unsere Java-Bibliothek ssh-proxy vor, mit der Portweiterleitungen einfach in Java-Applikationen eingerichtet werden können, z.B. in Integrations-Tests.
Ausblick
In einem weiteren zukünftigen Artikel werden wir zeigen, wie man auf Linux mit Systemd User Services und autossh Portweiterleitungen einrichten kann, die sich automatisch wieder aufbauen, wenn die Netzwerkverbindung unterbrochen wurde.
Footnotes
Wegen der kürzeren Schreibweise und des prägnanteren Namens verwenden wir in diesem Artikel „SSH” und „OpenSSH” synonym, obwohl streng genommen SSH für das Konzept für verschlüsselte Netzverbindungen steht und OpenSSH eine Implementierung dessen ist. Alle Beispiele im Artikel sind OpenSSH-spezifisch. ↩

Benedikt Waldvogel