Ich habe mehrere Himbeer-Pis, auf denen Arch Linux (keine GUI) ausgeführt wird, auf die ich zugreifen muss. Diese Pis befinden sich hinter Firewalls an jedem einzelnen Standort. Derzeit verwende ich openvpn, um eine Verbindung zu diesen herzustellen, aber die Kosten für dieses System sind pro Lizenz hoch. Ich benutze den Zugangsserver von ihnen.
Infolgedessen versuche ich, ein System zu entwerfen und einzurichten, das es mir ermöglicht, mich bei meinem VPN-Server (vps) anzumelden und einen Befehl auszuführen, um nach einem bestimmten Namen (OfficeDevice1991) zu suchen, z. B.:customcommandsearch "OfficeDevice1991"
und dann gibt es die IP-Adresse der Maschine oder etwas zurück, mit dem ich SSH verbinden kann. Ich suche auch nach der Möglichkeit, einen Befehl auszuführen, um jedes aktive verbundene Gerät aufzulisten. Es listet die IP, den Namen und vielleicht wie lange es aktiv ist.
Für dieses Ziel muss ich natürlich etwas erstellen, das den Namen des Geräts enthält (in diesem Fall OfficeDevice1991), und dann kann sich dieses Pi in meinen öffentlichen vps-Server einklinken. Vom öffentlichen Server aus kann ich mich anmelden und jedes damit verbundene Gerät durchsuchen und Informationen zurückgeben, die für die SSH-Verbindung erforderlich sind.
Ich habe mich mit Reverse-SSH beschäftigt und bisher habe ich mit den folgenden Befehlen einen meiner Test-Pis verbunden und von meinem vps aus darauf zugegriffen:
PI:
ssh -fN -R 12345:localhost:22 -i /publickeyfile [email protected] //Pi's command to connect to vpn
VPS:
ssh -p 12345 [email protected] //command for vpn to connect to pi
Das funktioniert großartig, aber wenn ich diese Methode verwenden würde, würde ich auf ein paar Probleme stoßen:
- Ich müsste eindeutige unbenutzte Ports einrichten
- Eine Möglichkeit, diese Ports/Tunnel offen zu halten
- Ich muss mir ein System einfallen lassen, um jedes Gerät zu identifizieren. Ich kann jeden Port mit einem Namen wie eine Textdatei lokal einloggen? Es wäre von Vorteil, dies nach Möglichkeit in das SSH-Setup für jedes Gerät aufnehmen zu können. Ich müsste trotzdem sicherstellen, dass die von mir verwendeten Ports nicht von anderen Programmen oder Geräten verwendet werden, die bereits vorhanden sind.
Was ich nicht tun möchte
-
Überprüfen Sie, welche Ports für jedes RPI kostenlos verwendet werden können
-
Muss
.ssh/config
manuell bearbeiten um einen Namen hinzuzufügen, der jeden Port repräsentiert, der dem RPI aus Teil 1 oben zugewiesen wurde.
Ich schreibe dies zur Information/Unterstützung, was ich für mein Ziel tun kann.
Kann mir jemand eine passende Lösung anbieten?
Akzeptierte Antwort:
Hier ist eine Lösung mit OpenSSH>=6.7 + socat:
-
OpenSSH>=6.7 kann die Unix-Domain-Socket-Weiterleitung verwenden
Das bedeutet, dass der Reverse-Tunnel-Endpunkt ein UNIX-Listening-Socket anstelle eines herkömmlichen TCP-Listening-Sockets ist. Sie können dann die Flottille von RPIs mit einem einfachen Benennungsschema einfacher verwalten:Der Name des Sockets ist der gewählte (und feste) Name des RPI, wie
OfficeDevice1991
. Es könnte sogar eine eindeutige Eigenschaft des RPI sein, solange es sich um einen gültigen Dateinamen handelt (da Unix-Socket-Namen Dateinamenkonventionen einhalten). Zum Beispiel seinen Hostnamen, die MAC-Adresse seiner Ethernet- oder WLAN-Karte …SSH kann Unix-Sockets für Tunnel handhaben, nicht um sich selbst zu verbinden. Es benötigt die Hilfe eines
ProxyCommand
um als Unix-Socket-Client arbeiten zu können. socat kann mit vielen Arten von Verbindungen umgehen, einschließlich Unix-Sockets.UPDATE:
Es gibt auch ein spezielles Problem zu behandeln:Die Unix-Socket-Datei wird beim sauberen Beenden nicht gelöscht und wäre zum Beispiel nach einem Absturz sowieso nicht gelöscht worden. Dazu ist die OptionStreamLocalBindUnlink=yes
erforderlich . Ich fand anfangs nicht, dass diese Option, wie der Name vielleicht andeutet, auf dem Knoten gesetzt werden muss, der den Unix-Socket erstellt. Am Ende wird es also auf dem Client mit einer lokalen Weiterleitung (-L
) sonst auf dem Server (insshd_config
) mit einer Remote-Weiterleitung (-R
). OP hat es dort gefunden. Diese Lösung verwendet eine Remote-Weiterleitung.Konfiguration auf VPS:
mkdir /rpi-access
(als root) bearbeiten Sie die
sshd_config
Datei (/etc/ssh/sshd_config
). Es erfordert diese zusätzliche Option:StreamLocalBindUnlink yes
Abhängig von den Standardoptionen ist möglicherweise auch
AllowStreamLocalForwarding yes
erforderlichUPDATE2:
Wird auch insshd_config
gesetzt die ParameterClientAliveInterval
undClientAliveCountMax
, wodurch ein Verbindungsabbruch in angemessener Zeit erkannt werden kann, z. B.:ClientAliveInterval 300 ClientAliveCountMax 2
Veraltete ssh-Verbindungen sollten dann auf dem VPS früher erkannt werden (~10 Minuten im Beispiel) und der entsprechende sshd-Prozess wird dann beendet.
Nutzung auf RPI:
ssh -fN -R /rpi-access/OfficeDevice1991:localhost:22 -i /privatekeyfile [email protected]
In einer Konfigurationsdatei wäre dies etwa so:
Host ip User useraccount RemoteForward /rpi-access/OfficeDevice1991:localhost:22 IdentityFile /privatekeyfile
Um es noch einmal zu wiederholen:
StreamLocalBindUnlink yes
eingestellt aufsshd
auf VPS-Seite Option ist wichtig:Der gerade erstellte Socket wird nicht entfernt, auch nicht beim normalen Beenden. Diese Option stellt sicher, dass der Sockel entfernt wird, falls er vor der Verwendung vorhanden ist, und somit für weitere Neuverbindungen wiederverwendet werden kann. Das bedeutet auch, dass man das bloße Vorhandensein des Sockets nicht als Zeichen dafür ansehen kann, dass das RPI verbunden ist (aber siehe später).Jetzt erlaubt dies auf VPS:
ssh -o 'ProxyCommand=socat UNIX:/rpi-access/%h -' [email protected]
Als Konfigurationsdatei, wenn man bedenkt, dass RPIs beispielsweise alle einen Namen haben, der mit OfficeDevice beginnt :
Host OfficeDevice* User rpiuseraccount ProxyCommand socat UNIX:/rpi-access/%h -
-
Um den Link beizubehalten, verwenden Sie einfach eine Schleife
Das RPI kann eine Schleife ausführen, die ssh immer dann wieder mit dem VPS verbindet, wenn die Verbindungen beendet werden. Dazu darf es nicht den Hintergrundmodus verwenden (kein
-f
). Es sollte auch ein Keepalive-Mechanismus verwendet werden. TCPKeepAlive (Systemebene) oder ServerAliveInterval (Anwendungsebene) sind verfügbar. Ich denke, TCPKeepAlive ist nur auf dem Server (der Seite, die die Verbindung empfängt) nützlich, also verwenden wir lieber ServerAliveInterval.Sein Wert (wie auch ServerAliveCountMax) sollte wahrscheinlich in Abhängigkeit von verschiedenen Kriterien angepasst werden:eine Firewall, die inaktive Verbindungen nach einer bestimmten Zeit verwirft, die gewünschte Wiederherstellungsverzögerung, keinen nutzlosen Verkehr erzeugen, … sagen wir hier 300s.
OfficeDevice1991 RPI:
#!/bin/sh while : ; do ssh -N -o ConnectTimeout=30 -o ServerAliveInterval=300 -R /rpi-access/OfficeDevice1991:localhost:22 -i /privatekeyfile [email protected] sleep 5 # avoid flood/DDoS in case of really unexpected issues done
Auch wenn die Remote-Seite den vorherigen Verbindungsfehler noch nicht erkannt hat und die alte ssh-Verbindung noch einige Zeit läuft,
StreamLocalBindUnlink yes
wird den Unix-Socket sowieso zwangsweise auf die neue Verbindung aktualisieren. -
es wird bereits von 1 behandelt.
Es gibt keine
customcommandsearch
erforderlich. Mit den richtigen Einstellungen in 1. einfach mitssh OfficeDevice1991
verbindet sich mit OfficeDevice1991.Bei Bedarf auf dem VPS als
root
Nur Benutzer, dieser Befehl:fuser /rpi-access/*
kann anzeigen, welche RPIs derzeit verbunden sind (natürlich mit Ausnahme derjenigen, die kürzlich die Verbindung vor der Erkennung verloren haben). Die veralteten Unix-Socket-Dateien werden nicht angezeigt, da kein Prozess an sie gebunden ist.