asmfreak
Goto Top

SSH: Shutdown mittels Skript

Hallo,

Ich möchte einen Server über SSH herunterfahren. Auf ihm ist SSH aktiviert, es gibt einen Account "SDAccount" als Mitglied der lokalen Administratoren. SSH auf dem Client ist aktiviert.
Manuell kann ich den Server herunterfahren über

ssh SDAccount@SSHServer
sudo -i
shutdown

wobei nach Zeile 1 und 2 jeweils das Passwort abgefragt wird. Ich bin mir im Klaren darüber, dass das aus Sicherheitsgründen genau so gemacht werden muss, inklusive der Tatsache, dass man sich bei den beiden Passworteingaben nicht verschreiben darf, da die Eingabe nicht angezeigt wird.

Allerdings muss ich das automatisieren können, da das Herunterfahren über ein Skript erfolgen soll, das via Aufgabenplanung aufgerufen wird. Ich dachte an ein PS-Skript, Batch wäre aber auch möglich.

Allerdings habe ich nach zahllosen Versuchen keine Möglichkeit gefunden, das zu bewerstelligen. Ich weiß, das PLink dazu (u. a.) geschaffen wurde, würde aber gerne unabhängig von weiteren Programmen sein: PowerShell gibt es auf allen Windows-Clients.

Hat jemand eine Lösung?

Danke für Zeit und Mühen.
Gruß,
ASMFreak

Content-ID: 672078

Url: https://administrator.de/forum/ssh-shutdown-mittels-skript-672078.html

Ausgedruckt am: 21.03.2025 um 09:03 Uhr

Crusher79
Crusher79 20.03.2025 um 19:02:04 Uhr
Goto Top
Hi,

klar...

https://github.com/darkoperator/Posh-SSH

if (!(Get-Module -Name posh-ssh)) {
    Import-Module -Name posh-ssh
}

$HostName = "TollerServer"  
$LinuxUser = "frank"  
$LinuxPassword = "SuperGeheim"  
$LinuxPasswordEnter = "SuperGeheim `r"  
$command2Execute = "sudo shutdown -h now"  

$CredentialLinux = New-Object System.Management.Automation.PSCredential ($LinuxUser, $LinuxPassword)
$linuxSSHsession = New-SSHSession -ComputerName $HostName -Credential $CredentialLinux -AcceptKey
$streamLinux = New-SSHShellStream -SessionId $linuxSSHsession.SessionId
Invoke-SSHStreamExpectAction -ShellStream $streamLinux -Command "$command2Execute" -ExpectRegex 'Password:' -Action $LinuxPassword -Verbose   
$linuxSSHsession | Remove-SSHSession 


Ungetestet. Bin mir gerade nicht sicher, ob es die Action so ohne Enter abfeuert. Müsste es aber. Ansonsten kann m an auch den 2. Nehmen mit Return am Ende.

Hier erwartet er im Stream "Password:" nachdem das Kommando abgesetzt wurde.

Geht auch mit Zertifikat ohne Kennwort. Wobei manche meine, es muss dann mindestens leer sein, damit der Code nicht zusammenbricht.

So könnte es hinhauen.
MichaMA
MichaMA 20.03.2025 um 19:21:55 Uhr
Goto Top
Auf dem Linux Server per visudo deinem User die Rechte geben den Befehl ohne password auszuführen, so ungefähr:

USERNAME ALL= NOPASSWORD /bin/shutdown 

Dann reicht ein
sudo shutdown -h now

Oder ein
ssh username@servername "sudo shutdown -h now"  

Musst aber die Pfade prüfen
TK1987
TK1987 20.03.2025 um 19:25:49 Uhr
Goto Top
Moin,

Zitat von @ASMFreak:
Ich bin mir im Klaren darüber, dass das aus Sicherheitsgründen genau so gemacht werden muss, inklusive der Tatsache, dass man sich bei den beiden Passworteingaben nicht verschreiben darf, da die Eingabe nicht angezeigt wird.
nö, wieso? Erlaube einfach dem entsprechenden User das System ohne Passworteingabe herunterzufahren. Einmalig...
cat << -- |sudo tee /etc/sudoers.d/010_allow_shutdown
SDAccount ALL=(ALL) NOPASSWD: /usr/sbin/poweroff
--

