WinRM sicuro per Ansible con certificati in 10 passaggi

Ansible sta diventando uno degli strumenti di gestione della configurazione più diffusi, se non il più diffuso, oggi. Ansible è uno strumento pratico (e gratuito nella maggior parte dei casi) che consente agli ingegneri DevOps e agli ingegneri / amministratori di sistema di creare e mantenere l’infrastruttura in modo idempotente, come infrastruttura come codice. Tuttavia, configurarlo per comunicare con Windows tramite WinRM per Ansible può essere una sfida.

Come molti altri componenti di infrastruttura, Ansible può distribuire e mantenere stati di configurazione su host Windows. Ansible si connette a questi host Windows tramite WinRM, anche se stanno sperimentando con SSH.

Quando si configura WinRM per Ansible, si hanno diverse opzioni che vanno dalla facilità di configurazione alle implicazioni di sicurezza. Molte persone scelgono l’approccio più semplice; autenticazione di base utilizzando HTTP. Sebbene si rinunci al lavoro aggiuntivo relativo ai certificati, non è mai una buona idea inviare dati non criptati su una rete a meno che non sia necessario.

In questo articolo, imparerai come configurare WinRm per Ansible utilizzando l’autenticazione basata su certificato utilizzando certificati auto-firmati in modo che Ansible possa comunicare con essi.

La configurazione che imparerai in questo tutorial non si applica solo ad Ansible come client. Questa autenticazione basata su certificato può essere applicata anche ad altri client WinRm come altri host Windows.

Presupposti

Questo articolo sarà una guida passo-passo. Se intendi seguire i passaggi presentati qui per configurare WinRm per Ansible, il tutorial presupporrà:

  • Di avere già installato Ansible su un host Linux.
  • Di aver configurato un inventario Ansible basato sui tuoi host Windows.
  • Di avere un server Windows 2016 o successivo da gestire. Alcuni cmdlet utilizzati in questo tutorial potrebbero non funzionare con versioni più vecchie di Windows.
  • L’host Windows non è in un dominio. Sebbene gli esempi siano stati eseguiti su un host non associato a un dominio, questa configurazione dovrebbe funzionare anche su host associati a un dominio.
  • Di avere accesso RDP o console all’host Windows e di essere connesso come amministratore locale.
  • Di essere familiare con PowerShell. Quasi tutti gli esempi utilizzeranno codice PowerShell per apportare modifiche all’host Windows.

Ogni sezione utilizzerà un frammento di codice che dipende dall’ultimo. Alcuni frammenti faranno riferimento a variabili definite in precedenza. Assicurati di lasciare aperta la console mentre copi/incolli questo codice se hai intenzione di seguirlo esattamente.

Se desideri solo il codice senza tutte le spiegazioni, puoi scaricare questo gist di GitHub.

Abilita il PowerShell Remoting per WinRm per Ansible

Anche se tutti i server Windows Server 2016 o successivi hanno il PowerShell Remoting abilitato, è sempre una buona idea confermarlo.

Sul server Windows da gestire, apri una console di PowerShell come amministratore ed esegui il seguente frammento di codice. Questo frammento di codice assicura che il servizio WinRm sia avviato e impostato per avviarsi automaticamente all’avvio del sistema.

Set-Service -Name "WinRM" -StartupType Automatic
Start-Service -Name "WinRM"

Successivamente, assicurati che il PowerShell Remoting sia abilitato controllando prima se ha qualsiasi configurazione di sessione attiva. Se così non fosse, assicurati che non abbia alcun listener disponibile. WinRm per Ansible deve avere almeno un listener. Se una di queste condizioni non restituisce nulla, esegui Enable-PSRemoting.

if (-not (Get-PSSessionConfiguration) -or (-not (Get-ChildItem WSMan:\localhost\Listener))) {
    ## Utilizza SkipNetworkProfileCheck per renderlo disponibile anche sui profili pubblici del firewall di Windows
    ## Utilizza Force per non essere richiesto se siamo sicuri o meno.
    Enable-PSRemoting -SkipNetworkProfileCheck -Force
}
Configuring WinRM for Ansible (a listener) via the winrm command

Abilita l’autenticazione basata su certificato

Per impostazione predefinita, WinRM non è configurato per l’autenticazione basata su certificato. È necessario abilitare questa funzionalità configurando WSMan come mostrato di seguito.

#region Abilita l'autenticazione basata su certificato
Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true
#endregion

Crea un account utente locale

Per utilizzare l’autenticazione WinRM basata su certificato per Ansible, è necessario “mappare” un account utente locale a un certificato. È possibile utilizzare l’account amministratore locale per fare ciò, ma è sempre una buona idea creare un account specifico per semplificare la gestione.

