Archiv für den Monat: September 2020

PowerShell Remoting mit SSH und Public Key-Authentifizierung

Public Key-Authentifizierung bedeutet keine Passwort-Eingabe vor Invoke-Command oder Enter-PSSession mit SSH und ist natürlich ideal.

Klingt für einen reinen Windows-Admins eventuell etwas kompliziert, ist aber sehr einfach.

Da die Schrittfolge an so vielen Stellen im Internet und auch in der offiziellen Micorsoft-Dokumentation beschrieben ist, beschränke ich mich nur auf Stichworte.

Im Folgenden soll gezeigt werden, wie PowerShell-Remoting per SSH zwischen einem Windows- und einem Linux-Computer (mit Ubuntu 18.04) ohne Passworteingabe möglich ist.

Voraussetzung ist, dass unter Windows OpenSSH installiert wurde, auf dem Linux-Computer muss SSH eventuell auch nachinstalliert werden.

Schritt 1: Im ersten Schritt wird per ssh-genkey ein Paar aus privaten und öffentlichem Schlüssel erzeugt. ssh-genkey kann direkt aufgerufen werden, wenn Open SSH installiert wurde.

Das Ergebnis sind zwei Dateien: id_rsa mit dem privaten Schlüssel und id_rsa.pub mit dem öffentlichen Schlüssel.

Schritt 2: Im zweiten Schritt muss der SSH-Agent-Dienst gestartet werden. Wer SSH dauerhaft nutzen möche, setzt den Starttyp auf „Automatic“.

Schritt 3: Im dritten Schritt wird der private Schlüssel dem SSH-Agent übergeben:

ssh-add C:/Users/pemoadmin/.ssh/id_rsa

Damit ist der Windows-Teil erledigt.

Bei mir kam die Frage auf, wie der Public Key auf den Linux-Computer übertragen wird. Als Datei, das ist klar. Aber wo muss die Datei abgelegt werden, spielt der Dateiname eine Rolle und muss ich eventuell noch irgendwelche Berechtigungen setzen?

Nach zahlreichen Versuchen (mit denen ich mir fast einen schönen Sonntagnachmittag etwas verdorben hätte) fand ich am Montagmorgen in einem Blog-Eintrag von Christropher Hart (https://www.chrisjhart.com/Windows-10-ssh-copy-id/) die Lösung. Die Datei muss authorized_keys heißen und wird im (bereits vorhandenen) .ssh-Verzeichnis im home-Verzeichnis auf dem Linux-Computer abgelegt.

Am einfachsten wird die Datei per SSH übertragen:

type $env:USERPROFILE\.ssh\id_rsa.pub | ssh pemo@172.22.153.50 "cat >> .ssh/authorized_keys"

Eine Kleinigkeit fehlt natürlich noch. In der sshd_config-Datei auf dem Linux-Computer muss die Public Key-Authentifizierung durch Ändern eines Eintrags aktiviert werden:

PubkeyAuthentication yes

Die Passwort-Authentifizierung muss nicht deaktiviert werden. Dann stehen beide Varianten zur Auswahl.

Zum Schluss muss der sshd-Dienst auf dem Linux-Computer neu gestartet werden:

service sshd reload

Dank Public Key-Authentifizierung wird ein Invoke-Command jetzt sehr einfach, da weder der SSHTransport– noch der Subsystem-Parameter angegeben werden müssen:

$S1 = New-PSSession -Hostname $Hostname -Username $Username
Invoke-Command -ScriptBlock { Get-Process} -Session $S1
$S1 Remove-PSSession -Session $S1

PS: Eher eine persönliche Erinnerung. Diesen Eintrag habe ich am 21.09.2020 an einem herrlichen Spätsommermorgen auf dem dem Balkon geschrieben.

PowerShell-Remoting mit SSH

PowerShell-Remoting mit SSH ist gut dokumentiert. Zwei sehr gute Artikel sollten für einen erfolgreichen Probelauf genügen:

https://www.thomasmaurer.ch/2020/04/enable-powershell-ssh-remoting-in-powershell-7/

und

https://4sysops.com/archives/enable-powershell-core-6-remoting-with-ssh-transport/

Ein dritter Artikel beschreibt die Verwendung der Public Key-Authentifizierung als Alternative zur Passwort-Eingabe:

https://4sysops.com/archives/powershell-remoting-with-ssh-public-key-authentication/

Was eventuell noch nicht jeder weiß, der sich für das Thema SSH mit PowerShell interessiert. Seit ein paar Monaten gibt es in der PowerShell Gallery ein kleines Modul des PowerShell-Teams, mit dessen einzigem Enable-SSHRemoting-Command das Einrichten von SSH auf dem Remote-Computer sehr einfach wird. Der Eintrag

PasswordAuthentication yes

muss allerdings auch in diesem Fall einkommentiert werden, falls eine Kennwort-Authentifizierung gewünscht ist. Für das erste Kennenlernen ist dies die einfachste Variante.

Das Modul gibt es unter

https://www.powershellgallery.com/packages/Microsoft.PowerShell.RemotingTools/0.1.0

Damit hat es sogar bei mir auf Anhieb funktioniert;)

