Manchmal möchte man auf einen entfernten Rechner sicher zugreifen, dafür gibt
es natürlich ssh
. Was aber tun, wenn die IP-Adresse des Rechners nicht bekannt
und dieser hinter einer Firewall und/oder zusätzlich einem NAT-Gateway steckt?
Dann könnte man natürlich über einen externen Anbieter eine Verbindung aufbauen
z.B. ein VPN, aber dafür muss normalerweise UDP-Trafik erlaubt sein und man
muss dem Anbieter vertrauen. Gut wäre eine Lösung, wo auf einem Remote-Rechner
keine Geheimnisse hinterlegt sein müssen.
Da ssh
ein flexibles Werkzeug ist und auch für das Weiterleiten von TCP-Ports
verwendet werden kann, kann ich auch entfernte Dienste an anderen Orten
verfügbar machen. Durch die sichere Authentifizierung mittels Schlüssel ist auch
Fremden der Zugriff nicht möglich und diese Schlüssel können mit den
eingebauten Funktionen selbst erzeugt und verteilt werden.
Dafür läuft auf dem Remote-Client ein "Service", der den für den Dienst nötigen Port an einen Server mit dem festen Namen oder IP weiterleitet. Wenn wir nun diesen Port des Servers nutzen oder selbst weiterleiten, dann haben wir Zugriff auf den Dienst des Remote-Client.
Auf den Server ist eigentlich keine weitere Einstellung nötig, außer um das
System robuster zu gestalten, sollten in der Datei /etc/ssh/sshd_config
die
folgenden Parameter gesetzt werden:
ClientAliveInterval 60
ClientAliveCountMax 3
Dies bewirkt, dass erst nach 180s die Verbindung abgebaut würde, womit kurze Netzunterbrechungen toleriert werden.
Sinnvoll ist evtl. auch ein separater Nutzer, der für die Anmeldung der Remote-Clients verwendet wird. Damit kann man dann später gezielt nach der Verbindung suchen und diese z.B. abbauen, falls doch mal etwas nicht wie gewünscht funktioniert. Hierzu ein Beispiel:
Match User emrcall
AllowTcpForwarding yes
X11Forwarding no
PermitTunnel no
GatewayPorts no
AllowAgentForwarding no
ClientAliveInterval 60
ClientAliveCountMax 3
ForceCommand date
Dieser User benötigt noch nicht einmal ein Passwort, da ja die Anmeldung
ausschließlich per ssh-key erfolgt. Je nach System ist dies aber für User ohne
Passwort gesperrt. Eine Änderung im 2. Feld der Datei /etc/shadow
von ! auf x
behebt dies.
Dieser Rechner soll einen oder mehrere Dienste auf dem Server anbieten. Dafür
nutzen wir ssh
mit der Option -R
(Remote Port Forwarding). Das Programm
sollte in einer Dienstüberwachung wie z. B. runit
laufen, denn die Verbindung
kann durch verschiedene Situationen abbrechen und damit endet auch die
Programmausführung.
Zuerst erstellen wir ein neues ssh-Schüsselpaar für diese Aufgabe, wobei wir
den Öffentlichen-Schüssel in die Datei ~/.ssh/authorized_keys
des für diesen
Zweck verwendeten Users eintragen. Die Datei darf nur vom User beschreibbar
sein (0600).
Mit dem oben angegebenen Beispiel-Setup sollte eine ssh-Verbindung mit dem gerade erzeugten Key die Uhrzeit des Servers liefern.
Um unsere Portweiterleitung als Dienst laufen zu lassen, bietet es sich an, die
Parameter in ein kleines Script zu schreiben, z. B. nach /etc/rpfs.sh
. Hier
ein Beispiel:
#!/bin/sh
ssh -q -o ConnectTimeout=45 -o ServerAliveInterval=30 -o ServerAliveCountMax=3 \
-N -R 4022:localhost:22 -i /home/user/.ssh/id_rsa_ermcall \
ermcall@server_ip_or_name
Dieses Script machen wir mit chmod +x /etc/rpfs.sh
ausführbar. Wenn wir dies
nun direkt starten, können wir auf den Server über Port 4022 auf den ssh-Server
des Remote-Clients zugreifen. Eine Anmeldung sollte aber nicht möglich sein, da
auf dem Server nicht die notwendigen Schlüssel verfügbar sind und auch nicht
sein sollten (Private Schlüssel sollten auch Privat bleiben).
Damit dies auch immer zur Verfügung steht, erstellen wir dafür einen Dienst, bei runit mit:
mkdir /etc/sv/emrcall
cd /etc/sv/emrcall
echo -en '#!/bin/sh\n\nexec /etc/rpfs.sh' > run
chmod +x run
ln -s /run/runit/supevrise-emrcall supervise
cd
ln -s /etc/sv/emrcall /var/service
Je nach System können die Pfade etwas anders sein, schaut einfach in die Dokumentation.
Frank hat stattdessen eine Service-Definition erstellt, was natürlich auch funktioniert.
[Unit]
Description=Keeps a tunnel to 'server_ip_or_name' open
After=network-online.target ssh.service
[Service]
User=user
Environment="AUTOSSH_PORT=0"
Environment="AUTOSSH_GATETIME=0"
RestartSec=30
Restart=always
ExecStart=/usr/bin/autossh -NT -o "ExitOnForwardFailure=yes" \
-R 4022:localhost:22 -p 22 -l ermcall server_ip_or_name \
-i /home/user/.ssh/id_rsa_ermcall
ExecStop=killall -s KILL autossh
TimeoutStopSec=10
[Install]
WantedBy=multi-user.target
Auf dem Rechner, wo wir diesen Dienst nutzen wollen, benötigen wir das
Schlüsselmaterial, um auf unseren Remote-Client zugreifen zu können. Der
passende Öffentliche Schlüssel muss auf dem Remote-Client eingetragen sein.
Sind diese Vorarbeiten erledigt, bietet es sich an, in der lokalen ssh
Konfiguration, normalerweise unter ~/.ssh/config
dafür einen eignen Eintrag zu
erstellen. Hier ein Beispiel:
Host server1
Hostname server_ip_o_name
IdentityFile ~/.ssh/id_rsa_for_server_access
User server_user_account
Host rhost1
Hostname localhost
Port 4022
IdentityFile ~/.ssh/id_rsa_for_remote_client
ProxyCommand ssh -W %h:%p server1
Nun ist mit ssh rhost1
ein Zugriff auf den entfernten Rechner möglich und
wir haben nirgends auf dem Weg Geheimnisse hinterlegen müssen.
Es können über eine Verbindung natürlich auch mehre Ports freigegeben oder auch bezogen werden.
In sehr seltenen Fällen und bei einer nach jetzigen Erfahrungen sehr schlechten
Netzverbindung, geht evtl. die Weiterleitung kaputt und die Client-Seite
beendet nicht das Programm. Dann ist es notwendig, den passenden sshd
Prozess
serverseitig zu beenden.