Verify Spectre / Meltdown protections remotely with Puppet Bolt on Windows
And odds are, you’re already planning the rollout of the patches and BIOS updates needed to bring your environment into compliance, and you have a good idea of how many nodes need to be touched, but you don’t have an easy way of verifying the current state of things. But you need a quick way to determine what the current state is, and for that way to be repeatable so you can verify that your patching is successful.
Microsoft recently published a blog post on verifying Spectre/Meltdown using a new PowerShell module that was released to determine Spectre/Meltdown compliance. In this blog post we’ll go through the steps on how to accomplish the same thing using Puppet Bolt and Puppet Enterprise Task Management.
We’ll start by just issuing the commands needed to get the information we want, move on to how to make this a reusable script, then end with a way to make this a distributable Puppet task that can be run at any time.
To get up and running with Puppet Bolt, we need to install it on our workstation. A more complete walkthrough can be found here.
Note: Be sure to have ruby installed first.
That's all there is to installing Bolt!
The last thing we need is the SpeculationControl PowerShell module. We can install the module on our target nodes using the PowerShell Gallery. Alternately, we could avoid having to install anything on our target nodes by copying the content of the module into our commands or script. There are reasons for either approach, and your environment will largely decide the approach you take. We’re going to take the approach of installing it on target nodes in this post. To do so, we’ll run the following commands using bolt:
We first ran the
'PackageManagement\Get-PackageProvider cmdlet in order to ensure that NuGet is installed on the target node. If we hadn’t then the Install-Module cmdlet would prompt for confirmation to install the NuGet binary, which would block our bolt command (interactive prompts are not supported in bolt). We send the results of installing the module to Out-Null so we don’t pollute our result stream, and we could use the color of ‘Finished on’ to indicate success (green) or failure (red).
Now that we are setup, let’s start querying our target nodes.
Running the Puppet Bolt command
Let's see what kind of output we're going to get. We'll connect to the remote hosts using the WinRM transport with bolt, and run the
Note: We’re using the
--insecureoption here because this example is running against a test lab environment without WinRM setup to use SSL. If your nodes have SSL setup and running, you can skip using the
--insecureflag. We could also skip specifying some transport options by using a bolt configuration file, but for this post we will use all the command line options. For more information see the bolt documentation
That works, but the output is not ideal. There's a bunch of text that explains the report but it's mixed in with the
PSObject so our output isn't parseable. That's because the
Get-SpeculationControlSettings function uses
Write-Host instead of
Write-Verbose or another information stream method. Besides requesting that the
Get-SpeculationControlSettings function be updated, there are a few ways around this. In this case we will get around this by overriding the
That's better, we now have only the
PSObject with the data we need to start reporting on status of our target nodes. There's one more problem though, it's just unstructured text. We can use PowerShell's formatting capabilities and turn the unformatted text to JSON so we can parse it later.
We now have parseable text that we can use to report on our environment. But our command string is getting a little long, and it's not accounting for installing the module on our target hosts. Running this as a command would be too unwieldy. We need to turn this into a script that can handle these requirements. It's time to move to a Puppet Bolt script.
Running the Puppet Bolt script
We can run scripts using Bolt by issuing the
bolt script run subcommand. We'll first create a file called
spectre_meltdown.ps1 and add the code we already worked through, while also cleaning it up a little.
In the script we add the typical PowerShell module installation boilerplate to get the
SpeculationControl module on our target node, then use our
Write-Host trick to squash the informational messages we don't want.
Write-Hosttrick here is temporary. Ideally the function would not pollute the result stream and instead use one of the information streams, but we have to work with what we have right now. Long term, we should look at contributing a fix to the project for this module or using the save/restore pattern to ensure we don’t trip up and break a
Write-Hostused down the line. Since this is exploratory work, we are fine for now.
Get-SpeculationControlSettings we use the results to determine compliance, using the recommendations from the Microsoft security team that wrote the module. In the end, we output a
PSCustomObject and pipe it to the
ConvertTo-Json cmdlet to report on the status.
So now we have a reusable script that we can run against all the nodes in our environment. Running this from the command line yourself is quick and easy, but what about a week from now? Or having someone else run this? Time to move to Puppet Tasks.
Running the Puppet Bolt task
There are a few simple steps to creating Puppet Tasks that are outlined here and a lot of examples in our hands on lab Github Repo. For the purposes of our task today, we'll use Puppet Development Kit (PDK) to get us started.
Creating a Puppet task is easy with PDK. Since a Puppet task lives inside a Puppet module, we'll start by creating a module for our task using PDK.
PDK then walks us through an interview to help us create the Puppet module with all the plumbing and information we need, without us having to remember how to do it. Creating the task is just as simple:
We're now ready to start making our Puppet task. Open up the module in any editor you prefer, here we use Visual Studio Code with the Puppet VSCode extension installed.
PDK created the module skeleton for us, as well as some example files for our task. We'll keep
detect.json, but rename
detect.ps1 we'll add the code we already worked through when we made our Puppet Bolt script. A Puppet task can use the script as is, we don’t have to adjust the script or change anything to get it working. If we needed parameters or other metadata inside the script, we could add them now, but it’s not needed to get running.
Now our Puppet task is ready to be tested out on our target nodes. Let's run it against a single node to see what output we get back. We'll use Bolt just like before, but use the task subcommand.
Note: For eagle-eyed readers you may notice the runtime increased in this try compared to others in the article. The total runtime of the commands will vary depending on if the SpeculationControl PowerShell module is installed on the target node. If it’s not then the time will increase based on how long it takes to install the module on the target node.
Works like a charm. Notice we specified the modulepath since we're actively developing this. You wouldn't need to do this with a published module that is installed in your modulepath.
Working against one node is fine, how about against multiple nodes:
Success! Now that we have a Puppet task, we can add this to our Puppet Enterprise install and use it to report on our hosts.
James Pogran is a senior software engineer at Puppet.
- Read this for more on writing Puppet tasks
- For more examples on using Puppet tasks, check out the Puppet Tasks Hands-on-Lab GitHub repo
- Get Puppet Development Kit (PDK)
- Get the Puppet VS Code Extension
- Read about how to use Puppet Enterprise and our new meltdown module for detecting and remediating Meltdown/Spectre