Proteja WinRM para Ansible con Certificados en 10 Pasos

Ansible se está convirtiendo en una de las herramientas de gestión de configuración más populares, si no la más popular, en la actualidad. Ansible es una herramienta útil (y en la mayoría de los casos gratuita) que permite a los ingenieros de DevOps y a los ingenieros/administradores de sistemas construir y mantener infraestructuras en todos los entornos de manera idempotente, como infrastructura como código. Sin embargo, configurarlo para que se comunique con Windows a través de WinRM puede ser un desafío.

Al igual que muchos otros componentes de infraestructura, Ansible puede implementar y mantener estados de configuración en hosts de Windows. Ansible se conecta a estos hosts de Windows a través de WinRM, aunque están experimentando con SSH.

Cuando configuras WinRM para Ansible, tienes varias opciones diferentes que van desde la facilidad de configuración hasta las implicaciones de seguridad. Muchas personas eligen el enfoque sencillo; autenticación básica mediante HTTP. Aunque te ahorras el trabajo adicional de trabajar con certificados, nunca es una buena idea enviar datos sin cifrar a través de una red a menos que sea necesario.

En este artículo, aprenderás cómo configurar WinRm para Ansible utilizando autenticación basada en certificado con certificados autofirmados para que Ansible pueda comunicarse con ellos.

La configuración que aprenderás en este tutorial no se aplica únicamente a Ansible como cliente. Esta autenticación basada en certificados puede aplicarse igualmente a otros clientes de WinRm, como otros hosts de Windows.

Suposiciones

Este artículo será un recorrido tutorial. Si tienes la intención de seguir los pasos presentados aquí para configurar WinRm para Ansible, el tutorial asumirá lo siguiente:

  • Ya tienes Ansible instalado en un host de Linux.
  • Has configurado un inventario de Ansible basado en tus hosts de Windows.
  • Tienes un servidor Windows 2016 o posterior para gestionar. Algunos cmdlets utilizados en este tutorial no funcionarán con versiones anteriores de Windows.
  • El host de Windows no está en un dominio. Aunque los ejemplos se realizaron en un host que no está unido a un dominio, esta configuración debería funcionar también en hosts unidos a un dominio.
  • Tienes acceso RDP o de consola al host de Windows y has iniciado sesión como administrador local.
  • Estás familiarizado con PowerShell. Casi todos los ejemplos utilizarán código PowerShell para realizar cambios en el host de Windows.

Cada sección usará un fragmento de código que depende del anterior. Algunos fragmentos harán referencia a variables definidas anteriormente. Asegúrate de dejar abierta tu consola mientras copias/pegas este código si planeas seguirlo exactamente.

Si solo deseas el código sin toda la explicación, siéntete libre de descargar este gist de GitHub.

Habilitar el Acceso Remoto de PowerShell para WinRm para Ansible

Aunque todos los servidores Windows Server 2016 o posterior tienen habilitado el Acceso Remoto de PowerShell, siempre es buena idea confirmarlo.

En el host de Windows que deseas gestionar, abre una consola de PowerShell como administrador y ejecuta el siguiente fragmento de código. Este fragmento de código asegura que el servicio WinRm esté iniciado y configurado para iniciarse automáticamente al arrancar el sistema.

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

A continuación, asegúrate de que el Acceso Remoto de PowerShell esté habilitado primero comprobando si tiene alguna configuración de sesión activa. Si no es así, asegúrate de que no tenga ningún listener disponible. WinRm para Ansible debe tener al menos un listener. Si alguna de estas condiciones no devuelve nada, ejecuta Enable-PSRemoting.

if (-not (Get-PSSessionConfiguration) -or (-not (Get-ChildItem WSMan:\localhost\Listener))) {
    ## Usa SkipNetworkProfileCheck para hacerlo disponible incluso en los perfiles públicos del Firewall de Windows
    ## Usa Force para no ser preguntado si estamos seguros o no.
    Enable-PSRemoting -SkipNetworkProfileCheck -Force
}
Configuring WinRM for Ansible (a listener) via the winrm command

Habilitar la Autenticación Basada en Certificados

Por defecto, WinRM no está configurado para la autenticación basada en certificados. Debes habilitarlo configurando WSMan como se muestra a continuación.

#región Habilitar la autenticación basada en certificados
Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true
#endregion

Crear una cuenta de usuario local

Para usar WinRM basado en certificados para la autenticación de Ansible, debes “mapear” una cuenta de usuario local a un certificado. Podrías usar la cuenta de administrador local para hacer esto, pero siempre es una buena idea crear una cuenta específica para facilitar la gestión.

El siguiente fragmento de código está creando una cuenta de usuario local para WinRM para Ansible llamada ansibletestuser con una contraseña de p@$$w0rd12 si no existe. Para asegurar que siempre permanezca activa, la contraseña de esa cuenta nunca caducará.

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

Crear el certificado del cliente

WinRM para Ansible (de manera segura) debe tener dos certificados; un certificado de cliente y un certificado de servidor.

Puedes usar el mismo certificado para cliente/servidor, pero tuve problemas con este enfoque. En la documentación de Ansible y muchas otras fuentes, encontrarás instrucciones para generar el certificado del cliente a través del cmdlet New-SelfSignedCert de PowerShell. Aunque este método puede funcionar, no pude hacerlo funcionar fácilmente.

