Montag, 16. Januar 2017

Per ssh Remote Befehle direkt ausführen

Hinweis 2021: Dieser Artikel ist etwas in die Jahre gekommen und ich habe mittlerweile einige Dinge neu beschrieben:
---
Die Secure Shell (ssh) ist auf den meisten Linux System aktiviert. Bedienung und Administration sind ohne ssh kaum vorstellbar. 
Oft will man Dinge Remote per Befehl ausführen, ohne interactive Anmeldung. Einfach Befehl absetzen und fertig, dabei aber sicher und ohne "alle Tore aufzureißen". Glücklicherweise ermöglicht ssh nicht einfach die Übergabe der Benutzerpasswortes im Klartext.
Alle Beschreibungen die ich bisher fand, waren ziemlich kompliziert und ich habe sie am Ende nicht verstanden. Eigentlich ist es ganz einfach, vielleicht schreibt das deswegen kaum jemand auf. Ich beschreibe hier das Verfahren mit rsa Schlüsselaustausch ohne "passphrase". Damit ist sichergestellt, dass ein Benutzer auf einem lokalen System einen anderen Benutzeraccount auf einem Remote System ohne Abfrage eines Passwortes verwenden kann und dort Befehle ausführen kann, da er vorher mit dem Schlüssel dazu autorisiert wurde.

Beispiel Scenario

Lokaler Host:
System: Raspbian jessie-lite; Benutzer: Pi - im Terminal angemeldet

Remote Host:
System: Raspbian jessie-lite; Benutzer: Pi - nicht angemeldet

Einrichtung

Alle Aktionen finden im Terminal des "Lokalen Hosts" statt.
# Die Frage nach der passphrase wird zweimal mit enter quittiert, also die passphrase bleibt leer
ssh-keygen -t rsa 
# passwort des remote users wird abgefragt
ssh-copy-id -i ~/.ssh/id_rsa pi@<remote-system>

Verwendung

Das war es schon! Jetzt kann man remote z.B. den Status eines Dienstes abfragen, die Ausgabe kommt ins lokale Terminal!
ssh pi@<remote-system> 'sudo systemctl status myservice'
Achtung!
Es kann sein, dass ssh-copy-id auf dem System nicht verfügbar ist! 
Das Tool ssh-copy-id unterstützt nur .ssh auf dem Zielsystem! Bei OpenWrt z.B. schlägt es sowohl als Quelle als auch Ziel fehl.
In beiden Fällen geht der Einzeiler aus diesem Artikel.

 Dienst Benutzer verwenden

Serviceuser sind meist mit passwortlosem Login ausgestattet und die interaktive Anmeldung ist verhindert (z.B. Benutzer fhem). Um für diese Benutzer eine ssh remote Anmeldung zu erstellen sind noch zusätzliche Schritte nötig. Zunächst mal das Login ermöglichen und ein passwort erstellen:
sudo cp /etc/passwd /etc/passwd.sav
sudo nano /etc/passwd
# in der Zeile: fhem:x:999:20::/opt/fhem:/bin/false 
# von /bin/false auf /bin/bash ändern und speichern

Dann für den Benutzer fhem ein Kennwort eingeben
sudo passwd fhem

Nach dem Erstellen des rsa Schlüssels und Test wieder den alten Zustand herstellen
sudo cp /etc/passwd.sav /etc/passwd

Hier noch alle Schritte für den Benutzer fhem in effizienter Form. Ich habe dabei den manuellen Vorgang mit dem Editor nano durch den nicht interaktiven Stream Editor (sed) ersetzt!
sudo cp /etc/passwd /etc/passwd.sav
sudo  sed -i -e 's/fhem:.*/fhem:x:999:20::\/opt\/fhem:\/bin\/bash/' /etc/passwd
sudo passwd fhem             # Passwort vergeben und bestätigen
su fhem                      # Als fhem einloggen, es startet eine neue session!
ssh-keygen -t rsa            # Speichert automatisch in /opt/fhem/
# Achtung ssh-copy-id funktioniert nicht immer! Alternative siehe oben
ssh-copy-id -i ~/.ssh/id_rsa <user>@<remote-system> # gleich den angebotenen Test machen
ssh <user>@<remote-system>   # es startet eine neue session!
exit                         # aus der ssh Test session vom Remotehost!
exit                         # aus der Anmeldung von fhem!  
sudo cp /etc/passwd.sav /etc/passwd
Wenn man es für einen anderen Benutzer (z.B. Pi) schon gemacht hat, geht es eventuell auch einfacher: Artikel im Forum Ich habe das noch nicht getestet!

