Securing sensitive data in Puppet

Puppet's catalog contains sensitive information in clear text. Puppet uses the Sensitive data type to mark your sensitive data — for example secrets, passwords and private keys — with a flag that hides the value from certain parts of Puppet, such as reports. However, you can still see this information in plain text files in the cached catalog and other administrative functions.

There are several methods you can use to keep your sensitive data secure in all parts of Puppet — depending on what you want to secure — using one or a combination of the methods outlined below.

Securing sensitive data on-disk and in your repository with hiera-eyaml

The hiera-eyaml gem — a backend for Hiera — can protect your sensitive data on-disk and in your repository. It works by encrypting the sensitive data in plain text YAML files, without securing your entire code base. This means that you can allow other people access to the code, without access to the sensitive data in that code. To encrypt sensitive data with hiera-yaml, run through the following steps:

Install hiera-eyaml:
puppetserver gem install hiera-eyaml
Use the -l parameter to pass a label for the encrypted value:
eyaml encrypt -l 'some_easy_to_use_label' -s 'yourSecretString'
Add the encrypted value to the class parameter in your Hiera configuration:
mymodule::mykey: >
ENC[PKCS7,...
...
...]

During catalog compilation, puppetserver automatically decrypts the secret using hiera-eyaml and injects the decrypted secret into the catalog.

For more information on using hiera-eyaml, and other hiera-eyaml use cases, see the hiera-eyaml README.

Securing sensitive data in the catalog with lookup_options

If you have class parameters that accept passwords, you need to declare the class parameter as Sensitive. For example, to define the mykey as Sensitive, you would add the following code to your manifest:
class mymodule (  
Sensitive[String[1]] mykey
) { .. }
Note: Sensitive[String[1]] means it's a sensitive string with a length of 1 or greater — not empty.
If you use hiera-emyal, you need Puppet to convert the values returned to a sensitive value that your class recognizes. Using Hiera’s lookup_options, you can use the convert_to key, to cast a parameter to the Sensitive type, to ensure that unsecured values are not returned when searched for with automatic parameter lookup. For example:
mymodule::mykey: 42
lookup_options:
  mymodule::mykey:
    convert_to: "Sensitive"
You can also specify a regex instead of a literal parameter name, and Puppet automatically converts the matching parameters to Sensitive. For example:
lookup_options:
'^profile::.+::sensitive_\w+$':
convert_to: 'Sensitive'

Securing sensitive data in the cached catalog with the node_encrypt module

While the previous two methods secure your data in most parts of Puppet, your data is still exposed in the cached catalog. The node_encrypt module encrypts data on puppetserver before it goes into the catalog, and it is only decrypted on the agent when needed, for example, to manage configuration files.

For example:
file { '/etc/secretfile.cfg':
    ensure => file,
    content => lookup('secret_key').node_encrypt::secret
}

For more information, see the node_encrypt module on the Forge.

Securing sensitive data in EPP templates

Puppet (version 6.20 and later) can render an Embedded Puppet (EPP) template containing a Sensitive value, without unwrapping it. For example:
host=<%= scope['db_host'] %>
password=<%= scope['sensitive::db_password'] %>
The rendered output is automatically marked as sensitive and used as the file content:
file { '/etc/service.conf':
  ensure => file,
  content => epp('<module>/service.conf.erb')
}
Note: Embedded Ruby (ERB) templates do not support interpolation of sensitive values — you have to manually unwrap and re-wrap these.

Writing deferred functions to retrieve secrets

Deferred functions allow you to retrieve sensitive information on the agent at runtime. This means that the primary server does not require access to secrets and allows you to manage secrets with a dedicated secret server and policies consistent with the rest of your infrastructure.

Using the deferred type, you can create a function to integrate with any secret storage you have access to. The deferred type allows you to call this function during catalog enforcement to lookup secrets — using information known only to the agent. The secret is not in plain text during compilation, and therefore not in the catalog. When the function returns the looked up value, it adds a flag to indicate that the information is sensitive. Puppet then redacts the sensitive information from its reports.

Note: There are several modules on Puppet Forge that integrate with external secret servers.