Secure o WinRM para o Ansible com Certificados em 10 Passos

O Ansible está se tornando uma das ferramentas de gerenciamento de configuração mais, se não a mais, utilizadas hoje em dia. O Ansible é uma ferramenta útil (e gratuita na maioria dos casos) que permite aos engenheiros de DevOps e aos engenheiros/administradores de sistemas construir e manter infraestrutura em todos os ambientes de maneira idempotente, como código de infraestrutura. No entanto, configurá-lo para se comunicar com o Windows via WinRM pode ser um desafio.

Assim como muitos outros componentes de infraestrutura, o Ansible pode implantar e manter estados de configuração em hosts Windows. O Ansible se conecta a esses hosts Windows por meio do WinRM, embora estejam experimentando com SSH.

Ao configurar o WinRM para o Ansible, você tem várias opções, desde a facilidade de configuração até as implicações de segurança. Muitas pessoas optam pela abordagem fácil; autenticação básica usando HTTP. Embora você evite o trabalho extra envolvendo certificados, nunca é uma boa ideia enviar dados não criptografados pela rede, a menos que seja estritamente necessário.

Neste artigo, você aprenderá como configurar o WinRM para o Ansible usando autenticação baseada em certificado com certificados autoassinados para que o Ansible possa se comunicar com eles.

A configuração que você aprenderá neste tutorial não se aplica apenas ao Ansible como cliente. Essa autenticação baseada em certificado pode ser aplicada da mesma forma a outros clientes WinRM, como outros hosts Windows.

Pressupostos

Este artigo será um tutorial passo a passo. Se você pretende seguir as etapas apresentadas aqui para configurar o WinRM para o Ansible, o tutorial presumirá:

  • Você já tem o Ansible instalado em um host Linux.
  • Você configurou um inventário do Ansible com base em seus hosts Windows.
  • Você tem um servidor Windows 2016 ou posterior para gerenciar. Alguns cmdlets usados neste tutorial não funcionarão com versões mais antigas do Windows.
  • O host Windows não está em um domínio. Embora os exemplos tenham sido executados em um host não associado a um domínio, essa configuração deve funcionar também em hosts associados a domínio.
  • Você tem acesso RDP ou console ao host Windows e está conectado como administrador local.
  • Você está familiarizado com o PowerShell. Quase todos os exemplos usarão código PowerShell para fazer alterações no host Windows.

Cada seção usará um trecho de código que depende do último. Alguns trechos de código farão referência a variáveis definidas anteriormente. Certifique-se de deixar seu console aberto ao copiar/colar este código se planeja segui-lo exatamente.

Se você só quer o código sem toda a explicação, sinta-se à vontade para baixar este gist do GitHub.

Habilitar a Remoção do PowerShell para WinRm para o Ansible

Embora todos os servidores Windows Server 2016 ou posterior tenham a Remoção do PowerShell habilitada, é sempre uma boa ideia confirmar isso.

No host Windows que deseja gerenciar, abra um console do PowerShell como administrador e execute o seguinte trecho de código. Este trecho de código garante que o serviço WinRm seja iniciado e configurado para iniciar automaticamente durante a inicialização do sistema.

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

Em seguida, verifique se a Remoção do PowerShell está habilitada, verificando primeiro se existem configurações de sessão ativas. Se não houver, certifique-se de que não há nenhum ouvinte disponível. O WinRm para o Ansible deve ter pelo menos um ouvinte. Se alguma dessas condições retornar vazia, execute Enable-PSRemoting.

if (-not (Get-PSSessionConfiguration) -or (-not (Get-ChildItem WSMan:\localhost\Listener))) {
    ## Use SkipNetworkProfileCheck para tornar disponível mesmo em perfis públicos do Firewall do Windows
    ## Use Force para não ser solicitado se estamos certos ou não.
    Enable-PSRemoting -SkipNetworkProfileCheck -Force
}
Configuring WinRM for Ansible (a listener) via the winrm command

Habilitar a Autenticação Baseada em Certificado

Por padrão, o WinRM não está configurado para autenticação baseada em certificado. Você deve habilitar isso configurando o WSMan conforme mostrado abaixo.

#região Habilitar autenticação baseada em certificado
Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true
#endregion

Crie uma Conta de Usuário Local

Para usar a autenticação baseada em certificado do WinRM para o Ansible, você deve “mapear” uma conta de usuário local para um certificado. Você pode usar a conta do administrador local para fazer isso, mas sempre é uma boa ideia criar uma conta específica para facilitar a administração.

O trecho de código a seguir está criando uma conta de usuário local para o WinRM para o Ansible chamada ansibletestuser com uma senha de p@$$w0rd12 se ela não existir. Para garantir que ela sempre permaneça ativa, a senha dessa conta nunca expirará.

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

Crie o Certificado do Cliente

O WinRM para o Ansible (de forma segura) deve ter dois certificados; um certificado de cliente e um certificado de servidor.

Você pode usar o mesmo certificado para cliente/servidor, mas eu tive problemas com essa abordagem. Na documentação do Ansible e muitas outras fontes, você encontrará instruções para gerar o certificado do cliente via cmdlet New-SelfSignedCert do PowerShell. Embora esse método possa funcionar, eu não consegui fazê-lo funcionar facilmente.

