SSH/Gateway
Management von IT-Komponenten über einen SSH-Tunnel von beliebigen Lokationen aus
Funktion
- SSH-Tunnel
- Ein von außen (über das Internet) initiierter Zugriff würde ein Öffnen der Firewall erfordern und ist daher meist nicht gewünscht
- Deswegen baut das zu managende Zielsystem selbst aktiv eine Verbindung zu einem Gateway im Internet auf
- Auf diesem Gateway muss lediglich ein SSH-Server laufen, was bei den meisten üblichen Linux-Distributionen der Standard ist.
- Supporter baut mit seinem Support-Client selbst auch einen SSH-Tunnel zum Gateway auf und hat darüber dann Zugriff auf das zu managende Zielsystem.
Parameter
Als Beispiel für diese Anleitung werden folgende Parameter angenommen:
- Gateway
- Hostname: gateway.example.de
- Benutzer, unter welchem die Tunnel erstellt werden: sshtunnel(oder verschiedene)
- Zielsystem
- Hostname: mgmtdest.example.de
- Benutzer, unter welchem die Tunnel erstellt werden: support
Konfiguration
Gateway
Als Gateway kann ein beliebiger Linux-Server im Internet dienen
- Ein SSH-Server wird dort bereits aktiv sein
- Für den Tunnel-Aufbau wird ein separater User angelegt:
root@gateway# useradd --create-home \ --groups users \ --shell /bin/bash \ sshtunnel root@gateway# passwd --lock sshtunnel
Durch "passwd --lock" wird ein Login mit Paßwort gesperrt, SSH-Schlüssel funktionieren noch.
Die Supporter sollten nun unter diesem User ihren SSH-Key hinterlegen
- Außerdem sollte die passwortbasierte Authentifizierung auch am SSH-Server abgeschaltet werden.
Sollen auf dem Gateway verschiedene Nutzergruppen mit Zugriff auf unterschiedliche Zielsystem existieren (mehrere Kunden, verschiedene Dienstleister oder ähnliche Szenarien), empfiehlt es sich, für jede Kombination aus Supportern und Zielsystemen einen eigenen Tunnelnutzer anzulegen:
root@gateway# useradd --create-home \ --groups users \ --shell /bin/bash \ --uid 3001 \ --comment "User für Tunnel zu Systemen vom Kunden1" \ ssh-kunde1 root@gateway# passwd --lock ssh-kunde1
Das Verbinden des vom Zielsystem kommenden Tunnels mit dem des Supporters geschieht eigentlich über einen TCP-Port
- Der Supporter gibt beim Aufbau des Tunnels den gleichen Port auf dem Gateway an, den das Zielsystem für seinen Tunnel nutzt
- Da TCP-Ports über keine Zugriffssteuerung verfügen, könnte sich beim o.g
- Szenario mit mehreren Nutzergruppen z.B. auch der Kunde1 mit dem SSH-Schlüssel seines Zielsystems mit dem Tunnel zum Zielsystem des Kunden2 verbinden
- Um das zu verhindern, bietet sich die Nutzung von UNIX Sockets an
- Diese sind nur für den jeweiligen Tunnel-User erreichbar.
Bei Nutzung von UNIX Sockets sollte die Konfiguration des SSH-Daemons wie folgt ergänzt werden:
# Specifies whether to remove an existing Unix-domain socket file # for local or remote port forwarding before creating a new one. StreamLocalBindUnlink yes
Ohne diesen Eintrag wir des Socket beim Abbau des Tunnels nicht gelöscht, wonach der Tunnel nicht wieder mit dem gleichen Socket-Namen aufgebaut werden könnte.
Zielsystem
Für den Tunnelaufbau zum Gateway wird ein separater User verwendet:
root@mgmtdest# useradd -m -G users -s /bin/bash support
Nach Login als dieser neue User wird ein SSH-Key für den Tunnel erzeugt (als Name wird im Beispiel der FQDN des Gateways genutzt, das ist aber nicht zwingend):
root@mgmtdest# sudo -u support -i support@mgmtdest$ ssh-keygen -f ~/.ssh/gateway.example.de -t ecdsa
Eine Passphrase wird nicht verwendet (nur Enter drücken).
Der öffentliche Schlüssel muss nun auf dem Gateway der Datei ~/.ssh/authorized_keys des Tunnel-Users hinzugefügt werden
- Angezeigt werden kann der Schlüssel mittels:
support@mgmtdest$ cat ~/.ssh/gateway.example.de.pub ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGFrouMZKE6ozpWLj+xH3mONc6LWL3ZbnFbpL7sMKAjMSiTlBzek0doEHrh9VvTmKce4pmHK9ilkI4ILFw7t+sk support@mgmtdest
Auf dem Gateway wird als der dortige Tunnel-User der Schlüssel wie folgt eingefügt:
sshtunnel@gateway$ echo command=\"\",no-pty ecdsa-sha2-nistp256 \ AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBB\ GFrouMZKE6ozpWLj+xH3mONc6LWL3ZbnFbpL7sMKAjMSiTlBzek0doE\ Hrh9VvTmKce4pmHK9ilkI4ILFw7t+sk \ support@mgmtdest \ >>~/.ssh/authorized_keys
Durch das Voranstellen von command="",no-pty kann ein User mit diesem Key auf dem Gateway kein Kommando ausführen und kein Terminal öffnen.
Zurück auf dem Zielsystem wird nun zum Test und zur Aufnahme als "known host" eine SSH-Verbindung zum Gateway aufgebaut:
support@mgmtdest$ ssh -i ~/.ssh/gateway.example.de sshtunnel@gateway.example.de
Nach Vergleich des angezeigten Fingerprints und Bestätigung durch Eingabe von "yes" erscheint eine Fehlermeldung "PTY allocation request failed on channel 0" und die Verbindung wird wieder abgebrochen
- Das ist so gewollt.
Der Tunnel kann nun mit folgendem Kommando aufgebaut werden:
ssh -NTC\ -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -o StrictHostKeyChecking=no\ -i ${SSHKEY} -R ${GWSOCK}:localhost:${LOCALPORT}\ ${GWUSER}@${GWHOST}
Durch den Parameter "-R" wird ein reverse Tunnel erstellt, denn es soll ja aus Richtung Gateway auf das Zielsystem zugegriffen werden können
- Die verwendeten Variablen haben folgende Bedeutung:* ${SSHKEY}: der komplette Pfad zum SSH-Key, z.B. ~/.ssh/gateway.example.de
- ${GWSOCK}: Der Socket, welcher auf dem Gateway für den Tunnel verwendet wird
- Diesen muß man dort ansprechen, um sich mit dem Zielsystem zu verbinden
- Werden mehrere Tunnel zum Gateway aufgebaut (was recht wahrscheinlich ist), muß natürlich für jeden ein anderer Socket verwendet werden
- Ein Socket kann ein TCP- oder ein UNIX-Socket sein. Ein TCP-Socket ist einfach eine Port-Nummer
- Da zum Tunnelaufbau normale User (nicht root) genutzt werden, können dabei nur Ports über 1024 verwendet werden.Bei einem UNIX-Socket muss dessen kompletter Pfad angegeben werden, z.B. "/home/ssh-kunde1/zielsystem3.socket". Das darf nicht mit "~/zielsystem3.socket" abgekürzt werden
- ${LOCALPORT}: Das ist der Port, mit welchem der Tunnel auf dem Zielsystem verbunden wird
- Soll über den Tunnel eine SSH-Sitzung aufgebaut werden, nimmt man den Port 22. Läuft auf dem Zielsystem ein Webserver und dieser soll über den Tunnel erreichbar sein, wird Port 443 genommen (für verschlüsselte Seiten).
- ${GWUSER}: Der User auf dem Gateway
- In unserem Beispiel also "sshtunnel" oder "ssh-kunde1".
- ${GWHOST}: Der Hostname des Gateways, also z.B. "gateway.example.de".
Soll der Tunnel automatisch beim Booten gestartet werden, erstellen wir für Systemd-Systeme eine passende Service-Unit:
/etc/systemd/system/ssh-reverse@.service
[Unit] Description=Reverse SSH Tunnel Service ConditionPathExists=|/usr/bin After=network.target [Service] User=support Environment="GWSOCK=40%I" Environment="GWUSER=sshtunnel" Environment="GWHOST=gateway.example.de" Environment="SSHKEY=/home/support/.ssh/gateway.example.de" ExecStart=/usr/bin/ssh -NTC -o ServerAliveInterval=60 -o ExitOnForwardFailure=yes -o StrictHostKeyChecking=no -i ${SSHKEY} -R ${GWSOCK}:localhost:%I ${GWUSER}@${GWHOST} # Restart every >2 seconds to avoid StartLimitInterval failure RestartSec=3 Restart=always [Install] WantedBy=multi-user.target
Durch das @ im Namen der Unit können verschiedene Instanzen, also mehrere Tunnel erstellt werden
- Z.B. einer für SSH und einer für HTTPS:
root@mgmtdest# systemctl daemon-reload root@mgmtdest# systemctl start ssh-reverse@22.service root@mgmtdest# systemctl start ssh-reverse@443.service
Der Port des Tunnels auf dem Gateway wird auf "40%I" gesetzt wobei %I dem Instanznamen (im Beispiel 22 und 443) entspricht
- Im Beispiel ergibt das auf dem Gateway TCP-Sockets mit den Ports 4022 und 40443.
Die Parameter für die einzelnen Instanzen können und sollten über Drop-In-Files angepaßt werden, z.B.:
/etc/systemd/system/ssh-reverse@22.service.d/tunnel.conf:
[Service] # Port of the tunnel at the remote gateway Environment="GWSOCK=/home/ssh-kunde1/ziel3-ssh.socket" # User at gateway to create the tunnel Environment="GWUSER=ssh-kunde1" # remote gateway ##Environment="GWHOST=gateway.example.de" # Location of the SSH key ##Environment="SSHKEY=/home/support/.ssh/gateway.example.de"
/etc/systemd/system/ssh-reverse@443.service.d/tunnel.conf:
[Service] # Port of the tunnel at the remote gateway Environment="GWSOCK=/home/ssh-kunde1/ziel3-https.socket" # User at gateway to create the tunnel Environment="GWUSER=ssh-kunde1" # remote gateway ##Environment="GWHOST=gateway.example.de" # Location of the SSH key ##Environment="SSHKEY=/home/support/.ssh/gateway.example.de"
In diesen Beispielen werden die auf dem Gateway verwendeten Sockets angepaßt, statt den TCP-Ports verden verschiedene UNIX-Sockets verwendet.
- Außerdem wird ein anderer User auf dem Gateway verwendet.
Supportclient
Als normaler User, dessen SSH-Key beim Tunnel-User auf dem Gateway ("sshtunnel") hinterlegt ist, wird der Tunnel zum Gateway wie folgt aufgebaut:
user@supportclient$ ssh -nNT -L 10022:localhost:10001 \ sshtunnel@gateway.example.de
Damit wird ein Tunnel vom lokalen Port 10022 zum Port 10001 auf dem Gateway erstellt
- Hat das Zielsystem, wie im Beispiel, einen reverse Tunnel zum Gateway von seinem lokalen Port 22 auf den Port 10001 auf dem Gateway gestartet, kann man nun vom Supportclient auf das Zielsystem mit SSH zugreifen:
user@supportclient$ ssh -p 10022 admin@localhost
"admin" ist ein Nutzer auf dem Zielsystem, über welchen der Support abgewickelt wird.
Wird der Tunnel vom Zielsystem aus auf dem Gateway mit dem UNIX-Socket "" erstellt, wird der Tunnel vom Support-Client zum Gateway stattdessen so aufgebaut:
user@supportclient$ ssh -nNT -L 10022:/home/ssh-kunde1/ziel3-ssh.socket \ sshtunnel@gateway.example.de
Ist ein Zugriff mittels Browser auf das Zielsystem notwendig, wird ein zweiter Tunnel zum Gateway erstellt:
user@supportclient$ ssh -nNT -L 10443:localhost:10002 \ sshtunnel@gateway.example.de
Oder bei einem UNIX-Socket:
user@supportclient$ ssh -nNT -L 10443:/home/ssh-kunde1/ziel3-https.socket \ sshtunnel@gateway.example.de
Wird nun im Browser https://localhost:10443 eingegeben, ist man direkt mit dem Webserver auf dem Zielsystem verbunden.