Mittwoch, 18. Januar 2023

ssh Zugang für fhem über Script einrichten

Ich mache einige Dinge aus dem Docker Container heraus über ssh auf dem Docker Host, anstatt den Container zu sehr zu verbiegen. (Artikel andocken).

Dafür habe ich mal ein Script gebastelt und ein paar Aufruf Varianten ausgearbeitet. Damit sollte mit wenigen Handgriffen die Einrichtung des ssh public key Zuganges innerhalb FHEM auf einen anderen Host gelingen. 

Das Script hat nur wenige Zeilen es werden beliebig Hostnamen oder IP Adressen als Argumente übergeben. 

  1. Die eventuell vorhandenen Einträge in known_host werden gelöscht und neu eintragen.
  2. Es wird überprüft ob bereits ein ssh Key vorhanden ist, falls nicht wird ein Neuer erzeugt.
  3. Der public key wird zur möglichen Weiterverarbeitung (in authorized_keys) ausgegeben.

Ich zeige den Aufruf 

  • als komplettes Here Doc, als lokales Script oder Script vom GitHub, 
  • als anderer User,
  • oder direkt im Docker Container.

Das eigentliche Ziel: der public key von User fhem auf dem HostA wird dem User userB auf HostB gegeben damit User fhem sich als userB ohne Passworteingabe an HostB anmelden / Befehle ausführen kann.

Man beachte in allen Beispielen die unterschiedliche Verwendung der Shell Optionen -c und -s!

Erstmal nur zur Übung

Ich habe hier einige Varianten herausgegriffen die mir persönlich gefallen, es gibt eine ungeheure Vielzahl anderer Varianten.

Das kurze Here Doc soll erst mal das Prinzip an Hand von einem Einzeiler demonstrieren:

bash <<'EOF' -s arg1 arg2
whoami ; for arg in $* ; do echo $arg; done
EOF

Man kann es als andere User ausführen:

sudo -u fhem bash <<'EOF' -s arg1 arg2
whoami ; for arg in $* ; do echo $arg; done
EOF

Oder man kann es als ein bestimmter User in einem laufendem Container ausführen:

docker exec -iu fhem ContainerName bash <<'EOF' -s arg1 arg2
whoami ; for arg in $* ; do echo $arg; done
EOF

Ich habe mal das gleiche Script etwas strukturiert in eine Datei geschrieben und am Ende zwei Aufrufe analog der Vorherigen als Beispiel angefügt.

cat <<'EOF' >demo1.sh
whoami
for arg in $* ; do
  echo $arg
done
EOF
sudo -u fhem bash demo1.sh arg1 arg2
docker exec -iu fhem ContainerName bash < demo1.sh -s arg1 arg2

Tipp: man kann die Befehle auch manuell in der Docker Shell ausführen.

docker exec -itu fhem ContainerName bash

Um die folgenden Codezeilen übersichtlich zu halten, füge ich mal ein paar Variablen ein. Der erste Befehl ist nützlich um die vorhandenen Containernamen zu listen. Das Beispielscript habe ich für diesen Teil auf GitHub abgelegt. Die Variable arguments soll die Hostnamen und/oder IP Adressen enthalten. Ich habe dafür verschiedene Möglichkeiten ausprobiert, es gibt keine Universallösung, je nach dem welches System und welche Komponenten installiert sind. Am Ende habe ich noch ein paar Varianten aufgezeigt.

docker ps --format '{{.Names}}'
script_name=https://raw.githubusercontent.com/heinz-otto/scripts/master/Bash/demo1.sh
container_name=dockerbuild-fhem-1
arguments="$(hostname) $(host -t A $(hostname) | awk '{ print $4 }')"

Man kann jetzt das Script vom Github holen und über stdin der Bash übergeben. Hier ist zu beachten, dass der Scripttext mit der Option -c abgetrennt wird. Die beiden letzten Zeilen sollen nur zeigen, wie man das Script lokal speichern und den Namen aus dem kompletten Pfad extrahieren kann.

