Secret agents, man: secrets store integrations in Puppet 6
Maybe you’ve heard the Johnny Rivers song “Secret Agent Man.” If not, take a listen. We’ll wait.
Now that we have the theme song in your head, we’re excited to tell you about how open source Puppet 6 and Puppet Enterprise 2019 make it easy to use secrets from stores like HashiCorp Vault, CyberArk Conjur and AIM, Azure Key Vault and AWS Secrets Manager. As part of the recently announced Puppet 6, agents can now fetch data for themselves at catalog application time.
A key use case for this is securely retrieving sensitive information like passwords from a secrets store. This is not the only use case, though, and Puppet 6 provides a generic ability to fetch any kind of data or calculate values locally.
These capabilities are powered by the new Deferred type that instructs agents to execute a function locally to resolve a data value at the time of catalog application. When compiling catalogs, functions are executed on the Puppet master with results entered into the catalog directly. The complete and fully resolved catalog is then sent to the agent for application. Starting in Puppet 6, when a function is called it can now be deferred until the agent applies the catalog, meaning the agent calls the function on the agent instead of on the master. The upshot of this is that agents can use a function to fetch data like secrets directly rather than having the Puppet master act as an intermediary.
Using a Deferred function
Prior to Puppet 6, you could use a function executed on the master to evaluate a result and store it in the catalog. Consider the Puppet code below that prints the result of myfunction:
In Puppet 6 you can convert the function to execute on the agent by wrapping the function call with the Deferred type. The function name is the first parameter and the function's parameters are passed as an array. Converting the call to myfunction as above to use Deferred would be:
Secrets store integrations
Also new in Puppet 6 is the ability to package functions in a module that will be synced down to agents. This means that the workflow to use Deferred functions is the same module adoption workflow that you already use. For most users you just add the new module to your Puppetfile. Community modules providing integrations with secrets store include:
- Azure Key Vault: works on both the master and server side
- Cyberark Conjur: works on the master side
- HashiCorp Vault: works on the agent side
An example of writing a Puppet function for Deferred usage
- Install Puppet Development Kit (PDK). For EL7:
You may need to restart your shell for Puppet to be in your path.
- From a working directory:
pdk new module mymodule(accepting defaults for questions is fine)
pdk new class mymodule
mkdir -p lib/puppet/functionsPaste this code into
- Paste this code into
- Running ‘/opt/puppetlabs/bin/puppet apply manifests/init.pp’ should output
Notice: FOONote the use of
upfunction. This tells the agent to not store the cleartext value in logs or reports. On the command line and in the Puppet Enterprise GUI, sensitive data will appear as [redacted].
Notes on using Deferred functions
- If an agent is applying a cached catalog or with
--catalog, the Deferred function will still be called at application time and the value returned then will be used.
- It is the responsibility of the function to handle edge cases such as the a remote store being unavailable, default values or caching values to use if live data is unavailable.
- Currently Deferred only supports Puppet's function API for Ruby.
- If the function called on the agent side does not return a Sensitive then the value would appear in reports and logs. If this is undesirable, you can wrap the return value in a Sensitive in your Puppet code. For example:
$d = Sensitive(Deferred("myupcase", ["foo"]))