Anschließend kannst du das System ganz einfach per SSH herunterfahren...
ssh SDAccount@SSHServer '/usr/bin/sudo /usr/sbin/poweroff'  

Gruß Thomas
TK1987
TK1987 20.03.2025 aktualisiert um 19:28:09 Uhr
Goto Top
Moin,

unnötig. Der OpenSSH-Client ist längst standardmäßig in Windows vorhanden.

Gruß Thomas
Crusher79
Crusher79 20.03.2025 um 19:39:46 Uhr
Goto Top
Zitat von @TK1987:

Moin,

unnötig. Der OpenSSH-Client ist längst standardmäßig in Windows vorhanden.

Gruß Thomas

Aber doch nicht Posh-SSH? Ist ja ein Wrapper. Geht ja um die anderen Funktionen.

Oder wie baut man sowas wie expect unter Linux mit Windows PS nach? Await hab ich schon gesehen. Aber Stream Inspektion und Autofill.

Wie machst du das unter Windows?
ASMFreak
ASMFreak 20.03.2025 um 20:29:35 Uhr
Goto Top
Hallo Crusher79,

Danke für den Code. Ich habe nur die Varablennamen an die in meinem Skript angepasst, die schon da sind. Fehlermeldungen:

Import-Module : Das angegebene Modul "posh-ssh" wurde nicht geladen, da in keinem Modulverzeichnis eine gültige  
Moduldatei gefunden wurde.
In I:\Skripte\ShutDownMitPowerShell\Test.ps1:12 Zeichen:5
+     Import-Module -Name posh-ssh
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (posh-ssh:String) [Import-Module], FileNotFoundException
    + FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand

New-Object : Für "PSCredential" und die folgende Argumenteanzahl kann keine Überladung gefunden werden: "2".  
In I:\Skripte\ShutDownMitPowerShell\Test.ps1:21 Zeichen:20
+ ... tialLinux = New-Object System.Management.Automation.PSCredential ($Us ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

New-SSHSession : Die Benennung "New-SSHSession" wurde nicht als Name eines Cmdlet, einer Funktion, einer Skriptdatei  
oder eines ausführbaren Programms erkannt. Überprüfen Sie die Schreibweise des Namens, oder ob der Pfad korrekt ist
(sofern enthalten), und wiederholen Sie den Vorgang.
In I:\Skripte\ShutDownMitPowerShell\Test.ps1:22 Zeichen:20
+ $linuxSSHsession = New-SSHSession -ComputerName $sshserver -Credentia ...
+                    ~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (New-SSHSession:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

New-SSHShellStream : Die Benennung "New-SSHShellStream" wurde nicht als Name eines Cmdlet, einer Funktion, einer  
Skriptdatei oder eines ausführbaren Programms erkannt. Überprüfen Sie die Schreibweise des Namens, oder ob der Pfad
korrekt ist (sofern enthalten), und wiederholen Sie den Vorgang.
In I:\Skripte\ShutDownMitPowerShell\Test.ps1:23 Zeichen:16
+ $streamLinux = New-SSHShellStream -SessionId $linuxSSHsession.Session ...
+                ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (New-SSHShellStream:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Invoke-SSHStreamExpectAction : Die Benennung "Invoke-SSHStreamExpectAction" wurde nicht als Name eines Cmdlet, einer  
Funktion, einer Skriptdatei oder eines ausführbaren Programms erkannt. Überprüfen Sie die Schreibweise des Namens,
oder ob der Pfad korrekt ist (sofern enthalten), und wiederholen Sie den Vorgang.
In I:\Skripte\ShutDownMitPowerShell\Test.ps1:24 Zeichen:1
+ Invoke-SSHStreamExpectAction -ShellStream $streamLinux -Command "$com ...  
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Invoke-SSHStreamExpectAction:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Remove-SSHSession : Die Benennung "Remove-SSHSession" wurde nicht als Name eines Cmdlet, einer Funktion, einer  
Skriptdatei oder eines ausführbaren Programms erkannt. Überprüfen Sie die Schreibweise des Namens, oder ob der Pfad
korrekt ist (sofern enthalten), und wiederholen Sie den Vorgang.
In I:\Skripte\ShutDownMitPowerShell\Test.ps1:25 Zeichen:20
+ $linuxSSHsession | Remove-SSHSession
+                    ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (Remove-SSHSession:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

Gruß,
ASMFreak
ASMFreak
ASMFreak 20.03.2025 um 20:33:55 Uhr
Goto Top
Hallo Thomas,

Danke. auch bei Dir Fehlermeldungen, mit denen ich nicht klarkomme face-smile :

In I:\Skripte\ShutDownMitPowerShell\Test.ps1:9 Zeichen:6
+ cat << -- |sudo tee /etc/sudoers.d/010_allow_shutdown
+      ~
Dateispezifikation nach dem Umleitungsoperator fehlt.
In I:\Skripte\ShutDownMitPowerShell\Test.ps1:9 Zeichen:5
+ cat << -- |sudo tee /etc/sudoers.d/010_allow_shutdown
+     ~
Der Operator "<" ist für zukünftige Versionen reserviert.  
In I:\Skripte\ShutDownMitPowerShell\Test.ps1:9 Zeichen:7
+ cat << -- |sudo tee /etc/sudoers.d/010_allow_shutdown
+       ~
Dateispezifikation nach dem Umleitungsoperator fehlt.
In I:\Skripte\ShutDownMitPowerShell\Test.ps1:9 Zeichen:6
+ cat << -- |sudo tee /etc/sudoers.d/010_allow_shutdown
+      ~
Der Operator "<" ist für zukünftige Versionen reserviert.  
    + CategoryInfo          : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : MissingFileSpecification

Gruß, ASMFreak
ASMFreak
ASMFreak 20.03.2025 um 20:36:32 Uhr
Goto Top
Hallo MichaMA,

läuft auf den Vorschlag von TK1987 hinaus. Danke auch Dir.

Gruß,
ASMFreak
Crusher79
Crusher79 20.03.2025 um 21:00:55 Uhr
Goto Top
Also das bringt mich auch gerade um!

3x problemlos shutdown, nun will er nicht mehr.

Setze den normal gegen gegen eine Watchguard ein und einen Ubuntu Server Da kann ich aus dem Stream sogar Daten einfach abgreifen.

8 zusätzliche Sicherheitsupdates können mit ESM Apps angewendet werden.
Erfahren Sie mehr über die Aktivierung des ESM Apps-Dienstes at https://ubuntu.com/esm

Last login: Thu Mar 20 20:24:19 2025 from 172.18.0.101
sudo shutdown -r now
crusher@crusher-virtual-machine:~$ sudo shutdown -r now
[sudo] Passwort für crusher: 

Bei dir geht es nicht, da Posh-SSH fehlt!

Install-Module -Name Posh-SSH


Normal soll es teils als "Einzeiler" so laufen, dass ein Kommando im Stream abgesetzt wird und gewartet wird, bis ein Ausdruck kommt.

Eine Fehlerquelle: Deutsch / Englisch - Passwort vs. Password...


Bin auch gerade ratlos. Eigentlich ist es genau dafür gedacht. Siehe auch dieses Bsp.

Invoke-SSHStreamExpectSecureAction -ShellStream $stream -Command 'su -' -ExpectString 'Passord:' -SecureAction (read-host -AsSecureString) -Verbose  

Hier wird aber durch read-host wieder Eingabe nötig.

Normal kann man Passwort als Secure String hinterlegen. Zertifikate abfragen, Invoke-xyz irgendwas....

Ich kaue auch gerade drauf rum. Oben an dem Ausschnitt sieht man aber, dass der gesamte Remote-Stream abgegriffen wird und auch der Eingabeprompt erscheint.
TK1987
TK1987 20.03.2025, aktualisiert am 21.03.2025 um 08:17:30 Uhr
Goto Top
Zitat von @ASMFreak:
Danke. auch bei Dir Fehlermeldungen, mit denen ich nicht klarkomme face-smile :
Das musst du natürlich auch auf dem Server ausführen.
Crusher79
Crusher79 20.03.2025 aktualisiert um 21:15:30 Uhr
Goto Top
So ich habs...

AUSFÜHRLICH: Executing command sudo shutdown -r now.
AUSFÜHRLICH: Waiting for match.
AUSFÜHRLICH: Matching by RegEx.
AUSFÜHRLICH: Executing action.
AUSFÜHRLICH: Action has been executed.
True


Broadcast message from root@crusher-virtual-machine on pts/1 (Thu 2025-03-20 21:11:29 CET):

The system will reboot now!

Unten steht vlt. mit Dispose etc. nicht benötigtes. Aber damit hat es nun 3 mal hingahauen

Regex gegen das Linux Prompt!

Das hab ich gerade 3 Mal hintereinander gemacht.

Wie gesagt, du musst noch das Modul installieren.



$HostName = "172.18.0.179"    
$LinuxUser = "crusher"    
$command2Execute = "sudo shutdown -r now"  
$LinuxPassword = "xxxxxx"  

$Passwordx = ConvertTo-SecureString $LinuxPassword -AsPlainText -Force
$CredentialLinux = New-Object System.Management.Automation.PSCredential ($LinuxUser, $Passwordx)
$linuxSSHsession = New-SSHSession -ComputerName $HostName -Credential $CredentialLinux -AcceptKey
$streamLinux = New-SSHShellStream -SessionId $linuxSSHsession.SessionId

$sudoPassPropmp = [regex]':\s$'  
Invoke-SSHStreamExpectSecureAction -ShellStream $streamLinux -Command $command2Execute -ExpectRegex $sudoPassPropmp -SecureAction $Passwordx -Verbose

sleep -Seconds 1
$streamLinux.Read()
sleep -Seconds 1
$streamLinux.Close()
$streamLinux.Dispose()

$linuxSSHsession | Remove-SSHSession 
Crusher79
Crusher79 20.03.2025 aktualisiert um 21:23:29 Uhr
Goto Top
@ASMFreak

So funktioniert es. Wie du denken kannst wegen dem Regex auch in allen Sprachen.

$sudoPassPropmp = [regex]':\s$'    

Ich überlege gerade, ob das Teil irgendwo das Kennwort sonst hinschreibt.

So nun 6x hintereinander getestet. Das läuft soweit.

Normal schließt man Sitzungen am Ende. Da wird aber eh shutdown machen, kann man hier wohl auch drauf verzichten. Ich glaub die Position ohne Regex stimmt nicht. Pattern-Match war ja vorhanden, nur das Kammando verpufft.

Wie du oben sehen kannst: Kein ENTER mit `r \r oder so.

Commando führt das Modul schon am Ende selber aus.
Crusher79
Crusher79 20.03.2025 um 21:29:49 Uhr
Goto Top
Noch ein Nachtrag:

sleep -Seconds 1
$streamLinux.Read()

Ich weiß nicht warum, ohne 1 Gedenksekunde und erneuten Stream.Read() mag er nicht. Mit den 2 Zeilen fährt der PC sofort runter.

Wie gesagt, hab damit auch noch nicht viel gemacht. Watchguard z.B. geht auch ganz gut. Entweder Loops oder Waits ggf. noch reinballern.

Mit diesen simplen Zeilen oben geht es auf anhieb. Normal wartet man ja teils bei Prozessen. Ein Invoke, der einen Shutdown abfeuert, sollte sich aber ja sofort bemerkbar machen.

Ich weiß es wirklich nicht. Aber mit den 2 Zeilen mehr geht es sofort, wiederkehrend, sicher..............
mbehrens
mbehrens 20.03.2025 um 22:40:31 Uhr
Goto Top
Zitat von @ASMFreak:

wobei nach Zeile 1 und 2 jeweils das Passwort abgefragt wird. Ich bin mir im Klaren darüber, dass das aus Sicherheitsgründen genau so gemacht werden muss, inklusive der Tatsache, dass man sich bei den beiden Passworteingaben nicht verschreiben darf, da die Eingabe nicht angezeigt wird.

Warum statt Kennwort nicht einfach ein Schlüsselpaar verwenden?
Crusher79
Crusher79 20.03.2025 um 23:03:01 Uhr
Goto Top
Zitat von @mbehrens:

Warum statt Kennwort nicht einfach ein Schlüsselpaar verwenden?

Hilft nur beim Erstellen der Session. Nachher muss man teils prompt auswerten. Ohne root login hat man immer eine Hürde. Und root Login rächt sich ggf irgendwann.