OpenSSH (Cygwin) für Windows

Mittlerweile ist dieser Abschnitt überholt, ich lasse ihn aber stehen. Am Ende habe ich die Deinstallation ergänzt. Es gibt als zukunftsorientierte Alternative, eine funktionsfähige Anleitung in meinem "Windows hat jetzt ssh" Artikel
Es gibt mehrere OpenSSH Implementierungen für Windows. Die einzige die wirklich wie gewollt funktioniert war für mich Cygwin. Die Installation läuft mehrstufig ab, hier habe ich eine gute Anleitung gefunden.

Installation

Zunächst lädt man nur eine kleine setup-x86_64.exe herunter, diese führt eine Installation mit nachladen aus dem Internet durch. Im ersten Schritt wird der Pfad für die Installation selbst bestimmt, von dort aus könnten weitere Installationen vorgenommen werden.
Dann werden die zu installierenden Pakete ausgewählt, per default ist gar nichts ausgewählt. Man kann mit search und "ssh" die Auswahl erleichtern.

Da ich kein komplettes System brauche wähle ich nur ssh aus. Die dazu notwendigen Pakete wählt das Setup Programm automatisch. Die Installation des Programmes selbst erfolgte bei mir in c:\cygwin64.

Nach der Installation hat man ein Icon "Cygwin64 Terminal" auf dem Desktop, der wird gestartet und ein "vertrautes" Terminalfenster öffnet sich. Dort wird mit ssh-host-config der ssh Daemon installiert.
Es werden eine Reihe Fragen gestellt, die ich so beantwortet habe:
*** Query: Should StrictModes be used? (yes/no) yes
*** Query: Should privilege separation be used? (yes/no) yes
*** Query: new local account 'sshd'? (yes/no) yes
*** Query: Do you want to install sshd as a service?
*** Query: (Say "no" if it is already installed as a service) (yes/no) yes
*** Query: Enter the value of CYGWIN for the daemon: []
...
*** Info: This script plans to use 'cyg_server'.
*** Info: 'cyg_server' will only be used by registered services.
*** Query: Do you want to use a different name? (yes/no) no
*** Query: Create new privileged user account '<host>\cyg_server' (Cygwin name: 'cyg_server')? (yes/no) yes
*** Query: Please enter the password:
*** Query: Reenter:

Jetzt muss der Dienst gestartet werden: 
$ net start sshd
CYGWIN sshd wird gestartet.
CYGWIN sshd wurde erfolgreich gestartet.

Damit der ssh Server nicht durch die Firewall ausgebremst wird, kann man mit folgendem Powershell Befehl noch die entsprechende Ausnahme definieren:
New-NetFirewallRule -Protocol TCP -LocalPort 22 -Direction Inbound -Action Allow -DisplayName SSH
Damit ist die Installation beendet.

Besonderheiten:
  • Der Benutzer Name wird Case Sensitiv verarbeitet, Administrator muss also groß geschrieben werden. 
  • Alle Pfadnamen in Befehlen müssen mit doppelten Backslash geschrieben werden.

Und siehe da, man kann  mit "powershell C:\\Tools\\Scripts\\SendeEmailV2.ps1" auch Powershell Befehle ausführen.
Und natürlich geht der folgende Befehl direkt aus der FHEM Kommandozeile.
"ssh username@host 'powershell C:\\Tools\\Scripts\\SendeEmailV2.ps1'"

Die Pfade von Windows müssen aber innerhalb von ' ' stehen! Will man noch Variablen übergeben dürfen die nicht innerhalb ' ' stehen.
"ssh username@host 'powershell C:\\Tools\\Scripts\\SendeEmailV2.ps1' $NAME $EVENT"

Deinstallation

das Setup erzeugt leider keine Deinstallation, man muss es manuell bewerkstelligen. Die Grundlage für den folgenden Code habe ich aus dem Cygwin Wiki.
# Den Service anhalten und entfernen
# Remove-Service existiert erst ab Powershell 6
Get-Service -DisplayName CYGWIN*|Stop-Service
C:\cygwin64\bin\cygrunsrv -E sshd
C:\cygwin64\bin\cygrunsrv -R sshd
# Prüfen ob der Service entfernt ist
Get-Service -DisplayName CYGWIN*

# cygwin Benutzer löschen
Get-LocalUser sshd|Remove-LocalUser
Get-LocalUser cyg*|Remove-LocalUser

# Registry Key löschen und prüfen
Test-Path HKLM:\Software\Cygwin
gci HKLM:\Software\Cygwin
Remove-Item HKLM:\Software\Cygwin

# Firewall Regel löschen
get-NetFirewallRule -DisplayName ssh*|Remove-NetFirewallRule

# Noch ein paar Pfade/Dateien löschen, einfach mit dem explorer
# C:\cygwin64, User profil von sshd und cyg*, Verknüpfungen auf dem Desktop 

Public Key für die Windows Anmeldung einrichten

Die Sache mit dem Public Key funktioniert nicht ganz so einfach wie unter Linux.
Aber ich war nicht der erste und habe hier ein gute Anleitung gefunden. Diese ist ziemlich umfangreich, deshalb habe ich diese Schritte herausgepickt:

Homedirectory auf Windows vorbereiten. 

Am einfachsten mit Putty am neuen Windows Server ssh anmelden. Dann steht man gleich im Anmelde Fenster im Homedirectory des Benutzers.
ssh <user>@<remote-system>
mkdir .ssh
cd .ssh
mkdir otherkeys

Public Key kopieren

Jetzt vom Raspberry: Achtung! Die Datei authorized_keys wird mit dem Befehl neu erzeugt/überschrieben:
su fhem
scp ~/.ssh/id_rsa.pub <user>@<remote-system>:.ssh/authorized_keys

Das wars, die ssh Anmeldung ohne Passwort sollte funktionieren.

Ärger mit known_hosts

Hat man den Ziel Host unter altem Namen oder gleicher IP neu installiert, funktioniert der alte Eintrag in der Datei .ssh/known_hosts nicht mehr. Der lässt sich nicht einfach überschreiben, man muss ihn vorher löschen. Hat man Hostname und IP-Adresse verwendet, existieren mehrere Einträge!
ssh-keygen -R <Hostname>
ssh-keygen -R <IP Adresse>

Hat man den Namen oder die IP Adresse geändert, muss ein neuer Eintrag geschrieben werden. Dazu muss man nicht zwingend wieder unter anderem Namen (fhem) angemeldet sein.
Diese Methode eignet sich auch, wenn man den Eintrag in der known_host ohne Benutzer-Interaktion vornehmen will/muss.
sudo su
ssh-keyscan -H <Hostname/IP-Adresse> >> /opt/fhem/.ssh/known_hosts

Die Einträge lassen sich mit ssh-keygen auch auflisten, im Beispiel vom aktiven User
ssh-keygen -l -f ~/.ssh/known_hosts

Sicherheit

Wie sicher ist das Ganze?

Per default erzeugt ssh-keygen 2048 bit Schlüssel, diese gelten als sicher. Die Dateien sind im Benutzerverzeichnis gesichert. Nur der öffentliche Schlüssel ist von anderen Benutzern lesbar. Der Remotehost ist verschlüsselt abgelegt.
-rw------- 1 pi pi 1679 Jan 16 14:06 id_rsa
-rw-r--r-- 1 pi pi  392 Jan 16 14:06 id_rsa.pub
-rw-r--r-- 1 pi pi  444 Jan  8 19:21 known_hosts

Lässt sich die Sicherheit erhöhen?

ssh-keygen -t rsa -b 4096
Private key in pkcs8 wandeln. -> https://www.mittwald.de/blog/mittwald/howtos/ssh-key-erstellen
Schlüssel mit passphrase erzeugen und ssh-agent verwenden oder hier.

