How to Set Up WinRM Over HTTPS with Puppet
Ever since I started working with Bolt to automate tasks against Linux and Windows servers, I made a mental note that for my Windows servers, I was going to implement WinRM over HTTPS some day. That --no-ssl
command line parameter for Bolt was a daily reminder to get it done.
Fortunately, it turns out there are a couple of Forge modules available that make setting this up a breeze! So I thought I’d write a post to share this with you all.
How to Set Up WinRM Over HTTPS with Puppet
Decide Which Certificates to Use
The most important step is to decide what certificates should be used on the servers. Every server is going to require a valid local certificate with the Subject Name matching the server's FQDN and the certificate issued by a trusted authority. If you don't have your own PKI infrastructure today, that can sure sound like a daunting prospect.
Many articles for enabling WinRM over HTTPS will have you generate self-signed certificates on the servers and then basically disable all SSL verification checks on the client to be able to connect. You can do this with Bolt too by using --no-ssl-verify
. But in that case I would be replacing --no-ssl
with--no-ssl-verify
, which is 7 letters more to type...
We can do better than that. If you have a PKI infrastructure in place today that you'd like to leverage, simply skip to Step 2. If not, keep on reading.
If you're running a Puppet server, you actually already have a PKI platform that can be used! The Puppet server takes care of everything we need for this to work seamlessly:
- The server runs a Certificate Authority that generates certificates for all managed servers
- The certificates have a Subject Name that (by default) is identical to the server's FQDN
- All certificates can be trusted by simply trusting the Puppet server CA certificates
So, all we need for the Puppet PKI platform to be used for WinRM is to import the Puppet certificate to each server's local certificate store and add the Puppet server CA certificates to list of the Trusted Root Certificates.
Want to Get Better at Puppet?Boost your Puppet skills and build a better DevOps career. Take three free courses and sign up for more – on-demand or with a class. |
Add the Puppet Certificates to the Local Certificate Store
We provide a module to automate this: the puppetlabs/windows_puppet_certificates module. After adding the module to your Puppetfile, you'll see a handy new puppet_cert_paths
fact get reported, which will show the locations of Puppet certificates on the server. >Make sure you’re using the latest 0.2.1 version of the module, as this contains a required fix.
The module also provides a class to automatically import the Puppet certificates into the certificate store. Let's build a manifest for our WinRM configuration:
site-modules/profile/manifests/winrm_config.pp
class profile::winrm_config { # Add Puppet CA and the node's certificate to the local store class { 'windows_puppet_certificates': manage_master_cert => true, manage_client_cert => true, } }
Configure WinRM with the HTTPS Listener
Next, we can configure WinRM with a new HTTPS listener. The nekototori/winrmssl module provides a winrmssl
resource that gives us everything we need. The resource works as follows:
- You provide the resource with the name (DN) of a certificate issuer
- The resource then looks for local certificates in the certificate store that have a Subject Name that matches the FQDN of the system and have been issued by the certificate issuer mentioned above
- If such a certificate is found, WinRM is configured with a HTTPS listener based on this certificate
It can be used in two ways:
- You can specify the value (DN) of the certificate issuer to use. This is meant for using an existing PKI infrastructure.
- You can specify the path to a PEM certificate of a CA, and it will extract the 'Issued By' details from that certificate automatically. This is meant for using the Puppet CA platform.
For an existing PKI infrastructure, the Puppet code for this module would look like this:
winrmssl {'My PKI': ensure => present, issuer => 'CN=Example Issuer CA Authority, OU=Example Corp, OU=Test', disable_http => false }
For your Puppet CA platform, the Puppet code for this module would look like this:
winrmssl {'Puppet Enterprise CA': ensure => present, issuer => $facts['puppet_cert_paths']['ca_path'], disable_http => false, require => Class['windows_puppet_certificates'] }
In my case, I'm using the Puppet CA, so let's update the winrm_config manifest with it:
site-modules/profile/manifests/winrm_config.pp
class profile::winrm_config { # Add Puppet CA and the node's certificate to the local store class { 'windows_puppet_certificates': manage_master_cert => true, manage_client_cert => true, } # Configure HTTPS for WinRM, using the imported Puppet node certificate # Use $facts['puppet_cert_paths']['ca_path'] to auto-select the Puppet CA as the certificate issuer winrmssl {'Puppet Enterprise CA': ensure => present, issuer => $facts['puppet_cert_paths']['ca_path'], disable_http => false, require => Class['windows_puppet_certificates'] } }
Open the Firewall and Limit the Use of the HTTP Listener
The new HTTPS listener runs on port TCP 5986, which needs to be opened up on the Windows firewall. We can use the geoffwilliams/windows_firewall module for that.
At the same time, you probably saw the disable_http => false
parameter in the previous step. You might be wondering: "Why aren't you disabling HTTP altogether for WinRM?" The truth is, you’re likely to need it if you're using PowerShell DSC. And if you're using Puppet to manage Windows, it's likely that you're already using some PowerShell DSC, or that you will use it in the future. The invoke-dscresource
command does not support SSL and thus will break if we completely disable the HTTP listener. But we can limit its surface and make this a non-issue by changing the firewall rule for the HTTP listener to only allow connections to 127.0.0.1
. This way, PowerShell DSC will still work while nothing else can use the HTTP listener.
Let's add both firewall rules to our winrm_config manifest:
site-modules/profile/manifests/winrm_config.pp
class profile::winrm_config { # Add Puppet CA and the node's certificate to the local store class { 'windows_puppet_certificates': manage_master_cert => true, manage_client_cert => true, } # Configure HTTPS for WinRM, using the imported Puppet node certificate # Use $facts['puppet_cert_paths']['ca_path'] to auto-select the Puppet CA as the certificate issuer winrmssl {'Puppet Enterprise CA': ensure => present, issuer => $facts['puppet_cert_paths']['ca_path'], disable_http => false, require => Class['windows_puppet_certificates'] } windows_firewall_rule { default: ensure => present, action => 'allow', protocol => 'tcp', profile => ['public','private','domain'], ; 'Windows Remote Management (HTTPS-In)': local_port => '5986', description => 'Inbound rule for Windows Remote Management via WS-Management. [TCP 5986]', ; 'Windows Remote Management (HTTP-In)': local_port => '5985', local_address => '127.0.0.1', description => 'Restricted inbound rule for local Management via WS-Management. [TCP 5985]', } }
Now classify all nodes with the above manifest and do a Puppet run to enforce the changes!
Test WinRM Over HTTPS with PowerShell
Once the nodes have run Puppet, let's see if we can seamlessly create a remote PowerShell session from another Windows server. Log into one of your managed servers, open a PowerShell session, and enter:
Enter-PSSession -ComputerName <fqdn of another server> -UseSSL
If all works well, this should just work:
PS C:\Users\administrator> enter-pssession -computername server2.puppet.local -UseSSL [server2.puppet.local]: PS C:\Users\administrator\Documents>
Test WinRM Over HTTPS with Bolt
And finally, let’s tell Bolt to now use SSL for WinRM! By default, Bolt does not have a list of CAs it trusts. You need to set a cacert
parameter in either bolt.yaml
or in your inventory.yaml
to inform Bolt which CA(s) to trust.
To do this, you need a copy of the ca.pem
from the Puppet server. If you're running Bolt on any server that is already managed by that Puppet server, you can find this file here:
- Windows:
C:\ProgramData\Puppetlabs\puppet\etc\ssl\certs\ca.pem
- Linux:
/etc/puppetlabs/puppet/ssl/certs/ca.pem
So, assuming we have a copy of ca.pem
in one of those locations, we can use that in our bolt.yaml
. Let's try it on the same Windows server that we used in Step 4:
- Create a
bolt.yaml
in.puppetlabs\bolt
in your home directory (~/.puppetlabs/bolt/bolt.yaml
, where~
is identical to%HOMEDRIVE%%HOMEPATH%
) - Add this
winrm
section to yourbolt.yaml
:
winrm: cacert: C:\ProgramData\Puppetlabs\puppet\etc\ssl\certs\ca.pem
Now we can run Bolt. Let's try it:
PS C:\Users\administrator> bolt command run 'hostname' --targets winrm://server2.puppet.local --user administrator --password-prompt Please enter your password: Started on winrm://server2.puppet.local... Finished on winrm://server2.puppet.local: STDOUT: server2 Successful on 1 node: winrm://server2.puppet.local Ran on 1 node in 1.62 sec
No more --no-ssl
, yay!
Learn More
- Get the best of the PowerShell gallery