Im heutigen Beitrag aus der Kategorie Wochenend-Projekte geht es darum, wie man mit Hilfe eines Reverse-SSH-Tunnels ein Offsite-Backup für die heimische Datensammlung realisieren kann. Der Beitrag soll mir als Dokumentation und euch als Anregung für ähnliche Projekte dienen.
Anwendungsfall (Use Case)
In meinem Heimnetzwerk gibt es ein NAS, welches der Familie als allgemeines Datengrab dient. Lagen hier anfangs nur Datensicherungen anderer Geräte, verwalten wir heute, neben anderen Daten, unsere Familien- und Urlaubsfotos auf diesem Gerät. Es existiert eine lokale Datensicherung dieser Daten, die vor versehentlichem Löschen schützt.
Nun sind uns einige dieser Daten so wichtig, dass wir sie auch gern gegen Verlust durch z.B. Feuer oder Diebstahl schützen möchten. Dazu möchte ich die betreffenden Daten gern außerhalb der eigenen vier Wände an einem anderen Standort sichern. Diese Form der Datensicherung bezeichne ich im Folgenden auch als Offsite-Backup.
Anforderungen
An das Offsite-Backup stelle ich folgende Anforderungen:
- Die Datenübertragung erfolgt über gesicherte Verbindungen
- Die Daten werden in einem Format gespeichert, welches das Lesen ohne Abhängigkeit zu bestimmten Programmen ermöglicht
- Die Datenübertragung soll manuell angestoßen werden und optional zeitgesteuert ausgeführt werden können
- Eine Wiederherstellung der Daten soll über den gleichen Kanal wie die Datensicherung möglich sein
- Die Daten sollen nicht bei einem kommerziellen Anbieter gespeichert werden
- Die Daten sollen nicht in einem Speicher abgelegt werden, der sich des unmittelbaren Zugriffs entzieht
- In den Routern der beteiligten Heimnetzwerke sollen keine eingehenden Portweiterleitungen konfiguriert werden müssen
Alles was ich benötige, um die obigen Anforderungen umzusetzen sind:
- Ein Raspberry Pi
- Eine externe USB-Festplatte zum Anschluss an den Pi
- Einen Linux-Root-Server im Internet
- Einen LAN-Port und Stromanschluss im Elternhaus
Zu meinem Glück sind all diese Dinge bereits vorhanden und ich muss nicht erst eine Einkaufstour unternehmen.
Die Lösung
In groben Zügen erklärt, sieht die Lösung wie folgt aus. An den Raspberry Pi wird eine USB-Festplatte angeschlossen, welche als Speicher für die zu sichernden Daten dient. Der Pi wird in meinem Elternhaus an das dortige Heimnetzwerk angeschlossen. Von dort ausgehend baut er einen Reverse-SSH-Tunnel zu meinem Linux-Server auf, welcher bei einem Hoster läuft und auf eingehende SSH-Verbindungen lauscht. So kann ich von dem Linux-Server aus auf den Pi zugreifen, ohne dass im DSL-Router in meinem Elternhaus eine Portweiterleitung eingerichtet werden muss. Darüber hinaus entfällt auch die Notwendigkeit, die Gegenstelle über die sich ändernde dynamische IP-Adresse auf dem Laufenden zu halten.
Nun kann ich von dem NAS aus meinem Heimnetzwerk eine SSH-Verbindung über den Linux-Server zum Raspberry Pi in meinem Elternhaus aufbauen und Daten dorthin übertragen. Auch in meinem Heimnetzwerk muss dazu keine Portweiterleitung geschaffen werden.
Konfiguration auf dem Pi
Auf dem Raspberry Pi habe ich das Betriebssystem Raspbian installiert. Anschließend habe ich ein SSH-Schlüsselpaar erstellt und den öffentlichen SSH-Schlüssel auf dem Linux-Server hinzugefügt, um ohne die Eingabe eines Passworts eine SSH-Verbindung herstellen zu können. Für die Verbindung verwende ich normale Linux-Benutzer ohne besondere Privilegien. Nun kann mit dem Befehl ssh -R 22222:localhost:22 user@Linux-Server
ein Reverse-SSH-Tunnel zum Raspberry Pi aufgebaut werden. Der Reverse-SSH-Tunnel ermöglicht es, vom Linux-Server aus mit dem Befehl ssh -p22222 localhost
eine Verbindung zum Raspberry Pi aufbauen zu können.
Damit der Reverse-SSH-Tunnel nach der Provider-Zwangstrennung automatisch wieder aufgebaut wird, habe ich auf dem Raspberry Pi folgende Service-Unit erstellt:
[Unit] Description="Reverse ssh tunnel to proxy" After=network.target [Service] User=pusemuckel ExecStart=/usr/bin/ssh -NTi /home/pusemuckel/.ssh/id_rsa -o ServerAliveInterval=60 -R 22222:localhost:22 user@Linux-Server RestartSec=73 Restart=always [Install] WantedBy=multi-user.target
Die Option RestartSec gibt an, dass bei einem Verbindungsabbruch 73 Sekunden gewartet wird, bevor ein erneuter Verbindungsversuch gestartet wird. Damit möchte ich dem DSL-Router Zeit für die Wiedereinwahl geben.
Konfiguration auf dem NAS
Auf dem NAS erstelle ich im HOME-Verzeichnis des Benutzers, welcher später die Datensicherung ausführen wird, die Datei ~/.ssh/config mit folgendem Inhalt, um auf einfache Weise eine SSH-Verbindung zum Raspberry Pi im entfernten Standort herstellen zu können.
Host raspi HostName localhost Port 22222 ProxyCommand ssh -i ~/.ssh/nas_rsa -A -W %h:%p user@linux-server IdentityFile ~/.ssh/nas_rsa
ProxyCommand gibt an, dass der Linux-Server als Proxy bzw. Zwischenpunkt der Verbindung dient. In neueren SSH-Versionen kann man statt dessen auch die etwas einfacher zu handhabende Option JumpHost verwenden.
Neben der obigen Datei wurde auf dem NAS ein SSH-Schlüsselpaar erstellt, dessen öffentlicher Schlüssel auf dem Linux-Server und dem Raspberry Pi im entfernten Standort hinterlegt wurde. Dadurch ist für die Durchführung der Datensicherung keine Passworteingabe erforderlich. Da beide Endpunkte der Verbindung in vertrauenswürdigen Netzen liegen, halte ich diese Vorgehensweise für vertretbar.
Probleme
Leider läuft das Ganze noch nicht so rund und störungsarm, wie ich es mir wünsche. Bei der Zwangstrennung reißt die Verbindung ab, ohne dass der Raspberry Pi eine Chance hätte, den Socket auf dem Server zu schließen.
Da der verwendete Port noch auf dem Linux-Server gebunden ist, schlägt eine erneute Einwahl fehl. Erst wenn der Socket nach einem Timeout geschlossen wurde, ist eine erneute Wiederherstellung der Verbindung möglich.
Update 2018-05-21: Danke an Christian F., welcher mich auf die Optionen ClientAliveCountMax und ClientAliveInterval hingewiesen hat. Mit diesen Optionen kann der SSH-Server erkennen, dass die Verbindung zum Client abgerissen ist und den Socket schließen.
Der Standardwert für ClientAliveCountMax beträgt 3. Dabei handelt es sich um client alive messages, welche der Server zum Client sendet. Bleiben alle drei unbeantwortet, baut der Server die Verbindung ab und gibt den Socket frei. Den Wert für ClientAliveInterval habe ich auf 15 gesetzt. Er gibt an, dass nach 15 Sekunden Inaktivität eine client alive message vom Server an den Client gesendet wird, um zu überprüfen, ob der Client noch antwortet.
Antwortet der Client nicht, wird mit der obigen Konfiguration die Verbindung vom Server nach 45 Sekunden abgebaut und der Socket freigegeben. Der Client kann sich anschließend wieder neu verbinden. Auf dem Client habe ich den Parameter RestartSec auf 73 Sekunden gesetzt (einfach weil mir die Zahl so gut gefällt). Nach dem Abriss der Verbindung durch die Zwangstrennung wird der Tunnel nach 73 Sekunden wieder aufgebaut.
Fazit
Mit dieser kleinen Bastellösung kann ich meine Daten über einen gesicherten Kanal in einen entfernten Standort sichern. Das manuelle Offsite-Backup klappt bereits gut, wenn der SSH-Tunnel zwischen Linux-Server und Raspberry Pi steht.
Da das Problem mit der Zwangstrennung nun etwas entschärft werden konnte, steht auch einem zeitgesteuerten Backup grundsätzlich nichts mehr im Wege. Es bleibt lediglich das Restrisiko, dass sich der Zeitpunkt der Zwangstrennung ändert und diese dann das zeitgesteuerte Backup unterbricht. Mit diesem Risiko komme ich jedoch zurecht.
glaub ich hätte einfach autossh mit -fNC -R genommen
Für die Verbindung zum Sever könntest du autossh verwenden das baut automatisch immer eine neue Verbindung auf dann solltest du dir das mit dem Timeout sparen können so funktioniert es bei mir ganz gut nur eben nicht über Server im Internet sondern direkt vom Elternhaus zum Server Zuhause der per Dyn eh immer erreichbar is
Hilft mir autossh denn wirklich weiter? Der Pi erkennt ja, dass die Verbindung unterbrochen wurde und versucht diese wieder aufzubauen.
Was scheiert ist das Binden des Ports für den Rückkanal auf dem Server, da hier noch ein Socket existiert.
Mir ist noch nicht klar warum dies bei Verwendung von autossh anders sein sollte.
MfG
Jörg
Ich möchte mich an dieser Stelle bei Christian F. bedanken, welcher mich auf die Optionen ClientAliveCountMax und ClientAliveInterval in der sshd_config hingewiesen hat.
Mit diesen Optionen konnte ich das Problem der Zwangstrennung deutlich reduzieren. Ich habe den obigen Artikel entsprechend aktualisiert.
ich wollte das benutzen um Support bei der Verwandschaft zu leisten.
Sie müssten „nur“ die Reverse-SSH-Verbindung herstellen.
Ich frage mich dabei, ob man ihren Benutzer auf meinem (Proxy-)Server extra absichern könnte, so dass er auch nichts anderes machen kann ?!
Hallo Tom,
du kannst dem Benutzer, welcher für den SSH-Zugriff verwendet wird, eine eingeschränkte Login-Shell konfigurieren. Zum Beispiel die rbash(1).
So sind folgende Dinge in einer rbash *nicht* erlaubt:
* Verzeichniswechsel mit cd
* Verändern der Variablen SHELL, PATH, ENV, oder BASH_ENV
* Ausgabeumleitung mit <, >, >&1, etc. pp.
Ich hoffe diese Hinweise helfen dir etwas weiter.
Gruß
Jörg
Pingback: Mit Dokumentation zum Datenverlust | My-IT-Brain
Pingback: Meine privaten Arbeitsmittel Anfang 2022 | My-IT-Brain