Insgesamt sind damit folgende Schritte auf dem SSH-Server zu erledigen:

  1. PowerShell 7 installieren
  2. OpenSSH Client und OpenSSH Server installieren
  3. Enable-SSHRemoting ausführen oder den subsystem-Eintrag in der sshd-config-Datei „zu Fuß“ anlegen
  4. sshd-Dienst (nicht ssh) neu starten – unter Linux service ssh restart (eigentlich gar nicht so schwer;)

Damit kann per New-PSSession, Enter-PSSession und Invoke-Command unter einer PowerShell ab Version 6.0 SSH über den SSHTransport-Parameter verwendet werden:

#requires -version 
$hostname="Ubunti
#$hostname="172.23.25.12"
$Username="pemo"
$S1 = New-PSSession -Hostname $Hostname -Username $Username -SSHTransport -Subsystem powershell
Invoke-Command -ScriptBlock { Get-Process} -Session $S1
Remove-PSSession -Session $S1

Der Subsystem-Parameter wählt den Namen des auf dem SSH-Server angelegten subsystem-Eintrag in sshd_config aus. Das Passwort wird bei jedem (!) Aufruf abgefragt. Wer das nicht möchte, muss Public Key-Authentizierung verwenden. Wie das geht, wird in einem der genannten Blog-Artikel beschrieben.

Viel Erfolg!

Abb. Dank snap besteht die Installation der PowerShell unter Ubuntu aus einem Aufruf (sofern sie nicht bereits vorinstalliert ist)

Anlegen einer Custom Rule für Scriptanalyzer

Der PowerShell Scriptanalyzer ist das Werkzeug, um für PowerShell-Skripte mit einfachen Mitteln eine umfassende Qualitätsanalyse durchzuführen.

Eine einer Stärken ist seine Erweiterbarkeit. Durch das Definieren von Custom Rules lassen sich theoretisch beliebige Merkmale eines Skriptes testen (z.B. ob Kommentare irgendwelche unflätigen Begriffe enthalten;).

Grundsätzlich ist das Definieren einer solchen Custom Rule einfach, denn sie besteht nur aus einer Function, die in einer Psm1-Datei abgelegt werden muss. Beim Aufruf von Invoke-ScriptAnalyzer wird der Pfad der Psm1-Datei über den CustomRulePath-Parameter angegeben.

PowerShell-Experte Mathieu Buisson beschreibt in einem sehr guten Blog-Beitrag ein kleines Beispiel, das bei mir auf Anhieb funktioniert hat:

https://mathieubuisson.github.io/create-custom-rule-psscriptanalyzer/

Ich war für sein Beispiel, das man sich lediglich aus dem GitHub-Repo kopieren muss, sehr dankbar, da ich zuvor „stundenlang“ herumprobiert hatte, um eine eigene Regel zum Laufen zu bringen (ich hatte vor Jahren bereits einmal eine Custom Rule als Psm1-Datei erstellt, war aber zu bequem, das Beispiel von damals zu suchen). Was ich auch probiert hatte, das Ergebnis war eine Fehlermeldung, in der behauptet wurde, dass meine psm1-Datei keine Regeln enthielt. Wirklich nervig. Irgendwann kam ich auf die Lösung. Ich hatte offenbar übersehen, dass der Function-Parameter ScriptBlockAst heißen muss. Außerdem müssen die Typen der Input- und Output-Werte genau angegeben werden.

