Sécuriser WinRM pour Ansible avec des certificats en 10 étapes

Ansible devient l’un des outils de gestion de configuration les plus, voire le plus utilisé aujourd’hui. Ansible est un outil pratique (et gratuit dans la plupart des cas) qui permet aux ingénieurs DevOps et aux ingénieurs/administrateurs système de construire et de maintenir l’infrastructure sur tous les environnements de manière idempotente, à l’aide de l’infrastructure en tant que code. Cependant, le configurer pour communiquer avec Windows via WinRM peut être un défi.

Tout comme de nombreux autres composants d’infrastructure, Ansible peut déployer et maintenir des états de configuration sur des hôtes Windows. Ansible se connecte à ces hôtes Windows via WinRM, bien qu’ils expérimentent également avec SSH.

Lorsque vous configurez WinRM pour Ansible, vous avez plusieurs options allant de la facilité de configuration aux implications en termes de sécurité. Beaucoup de personnes choisissent l’approche facile : l’authentification de base en utilisant HTTP. Bien que vous évitiez le travail supplémentaire lié aux certificats, il n’est jamais recommandé d’envoyer des données non chiffrées sur un réseau à moins d’y être obligé.

Dans cet article, vous allez apprendre comment configurer WinRm pour Ansible en utilisant une authentification basée sur des certificats auto-signés, afin qu’Ansible puisse communiquer avec eux.

La configuration que vous allez apprendre dans ce tutoriel ne s’applique pas seulement à Ansible en tant que client. Cette authentification basée sur des certificats peut également s’appliquer à d’autres clients WinRm, tels que d’autres hôtes Windows.

Hypothèses

Cet article sera une marche à suivre basée sur un tutoriel. Si vous avez l’intention de suivre les étapes présentées ici pour configurer WinRm pour Ansible, le tutoriel supposera:

  • Vous avez déjà installé Ansible sur un hôte Linux.
  • Vous avez configuré un inventaire Ansible basé sur vos hôtes Windows.
  • Vous disposez d’un serveur Windows 2016 ou ultérieur à gérer. Certains cmdlets utilisés dans ce tutoriel ne fonctionneront pas avec les anciennes versions de Windows.
  • L’hôte Windows n’est pas dans un domaine. Bien que les exemples aient été exécutés sur un hôte non joint à un domaine, cette configuration devrait fonctionner également sur des hôtes joints à un domaine.
  • Vous avez accès à distance (RDP) ou en console à l’hôte Windows et vous êtes connecté en tant qu’administrateur local.
  • Vous êtes familier avec PowerShell. Presque tous les exemples utiliseront du code PowerShell pour effectuer des modifications sur l’hôte Windows.

Chaque section utilisera un extrait de code qui dépend du précédent. Certains extraits feront référence à des variables définies précédemment. Assurez-vous de laisser votre console ouverte lorsque vous copiez/collez ce code si vous prévoyez de le suivre exactement.

Si vous voulez simplement le code sans toutes les explications, n’hésitez pas à télécharger cet extrait GitHub.

Activer le remoting PowerShell pour WinRm pour Ansible

Bien que tous les serveurs Windows Server 2016 ou ultérieurs aient le remoting PowerShell activé, il est toujours bon de le vérifier.

Sur l’hôte Windows à gérer, ouvrez une console PowerShell en tant qu’administrateur et exécutez l’extrait de code suivant. Cet extrait de code assure que le service WinRm est démarré et configuré pour démarrer automatiquement lors du démarrage du système.

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

Ensuite, assurez-vous que le remoting PowerShell est activé en commençant par vérifier s’il a des configurations de session actives. Si ce n’est pas le cas, vérifiez ensuite qu’il n’a pas d’écouteurs disponibles. WinRm pour Ansible doit avoir au moins un écouteur. Si l’une de ces conditions ne renvoie rien, exécutez la commande Enable-PSRemoting.

