Wenn du als Administrator in einem stark abgesicherten Netzwerk arbeitest, kann es sein, dass du nicht jeden Server ohne Umwege erreichst. Häufig ist die Verbindung zu einem Jumphost notwendig. Wer kann sich aber schon immer merken, bei welchem Server das wirklich erforderlich ist und wann man getrost auf den Umweg verzichten kann! Ich schaffe das zumindest nicht.
Deshalb habe ich mir gedacht, dass ein Skript für diesen Anwendungsfall sehr praktisch wäre. Das spart nämlich Zeit bei der täglichen Arbeit und man muss sich die Optionen für das SSH-Kommando nicht merken. Das Shell-Skript habe ich für mein Ubuntu-Notebook entwickelt. Wie es aussieht und wie man überhaupt eine Verbindung über einen Jumphost aufbaut, erfährst du im Folgenden.
Wie eine Verbindung über einen Jumphost aufgebaut wird:
SSH-Verbindungen über einen anderen Host werden in der Regel mit der Option -J ausgeführt. Dieser Parameter gibt dem SSH-Client an, über welchen Jumphost eine Verbindung hergestellt werden soll. Der Syntax ist wie folgt:
ssh -J [user@]Jumphost [user@]Targethost
Hierbei steht Jumphost für den Host, über den du die Verbindung herstellen möchten, und Targethost ist der Host, zu dem du eigentlich gelangen möchtest. Wenn du den obigen Befehl ausführst, wird also eine Verbindung zum Jumphost hergestellt, und von dort aus wird dann eine Verbindung zum Zielhost aufgebaut. Mehr tut das kleine Kommando gar nicht.
Und selbstverständlich werden auf diese Weise deine Authentifizierungsdaten über eine verschlüsselte Verbindung an den Zielhost weitergeleitet. Selbstredend ist dabei zu beachten, dass der Jumphost in der Lage sein muss, eine Verbindung zum Zielhost herzustellen. Aber das sollte jedem meiner Leser klar sein, denke ich. Doch eine Frage stellt sich noch, nämlich warum der ganze Aufwand.
Insgesamt bietet die Verwendung eines Jumphosts eine zusätzliche Schutzebene für SSH-Verbindungen. Letztendlich muss nämlich nur der Jumphost in der Lage sein, das Target im Netzwerk zu erreichen. Insbesondere in Netzwerken mit hohen Sicherheitsanforderungen ist dies daher häufig Standard. Außerdem muss so nicht für jeden Administrator eine passende Firewallregel erstellt werden.
Was die Netzwerker in Freude versetzt, kann uns Systemer aber ganz schön nerven. Vor allem dann, wenn man bestimmte Server nur über einen Jumphost erreicht und andere wiederum nur vom eigenen Arbeitsgerät. Genau hier kommt mein Skript ins Spiel. Es prüft zuerst, ob eine Verbindung vom eigenen PC möglich ist und falls nicht, wird der Jumphost benutzt.
Was mein Skript genau macht und wie du es bei dir einrichtest:
Mein kleines Skript dient dazu, eine SSH-Verbindung zu einem Remote-Host aufzubauen. Dabei wird zuerst getestet, ob man den Server direkt vom eigenen Rechner erreicht und falls das nicht klappt, wird die Verbindung über einen Jumphost aufgebaut. Die Werte für den Jumphost und den Benutzernamen müssen dabei natürlich noch auf deine netzwerkseitigen Gegebenheiten angepasst werden.
Ansonsten kannst du das Skript direkt unter /usr/bin mittels vorangestelltem sudo abspeichern. Ausführbar muss die Datei natürlich auch noch sein. Dies gelingt dir mithilfe von chmod 777. Damit ist das Skript für jeden Benutzer auf dem System verfügbar und kann direkt in der Konsole aufgerufen werden. Ein vollständiger Pfad wird dabei nicht benötigt.
Für den Anfang ist es sicherlich noch praktisch zu wissen, wie die genau Syntax lautet. Hierfür kannst du entweder direkt mit –help in der Konsole arbeiten oder du schaust dir folgendes Beispiel an:
ssh-jumphost -t <target_host> -u <target_user> [-j <jump_host>]
Im Prinzip genügt es, wenn du das Ziel deiner Begierde mittels -t angibst und mithilfe von -u noch den Nutzernamen festlegst mit dem du dich am Zielserver anmelden möchtest. Optional ist dabei noch die Angabe eines Jumphosts. Im Skript selbst ist dieser nämlich bereits hinterlegt.
Da man aber manchmal mehrere hat, wollte ich diese Option noch integrieren. Jetzt aber genügend um den heißen Brei herumgeredet, im Folgenden findest du das kleine Shell-Skript. Sollte dich die genau Funktionsweise der einzelnen Zeilen interessieren, dann findest du weiter unten noch eine kurze Erklärung.
#!/bin/bash
# Bitte noch an dein Netzwerk anpassen:
jump_host="jumphost.fabianscloud.de"
jump_user="rootinator"
# Hilfe aufrufen:
if [[ $1 == "--help" ]]; then
echo "Usage: $0 -t <target_host> -u <target_user> [-j <jump_host>]"
exit 0
fi
# Optionen parsen:
while getopts "t:u:j:h" opt; do
case $opt in
t) target_host=$OPTARG ;;
u) target_user=$OPTARG ;;
j) jump_host=$OPTARG ;;
h) echo "Usage: $0 -t <target_host> -u <target_user> [-j <jump_host>]" >&2
exit 0 ;;
*) echo "Usage: $0 -t <target_host> -u <target_user> [-j <jump_host>]" >&2
exit 1 ;;
esac
done
# Überprüfen, ob Ziel-Host und Benutzername vorhanden sind:
if [[ -z $target_host ]] || [[ -z $target_user ]]; then
echo "Usage: $0 -t <target_host> -u <target_user> [-j <jump_host>]" >&2
exit 1
fi
# Verbindung zum Ziel-Host herstellen und testen:
if ssh -o ConnectTimeout=5 $target_user@$target_host exit 2> /dev/null; then
# Verbindung zum Ziel-Host herstellen:
ssh $target_user@$target_host
else
# Verbindung zum Jumphost herstellen:
ssh -J $jump_user@$jump_host $target_user@$target_host
fi
Wie der Code im Detail funktioniert:
Nicht jede einzelne Zeile in einem Skript ist sofort verständlich. Natürlich sind Dinge wie die Shebang oder Variablen für meine Leser nur alte Hüte. Daher möchte ich gleich etwas tiefer eintauchen und mit der Ausgabe der Hilfe beginnen.
if [[ $1 == "--help" ]]; then
echo "Usage: $0 -t <target_host> -u <target_user> [-j <jump_host>]"
exit 0
fi
Dieses if-Statement prüft, ob das erste Argument, das an das Skript übergeben wird, der Zeichenfolge –help entspricht. Wenn dies der Fall ist, wird eine Hilfeanleitung ausgegeben, und das Skript wird mit dem Exit-Code 0 beendet.
while getopts "t:u:j:h" opt; do
case $opt in
t) target_host=$OPTARG ;;
u) target_user=$OPTARG ;;
j) jump_host=$OPTARG ;;
h) echo "Usage: $0 -t <target_host> -u <target_user> [-j <jump_host>]" >&2
exit 0 ;;
*) echo "Usage: $0 -t <target_host> -u <target_user> [-j <jump_host>]" >&2
exit 1 ;;
esac
done
Hier wird eine while-Schleife ausgeführt, um die Optionen zu parsen, die dem Skript übergeben werden. Die Optionen, die vom Skript unterstützt werden, sind -t für den Ziel-Hostnamen, -u für den Ziel-Benutzernamen, -j für den Jumphost und -h für die Hilfe.
Die getopts-Funktion wird verwendet, um die Optionen zu analysieren. Der opt-Parameter gibt die Option an, die analysiert wird, und der OPTARG-Parameter gibt den zugehörigen Wert an.
In der case-Anweisung wird für jede unterstützte Option eine entsprechende Aktion ausgeführt. Wenn beispielsweise die Option -t angegeben wird, wird der Wert der Option der Variablen target_host zugewiesen. Gleiches Spiel bei der Option -j. Wird diese angegeben, wird der Wert der Option der Variablen jump_host zugewiesen.
Sollte hingegen eine unbekannte Option angegeben werden oder einfach nur -h, dann wird eine kleine Hilfe ausgegeben, und das Skript wird umgehend beendet. Bis zum jetzigen Zeitpunkt wurde aber noch nicht untersucht, ob alle benötigten Optionen bei der Verwendung des Programms gesetzt wurden.
if [[ -z $target_host ]] || [[ -z $target_user ]]; then
echo "Usage: $0 -t <target_host> -u <target_user> [-j <jump_host>]" >&2
exit 1
fi
Dieses if-Statement prüft, ob der Ziel-Hostname und der Benutzername angegeben wurden. Wenn eine oder beide Variablen leer sind, wird eine Fehlermeldung ausgegeben, und das Skript wird mit dem Exit-Code 1 beendet. Schließlich muss SSH wissen, wohin man sich verbinden möchte und welchen Nutzer man dafür am Zielsystem einsetzen will.
if ssh -o ConnectTimeout=5 $target_user@$target_host exit 2> /dev/null; then
ssh $target_user@$target_host
else
ssh -J $jump_user@$jump_host $target_user@$target_host
fi
Die if-Anweisung überprüft das Ergebnis des SSH-Kommandos. Dieses versucht, eine Verbindung zum Zielhost herzustellen. Sollte dies binnen 5 Sekunden nicht geklappt haben, dann wird die Verbindung über einen Jumphost hergestellt.