Zum Inhalt springen
Tooling

SSH-Tunneling: Der unterschaetzte Werkzeugkasten

8 min Lesezeit
SSH Networking Security

SSH ist für die meisten Entwickler gleichbedeutend mit ssh user@host — eine Remote Shell öffnen, Befehle ausführen, fertig. Das ist ungefähr so, als würde man einen Schweizer Taschenmesser ausschließlich zum Flaschenöffnen benutzen. Die eigentliche Stärke von SSH liegt in seinen Tunnel-Fähigkeiten: Port Forwarding, dynamische Proxies und Jump Hosts. Werkzeuge, die Netzwerkprobleme lösen, für die viele reflexartig nach einem VPN greifen.

Warum SSH-Tunnel statt VPN?

Ein VPN verbindet ganze Netzwerke. Das ist maechtig, aber auch aufwändig: Konfiguration, Zertifikate, Client-Software, Routing-Tabellen. Fuer viele Alltagsprobleme ist das wie mit Kanonen auf Spatzen schiessen.

SSH-Tunnel sind chirurgisch präzise. Ein einzelner Port, eine einzelne Verbindung, sofort verfügbar auf jedem System mit OpenSSH. Kein Setup, keine zusätzliche Software, keine Adminrechte nötig.

Local Port Forwarding (-L)

Das häufigste Szenario: Ein Dienst läuft in einem Remote-Netzwerk und ist nicht direkt erreichbar — eine Datenbank hinter einer Firewall, ein internes Monitoring-Dashboard, eine API im Staging-Netz.

Local Forwarding bindet einen lokalen Port an einen entfernten Dienst. Alles, was auf dem lokalen Port ankommt, wird durch den SSH-Tunnel zum Ziel geleitet.

Local Forwarding: Remote-Datenbank lokal verfügbar machen bash
# Syntax: ssh -L [local_bind:]local_port:remote_host:remote_port user@ssh_server

# PostgreSQL hinter Firewall lokal auf Port 5433 verfügbar machen
ssh -L 5433:db-intern.example.com:5432 deploy@bastion.example.com

# Jetzt verbinden: psql -h localhost -p 5433 -U app mydb

# Internes Grafana-Dashboard lokal öffnen
ssh -L 3000:monitoring.internal:3000 deploy@bastion.example.com
# Browser: http://localhost:3000

Der entscheidende Punkt: remote_host wird aus Sicht des SSH-Servers aufgeloest. db-intern.example.com muss nicht von deinem Rechner erreichbar sein — nur vom Bastion Host.

Remote Port Forwarding (-R)

Remote Forwarding funktioniert in die umgekehrte Richtung: Ein lokaler Dienst wird über den SSH-Server erreichbar gemacht. Zwei typische Anwendungsfaelle:

  1. Webhook-Entwicklung: Ein Payment-Provider muss deinen lokalen Dev-Server erreichen
  2. Demo für Kunden: Eine lokal laufende App kurzzeitig extern zugänglich machen
Remote Forwarding: Lokalen Dienst extern verfügbar machen bash
# Syntax: ssh -R [remote_bind:]remote_port:local_host:local_port user@ssh_server

# Lokalen Dev-Server (Port 3000) auf dem Remote-Server unter Port 8080 verfügbar machen
ssh -R 8080:localhost:3000 deploy@public-server.example.com

# Jetzt erreichbar: http://public-server.example.com:8080

# Nur auf localhost des Remote-Servers binden (sicherer)
ssh -R 127.0.0.1:8080:localhost:3000 deploy@public-server.example.com

Dynamic Port Forwarding (-D)

Dynamic Forwarding erstellt einen lokalen SOCKS5-Proxy. Statt einen einzelnen Port weiterzuleiten, kannst du beliebigen Traffic durch den SSH-Tunnel routen. Das ist besonders nuetzlich, wenn du im Browser durch ein Remote-Netzwerk navigieren musst — zum Beispiel interne Webanwendungen erreichen, die nur aus dem Firmennetz zugänglich sind.

SOCKS-Proxy für dynamisches Forwarding bash
# Syntax: ssh -D [bind_address:]port user@ssh_server

# SOCKS5-Proxy auf Port 1080 starten
ssh -D 1080 deploy@office-gateway.example.com

# Firefox: Einstellungen > Netzwerk > SOCKS Host: localhost, Port: 1080
# Oder per CLI:
curl --socks5 localhost:1080 http://intranet.corp.local/api/status

# Mit nötig: DNS-Anfragen auch durch den Tunnel leiten
ssh -D 1080 -o "ProxyCommand=none" deploy@office-gateway.example.com

Der Vorteil gegenueber Local Forwarding: Du musst nicht im Voraus wissen, welche Hosts und Ports du brauchst. Der gesamte Traffic, den du durch den Proxy leitest, wird im Remote-Netzwerk aufgeloest.

Jump Hosts / ProxyJump (-J)

In professionellen Umgebungen erreichst du Zielserver selten direkt. Der Weg fuehrt über einen oder mehrere Bastion Hosts. Frueher hat man das mit verschachtelten SSH-Sessions oder ProxyCommand geloest — umstaendlich und fehleranfaellig.

Seit OpenSSH 7.3 gibt es -J (ProxyJump), und es ist elegant:

ProxyJump: Durch Bastion Hosts springen bash
# Syntax: ssh -J jump_host1,jump_host2 target_host

# Durch einen Bastion Host zum Zielserver
ssh -J bastion.example.com webserver.internal

# Durch zwei Hops
ssh -J bastion.example.com,dmz-gateway.internal db-server.private