if (-not (Get-PSSessionConfiguration) -or (-not (Get-ChildItem WSMan:\localhost\Listener))) {
    ## Utilisez SkipNetworkProfileCheck pour le rendre disponible même sur les profils publics du pare-feu Windows
    ## Utilisez Force pour ne pas être invité si nous sommes sûrs ou non.
    Enable-PSRemoting -SkipNetworkProfileCheck -Force
}
Configuring WinRM for Ansible (a listener) via the winrm command

Activer l’authentification basée sur certificat.

Par défaut, WinRM n’est pas configuré pour l’authentification basée sur les certificats. Vous devez activer cela en configurant WSMan comme indiqué ci-dessous.

#region Activer l'authentification basée sur les certificats
Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true
#endregion

Créer un compte d’utilisateur local

Pour utiliser WinRM basé sur les certificats pour l’authentification Ansible, vous devez « mapper » un compte d’utilisateur local à un certificat. Vous pourriez utiliser le compte administrateur local pour cela, mais il est toujours préférable de créer un compte spécifique pour faciliter la gestion.

Le code suivant crée un compte d’utilisateur local pour WinRM pour Ansible appelé ansibletestuser avec un mot de passe de p@$$w0rd12 s’il n’existe pas. Pour garantir qu’il reste toujours actif, le mot de passe de ce compte n’expirera jamais.

$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
}

Créer le certificat client

WinRM pour Ansible (de manière sécurisée) nécessite deux certificats : un certificat client et un certificat serveur.

Vous pouvez utiliser le même certificat pour le client et le serveur, mais j’ai rencontré des problèmes avec cette approche. Dans la documentation d’Ansible et de nombreuses autres sources, vous trouverez des instructions pour générer le certificat client via la cmdlet New-SelfSignedCert de PowerShell. Bien que cette méthode puisse fonctionner, je n’ai pas réussi à la mettre en œuvre facilement.

Pour créer le certificat client pour WinRM pour Ansible, vous devez créer une clé privée et une clé publique. Commencez par vous connecter en SSH à l’hôte Ansible et exécutez la commande openssl suivante. Cette commande crée une clé privée dans un fichier appelé cert_key.pem et une clé publique appelée cert.pem. L’utilisation de la clé sera une authentification client (1.3.6.1.4.1.311.20.2.3) « mapped » au compte d’utilisateur local que vous avez précédemment créé appelé ansibletestuser.

## Il s'agit de la clé publique générée à partir du serveur Ansible en utilisant :
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 pas de plus vers l’authentification WinRM d’Ansible !

Importer le certificat client

Une fois que vous avez créé le certificat client pour WinRM pour Ansible, vous devrez l’importer dans deux magasins de certificats sur l’hôte Windows pour que WinRM sur Ansible fonctionne. Pour ce faire, transférez d’abord la clé publique cert.pem vers l’hôte Windows. L’exemple ci-dessous suppose que la clé existe à l’emplacement C:\cert.pem.

Une fois que vous avez le certificat public sur l’hôte Windows, importez-le dans les magasins de certificats Trusted Root Certification Authorities et Trusted People à l’aide de la commande Import-Certificate comme indiqué ci-dessous.

$pubKeyFilePath = 'C:\cert.pem'

## Importez la clé publique dans les magasins de certificats Trusted Root Certification Authorities et Trusted People
$null = Import-Certificate -FilePath $pubKeyFilePath -CertStoreLocation 'Cert:\LocalMachine\Root'
$null = Import-Certificate -FilePath $pubKeyFilePath -CertStoreLocation 'Cert:\LocalMachine\TrustedPeople'

Créer le certificat serveur

WinRM pour Ansible a besoin d’un certificat défini avec une utilisation de clé pour l’authentification du serveur. Ce certificat sera stocké dans le magasin de certificats LocalMachine\My de l’hôte Windows. Créez le certificat auto-signé en utilisant le code ci-dessous.

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

Créez l’écouteur WinRm Ansible

Une fois que vous avez créé les deux certificats, vous devez maintenant créer un écouteur WinRm sur l’hôte Windows. Cet écouteur commence à écouter sur le port 5986 pour les connexions entrantes. Une fois créé, cet écouteur accepte les connexions entrantes et tentera de chiffrer les données à l’aide du certificat du serveur créé ci-dessus.