bash -c "$(wget -qO - ${script_name})" -s ${arguments}
docker exec -iu fhem ${container_name} bash -c "$(wget -qO - ${script_name})" -s ${arguments}
# oder erst lokal speichern
wget -O ${script_name##*/} ${script_name}
sudo -u fhem bash ${script_name##*/} ${arguments}

Script zur Einrichtung public key Zugang

Bisher war alles nur Übung jetzt zur eigentliche Aufgabenstellung: ssh Zugang für anderen User einrichten.

Dem Script wird als Parameter eine Leerzeichen getrennte Liste von Hostnamen und / oder IP-Adressen übergeben für die Einträge in der known_hosts Datei der FHEM Instanz erzeugt bzw. bereinigt werden. Damit wird die Sicherheitsabfrage bei der ersten Verbindung zum anderen anderen Host über ssh vermieden.

Zunächst das komplette Script als Here Doc:

docker exec -iu fhem dockerbuild-fhem-1 bash <<'EOF' -s odroidxu4 192.168.56.121
key_version=ed25519 
for arg in $* ; do
  yes '' | ssh-keygen -R ${arg}
  ssh-keyscan -t ${key_version} ${arg} 2>/dev/null >> ~/.ssh/known_hosts
done
if ! ls ~/.ssh/id_${key_version}.pub >/dev/null; then
    ssh-keygen -f ~/.ssh/id_${key_version} -P "" -t ${key_version}
  else
    echo "public key of type ${key_version} already in place"
fi
echo "echo $(cat ~/.ssh/id_${key_version}.pub) >>.ssh/authorized_keys"
EOF

Im Script kann man den Type des erzeugten private Keys einstellen - ed25519 - sollte als Standard überall passen. Man könnte rsa verwenden, dieser ist aber nur bei größeren Schlüssellängen wirklich sicher. Von der Verwendung von dsa wird generell abgeraten. 

Im Folgenden verwende ich das Script von meinem GitHub und ich habe die Variablennamen stark gekürzt, um die finale Codezeile kurz zu halten. 

Ausführung auf dem Docker Host

Scenario: Man ist als der User angemeldet, mit dem aus dem Container auf dem Docker Host Befehle über ssh ausgeführt werden sollen.

Der public key wird am Ende des Scripts ausgegeben, mit grep von den Statusmeldungen abgetrennt und auf dem Docker Host gleich in die authorized_keys Datei geschrieben.

Tipp: Die Codebox ist editierbar, einfach den Containernamen ändern: c=... den kompletten Text kopieren (ctrl+a) und ins Terminal werfen - sollte passen.

s=https://raw.githubusercontent.com/heinz-otto/scripts/master/Bash/setupSsh2Host.sh
c=dockerbuild-fhem-1
a="$(hostname) $(host -t A $(hostname) | awk '{ print $4 }')"
docker exec -iu fhem ${c} bash -c "$(wget -qO - ${s})" -s ${a}|grep -Eo 'ssh-.* ' >> ~/.ssh/authorized_keys

Will man den ssh Zugriff vom Container auf einen anderen Host im Netzwerk einrichten, ruft man das Script ohne Umleitung der Ausgabe auf und verfährt weiter wie im folgenden Abschnitt. 

a=ComputerName oder und IP
docker exec -iu fhem ${c} bash -c "$(wget -qO - ${s})" -s ${a}

Ausführung auf dem FHEM Server

Scenario: 

  • Host A - FHEM Server, User fhem
  • Host B - Zugriff von Host A User fhem als User userB auf HostB
  • Host x - Zugriff von Host A User fhem als User userX auf HostX

Man öffnet pro Host eine Terminalsitzung:

Sitzung A als beliebiger sudo User auf dem FHEM Server, Sitzung B als userB auf dem HostB und bei Bedarf  weitere Sitzungen analog C als userC auf dem HostC . Es folgen 2 Schritte:

  1. Man führt das Script in Sitzung A aus und kopiert die letzte Zeile "ssh-..." in die Zwischenablage. 
  2. In den anderen Sitzungen stellt man zunächst sicher, 
    • dass man sich im Homedir des Benutzers befindet der für die ssh Verbindung genutzt werden soll,
    • dass der Pfad .ssh schon exisitiert (mkdir .ssh) 
    • und schreibt anschließend den kopierten Key in die Datei .ssh/authorized_keys - entweder mit einem passenden Editor oder einfach mit echo <key> >>.ssh/authorized_keys .
Hier das Beispiel mit der direkten Ausführung des Scripts vom github:

s=https://raw.githubusercontent.com/heinz-otto/scripts/master/Bash/setupSsh2Host.sh
a="HostB IP-HostB HostX"
sudo -u fhem bash -c "$(wget -qO - ${s})" -s ${a}

In der Browser GUI von FHEM kann man sich sofort mit folgendem Befehl vom Erfolg überzeugen.

{qx 'ssh userB@HostB echo vom HostB' }

Es sollte die Textkette zurück kommen, ansonsten muss man im Log schauen was schief gelaufen sein könnte.

Beispiel um Hostname oder IP zu ermitteln

Für die Argumente kann man entweder die Strings direkt übergeben, oder die Werte beim Aufruf ermitteln lassen, zur Demonstration hier Beispiele mit echo. Beim Aufruf als Argument einfach das echo weglassen. 

h=HostB ; echo "${h} $(host -t A ${h} | awk '{ print $4 }')"
echo $(host -t AAAA $(hostname) | awk '{ print $5 }')
echo $(ping -4c 1 ${hostname} | awk 'NR==1{gsub(/\(|\)/,"",$3);print $3}')

Hat man das Script aus Versehen mehrfach ausgeführt, kann man die entstandenen Doubletten aus der Datei authorized_keys wieder entfernen.

sort -u ~/.ssh/authorized_keys -o ~/.ssh/authorized_keys

ToDo

Scriptname GitHub noch finalisieren 

Code Block


Keine Kommentare:

Kommentar veröffentlichen