Il seguente frammento di codice crea un account utente locale chiamato ansibletestuser con una password di p@$$w0rd12 se non esiste già. Per garantire che l’account rimanga sempre attivo, la password di tale account non scadrà mai.

$testUserAccountName = 'ansibletestuser'
$testUserAccountPassword = (ConvertTo-SecureString -String 'p@$$w0rd12' -AsPlainText -Force)
if (-not (Get-LocalUser -Name $testUserAccountName -ErrorAction Ignore)) {
    $newUserParams = @{
        Name                 = $testUserAccountName
        AccountNeverExpires  = $true
        PasswordNeverExpires = $true
        Password             = $testUserAccountPassword
    }
    $null = New-LocalUser @newUserParams
}

Crea il certificato del client

WinRM per Ansible (in modo sicuro) deve avere due certificati: un certificato del client e un certificato del server.

È possibile utilizzare lo stesso certificato per il client e il server, ma ho riscontrato problemi con questo approccio. Nelle documentazioni di Ansible e in molte altre fonti troverai istruzioni per generare il certificato del client tramite il cmdlet New-SelfSignedCert di PowerShell. Sebbene questo metodo possa funzionare, non sono riuscito a farlo funzionare facilmente.

Per creare il certificato client per WinRM per Ansible, è necessario creare una chiave privata e una chiave pubblica. Inizia effettuando l’accesso SSH all’host Ansible e esegui il seguente comando openssl. Questo comando crea una chiave privata in un file chiamato cert_key.pem e una chiave pubblica chiamata cert.pem. L’utilizzo della chiave sarà l’autenticazione del client (1.3.6.1.4.1.311.20.2.3) “mappata” all’account utente locale creato in precedenza chiamato ansibletestuser.

## Questa è la chiave pubblica generata dal server Ansible usando:
cat > openssl.conf << EOL
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_req_client]
extendedKeyUsage = clientAuth
subjectAltName = otherName:1.3.6.1.4.1.311.20.2.3;UTF8:ansibletestuser@localhost
EOL
export OPENSSL_CONF=openssl.conf
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -out cert.pem -outform PEM -keyout cert_key.pem -subj "/CN=ansibletestuser" -extensions v3_req_client
rm openssl.conf 

Un passo più vicino all’autenticazione Ansible WinRM!

Importa il Certificato Client

Una volta creato il certificato client per WinRM per Ansible, dovrai importarlo in due store di certificati sull’host Windows perché WinRM su Ansible funzioni. Per farlo, trasferisci prima la chiave pubblica cert.pem sull’host Windows. L’esempio qui sotto assume che la chiave esista in C:\cert.pem.

Una volta che hai il certificato pubblico sull’host Windows, importalo negli store di certificati Autorità di certificazione radice attendibili e Persone attendibili utilizzando Import-Certificate come mostrato di seguito.

$pubKeyFilePath = 'C:\cert.pem'

## Importa la chiave pubblica in Autorità di certificazione radice attendibili e Persone attendibili
$null = Import-Certificate -FilePath $pubKeyFilePath -CertStoreLocation 'Cert:\LocalMachine\Root'
$null = Import-Certificate -FilePath $pubKeyFilePath -CertStoreLocation 'Cert:\LocalMachine\TrustedPeople'

Crea il Certificato Server

WinRM per Ansible ha bisogno di un certificato definito con un utilizzo chiave per l’autenticazione del server. Questo certificato sarà memorizzato nello store di certificati LocalMachine\My dell’host Windows. Crea il certificato autofirmato usando il frammento di codice qui sotto.

$hostname = hostname
$serverCert = New-SelfSignedCertificate -DnsName $hostName -CertStoreLocation 'Cert:\LocalMachine\My'

Creare il listener WinRm di Ansible

Una volta creati entrambi i certificati, è necessario creare un listener WinRm sull’host Windows. Questo listener inizia ascoltando sulla porta 5986 per le connessioni in ingresso. Una volta creato, questo listener accetta le connessioni in ingresso e cercherà di crittografare i dati utilizzando il certificato del server creato in precedenza.

Il remoting di PowerShell utilizza questo listener WinRM come trasporto una volta che l’autenticazione è avvenuta.

Nel frammento di codice riportato di seguito, puoi vedere un ottimo esempio di verifica di un listener HTTPS esistente. Se non viene trovato alcun listener utilizzando il certificato del server creato in precedenza, ne verrà creato uno nuovo.

## Trova tutti i listener HTTPS
$httpsListeners = Get-ChildItem -Path WSMan:\localhost\Listener\ | where-object { $_.Keys -match 'Transport=HTTPS' }

