OpenSSH Tunnel im Hintergrund (inkl. Monitoring)

Oft möchte man eine SSH-Verbindung im Hintergrund permanent laufen lassen. Zum Beispiel, um einen Tunnel geöffnet zu halten. Das ist mit öffentlichen Schlüsseln und den Kommandozeilen Paramtern -f und-N leicht möglich.

Hintergrund-Tunnel

Ein Befehl wie der folgende ist dazu in der Lage einen entfernten Port auf dem lokalen Computer zugänglich zu machen (wie zum Beispiel für Munin).

/usr/bin/ssh -fqnN -L4959:localhost:4949 root@irgendwo.im.internet

Die Parameter von links nach rechts haben folgende Bedeutung.

  • -f – In den Hintergrund wechseln, bevor der Befehl ausgeführt wird.
  • -q – Möglichst wenig Ausgaben erzeugen (die aus dem Hintergrund sowieso nicht gelesen werden können)
  • -n – Leitet die Standardeingabe stdin aus /dev/null weiter und ist Voraussetzung für den Betrieb im Hintergrund
  • -N – Führt auf der entfernten Seite keinen Befehl aus (der unnötig Kontingente belasten würde)
  • -L4959:localhost:4949 – Leitet alle Anfragen die lokale an Port 4959 gesendet werden über den Tunnel an den entfernten Server auf dessen Adresse localhost und Port 4949 (Standardport von munin-node)

Der Befehl funktioniert und erlaubt dem lokalen Benutzer den Zugriff auf den entfernten Port mit Verschlüsselung und gegebenenfalls auch Komprimierung (dafür -C den Argumenten hinzufügen).

Monitoring

Was aber, wenn der ssh Client im Hintergrund stirbt, oder die Verbindung aus anderen Gründen beendet wird? Diesem Problem lässt sich mit dem Ubuntu und Debian eigenen start-stop-daemon und einem kleinen Skript namens sshpin beikommen.

#!/bin/bash
USERHOST="root@irgendwo.im.internet"
NAME="haxogen"
TUNNEL_ARGS=" -fqnN -L4959:localhost:4949 ${USERHOST} -MS ~/.ssh/ctl-${NAME}"

# No Changes below here, or they'll haunt you
PATH=/sbin:/usr/sbin:/bin:/usr/bin
SSH="/usr/bin/ssh"
${SSH} -S "~/.ssh/ctl-${NAME}" -O check ${USERHOST} 2> /dev/null
if [ $? -ne 0 ]; then
	echo "Tunnel ${NAME} nicht gefunden, starte neu." | logger -t sshpin
	start-stop-daemon -bv --start --exec ${SSH} -- ${TUNNEL_ARGS} | logger -t sshpin
fi

Das Skript startet den gewünschten Tunnel mit den angegebenen Argumenten, die in der Variable TUNNEL_ARGS hinterlegt werden. Der Clou: Über den wenig bekannten ControlSocket Mechanismus von OpenSSH wird vor dem Starten geprüft, ob bereits eine Instanz von ssh läuft und nur wenn diese weder eine Verbindung aufgebaut hat, noch läuft, wird eine neue Verbindung aufgebaut. Zusätzlich produziert das Skript Einträge unterhalb von /var/log die auf das Problem hinweisen und wie folgt aussehen.

Aug  2 16:16:18 firewall sshpin: Tunnel haxogen nicht gefunden, starte neu.

Das Skript kann angepasst werden und mittels dem Befehl crontab -e wie folgt in die Cron-Tabelle aufgenommen werden.

@hourly /usr/local/sbin/sshpin.haxogen

Natürlich sind auch mehrere Tunnel kein Problem, dafür wird das Skript kopiert und mit anderem Namen in die Cron-Tabelle eingefügt. Jetzt gibt es keine Ausrede mehr für unverschlüsselt lauschende Munin Instanzen, die leider noch relativ oft zu finden sind.

6 comments

    1. Hallo,
      autossh hatte ich auch auf dem Radar. Welchen Vorteil siehst du denn, im Vergleich zu meiner Lösung? Die Skripte von Marco sind umfangreicher und länger. Beziehungsweise welche Funktion fehlt dir bei meiner Lösung?

      Grüße

      1. Ich bin zwar nicht Christoph, aber antworte mal. ;-)

        Wenn ich es richtig verstanden haben, wird dein Skript per Cron regelmäßig aufgerufen und baut ggf. einen neuen Tunnel auf. Das heißt, wenn der Tunnel nicht mehr besteht muss bis zum nächsten Cronlauf gewartet werden, autossh hingegen arbeitet eher als Deamon und baut sofort die neue Verbindung auf.

        1. Hallo Erik,
          da hast du Recht. Was mit meiner Variante möglich ist, ist dass bei Ende der Verbindung durch den entfernten Host die Verbindung durch den lokalen Client wieder aufgebaut wird. Das werde ich im Laufe des Tages mit berücksichtigen (hatte gestern keine Lust nochmal die Manpage zu wälzen). Darüber hinaus sind dann die Grenzen des Skripts erreicht, mehr ginge nur mit einem Daemon und den möchte ich nur ungern auf meinem Router laufen lassen.

          Das ist übrigens alles so klein gehalten, damit es lesbar und verstehbar bleibt, ich bin eher ein Minimalist :)

          Grüße

  1. Wäre das nicht auch etwas für upstart?
    Mittels Upstart müsste man durch Bedingungen wie eine vorhandene Internetverbindung einen Dienst starten können. Zudem ist Upstart soweit ich weiss auch in der Lage abgestürzte Programme wieder zu starten.

    1. Hallo Otto,
      leider gibt es noch kein einheitliches Initsystem. Damit meine Skripte zumindest zwischen den Debian-Derivaten portabel bleiben, benutze ich nur Funktionen, die überall vorhanden sind. Wie zum Beispiel Cron :)

      Grüße

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>