Reverse Secure Shell nutzen um Firewalls zu überwinden

Ein Host hinter einer Unternehmensfirewall oder hinter einem NAT-Router kann kann normalerweise nicht von außen erreicht werden. Dies ist grundsätzlich auch ein Sicherheitsmerkmal, aber in bestimmten Situationen kann es dennoch notwendig sein, den Zugang zu gewähren. Eine elegante Möglichkeit ist die Einrichtung eines Tunnels über Reverse-SSH.
Dies entspricht im Prinzip dem Verfahren, das auch von Anbietern bekannter Fernwartungssoftware wie PcVisit oder TeamViewer verwendet wird. Was man dafür braucht und wie man es einrichtet, wird im Folgenden beschrieben.

Konzept

Der Schlüssel liegt darin, die Verbindung umzukehren. Anstatt einen Port direkt nach außen anzubieten, baut der Server eine SSH-Verbindung(1) zu einem einem externen Host auf. Dieser wird auch als Jump-Host bezeichnet, in Anlehnung an die Tatsache, dass er als Sprungbrett in ein anderes Netzwerk dient. Die SSH-Verbindung fungiert als verschlüsselter Tunnel, über den jeder Port des Server mit dem Jump-Host verbunden werden kann. Der Jump-Host selbst kann dann diese Ports wieder zur Verfügung stellen, zum Beispiel für Clients aus einem anderen Netzwerk, die Zugriff auf den Jump-Host haben(2). Alle Verbindungen, die über die entsprechenden Ports abgewickelt werden, werden vom Jump-Host an den Server(3) weitergeleitet.

.--------.             .---------.            .---------.  
|        |---+-(1)-+-->|         |            |         |
| Server |   |     |   |Jump Host|<----(2)--->| Client  |
|        |<--` (3) `---|         |            |         |
`--------`             `---------`            `---------` 

Implementierung

Der Server verwendet AutoSSH zur Aufrechterhaltung und Überwachung einer permanenten SSH-Sitzung mit dem Jump-Host. Die Authentifizierung basiert auf öffentlichen Schlüsseln. Der Login-Benutzername könnte vom Servernamen abgeleitet werden, um eine einfache Zuweisung der jeweiligen SSH-Konfiguration im Jump-Host zu ermöglichen, die erforderlich ist, um ein angemessenes Maß an Isolation zu gewährleisten. In der Praxis bedeutet dies, dass nur ein bestimmter Benutzername einen bestimmten Port weiterleiten darf, was eine netzseitige Isolierung von Hosts unterschiedlicher Nutzer oder Kunden ermöglicht.

Konfiguration

Jump Host

Erstellen eines “Remote-Host-Kontos” (SSH-Anmeldung):

jumphost$ doas useradd -m myserver
jumphost$ doas su - myserver
jumphost$ whoami
myserver

Setzen des initialen Passwort für die Schlüsselübertragung:

jumphost$ doas passwd myserver

Server

  1. Lokalen AutoSSH-Benutzer anlegen:
myserver$ doas useradd -m autossh
myserver$ doas su - autossh
myserver$ whoami
autossh
  1. Erstellen des private und public key:
myserver$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/autossh/.ssh/id_rsa): 
Created directory '/home/autossh/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/autossh/.ssh/id_rsa.
Your public key has been saved in /home/autossh/.ssh/id_rsa.pub.
  1. Übertragung des public key zum Jump-Host:
myserver$ ssh-copy-id -i /home/autossh/.ssh/id_rsa.pub -p 22 myserver@jumphost

Sicherheitsvorkehrungen auf dem Jump Host

Es wird im Folgenden davon ausgegangen, dass der Server nur einen einzigen Port an den Jump-Host binden können sollte. Außerdem sollte es nicht möglich sein, Befehle auf dem Jump-Host auszuführen. SSH sollte daher nur als Transportmittel für den getunnelten Port dienen. Dies kann durch Hinzufügen eines Eintrags in der authorized_keys-Datei des Login-Benutzers myserver (im Beispiel) erreicht werden. Achten Sie darauf, dass alles in der gleichen Zeile steht wie der Host-Schlüssel und der ssh-rsa-Parameter nicht dupliziert wird.

Generisches Beispiel für listen:

permitlisten="localhost:19103",permitlisten="localhost:9999",permitlisten="localhost:2211",no-pty,no-X11-forwarding,no-agent-forwarding,command="/bin/echo 
'Remote shell access has been disabled'" ssh-rsa ......

Beispiel für das Öffnen des lokalen Ports 10001 auf Anfrage des Servers:

permitopen="localhost:10001",no-pty,no-X11-forwarding,no-agent-forwarding,command="/bin/echo 
'Remote shell access has been disabled'" ssh-rsa ....

Außerdem muss im obigen Fall die GatewayPorts-Funktion in SSH aktiviert werden, wenn der Port an die externe Schnittstelle des Jump-Hosts weitergeleitet werden soll (nur empfohlen, wenn sich der jump host in einer vertrauenswürdigen Umgebung befindet).

# vi /etc/sshd/sshd_config

    GatewayPorts yes

Verbindungstest

Als Beispiel wird Port 9182 des Servers an den lokalen Port 10001 des Jump Hosts weitergeleitet. Dieser Port wird normalerweise vom Prometheus node_exporter verwendet und dient als praktisches Beispiel, wie z.B. wie z.B. die Überwachung eines entfernten Servers durch eine Firewall hindurch realisiert werden kann.

myserver$ ssh -p 22 -N -R 10001:localhost:9182 myserver@jumphost

Leiten Sie Port 9182 des Servers an den öffentlich zugänglichen (gatewayed) Port 10001 weiter im Jump-Host weiter:

myserver$ ssh -p 22 -N -R 0.0.0.0:10001:localhost:9182 myserver@jumphost

Der node_exporter des Servers ist jetzt über den Jump-Host zugänglich:

http://jumphost:10001

AutoSSH beim Systemstart starten

Die vorangegangenen Schritte gelten für jedes Linux / Unix / BSD-System weitgehend unverändert. Abschließend möchte ich hier noch einen Ausblick geben, wie AutoSSH am Beispiel von NetBSD in die Startsequenz des Betriebssystems integriert werden kann.

# vi /etc/rc.local

    su autossh -c "/usr/pkg/bin/autossh -f -M 9999 -p 22 -N -R 0.0.0.0:10001:localhost:9182 myserver@jumphost"

Noch besser ist es natürlich, den Aufruf in ein eigenes rc-Skript zu verpacken oder mit einem Dienstmanager (Monit, Initware, Systemd) zu starten.

Fazit

Dieser Ansatz eröffnet viele interessante Anwendungsfälle in verteilten Infrastrukturen. Ich selbst nutze diese Techniken zur Vernetzung von Systemen mit dynamischen IP-Adressen, zum Beispiel um verteilte Backups mit Bareos zu realisieren und zur Überwachung von Hosts mit Prometheus. Dieser Weg könnte auch geeignet sein geeignet sein, um temporär dedizierte Dienste aus einer geschlossenen Umgebung anzubieten, wobei hier immer das Risiko abgewogen werden sollte (die meisten geschlossenen Umgebungen sind aus guten Gründen geschlossen, und die Anwendung von solcher Techniken diese Schutzmaßnahmen umkehren).

Wie bereits eingangs erwähnt, funktionieren gängige Fernwartungsprodukte ähnlich, nur dass die Einrichtung hinter benutzerfreundlichen Fassaden erfolgt. In der Tat fände ich es interessant und wünschenswert, wenn es solche client- und serverseitigen Komponenten als freie Open-Source-Software geben würde.