Die größte Herausforderung beim Erstellen einer Custom Rule sind aber nicht die kleinen Formalitäten, sondern der Umstand, dass man sich in Grundzügen mit den Abstract Syntax Trees (AST) der PowerShell auskennen muss.

Meine Regel prüft, ob ein Skript Cmdlets oder Functions enthält, die keinen WhatIf-Parameter anbieten. Warum kann das wichtig sein? Weil das Commands sind, die nicht von der globalen WhatIfPreference-Einstellung betroffen sind. Möchte man ein unbekanntes Skript in einer „Sandbox“ ausführen, in der nichts „Schlimmes“ passieren kann, wären diese Commands davon nicht betroffen.

Auch wenn man in der Regel davon ausgehen kann, dass ein Command ohne WhatIf-Parameter grundsätzlich nichts macht, was negative Folgen haben könnte, 100% sicher kann man sich nicht sein, da der Autor des Cmdlets oder der Functions das Implementieren von Whatif über SupportsShouldProcess = $true im Rahmen von [CmdletBinding()] nicht für erforderlich gehalten oder schlicht vergessen haben könnte.

Wer daher wissen will, welche Commands von einem WhatIf-Modus unbeeindruckt bleiben, sollte diese Regel ausprobieren.

Die gesamte Datei gibt es in meinem GitHub-Repo

https://github.com/pemo11/whatifrule

Ich werde die einzelnen Schritte, insbesondere den AST-Teil, in naher Zukunft noch ausführlicher beschreiben.

Den folgenden Kommentar wollte ich in dem Blog von Mathieu Buisson schreiben, aber aus irgendeinem Grund war das Kommentieren nicht möglich:

Your article is very helpful, thank you – but I think that you should mention that the parameter name has to be ScriptBlockAst otherwise the rule is not found by ScriptAnalyzer by my experience.

Tipp des Tages: Text-Dateien im BOM-Format finden

Die PowerShell ISE hat die etwas lästige Eigenart, dass Textdateien mit einem sog. BOM-Header gespeichert. Das bedeutet konkret, dass die Datei mit den Byte-Werten 255 und 254 beginnt oder FF EF als Hexadezimalzahlen. BOM steht für Byte Order Mark und soll einen Hinweis darauf geben, dass als Zeichencode UTF-8 verwendet wird (ich hoffe, ich gebe das korrekt wieder). Mehr dazu unter https://de.wikipedia.org/wiki/Byte_Order_Mark.

Dass eine einfache Angelegenheit erstaunlich kompliziert sein kann, macht eine ausführliche Abhandlung deutlich, die das Zusammenspiel von VS Code mit der PowerShell in Bezug auf die Zeichencodierung beschreibt:

https://docs.microsoft.com/en-us/powershell/scripting/dev-cross-plat/vscode/understanding-file-encoding?view=powershell-7

Ich muss leider zugegeben, dass ich immer wieder in Situationen komme, in denen in die Umlaute einer in VS-Code gespeicherten Ps1-Datei durch „seltsame“ Sonderzeichen ersetzt werden (die in der oben genannten Doku natürlich alle beschrieben werden).

Wie dem auch sei, mich hat es einfach einmal interessiert, welche Ps1-oder allgemein Textdateien einen solchen BOM-Header besitzen. Der folgende Befehl gibt zunächst für die Windows PowerShell alle iese Dateien aus, in dem er jeweils die ersten beiden Bytes der Datei liest und diese als Hex-Werte mit FFEF vergleicht:

Der obige Befehl funktioniert nur mit der Windows PowerShell, da es den Wert Byte für den Encoding-Parameter nicht bei PowerShell 7 gibt. Dafür gibt es mit dem AsByteStream-Parameter zwar ein flexibleres Konzept, allerdings darf der TotalCount-Parameter nicht mit diesem Parameter kombiniert werden, so dass ein Select-Object mit -First 2 erforderlich wird.

Der folgende Befehl entspricht dem letzten Befehl, nur dass er ab PowerShell 6 funktioniert: