Published on 8 October 2018 by

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:

$d = myfunction("myarg1", "myarg2")

node default {
  notify { example :
    message => $d
  }
}

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:

$d = Deferred("myfunction", ["myarg1", "myarg2"])

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 (agent-side coming soon)
  • Cyberark AIM: works on the agent side (coming soon)
  • HashiCorp Vault: works on the agent side
  • AWS Secrets Manager: works on the agent side (coming soon)

An example of writing a Puppet function for Deferred usage

  1. Install Puppet Development Kit (PDK). For EL7:
sudo rpm -Uvh https://yum.puppet.com/puppet5/puppet5-release-el-7.noarch.rpm
sudo yum install pdk

You may need to restart your shell for Puppet to be in your path.

  1. From a working directory: pdk new module mymodule (accepting defaults for questions is fine) cd mymodule pdk new class mymodule mkdir -p lib/puppet/functions Paste this code into manifests/init.pp
# Simple example of calling a function at catalog apply time
#
# @summary Demonstrates calling a Deferred function that is housed with this module in lib/puppet/functions/myupcase.rb
#
# @example
#   puppet apply manifests/init.pp
class mymodule {
  $d = Deferred("mymodule::myupcase", ["foo"])

  notify { example :
    message => $d
  }
}

class { 'deferred': }
  1. Paste this code into lib/puppet/functions/myupcase.rb
Puppet::Functions.create_function(:'mymodule::myupcase') do
  dispatch :up do
    param 'String', :some_string
  end

  def up(some_string)
    Puppet::Pops::Types::PSensitiveType::Sensitive.new(some_string.upcase)
  end
End
  1. Running ‘/opt/puppetlabs/bin/puppet apply manifests/init.pp’ should output Notice: FOO Note the use of Sensitive in the up function. 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"]))

Learn more

Share via:
Posted in:
The content of this field is kept private and will not be shown publicly.

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.