February 14, 2020

How to Set Up WinRM Over HTTPS with Puppet

How to & Use Cases
Ecosystems & Integrations

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.

PUPPET TRAINING

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:

  1. You can specify the value (DN) of the certificate issuer to use. This is meant for using an existing PKI infrastructure.
  2. 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 your bolt.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!

GET PUPPET CERTIFIED

Learn More