PowerShell Remoting utilise cet écouteur WinRM comme moyen de transport une fois que l’authentification a eu lieu.

Dans l’exemple de code ci-dessous, vous pouvez voir un excellent exemple de vérification de l’existence d’un écouteur HTTPS. Si aucun écouteur n’est trouvé en utilisant le certificat du serveur créé ci-dessus, il en créera un nouveau.

## Trouver tous les écouteurs HTTPS
$httpsListeners = Get-ChildItem -Path WSMan:\localhost\Listener\ | where-object { $_.Keys -match 'Transport=HTTPS' }

## Si aucun écouteur n'est défini du tout ou si aucun écouteur n'est configuré pour fonctionner avec
## le certificat du serveur créé, en créer un nouveau avec un sujet correspondant au nom d'hôte de l'ordinateur
## et lié au certificat du serveur.
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 }
        # Utiliser SSL = $true
    }
    $null = New-WSManInstance @newWsmanParams
}

« Mapper » le certificat client sur le compte utilisateur local

La prochaine étape consiste à s’assurer que lorsque Ansible se connecte à l’hôte Windows en utilisant le certificat du serveur, il exécute ensuite toutes les instructions en tant qu’utilisateur local. Dans ce cas, toutes les activités effectuées par WinRm pour Ansible utiliseront le compte utilisateur local, ansibletestuser.

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

## Trouver l'empreinte du certificat pour le certificat client créé sur l'hôte 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

Autorisez WinRm pour Ansible avec le Contrôle de compte d’utilisateur (UAC)

Si vous utilisez un compte local pour mapper un certificat, vous devez également définir LocalAccountTokenFilterPolicy sur 1 pour vous assurer que UAC ne pose pas de problème. LocalAccountTokenFilterPolicy s’applique à tous les comptes locaux (pas les comptes de domaine) et fait en sorte que votre connexion réseau soit la partie limitée de votre jeton. Cela l’empêchera de se connecter, car Windows ne le considère pas comme un administrateur et WinRM exige par défaut que l’utilisateur soit un administrateur local.

En définissant LocalAccountTokenFilterPolicy, vous indiquez à Windows de ne pas créer un jeton limité pour les connexions réseau par un compte local et d’utiliser son jeton complet.

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

Ouvrez le port 5986 dans le pare-feu Windows

WinRm via HTTPS utilise le port 5986. Si vous avez le pare-feu Windows activé, vous devez ouvrir ce port. Vous pouvez le faire en exécutant le code PowerShell suivant. Ce code est trop permissif et permet à tous les ordinateurs de l’utiliser. Si vous souhaitez le restreindre davantage, assurez-vous d’utiliser le paramètre LocalAddress et spécifiez l’adresse IP d’Ansible.

#region Assurez-vous que WinRM 5986 est ouvert dans le pare-feu
 $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

Ajoutez l’utilisateur local au groupe Administrateurs

Vous vous demandez peut-être pourquoi ce tutoriel n’a pas simplement ajouté l’utilisateur local au groupe des administrateurs plus tôt. La raison est que, pour une raison inconnue, lorsque vous essayez de mapper le certificat client à l’utilisateur, le compte utilisateur ne peut pas être un administrateur local.

Exécutez le code suivant pour ajouter le compte utilisateur local ansibletestuser au groupe des administrateurs.

## Ajoute l'utilisateur local au groupe des administrateurs. Si cette étape n'est pas effectuée, Ansible renvoie une erreur "AccessDenied"
Get-LocalUser -Name $testUserAccountName | Add-LocalGroupMember -Group 'Administrators'

Conclusion

Si vous avez suivi chacune de ces étapes telles qu’indiquées, vous devriez maintenant être en mesure d’exécuter des commandes Ansible sur un hôte Windows. Utilisez le module win_shell pour effectuer vos tests. Si ce module réussit, vous avez configuré avec succès WinRM pour Ansible et l’authentification basée sur le certificat !

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