Para criar o certificado do cliente para o WinRM para o Ansible, você deve criar uma chave privada e uma chave pública. Comece fazendo SSH para o host do Ansible e execute o seguinte comando openssl. Este comando cria uma chave privada em um arquivo chamado cert_key.pem e uma chave pública chamada cert.pem. O uso da chave será autenticação de cliente (1.3.6.1.4.1.311.20.2.3) “mapeada” para a conta de usuário local que você criou anteriormente chamada ansibletestuser.

## Esta é a chave pública gerada a partir do servidor 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 

Um passo mais perto da autenticação do WinRM para o Ansible!

Importe o Certificado do Cliente

Depois de criar o certificado do cliente para WinRM para o Ansible, você terá que importá-lo em dois repositórios de certificados no host do Windows para que o WinRM no Ansible funcione. Para fazer isso, primeiro transfira a chave pública cert.pem para o host do Windows. O exemplo abaixo assume que a chave existe em C:\cert.pem.

Depois de ter o certificado público no host do Windows, importe-o nos repositórios de certificados Trusted Root Certification Authorities e Trusted People usando Import-Certificate como mostrado abaixo.

$pubKeyFilePath = 'C:\cert.pem'

## Importe a chave pública em Autoridades de Certificação Raiz Confiáveis e Pessoas Confiáveis
$null = Import-Certificate -FilePath $pubKeyFilePath -CertStoreLocation 'Cert:\LocalMachine\Root'
$null = Import-Certificate -FilePath $pubKeyFilePath -CertStoreLocation 'Cert:\LocalMachine\TrustedPeople'

Crie o Certificado do Servidor

O WinRM para o Ansible precisa de um certificado definido com um uso de chave para autenticação do servidor. Este certificado será armazenado no repositório de certificados LocalMachine\My do host Windows. Crie o certificado autoassinado usando o trecho de código abaixo.

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

Crie o Listener WinRm Ansible

Uma vez criados ambos os certificados, você deve agora criar um listener WinRm no host do Windows. Este listener começa a escutar na porta 5986 para conexões entrantes. Uma vez criado, este listener aceita conexões entrantes e tentará criptografar dados usando o certificado do servidor criado acima.

PowerShell Remoting usa este listener WinRM como transporte uma vez que a autenticação tenha ocorrido.

No trecho de código abaixo, você pode ver um ótimo exemplo de verificação de um listener HTTPS existente. Se nenhum listener for encontrado usando o certificado do servidor criado acima, ele criará um novo.

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

## Se nenhum ouvinte estiver definido ou nenhum ouvinte estiver configurado para funcionar com
## o certificado do servidor criado, crie um novo com um Assunto do nome do host do computador
## e vinculado ao certificado do 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
}

“Mapeie” o Certificado do Cliente para a Conta de Usuário Local

O próximo passo é garantir que quando o Ansible se conectar ao host do Windows usando o certificado do servidor, ele então executará todas as instruções como um usuário local. Neste caso, toda a atividade executada pelo WinRm para o Ansible usará a conta de usuário local, ansibletestuser.

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

## Encontre a impressão digital do certificado para o certificado do cliente criado no host do 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 com Controle de Conta de Usuário (UAC)

Se estiver usando uma conta local para mapear um certificado, você também deve definir o LocalAccountTokenFilterPolicy como 1 para garantir que o UAC não interfira. O LocalAccountTokenFilterPolicy se aplica a todas as contas locais (não contas de domínio) e faz com que seu logon de rede seja a parte limitada do seu token. Isso impedirá que ele faça logon, pois o Windows não o vê como Administrador e o WinRM, por padrão, requer que o usuário seja um administrador local.

Ao definir o LocalAccountTokenFilterPolicy, você está dizendo ao Windows para não criar um token limitado para logons de rede por uma conta local e usar seu token completo.

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

Abrir a Porta 5986 no Firewall do Windows

WinRm sobre HTTPS usa a porta 5986. Se você tem o Firewall do Windows ativado, deve abrir esta porta. Você pode fazer isso executando o seguinte trecho de código do PowerShell. Este trecho de código é excessivamente permissivo, permitindo que todos os computadores o usem. Se desejar restringir isso mais, certifique-se de usar o parâmetro LocalAddress e especificar o IP do Ansible.

#region Garantir que o WinRM 5986 esteja aberto no 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

Adicionar o Usuário Local ao Grupo de Administradores

Você pode estar se perguntando por que este tutorial não adicionou o usuário local ao grupo de Administradores anteriormente. A razão é que, por algum motivo desconhecido, quando você tenta associar o certificado do cliente ao usuário, a conta de usuário não pode ser um administrador local.

Execute o trecho de código a seguir para adicionar a conta de usuário local ansibletestuser ao grupo de administradores.

## Adicione o usuário local ao grupo de administradores. Se esta etapa não for realizada, o Ansible apresentará um erro "AccessDenied"
Get-LocalUser -Name $testUserAccountName | Add-LocalGroupMember -Group 'Administrators'

Conclusão

Se você seguiu cada uma dessas etapas conforme mostrado, agora deverá conseguir executar comandos do Ansible em um host Windows. Use o módulo win_shell para realizar seus testes. Se este módulo for bem-sucedido, você configurou com sucesso o WinRM para o Ansible e autenticação baseada em certificados!

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