Ansible for Cybersecurity Work - Part 1
Is it DevSecOp, SecDevOps, OpsSecDev?
The infosec field is full of buzzword now more so with the explosion of automation and AI. Luckily, I am not easily fooled by the buzzword and look for the real meat and bones. So when I was tasked with automating some tasks at work I jumped into an interesting technology called Ansible. Ansible is a tool for automation that is cross platform. It relies on setting up a secure connection to an endpoint and then Ansible handle executing tasks on the system.
In a Software/Hardware diverse environment a tool like Ansible is refreshing. But why would someone who work in Cybersecurity care about Ansible. Well technologies and environments change and having a tool that is cross-platform helps reduce complexity. I can use Ansible to manage Windows workstation and patch Linux servers. Deploy software to MacOS and manage Firewall settings. I’d like to take you along my journey of learning and implementing concepts like: Infrastructure as Code, DevSecOps, and Orchestration as a Cybersecurity Analyst.
Learning to Communicate
This series will be a quick and dirty view into how Ansible works. Let’s start with how Ansible communicates with endpoints. If you’re on a Unix based system that’s easy you will be using SSH. Simply setup SSH key based authentication and you are good to go. If you are on Windows based systems this gets a bit more interesting (or complicated). Yes, OpenSSH exists on Windows Servers and Workstations but it is not as robust as it is on Unix. Plus Microsoft has other tools for this type of automation. WinRM is the go tool technology on Windows system when using Ansible.
WinRM is not easy to configure or too understand. Most people get WinRM configurations wrong because Microsoft is not very good at explaining this tool. It took me about 3 months to fully understand the basics of WinRM. So I’d like to save you some time and explain the basics of WinRM. Let’s start with setting up a “Listener”.
WinRM Communication
WinRM communicates via an HTTP SOAP api. This means we can send WinRM communications via HTTP or HTTPS. Setting this is done with the winrm
listener commands in Windows. The method of achieving HTTPS communication is multifaceted. You can employ a reverse proxy, use Active Directory Certificate Services to deploy certificates, or use OpenSSL to create certificates. The use of self-signed certificates is also possible.
Ansbile Variable
Setting the following variable will let Ansible know which communication method to use.
ansible_winrm_scheme:
ex. ansible_winrm_scheme: https
Setting Up HTTPS Certificate Validation with OpenSSL
Generate Certificates with OpenSSL
Generate the CA Private Key
openssl genpkey -algorithm RSA -out ca-key.pem
Generate the CA certificate
openssl req -x509 -new -nodes -key ca-key.pem -sha256 3650 -out ca-cert.pem -subj "/C=US/ST=DC/L=Washington/O=ORG/OU=MyORG/CN=CA"
Generate the private key for the Windows host
openssl genpkey -algorithm RSA -out windows-host-key.pem
Create a certificate signing request (CSR)
openssl req -new -key windows-host-key.pem -out windows-host.csr -subj "/C=US/ST=DC/L=Washington/O=ORG/OU=MyORG/CN=hostname.domain.com"
Create a configuration file for the certificate exentensions
authorityKeyIdentifier=keyid,issuer basicConstraints=CA:FALSE keyUsage = digitalSignature, noRepudiation, keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = hostname.domain.com
Generate the certificate signed by the CA
openssl x509 -req -in windows-host.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out windows-host-cert.pem -days 365 -sha256 -extfile windows-host-ext.cnf
Create the PFX file for Windows Host
openssl pkcs12 -export -out windows-host-cert.pfx -inkey windows-host-key.pem -in windows-host-cert.pem -certfile ca-cert.pem -password pass:password
Import Certificates into the Windows Machine
Import the PFX Certificate into Windows Host
Use Powershell as an Administrator
$password = ConvertTo-SecureString -String "password" -Force -AsPlainText Import-PfxCertificate -FilePath "C:\path\to\windows-cert.pfx" -CertStoreLocation Cert:\LocalMachine\My -Password $password
Import the CA certificate into Windows Host
Import-Certificate -FilePath "C:\path\to\ca-cert.pem" -CertStoreLocation Cert:\LocalMachine\Root
Configure WinRM on the Windows Machine
Enable WinRM
winrm quickconfig -force
Create an HTTPS listener using the certificate
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Subject -eq "CN=hostname.domain.com"} $thumbprint = $cert.Thumbprint
Create the HTTPS Listener
winrm create winrm/config/Listener?Address=*+Transport=HTTPS @{Hostname="hostname.domain.com"; CertificateThumbprint="$thumbprint"}
Enable Certificate Authentication
winrm set winrm/config/service/auth @{Certificate="true"}
Set Trusted Hosts
This adds extra security by only allowing one control machine(node) to communicate with the endpoints. This is a winrm configuration. The value
ansible-controlnode
is the hostname for whatever you are using as the Ansible control node.Set-Item wsman:\localhost\Client\TrustedHosts -Value "ansible-controlnode"
Configure Ansible to Use the Certificates
Place the CA Certificate and Client key/cert on the Ansible Control Node
openssl pkcs12 -in windows-host-cert.pfx -clcerts -nokeys -out client-cert.pem -password pass:password openssl pkcs12 -in windows-host-cert.pfx -nocerts -nodes -out client-key.pem -password pass:password
Setting Up HTTPS with Self-Signed Certificate with Powershell
Generate the Self-signed certificate
In this example -DnsName
is set the Hostname of the machine (FQDN).
New-SelfSignedCertificate -DnsName "MyMachine01.local" -CertStoreLocation Cert:\LocalMachine\My
Configure WinRM Listener
In this example Hostname
is the the hostname of the machine, and CertificateThumbprint
is get the Thumbprint from the Self-signed certificate.
winrm create winrm/config/Listener?Address=*+Transport=HTTPS '@{Hostname="MyMachine01.local"; CertificateThumbprint="thumbprintondevice"}'
Configure Firewall to Allow TCP 5986 aka WinRM over HTTPS
New-NetFirewallRule -DisplayName "WinRM over HTTPS" -Direction Inbound -Protocol TCP -LocalPort 5986 -Action Allow -Profile Domain
Configure TrustedHosts for WinRM
In this example I am setting the Trusted Hosts value to MyNode00.local
. Ideally this will be the ansible control node.
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "MyNode00.local"
Remove TrustedHosts Value
If needed you can remove the trusted host value.
Remove-Item WSMan:\localhost\Client\Trustedhosts
Get TrustedHosts Value
Get-Item WSMan:\localhost\Client\Trustedhosts
Powershell Script to Enable WinRM Listener and Configure Firewall for HTTPS with Self-signed Certificate communication for WinRM
# Get Variables
# Hostname
$fqdn = $env:computername +'.'+ $env:userdnsdomain
# Create Variables
# Trusted Host
$trusthost = "MyNode00.local"
# Certificate Store "My"
$mystore = "Cert:\LocalMachine\My"
# Certificate Store "Root"
$rootstore = "Cert:\LocalMachine\root"
# Create new Self-signed certificate
Write-Verbose "Creating Self-Signed Certificate"
New-SelfSignedCertificate -DnsName "$fqdn" -CertStoreLocation Cert:\LocalMachine\My
# Create new Self-signed certificate and expire in 6 months
#New-SelfSignedCertificate -DnsName "$fqdn" -CertStoreLocation Cert:\LocalMachine\My -NotAfter (Get-Date).AddMonths(6)
# Get thumbrprint from Self-signed certificate
$cert = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Subject -eq "CN=$fqdn"}
$thumbprint = $cert.Thumbprint
# Find and start the WinRM service.
Write-Verbose "Verifying WinRM service."
If (!(Get-Service "WinRM")) {
Write-ProgressLog "Unable to find the WinRM service."
Throw "Unable to find the WinRM service."
}
ElseIf ((Get-Service "WinRM").Status -ne "Running") {
Write-Verbose "Setting WinRM service to start automatically on boot."
Set-Service -Name "WinRM" -StartupType Automatic
Write-ProgressLog "Set WinRM service to start automatically on boot."
Write-Verbose "Starting WinRM service."
Start-Service -Name "WinRM" -ErrorAction Stop
Write-ProgressLog "Started WinRM service."
}
# Configure WinRM Listener
Write-Verbose "Configure HTTPS Listener"
winrm create winrm/config/Listener?Address*+Transport=HTTPS '@{Hostname="$fqdn";CertificateThumbprint="$thumbprint"}'
#Configure Kerberos authentication for WinRM
Write-Verbose "Configure Kerberos Auth for WinRM"
winrm set WinRM/Config/Client/Auth '@{Basic="false";Digest="false";Kerberos="true";Negotiate="false";Certificate="true";CredSSP="false"}'
# Delete HTTP Listener
Write-Verbose "Deleting HTTP Listner"
winrm delete WinRM/Config/Listener?Address=*+Transport=HTTP
# Configure Firewall Rule
Write-Verbose "Configure Firewall Rule"
New-NetFirewallRule -DisplayName "WinRM over HTTPS" -Direction Inbound -Protocol TCP -LocalPort 5986 -Action Allow -Profile Domain
# Configure TrustedHosts
# This is how you prevent lateral movement!
Write-Verbose "Set WinRM Trusted Hosts"
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "$trusthost"
WinRM is NOT Powershell Remoting
This trips people up, don’t make the same mistake.
Conclusion
This is months of research and hacking together scripts. I haven’t gotten this work to a point were I feel it is production ready. Though I feel confident that is is a great starting point. I learned a lot about Certificate management, Windows Automation, Ansible Configurations, and Powershell. This is a fun project because it really pushed the boundaries of my knowledge of Linux and Windows systems. I am so grateful I have the opportunity to work in this field because I get to work on cool stuff like this.
Thank You
If you enjoyed or found any of the content on my site helpful, you can buy me a cup of coffee or send some bitcoin âš¡ so I can continue to bring you amazing content for free!
You can Buy Me A Coffee
Tip with some Sats
Setup
- Keyboard: Keyboardio Atreus (JWICk Ultimate Black Linear)
- Mouse: MX Master (Original)
- Emacs (WSL term)