Einrichtung eines Gateway mit SSH

Eine Kombination aus Remote und Local Port Weiterleitung zur Einrichtung eines Gateways mit SSH

Mit Hilfe einer Kombination aus Remote und Local Port Weiterleitung ist es möglich, Verbindungen zwischen Rechnern herzustellen, die auf direktem Weg untereinander nicht erreichbar sind. Dazu ist kein "offenes" Gateway erforderlich.

In einem anderen Artikel dieses Blog wurde beschrieben, wie man mit einer Remote Port Weiterleitung Dienste im Heimnetzwerk für den Zugriff aus dem Internet verfügbar machen kann. Dazu wird ein Zugang per SSH auf einem Rechner im Internet benötigt, den man im Prinzip über jeden VPS (Virtual Private Server) erhält.

Das Problem dabei ist, dass der SSH-Server als offenes Gateway arbeiten muss, was in der Standardkonfiguration eines SSH-Servers normalerweise deaktiviert ist.

Dieser Artikel zeigt nun, wie man mit einer Kombination aus Remote und Local Port Weiterleitung die Einrichtung eines offenen SSH-Gateways umgehen kann.

Die hier beschriebene Lösung funktioniert jedoch nur für Anwender, die einen SSH-Zugang zum Server haben. Eine Lösung, die den Zugriff für jederman verfügbar macht und dennoch auf die EInrichtung eines offenen Gateways verzichtet, wird in diesem Artikel vorgestellt.

Beispiel: Zugriff auf die interne Website eines Pi-hole

In einem Heimnetzwerk soll auf einem Raspberry Pi das Pogramm Pi-hole laufen. Pi-hole blockt jegliche Art von Internetwerbung im Heimnetzwerk, indem es DNS-Anfragen auflöst und bekannte Domainnamen von Werbeanbietern einfach ignoriert. Dieser Raspberry Pi soll im folgenden den Namen Cadamosto haben.

Das Programm Pi-hole beinhaltet eine Weboberfläche, über die das Programm konfiguriert werden kann und die gleichzeitig Statistiken und Logs in ansprechender und übersichtlicher Form in einem Dashboard darstellt. Dafür wird bei der Installation des Pi-hole zusätzlich ein lighttpd Webserver installiert, der auf dem Port 80 Anfragen entgegennimmt.

Die nachfolgende Abbildung zeigt das Dashboard des Pi-hole, das mit http://localhost/admin/ auf dem Raspberry Pi aufgerufen werden kann:

Dashboard des Pi-hole auf Cadamosto

Ziel ist es, dieses Dashboard auch von außerhalb des Heimnetzwerks aufrufen zu können, z.B. wenn man mal unterwegs ist. Die Lösung besteht in der Einrichtung einer Remote Port Weiterleitung auf dem Raspberry Pi und einer Local Port Weiterleitung auf dem Rechner, mit dem man sich außerhalb des Heimnetzwerkes befindet.

Damit diese Kombination aus Remote und Local Port Weiterleitung überhaupt möglich ist, wird ein Rechner im Internet benötigt – zum Beispiel ein VPS eines beliebigen Anbieters – auf dem man sich per SSH anmelden kann. Dieser Rechner soll Cayenne heißen.

Einrichtung der Remote Port Weiterleitung

Die erste Aufgabe ist die Einrichtung einer Remote Port Weiterleitung von Cadamosto zu Cayenne:

uwe@cadamosto:~ $ ssh -fN -o ExitOnForwardFailure=yes -R 8080:localhost:80 Cayenne

Cayenne ist in diesem Befehl ein Alias für den vollständigen Hostnamen des Rechners Cayenne.

Der Befehl richtet eine Port Weiterleitung als Hintergrundprozess vom Port 8080 auf Cayenne zum Port 80 auf Cadamosto ein. Da die Weiterleitung von Cadamosto aus eingerichtet wird, aber von Cayenne zurück zu Cadamosto führt, heißt sie Remote Port Weiterleitung oder auch manchmal Reverse Port Weiterleitung.

Für den Beginn der Port Weiterleitung muss auf Cayenne ein freier Port gewählt werden. Im hier verwendeten Beispiel wird davon ausgegangen, dass auf Cayenne bereits ein Webserver auf dem Port 80 läuft, daher wird der Port 8080 gewählt.

Die Weiterleitung ist auf Cadamosto (Raspberry Pi) zu sehen, wenn man sich alle Prozesse mit dem Befehl ps ausgeben lässt:

uwe@cadamosto:~ $ ps -x -o pid,cmd
  PID CMD
 4585 sshd: uwe@pts/2
 4588 -bash
 7855 ps -x -o pid,cmd
14072 /lib/systemd/systemd --user
14075 (sd-pam)
14871 ssh -fN -o ExitOnForwardFailure=yes -R 8080:localhost:80 Cayenne
uwe@cadamosto:~ $

