Unable to connect to Windows 2016 over HTTPS using self signed certificates; using ansible securely in a mixed active directory environment

Good afternoon,

I have been trying to get ansible working over a Windows 2016 HTTPS listener with encryption using TLS encryption and self-signed certificate authentication.

User authentication over HTTP is not going to be sufficient. The environment where I work is a mix of nodes where some use active directory.

Here is the matrix for authentication:

https://docs.ansible.com/ansible/latest/user_guide/windows_winrm.html

Given this matrix, I expected that I would have been able to authenticate with a local account that is part of the administrators group using certificates. I followed this process, taken from the Windows ansible guides:

https://docs.ansible.com/ansible/latest/user_guide/windows_winrm.html

Enable TLS

Function Enable-TLS12 {

param(

[ValidateSet(“Server”, “Client”)]

[String]$Component = “Server”

)

$protocols_path = ‘HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols’

New-Item -Path “$protocols_path\TLS 1.2$Component” -Force

New-ItemProperty -Path “$protocols_path\TLS 1.2$Component” -Name Enabled -Value 1 -Type DWORD -Force

New-ItemProperty -Path “$protocols_path\TLS 1.2$Component” -Name DisabledByDefault -Value 0 -Type DWORD -Force

}

Enable-TLS12 -Component Server

Not required but highly recommended to enable the Client side TLS 1.2 components

Enable-TLS12 -Component Client

Restart-Computer

Enable certificate authentication

Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true

Enable LocalTestAccount certificate

Please note that I changed the -TextExtension which corresponds to the EKU to have the trailing digit be a 1 instead of a 2, which differs from: https://docs.ansible.com/ansible/latest/user_guide/windows_winrm.html#generate-a-certificate

Using the code from the documentation gives an EKU error: “The Enhanced Key Usage (EKU) field of the certificate is not set to “Server Authentication””

$username = “LocalTestAccount”

$output_path = “C:\temp”

Instead of generating a file, the cert will be added to the personal

LocalComputer folder in the certificate store

$cert = New-SelfSignedCertificate -Type Custom `

-Subject “CN=$username” `

-TextExtension @(“2.5.29.37={text}1.3.6.1.5.5.7.3.1”,“2.5.29.17={text}upn=$username@localhost”) `

-KeyUsage DigitalSignature,KeyEncipherment `

-KeyAlgorithm RSA `

-KeyLength 2048

Export the public key

$pem_output = @()

$pem_output += “-----BEGIN CERTIFICATE-----”

$pem_output += [System.Convert]::ToBase64String($cert.RawData) -replace “.{64}”, “$&`n”

$pem_output += “-----END CERTIFICATE-----”

[System.IO.File]::WriteAllLines(“$output_path\cert.pem”, $pem_output)

Export the private key in a PFX file

[System.IO.File]::WriteAllBytes(“$output_path\cert.pfx”, $cert.Export(“Pfx”))

Import LocalTestAccount

$cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2

$cert.Import(“C:\temp\LocalTestAccount\cert.pem”)

$store_name = [System.Security.Cryptography.X509Certificates.StoreName]::Root

$store_location = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine

$store = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $store_name, $store_location

$store.Open(“MaxAllowed”)

$store.Add($cert)

$store.Close()

Map certificate to LocalTestAccount

$username = “LocalTestAccount”

$password = ConvertTo-SecureString -String “my_password” -AsPlainText -Force

$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password

This is the issuer thumbprint which in the case of a self generated cert

is the public key thumbprint, additional logic may be required for other

scenarios

$thumbprint = (Get-ChildItem -Path cert:\LocalMachine\root | Where-Object { $_.Subject -eq “CN=$username” }).Thumbprint

New-Item -Path WSMan:\localhost\ClientCertificate `

-Subject “$username@localhost” `

-URI * `

-Issuer $thumbprint `

-Credential $credential `

-Force

Create HTTPS Listener with Certificate

$selector_set = @{

Address = “*”

Transport = “HTTPS”

}

$value_set = @{

CertificateThumbprint = “my thumbprint code here”

}

Get-ChildItem -Path cert:\LocalMachine\My -Recurse | Where-Object { $_.Thumbprint -eq “my thumbprint code here” } | Select-Object *

New-WSManInstance -ResourceURI “winrm/config/Listener” -SelectorSet $selector_set -ValueSet $value_set

To be run on my Linux machine

Extract private key

openssl pkcs12 -in cert.pfx -nocerts -nodes -out cert_key.pem -passin pass: -passout pass:

certificate: cert.pem

The checks that I run:

Enumerate listeners (Microsoft documentation)

winrm enumerate winrm/config/Listener

SSL HTTPS enablement check

$listeners = Get-ChildItem WSMan:\localhost\Listener

If (!($listeners | Where-Object {$_.Keys -like “TRANSPORT=HTTPS”}))

{

Write-Host “SSL not enabled”

}

else

{

Write-Host “SSL enabled”

}

Ansible connection configuration flags (from my hosts file)

I have used multiple combinations of the below to get this to work. I’m trying to force port 5986 specifically for the encryption.

[dev:vars]

ansible_connection=winrm

ansible_user=LocalTestAccount

#ansible_winrm_transport=credssp

#ansible_pass=

#ansible_winrm_server_cert_validation=validate

#ansible_winrm_message_encryption=always

ansible_winrm_scheme=https

ansible_port=5986

ansible_winrm_cert_pem=/materials/LocalTestAccount_certificates/cert.pem

ansible_winrm_cert_key_pem=/materials/LocalTestAccount_certificates/cert_key.pem

ansible_winrm_server_cert_validation=ignore

ansible_winrm_transport=certificate

Error message

<10.138.12.123> ESTABLISH WINRM CONNECTION FOR USER: LocalTestAccount on PORT 5986 TO 10.138.12.123
10.138.12.123 | UNREACHABLE! => {
“changed”: false,
“msg”: “certificate: the specified credentials were rejected by the server”,
“unreachable”: true
}

Ansible version information

ansible --version
ansible 2.8.2

As long as we have LocalTestAccount available as part of the Administrators group, my understanding is that with certificates I could run any of the Windows modules and enjoy the benefits of encryption. At the end of the day, we need a recommended solution for mixed active directory nodes, full encryption, and preferably not using passwords in the configuration files.

If my certificate attempt is close and will satisfy my security concerns, please advise on how to fix it. If this is not the recommended path, please recommend something different.

Thank you.