Para crear el certificado del cliente para WinRM para Ansible, debes crear una clave privada y una pública. Comienza haciendo SSH al host de Ansible y ejecuta el siguiente comando openssl. Este comando crea una clave privada en un archivo llamado cert_key.pem y una clave pública llamada cert.pem. El uso de la clave será para autenticación de cliente (1.3.6.1.4.1.311.20.2.3) “mapeado” a la cuenta de usuario local que creaste anteriormente llamada ansibletestuser.

## Esta es la clave pública generada desde el servidor de 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 paso más cerca de la autenticación de WinRM para Ansible!

Importa el Certificado del Cliente

Una vez que hayas creado el certificado del cliente para WinRm para Ansible, tendrás que importarlo en dos almacenes de certificados en el host de Windows para que funcione WinRm en Ansible. Para hacerlo, primero transfiere la clave pública cert.pem al host de Windows. El ejemplo a continuación asume que la clave existe en C:\cert.pem.

Una vez que tengas el certificado público en el host de Windows, impórtalo en los almacenes de certificados Autoridades de Certificación Raíz de Confianza y Personas de Confianza usando Import-Certificate como se muestra a continuación.

$pubKeyFilePath = 'C:\cert.pem'

## Importa la clave pública en Autoridades de Certificación Raíz de Confianza y Personas de Confianza
$null = Import-Certificate -FilePath $pubKeyFilePath -CertStoreLocation 'Cert:\LocalMachine\Root'
$null = Import-Certificate -FilePath $pubKeyFilePath -CertStoreLocation 'Cert:\LocalMachine\TrustedPeople'

Crea el Certificado del Servidor

WinRM para Ansible necesita un certificado definido con un uso de clave para autenticación del servidor. Este certificado se almacenará en el almacén de certificados LocalMachine\My del host de Windows. Crea el certificado autofirmado usando el fragmento de código a continuación.

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

Crear el Escuchador WinRm de Ansible

Una vez que hayas creado ambos certificados, ahora debes crear un escuchador WinRm en el host de Windows. Este escuchador comienza a escuchar en el puerto 5986 para conexiones entrantes. Una vez creado, este escuchador acepta conexiones entrantes e intentará cifrar los datos utilizando el certificado del servidor creado anteriormente.

El Remoting de PowerShell utiliza este escuchador WinRM como transporte una vez que se ha realizado la autenticación.

En el fragmento de código a continuación, puedes ver un excelente ejemplo de cómo verificar si existe un escuchador HTTPS. Si no se encuentra ningún escuchador utilizando el certificado del servidor creado anteriormente, se creará uno nuevo.

## Buscar todos los escuchadores HTTPS
$httpsListeners = Get-ChildItem -Path WSMan:\localhost\Listener\ | where-object { $_.Keys -match 'Transport=HTTPS' }

## Si no se definen escuchadores en absoluto o no se configura ninguno para funcionar con
## el certificado del servidor creado, crear uno nuevo con un Asunto igual al nombre del host de la computadora
## y enlazado al certificado del servidor.
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
}

Asociar el Certificado del Cliente a la Cuenta de Usuario Local

El siguiente paso es asegurarse de que cuando Ansible se conecte al host de Windows utilizando el certificado del servidor, entonces llevará a cabo todas las instrucciones como un usuario local. En este caso, toda la actividad realizada por WinRm para Ansible usará la cuenta de usuario local, ansibletestuser.

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

## Buscar la huella digital del certificado para el certificado del cliente creado en el host de 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

Permitir WinRm para Ansible con Control de Cuenta de Usuario (UAC)

Si está utilizando una cuenta local para asignar un certificado, también debe establecer el LocalAccountTokenFilterPolicy en 1 para asegurarse de que UAC no interfiera. El LocalAccountTokenFilterPolicy se aplica a todas las cuentas locales (no a cuentas de dominio) y hace que su inicio de sesión de red sea la parte limitada de su token. Esto evitará que se inicie sesión ya que Windows no lo considera un Administrador y WinRM, por defecto, requiere que el usuario sea un administrador local.

Al establecer el LocalAccountTokenFilterPolicy, está indicando a Windows que no cree un token limitado para los inicios de sesión de red por una cuenta local y que use su token completo.

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

Abrir el puerto 5986 en el Firewall de Windows

WinRm sobre HTTPS utiliza el puerto 5986. Si tiene habilitado el Firewall de Windows, debe abrir este puerto. Puede hacerlo ejecutando el siguiente fragmento de código de PowerShell. Este fragmento de código es demasiado permisivo al permitir que todos los equipos lo utilicen. Si desea restringir esto aún más, asegúrese de utilizar el parámetro LocalAddress y especifique la IP de Ansible.

#región Asegurar que WinRM 5986 esté abierto en el 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

Agregar el Usuario Local al Grupo de Administradores

Puede estar preguntándose por qué este tutorial no agregó al usuario local al grupo de Administradores anteriormente. La razón es que, por alguna razón desconocida, cuando intenta asignar el certificado del cliente al usuario, la cuenta de usuario no puede ser un administrador local.

Ejecute el siguiente fragmento de código para agregar la cuenta de usuario local ansibletestuser al grupo de administradores.

## Agregue al usuario local al grupo de administradores. Si este paso no se realiza, Ansible mostrará un error "AccessDenied"
Get-LocalUser -Name $testUserAccountName | Add-LocalGroupMember -Group 'Administrators'

Conclusión

Si ha seguido cada uno de estos pasos como se muestra, ahora debería poder ejecutar comandos de Ansible contra un host de Windows. Utilice el módulo win_shell para realizar sus pruebas. Si este módulo tiene éxito, ¡ha configurado correctamente WinRM para Ansible y la autenticación basada en certificados!

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