Um die Weiterleitung zu beenden würde man ein SIGTERM an die PID schicken, z.B. mit kill 14871.

Es wäre natürlich von Vorteil, das Starten und Beenden der Weiterleitung mit einem einzigen Befehl durchführen zu können, am besten noch mit der Möglichkeit, den aktuellen Status abzufragen. Dies kann mit dem nachfolgenden Bash-Skript ReverseTunnel erreicht werden:

#!/bin/bash
# Script to start or stop remote port forwarding (rpf)
# Exit code is 1 if rpf is established, 0 if not
#
# Usage: ReverseTunnel (start|stop|status)

RHOST="server.example.com" # Remote Host (Cayenne)
RPORT="8080" # Remote Port on Remote Host
RUSER="$USER" # Remote User (here: same as local)
LHOST="localhost" # Local Host (Cadamosto)
LPORT="80" # Local Port on Local Host
SSHOPT="-fN -o ExitOnForwardFailure=yes" # Options for ssh

# Define the command to establish rpf
CMD="$(which ssh) $SSHOPT -R $RPORT:$LHOST:$LPORT $RUSER@$RHOST"

# Define a function to get the PID for a specific command
function getPID {
  pid=$(ps x -o user,pid,cmd | grep "$1" | grep -v "grep" | tr -s " " | cut -d" " -f2)
  [[ $pid ]] && echo $pid
}

# Get PID of $CMD (will be empty if rpf is not established)
PID=$(getPID "$CMD")

case $1 in
  start) # If parameter start is given

    # If PID exists then a rpf is already established
    if [ $PID ];then
      echo "Reverse tunnel already established!"
      echo "PID $PID ($RHOST:$RPORT -> $LHOST:$LPORT)"
      exit 1
    fi

    # Otherwise we can establish it
    $CMD > /dev/null 2>&1
    RETURNCODE=$?

    # Check the returncode and inform about new status
    if [ $RETURNCODE == 0 ];then
      PID=$(getPID "$CMD")
      echo "Reverse tunnel from $RHOST:$RPORT -> $LHOST:$LPORT established"
      echo "PID $PID"
      exit 1
    else
      echo "Reverse tunnel could not be established!"
      echo "Return code $RETURNCODE"
      exit $RETURNCODE
    fi
    ;;

  stop) # If parameter stop is given

    # If PID does not exist then a rpf is not established
    if [ -z $PID ];then
      echo "Reverse tunnel not established!"
      exit 0
    fi

    # Otherwise we can stop it by sending the SIGTERM to its PID
    kill $PID
    RETURNCODE=$?

    # Check the return code and inform about status
    if [ $RETURNCODE == 0 ];then
      echo "Reverse tunnel from $RHOST:$RPORT -> $LHOST:$LPORT terminated"
      exit 0
    else
      echo "Reverse tunnel could not be terminated!"
      echo "Return code $RETURNCODE"
      exit $RETURNCODE
    fi
    ;;

  status) # If parameter status is given

    # If PID exists then a rpf is already established
    if [ $PID ];then
      echo -n "Reverse tunnel already established with PID $PID"
      echo " from $RHOST:$RPORT -> $LHOST:$LPORT"
      exit 1
    fi

    # If PID does not exist then a rpf is not established
    if [ -z $PID ];then
      echo "Reverse tunnel not already established"
      exit 0
    fi
    ;;

  *) # If none or any other parameter is given, print usage info and exit
    echo "Usage: $(basename $0) (start|stop|status)" && exit 125
    ;;
esac

Mit dem Befehl ReverseTunnel start kann man die Remote Port Weiterleitung aktivieren und mit ReverseTunnel stop wieder deaktivieren. Mit ReverseTunnel status lässt sich der aktuelle Status der Port Weiterleitung abfragen.

Auf dem VPS Cayenne kann man anhand der Liste der offenen Ports sehen, dass es diese Weiterleitung gibt – allerdings leider nicht, wohin diese führt bzw. von wo aus sie eingerichtet wurde:

uwe@cayenne:~$ sudo netstat -ltpn | grep 8080
tcp        0      0 127.0.0.1:8080      0.0.0.0:*       LISTEN      25789/sshd: uwe     
tcp6       0      0 ::1:8080            :::*            LISTEN      25789/sshd: uwe     
uwe@cayenne:~$

Der Befehl netstat wird hier mit sudo aufgerufen, da sonst nicht alle Details angezeigt werden.

Wie man sieht, wird nur der lokal erreichbare Port 8080 von Cayenne weitergeleitet. Von außerhalb des Rechners Cayenne ist die Nutzung der Port Weiterleitung nicht möglich. Das ist der Nachteil (oder, unter Sicherheitsaspekten: Vorteil) der beschriebenen Standardeinstellung, dass der SSH-Server kein offenes Gateway bildet.