## Se nessun listener è definito o nessun listener è configurato per funzionare con
## il certificato del server creato, ne verrà creato uno nuovo con un soggetto che corrisponde al nome host del computer
## e vincolato al certificato del server.
if ((-not $httpsListeners) -or -not (@($httpsListeners).where( { $_.CertificateThumbprint -ne $serverCert.Thumbprint }))) {
    $newWsmanParams = @{
        ResourceUri = 'winrm/config/Listener'
        SelectorSet = @{ Transport = "HTTPS"; Address = "*" }
        ValueSet    = @{ Hostname = $hostName; CertificateThumbprint = $serverCert.Thumbprint }
        # UseSSL = $true
    }
    $null = New-WSManInstance @newWsmanParams
}

“Mappa” il certificato del client all’account utente locale

Il passo successivo consiste nel garantire che quando Ansible si connette all’host Windows utilizzando il certificato del server, eseguirà tutte le istruzioni come utente locale. In questo caso, tutte le attività eseguite da WinRm per Ansible utilizzeranno l’account utente locale, ansibletestuser.

$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $testUserAccountName, $testUserAccountPassword

## Trova l'impronta digitale del certificato del client creato sull'host di Ansible
$ansibleCert = Get-ChildItem -Path 'Cert:\LocalMachine\Root' | Where-Object {$_.Subject -eq 'CN=ansibletestuser'}

$params = @{
	Path = 'WSMan:\localhost\ClientCertificate'
	Subject = "$testUserAccountName@localhost"
	URI = '*'
	Issuer = $ansibleCert.Thumbprint
  Credential = $credential
	Force = $true
}
New-Item @params

Consenti WinRm per Ansible con il Controllo dell’Account Utente (UAC)

Se si utilizza un account locale per mappare un certificato, è necessario impostare anche il LocalAccountTokenFilterPolicy su 1 per garantire che UAC non crei problemi. Il LocalAccountTokenFilterPolicy si applica a tutti gli account locali (non agli account di dominio) e fa sì che l’accesso di rete sia la parte limitata del proprio token. Questo impedirà l’accesso come amministratore di Windows in quanto WinRM richiede per impostazione predefinita che l’utente sia un amministratore locale.

Impostando il LocalAccountTokenFilterPolicy, si sta dicendo a Windows di non creare un token limitato per gli accessi di rete tramite un account locale e di utilizzare il token completo.

$newItemParams = @{
    Path         = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
    Name         = 'LocalAccountTokenFilterPolicy'
    Value        = 1
    PropertyType = 'DWORD'
    Force        = $true
}
$null = New-ItemProperty @newItemParams

Apri la porta 5986 nel firewall di Windows

WinRm su HTTPS utilizza la porta 5986. Se hai il firewall di Windows abilitato, è necessario aprire questa porta. Puoi farlo eseguendo il seguente frammento di codice PowerShell. Questo frammento di codice è troppo permissivo, consentendo a tutti i computer di utilizzarlo. Se desideri limitare ulteriormente l’accesso, assicurati di utilizzare il parametro LocalAddress e specificare l’IP di Ansible.

#region Assicurarsi che la porta WinRM 5986 sia aperta nel firewall
 $ruleDisplayName = 'Windows Remote Management (HTTPS-In)'
 if (-not (Get-NetFirewallRule -DisplayName $ruleDisplayName -ErrorAction Ignore)) {
     $newRuleParams = @{
         DisplayName   = $ruleDisplayName
         Direction     = 'Inbound'
         LocalPort     = 5986
         RemoteAddress = 'Any'
         Protocol      = 'TCP'
         Action        = 'Allow'
         Enabled       = 'True'
         Group         = 'Windows Remote Management'
     }
     $null = New-NetFirewallRule @newRuleParams
 }
 #endregion

Aggiungi l’utente locale al gruppo degli amministratori

Potresti chiederti perché questo tutorial non abbia semplicemente aggiunto l’utente locale al gruppo degli amministratori in precedenza. Il motivo è che, per qualche motivo sconosciuto, quando si tenta di associare il certificato del client all’utente, l’account utente non può essere un amministratore locale.

Esegui il seguente frammento di codice per aggiungere l’account utente locale ansibletestuser al gruppo degli amministratori.

## Aggiungi l'utente locale al gruppo degli amministratori. Se questa fase non viene eseguita, Ansible visualizza un errore "Accesso negato"
Get-LocalUser -Name $testUserAccountName | Add-LocalGroupMember -Group 'Administrators'

Conclusioni

Se hai seguito ogni singolo passaggio come indicato, ora dovresti essere in grado di eseguire comandi Ansible su un host Windows. Utilizza il modulo win_shell per effettuare i tuoi test. Se questo modulo ha successo, hai configurato correttamente WinRM per Ansible e l’autenticazione basata su certificato!

Source:
https://adamtheautomator.com/winrm-for-ansible/