WinRM und JEA

Über Windows Remote Management (WinRM) lassen sich auf einem entfernten Windows-Host PowerShell-Sessions öffnen, Cmdlets ausführen und Ergebnisse zurückgeben. Für einen Linux System Engineer wird das Thema überall dort relevant, wo der eigene Linux-Host Zustände auf Windows-Seite abfragen oder verändern muss: Monitoring, Konfigurationsmanagement (Ansible), Backup-Status, Compliance-Checks.

Der einfache Weg, einen Windows-Host per WinRM anzusprechen, läuft über die Standard-Shell und bekommt dabei die vollen Rechte des verbindenden Accounts. Für Produktivumgebungen ist das meist zu viel. Die Gegenmassnahme heisst Just Enough Administration (JEA): eine eigene PowerShell-Session-Konfiguration, die nur eine explizit erlaubte Liste von Cmdlets sichtbar macht, freies Scripting unterbindet und die Session unter einem temporären virtuellen Account ausführt. Der verbindende User braucht keine Admin-Rechte mehr, und gekaperte Credentials wiegen entsprechend weniger schwer.

Architektur

Ein JEA-Endpoint auf Windows besteht aus fünf Teilen:

  1. Der WinRM-Dienst auf dem Windows-Host, der die Sessions annimmt (TCP 5985 für HTTP, 5986 für HTTPS).

  2. Ein reguläres PowerShell-Modul unter C:\Program Files\WindowsPowerShell\Modules\, das die Role-Capability-Datei aufnimmt.

  3. Eine Role Capability (.psrc) mit der Liste der sichtbaren Cmdlets und der zu importierenden Module. Ein Host kann mehrere Rollen haben.

  4. Eine Session Configuration (.pssc), die den Session-Typ (RestrictedRemoteServer), den Sicherheitskontext (RunAsVirtualAccount) und die Zuordnung von Benutzern zu Rollen definiert.

  5. Die Registrierung der Session Configuration als PowerShell-Endpoint mit Register-PSSessionConfiguration, über den sich der Benutzer danach verbindet.

Die Zugriffskontrolle läuft primär über die RoleDefinitions in der .pssc-Datei. Windows-ACLs auf den Endpoint kann man zusätzlich setzen, ein Deny-ACE schlägt dann wie immer jeden Allow-ACE, das ist aber nur zusätzliche Härtung, nicht der Hauptmechanismus.

Windows-Seite einrichten

Die folgenden Schritte laufen allesamt in einer elevated PowerShell auf dem Windows-Host.

WinRM aktivieren

Enable-PSRemoting -Force
Set-Service -Name WinRM -StartupType Automatic
Start-Service -Name WinRM

Enable-PSRemoting legt die Default-Listener an, setzt den Dienst auf Autostart und öffnet die nötigen Firewall-Regeln (5985/tcp, im Domain- und Private-Profil). ICMP hat mit WinRM nichts zu tun und muss nicht angefasst werden.

Für den produktiven Einsatz ist HTTPS (5986/tcp) Pflicht. Dafür wird ein gültiges Serverzertifikat auf Cert:\LocalMachine\My hinterlegt und daraus ein HTTPS-Listener erzeugt:

$cert = Get-ChildItem -Path Cert:\LocalMachine\My |
    Where-Object { $_.Subject -like "*CN=winsrv.example.com*" }

New-Item -Path WSMan:\localhost\Listener `
    -Transport HTTPS `
    -Address * `
    -CertificateThumbPrint $cert.Thumbprint `
    -Force

Status prüfen:

Get-Service -Name WinRM
winrm enumerate winrm/config/listener

Benutzer anlegen

Ist der Windows-Host AD-Mitglied, gehört das Konto ins AD, damit Kerberos als Auth-Methode zur Verfügung steht. Für Stand-alone-Hosts genügt ein lokaler Account:

$pw = Read-Host -AsSecureString -Prompt "JEA-Account-Passwort"
New-LocalUser -Name 'jea_monitor' `
    -Password $pw `
    -FullName 'JEA Read-Only Account' `
    -Description 'Zugang ausschliesslich zum JEA-Endpoint MonitoringJEA'

Der User muss nicht in die Administratoren-Gruppe. Den Admin-Kontext liefert später RunAsVirtualAccount. In RoleDefinitions wird die Identität voll qualifiziert angegeben:

  • lokal: <ComputerName>\jea_monitor

  • im AD: <Domain>\jea_monitor

PowerShell-Modul anlegen

Die .psrc-Datei muss in einem gültigen Modul liegen. Verzeichnisstruktur:

C:\Program Files\WindowsPowerShell\Modules\
    MonitoringJEA\
        MonitoringJEA.psd1
        RoleCapabilities\
            Monitoring.psrc

Modul und Rolle anlegen:

$moduleRoot = 'C:\Program Files\WindowsPowerShell\Modules\MonitoringJEA'
New-Item -Path $moduleRoot -ItemType Directory -Force | Out-Null
New-Item -Path (Join-Path $moduleRoot 'RoleCapabilities') -ItemType Directory -Force | Out-Null

New-ModuleManifest -Path (Join-Path $moduleRoot 'MonitoringJEA.psd1') `
    -ModuleVersion '1.0.0' `
    -Description 'JEA Read-Only Endpoint'

Role Capability (.psrc) definieren

Die .psrc legt fest, welche Cmdlets der Benutzer in der JEA-Session sieht. Alles, was nicht auf der Liste steht, existiert in der Session nicht. Ein Read-Only-Endpoint kommt typischerweise mit wenigen lesenden Cmdlets aus:

$roleCapArgs = @{
    Path             = 'C:\Program Files\WindowsPowerShell\Modules\MonitoringJEA\RoleCapabilities\Monitoring.psrc'
    VisibleCmdlets   = @('Get-Service', 'Get-Process', 'Get-ComputerInfo', 'Get-Counter')
    VisibleFunctions = @()
    VisibleAliases   = @()
    ModulesToImport  = @('Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility')
}
New-PSRoleCapabilityFile @roleCapArgs

Schreibende Cmdlets wie Stop-Service, Start-Service, Restart-Service und Stop-Process fehlen hier bewusst und sind in der Session damit nicht existent. Für konkrete Anwendungsfälle (Failover-Cluster, Exchange, SQL Server, Eventlog-Export) wird die Liste gezielt erweitert, niemals mit * abgekürzt.

Session Configuration (.pssc)

Die .pssc verklammert Rolle, User und Sicherheitskontext:

$sessionConfigArgs = @{
    Path               = 'C:\ProgramData\JEA\MonitoringJEA.pssc'
    SessionType        = 'RestrictedRemoteServer'
    RunAsVirtualAccount = $true
    LanguageMode       = 'NoLanguage'
    TranscriptDirectory = 'C:\ProgramData\JEA\Transcripts'
    RoleDefinitions    = @{
        'COMPUTERNAME\jea_monitor' = @{ RoleCapabilities = 'Monitoring' }
    }
}
New-Item -Path 'C:\ProgramData\JEA\Transcripts' -ItemType Directory -Force | Out-Null
New-PSSessionConfigurationFile @sessionConfigArgs

Die wichtigen Parameter:

  • SessionType = 'RestrictedRemoteServer' schaltet die klassische Remote-Shell ab und lässt nur explizit freigegebene Cmdlets zu.

  • RunAsVirtualAccount = $true führt die Session unter einem temporären lokalen Admin aus. Der verbindende User braucht keine Admin-Rechte auf dem Host.

  • LanguageMode = 'NoLanguage' blockiert freies Scripting in der Session. Der Client darf nur die freigegebenen Cmdlets aufrufen, keine Variablen, keine Pipelines, die sich wie Skripte verhalten, keine Invoke-Expression-Umgehungen. Ohne diese Zeile ist ein RestrictedRemoteServer-Endpoint nicht dicht.

  • TranscriptDirectory legt ein Voll-Transcript jeder Session ab. Für hochfrequente Abfragen ist das oft zu laut, für Audit-Zwecke dagegen die beste freie Kontrolle, was der Endpoint tatsächlich ausgeführt hat.

Vor der Registrierung die .pssc validieren:

Test-PSSessionConfigurationFile -Path 'C:\ProgramData\JEA\MonitoringJEA.pssc'

Das Kommando gibt True bei sauberer Konfiguration zurück.

Endpoint registrieren

Register-PSSessionConfiguration -Name 'MonitoringJEA' `
    -Path 'C:\ProgramData\JEA\MonitoringJEA.pssc' `
    -Force
Restart-Service -Name WinRM

Nach dem Neustart des WinRM-Dienstes ist der Endpoint unter dem Namen MonitoringJEA erreichbar. Verifikation:

Get-PSSessionConfiguration | Select-Object Name, Enabled
Get-PSSessionConfiguration -Name MonitoringJEA | Select-Object Name, Enabled, Permission

Transports und Authentifizierung

Welche Transport-Methode ein Linux-Client gegenüber WinRM verwendet, hängt von der Umgebung ab. WinRM selbst kennt fünf Varianten. Sie unterscheiden sich in Vertraulichkeit, Setup-Aufwand auf der Windows-Seite und der Frage, ob der Linux-Host Teil eines Active Directory ist:

Transport

Einsatz

Windows-seitige Voraussetzung

Kerberos

AD-integriert, empfohlene Wahl in Domain-Umgebungen. Single-Sign-On nach kinit ohne Passwort im Tool-Aufruf.

funktioniert mit Enable-PSRemoting; der Linux-Host bzw. der User muss im AD bekannt sein.

NTLM

universeller Fallback für Domain und Workgroup, wenn Kerberos nicht zur Verfügung steht.

funktioniert mit Enable-PSRemoting.

CredSSP

Multi-Hop-Szenarien, in denen der Endpoint die Credentials an einen weiteren Host weiterreichen muss.

Enable-WSManCredSSP -Role Server. Volle Delegation der Credentials, entsprechend heikel und genau zu begründen.

Basic

Testumgebungen oder explizites HTTPS-only. Ohne HTTPS Klartext-Credentials im Header.

winrm set winrm/config/service/auth '@{Basic="true"}'. In Produktion nur über HTTPS akzeptieren.

Plaintext

wie Basic, aber explizit über HTTP.

wie Basic. In Produktion nicht sinnvoll.

Für Kerberos ist auf dem Linux-Host /etc/krb5.conf gegen die AD-Domain konfiguriert und ein Ticket im Credential-Cache aktiv:

kinit user@EXAMPLE.COM
klist

Für JEA-Endpoints gilt unabhängig vom Transport: der Client muss das PowerShell Remoting Protocol (PSRP) sprechen und den Namen der Session Configuration (-ConfigurationName MonitoringJEA) mitgeben. Linux-Clients erreichen das über die Python-Library pypsrp; die ältere pywinrm-Library bedient die klassische WinRM-Shell, unterstützt aber keine benannten JEA-Endpoints. Welches Tool die Auswahl hinter den Kulissen trifft, dokumentiert das jeweilige Tool.

Default-Endpunkte härten

Warnung

Nur ausführen, nachdem (1) ein alternativer administrativer Zugang gesichert ist und (2) der JEA-Endpoint von aussen erfolgreich getestet wurde. Ein falsch gesetztes Deaktivieren der Default-Endpunkte sperrt den gesamten Remote-PowerShell-Zugriff auf den Host aus.

Nach abgeschlossenem JEA-Setup lassen sich die offenen Default-Endpunkte abschalten, damit Angreifer mit gekapertem Admin-Konto nicht die Standard-Shell nutzen können:

Disable-PSSessionConfiguration -Name Microsoft.PowerShell -Force
Disable-PSSessionConfiguration -Name Microsoft.PowerShell32 -Force
Disable-PSSessionConfiguration -Name Microsoft.PowerShell.Workflow -Force
Disable-PSSessionConfiguration -Name Microsoft.Windows.ServerManagerWorkflow -Force

Restart-Service -Name WinRM

Rollback in derselben Logik:

Enable-PSSessionConfiguration -Name Microsoft.PowerShell -Force
Enable-PSSessionConfiguration -Name Microsoft.PowerShell32 -Force
Enable-PSSessionConfiguration -Name Microsoft.PowerShell.Workflow -Force
Enable-PSSessionConfiguration -Name Microsoft.Windows.ServerManagerWorkflow -Force

Restart-Service -Name WinRM

Status aller Endpunkte:

Get-PSSessionConfiguration | Select-Object Name, Enabled

Troubleshooting

Die Symptome treffen im Alltag unabhängig davon zu, welches Tool auf der Linux-Seite verwendet wird. Die konkreten Fehlermeldungs-Wordings sind Tool-abhängig, die Ursachen dieselben.

HTTP 401 Unauthorized beim Verbinden

Authentifizierung schlägt fehl. Bei NTLM User im Format user@DOMAIN oder DOMAIN\user senden. Bei Kerberos klist kontrollieren und bei Bedarf kinit erneuern. Auf der Windows-Seite winrm get winrm/config/service/auth liefert die aktivierten Auth-Methoden.

HTTP 400 Bad Request beim Verbinden

Meist ein Transport-Mismatch: der Listener akzeptiert die gewählte Auth-Methode nicht. Bei Basic-Versuchen über HTTP prüfen, ob AllowUnencrypted und Basic serverseitig aktiv sind, und ob der Client wirklich über HTTPS-Port 5986 mit gültigem Zertifikat spricht.

Session verbindet sich, das gewünschte Cmdlet kommt als CommandNotFoundException zurück

Das ist kein Bug, sondern die Kern-Semantik eines JEA-Endpoints: nur in VisibleCmdlets gelistete Cmdlets existieren in der Session. Prüfen, ob der Cmdlet-Name korrekt in der .psrc steht.

Register-PSSessionConfiguration meldet „Die Datei kann nicht verarbeitet werden, weil die Role Capability nicht gefunden werden kann“

Modul-Layout prüfen: das .psd1 liegt direkt unter Modules\<Modulname>\, die .psrc unter Modules\<Modulname>\RoleCapabilities\. Der Rollenname in RoleDefinitions ist der Dateiname ohne Suffix und ohne Pfad.

In der Session kommt The syntax is not supported by this runspace zurück

Der gewollte Effekt von LanguageMode = 'NoLanguage'. Der Client darf nur freigegebene Cmdlets aufrufen - keine Variablenzuweisungen, keine if- oder foreach-Konstrukte, keine Expressions. Entweder das Kommando auf reine Cmdlet-Aufrufe eindampfen oder die komplexere Logik als Funktion in der .psrc freigeben und von aussen nur noch diese Funktion aufrufen.

Cmdlets tauchen nicht auf, obwohl sie in VisibleCmdlets stehen

Das zugehörige PowerShell-Modul ist nicht importiert. In der .psrc das Modul unter ModulesToImport aufnehmen oder das Standard-Modul Microsoft.PowerShell.Management bzw. Microsoft.PowerShell.Utility ergänzen.