In dem Artikel  ist auch beschrieben, wie man den ssh  Zugang zu Windows für Passwort Abfragen sperren kann, dann ist nur noch Anmeldung per Public Key möglich.

Falsche Rechte im Home Directory

Das Home Directory hat per default die Rechte Maske 755. Die obige Procedure setzt zwar die Rechte auf die .ssh Dateien, ist aber die Ordner Struktur darüber mit falschen Berechtigungen ausgestattet, wird eine Verwendung der Key Dateien vom System offenbar als "Unsicher" verhindert. Falls etwas nicht funktioniert, überprüfen:
/home/pi/.ssh:

drwx------ 2 pi pi 4096 Nov 24 17:50 .
drwxr-xr-x 3 pi pi 4096 Oct 25 14:32 ..
-rw------- 1 pi pi 1679 Jan 16  2017 id_rsa
-rw-r--r-- 1 pi pi  392 Jan 16  2017 id_rsa.pub
-rw------- 1 pi pi 1552 Nov 24 17:49 known_hosts
Siehe Beitrag im Forum.

Sudo und Passwortabfrage

Beim Benutzer pi wird das sudo Passwort nicht abgefragt, bei anderen Benutzern schon!? Ganz einfach: Die Datei /etc/sudoers enthält am Ende die Zeile #includedir /etc/sudoers.d obwohl auch in dieser Datei das Doppelkreuz für auskommentiert steht, ist es in dieser Zeile nicht so! Diese Zeile bewirkt, dass alle weiteren Dateien im Verzeichnis /etc/sudoers.d importiert werden. Unter raspbian gibt es dort eine Datei /etc/sudoers.d/010_pi-nopasswd diese enthält genau die eine Zeile, womit typischerweise die Passwortabfrage für sudo "für alles" ausgeschaltet wird.

5 Kommentare:

  1. Hallo Otto,
    Du beschreibst die Dinge so klasse - aber ich kapiere es doch nicht richtig. Ich kann nach Erzeugung des Key's mit diesem Kommando von der Kommandozeile im raspi (also Local-Host) problemlos ohne Passwort an den remote-raspi senden :
    ssh pi@192.168.2.21 '/usr/bin/omxplayer -o hdmi /home/pi/media/Ring10.mp3'
    Audio codec pcm_s16le channels 2 samplerate 22050 bitspersample 16
    Subtitle count: 0, state: off, index: 1, delay: 0
    have a nice day ;)
    und die Musik spielt im remote-pc.
    Will ich das von der FHEM-Kommandozeile mit :
    "ssh pi@192.168.2.21 '/usr/bin/omxplayer -o hdmi /home/pi/media/Ring10.mp3'"
    passiert gar nichts ???
    In der Logfile steht
    Host key verification failed.
    Aber warum geht es dann von Kommandozeile ?
    LG aus dem Nordschwarzwald von Peter

    AntwortenLöschen
    Antworten
    1. Hallo Peter,
      irgendwie funktioniert die Benachrichtigung für Kommentare nicht so richtig, hab es deshalb erst jetzt gelesen.
      Ich vermute, Du hast das Ganze zwar für Benutzer Pi eingerichtet, aber nicht für den Benutzer fhem!?
      Grüße Otto

      Löschen
  2. Hallo Otto, deine Tips sind immer sehr gut. Vielen Dank für die Arbeit. Allerdings gibt es bei deinem o.a. Tipp ein kleines Problem. Die Zeile "sudo sed -i -e 's/fhem:.*/fhem:x:999:20::\/opt\/fhem:\/bin\/bash/' /etc/passwd" führte bei mir zu einem Fehler, da die UID 999 schon im System vorhanden war. Das Anmelden mit fhem wurde dann immer für die bereits vorhandene UID durchgeführt und dadurch funktionierte die restlichen Befehle nicht wie gewünscht.

    AntwortenLöschen
    Antworten
    1. Da hast Du recht, wenn der User eine andere ID hat. Man muss das vorher kontrollieren. Allerdings weiß ich mittlerweile mehr :-) sudo -u fhem tut es auch ohne Passwort.

      Löschen
  3. Danke für den Hinweis.

    AntwortenLöschen