Das würde allerdings auch nicht dem Ziel entsprechen, dass die Website von unterwegs erreichbar sein soll.

Man könnte natürlich jetzt von unterwegs in einem Terminal eine SSH-Verbindung zu Cayenne herstellen, aber in einem Terminal könnte man die Website nicht aufrufen und wenn doch - z.B. mit lynx - würde diese nicht richtig angezeigt werden. Ziel ist es weiterhin, das Dashboard mit einem normalen Browser auf dem Rechner, mit dem man gerade arbeitet, aufzurufen.

Einrichtung der Local Port Weiterleitung

Dafür muss nun auf dem Rechner mit dem die Website aufgerufen werden soll eine Local Port Weiterleitung zu Cayenne eingerichtet werden. Im folgenden geschieht dies auf einem Laptop, das auf den Namen Caboto hört. Da auch auf Caboto bereits ein Webserver läuft und den Port 80 belegt, wird für die Port Weiterleitung der Port 8008 verwendet:

uwe@Caboto:~$ ssh -fN -o ExitOnForwardFailure=yes -L 8008:localhost:8080 Cayenne

Der Befehl richtet eine Port Weiterleitung als Hintergrundprozess vom Port 8008 auf Caboto zum Port 8080 auf Cayenne ein. Da die Einrichtung von Caboto aus erfolgt und von Caboto zu Cayenne führt, nennt diese sich Local Port Weiterleitung.

Auch auf Caboto kann man den Hintergrundprozess mit Hilfe von ps sehen und mit einem SIGTERM an die PID bei Bedarf beenden. Es könnte natürlich hier auch wieder ein Skript erstellt werden, dass beide Aufgaben erfüllt.

Der offene Port ist diesmal nicht auf Cayenne sondern auf Caboto zu sehen und wie man sieht, ist auch hier die Port Weiterleitung nur über die lokale Schnittstelle erreichbar:

uwe@Caboto:~$ sudo netstat -ltpn | grep 8008
tcp        0      0 127.0.0.1:8008      0.0.0.0:*       LISTEN      5813/ssh            
tcp6       0      0 ::1:8008            :::*            LISTEN      5813/ssh            
uwe@Caboto:~$

Jetzt kann man in einem Browser auf Caboto die Adresse http://localhost:8008/admin/ eingeben und das Dashboard des Pi-hole auf Cadamosto sehen:

Dashboard des Pi-hole auf Cadamosto von Caboto aus gesehen

Wie man sieht, müsste man sich hier erst erneut anmelden, um das vollständige Dashboard zu sehen, was übrigens trotz der vermeintlich unverschlüsselten Verbindung (http://…) kein Problem ist. Denn der Aufruf von localhost:8008 auf Caboto bewirkt ja eine unmittelbare Weiterleitung durch die bestehende SSH-Verbindung zu Cayenne. Dort wird die Anfrage an den lokalen Port 8080 gerichtet und wiederum unmittelbar durch die bestehende SSH-Verbindung zu Cadamosto weitergeleitet, wo sie auf dem lokalen Port 80 landet.

Mit anderen Worten ist die Verbindung auf dem ganzen Weg verschlüsselt und es besteht keine Notwendigkeit, die Website zusätzlich per Zertifikat zu sichern.

Die nachfolgende Abbildung zeigt noch einmal die ganze Konstruktion schematisch:

Remote und Local Port Weiterleitung im Zusammenspiel

Vom Raspberry Pi Cadamosto im Heimnetz wird eine Remote Port Weiterleitung zum VPS Cayenne eingerichtet, die Anfragen auf den Port 8080 auf Cayenne durch den Router hindurch zu Cadamosto auf Port 80 weiterleitet.

Vom Laptop Caboto wird unterwegs dann eine Local Port Weiterleitung zu Cayenne eingerichtet, die Anfragen auf den Port 8008 auf Caboto zu Cayenne auf Port 8080 weiterleitet - wo diese dann unmittelbar zu Cadamosto weitergeleitet werden.

Somit kann man nun unterwegs auf Caboto in einem Browser die Adresse http://localhost:8008/ eingeben und sieht das Dashboard des Pi-hole im Heimnetzwerk.

Weitere Möglichkeiten

Natürlich ist die Anwendung dieser kombinierten Port Weiterleitung nicht auf eine Website oder Websites im allgemeinen beschränkt. So könnte man zum Beispiel auch den SSH-Zugriff auf den Raspberry Pi nach außen freigeben, wenn man im Skript ReverseTunnel den lokalen Port 80 durch 22 und den entfernten Port 8080 durch 2222 ersetzt – letzteres, weil auf dem VPS Cayenne ja bereits ein SSH-Server auf Port 22 hört.

Mit einer entsprechend angepassten Local Port Weiterleitung hätte man dann von überall direkten Zugriff auf eine Konsole auf dem Heimnetzrechner Cadamosto.

Top