# Kombination mit Port Forwarding
ssh -J bastion.example.com -L 5432:localhost:5432 db-server.internal

Jeder Hop in der Kette nutzt SSH, sodass die gesamte Verbindung verschluesselt ist. Der Bastion Host sieht nur verschluesselten Traffic — er kann nicht mitlesen, was zwischen dir und dem Zielserver passiert.

SSH Config: Tunnel dauerhaft einrichten

Jedes Mal lange Kommandozeilen tippen ist keine Lösung für den Alltag. Die ~/.ssh/config macht Tunnel persistent und wiederverwendbar.

~/.ssh/config für wiederkehrende Tunnel-Setups bash
# Bastion Host als Jump Server definieren
Host bastion
    HostName bastion.example.com
    User deploy
    IdentityFile ~/.ssh/id_ed25519_work

# Interner DB-Server via Bastion
Host db-staging
    HostName db-staging.internal
    User deploy
    ProxyJump bastion
    LocalForward 5433 localhost:5432

# Internes Monitoring via Bastion
Host monitoring
    HostName monitoring.internal
    User deploy
    ProxyJump bastion
    LocalForward 3000 localhost:3000
    LocalForward 9090 localhost:9090

# SOCKS-Proxy ins Firmennetz
Host office-proxy
    HostName office-gateway.example.com
    User deploy
    DynamicForward 1080

# Globale Defaults
Host *
    ServerAliveInterval 60
    ServerAliveCountMax 3
    AddKeysToAgent yes

Danach reicht ein ssh db-staging — der Tunnel steht, die Datenbank ist auf localhost:5433 erreichbar. Kein Nachdenken, kein Nachschlagen.

Autossh: Tunnel die sich selbst heilen

SSH-Tunnel brechen ab. Netzwerkwechsel, instabiles WLAN, ein Neustart des Servers — die Verbindung ist weg und der Tunnel tot. autossh überwacht die Verbindung und baut sie automatisch wieder auf.

Autossh für zuverlässige Langzeit-Tunnel bash
# Installation
sudo apt install autossh   # Debian/Ubuntu
brew install autossh        # macOS

# Autossh mit Monitoring-Port
autossh -M 20000 -f -N -L 5433:db:5432 deploy@bastion.example.com

# -M 20000  : Monitoring-Port (autossh prueft die Verbindung)
# -f        : In den Hintergrund
# -N        : Keine Remote-Shell öffnen (nur Tunnel)

# Ohne Monitoring-Port (nutzt ServerAliveInterval)
autossh -M 0 -f -N \
  -o "ServerAliveInterval=30" \
  -o "ServerAliveCountMax=3" \
  -L 5433:db:5432 deploy@bastion.example.com

# Als systemd-Service für permanente Tunnel
# /etc/systemd/system/ssh-tunnel-db.service
# [Unit]
# Description=SSH Tunnel to Staging DB
# After=network-online.target
#
# [Service]
# User=deploy
# ExecStart=/usr/bin/autossh -M 0 -N -o "ServerAliveInterval=30" \
#   -o "ServerAliveCountMax=3" -L 5433:db:5432 deploy@bastion
# Restart=always
# RestartSec=10
#
# [Install]
# WantedBy=multi-user.target

Die Kombination aus autossh und systemd ergibt Tunnel, die Neustarts und Netzwerkausfaelle ueberleben — zuverlässig genug für produktionsnahe Setups.

Sicherheit: Was du beachten musst

SSH-Tunnel sind maechtig, und Macht erfordert Verantwortung. Ein paar Punkte, die in der Praxis oft uebersehen werden:

Key-Only Authentication: Passwort-Authentifizierung hat auf Servern nichts zu suchen. Ed25519-Keys sind der aktuelle Standard — klein, schnell, sicher.

Bind-Adressen kontrollieren: Standardmaessig binden Local Forwards an localhost. Wer -L 0.0.0.0:5433:db:5432 schreibt, macht die Datenbank für jeden im lokalen Netz erreichbar. Meistens nicht gewollt.

Forwarding auf dem Server einschraenken: In sshd_config kann gezielt gesteuert werden, was erlaubt ist:

  • AllowTcpForwarding no — kein Port Forwarding erlaubt
  • AllowTcpForwarding local — nur Local Forwarding
  • PermitOpen host:port — nur bestimmte Ziele erlaubbar
  • GatewayPorts no — Remote Forwards nur auf localhost

Dedizierte Tunnel-User: Fuer automatisierte Tunnel lohnt sich ein eigener User mit eingeschraenkter Shell (/bin/false oder command= in authorized_keys). So kann der Key nur für den Tunnel genutzt werden, nicht für eine interaktive Shell.

Monitoring: Wer nicht weiß, welche Tunnel aktiv sind, hat ein Problem. ss -tlnp auf dem Server zeigt alle lauschenden Tunnel-Ports.

Fazit

SSH-Tunneling ist kein Nischenthema — es gehört in den Werkzeugkasten jedes Entwicklers, der mit Remote-Systemen arbeitet. Local Forwarding für den Zugriff auf interne Dienste, Remote Forwarding für Webhook-Entwicklung, Dynamic Forwarding als Ad-hoc-VPN, und ProxyJump für mehrstufige Infrastrukturen.

Der groesste Vorteil: SSH ist ueberall. Kein Client zu installieren, keine Firewall-Regeln zu beantragen, keine Admins zu ueberzeugen. Ein einzelner Befehl, und der Tunnel steht. Wer das einmal verinnerlicht hat, fragt sich, warum er jemals ohne gearbeitet hat.