Configuring servers by hand is a costly and error-prone endeavor that increases in difficulty as the number of servers to be managed grows. A popular configuration management tool, Puppet allows engineers to specify desired server configuration in modules that can be applied to change the state of any number of targeted servers.
Challenges
Often Puppet needs access to secrets to apply the desired configuration. Database passwords, API keys, SSH keys – these are all examples of sensitive information that do not belong in a Puppet manifest checked into source control. Puppet is a collaborative tool; checking secrets into Puppet code makes it more difficult to share that code with internal teams or with the wider Puppet community via Puppet Forge.
Encryption solutions like hiera-eyaml tend to overprivilege the Puppet master by allowing it access to the decryption keys for all secrets its managed Puppet nodes may require. Since catalog compilation happens on the Puppet master, decryption keys must be available to the master during the compilation phase. Holding the keys to the kingdom, the Puppet master becomes a lucrative target for attackers.
The Conjur Puppet module
The Conjur Puppet module simplifies the operations of establishing Conjur host identity and allows authorized Puppet nodes to fetch secrets from Conjur.
Secrets can be extracted from source control and the Puppet master, reducing the threat surface of both. Additionally, the Puppet master receives an expiring Conjur auth token to fetch secrets during catalog compilation. This means that the Puppet master no longer has permanent access to the secrets required by Puppet nodes.
Examples
Establishing Conjur identity
To establish Conjur host identity with the module, create a Host Factory token for the Conjur layer into which you would like to enroll the Puppet node and pass it to the conjur
class. This token can be retrieved from Hiera or another trusted data source and should be rotated often.
class { conjur:
appliance_url => 'https://conjur.mycompany.com/',
authn_login => 'host/redis001',
host_factory_token => Sensitive('f9yykd2r0dajz398rh32xz2fxp1tws1qq2baw4112n4am9x3ncqbk3'),
ssl_certificate => file('conjur-ca.pem'),
version => 5
}
By default, all nodes using the Puppet module to bootstrap identity with host_factory_token
will have the Conjur annotation puppet: true
.
Fetching a secret
Once Conjur host identity has been established, secrets can be fetched from Conjur by using the conjur::secret
function in the Puppet manifest.
include conjur
# Fetching a database password to a local variable
$dbpass = conjur::secret('production/postgres/password').unwrap
# Writing a private SSL certificate to file, using Hiera attributes
file { '/etc/ssl/cert.pem':
content => conjur::secret("domains/%{hiera('domain')}/ssl-cert"),
ensure => file,
show_diff => false # only required for Puppet < 4.6
# diff will automatically get redacted in 4.6+ if content is Sensitive
}
Note in Puppet >= 4.6 conjur::secret
returns values wrapped in a Sensitive data type. To use a secret as a string, you need to explicitly request it using the unwrap
function. Using Sensitive makes it harder to mishandle secrets and prevents secrets leaking into Puppet logs (they will show up as Sensitive [value redacted]
).
Summary
The Conjur Puppet module can be used to establish Conjur identity and then use that identity to fetch secrets from Conjur. By avoiding the need to encrypt secrets in source control, the workflow for managing secrets with Puppet is simpler and more intuitive. The Puppet master has only temporary access to the secrets it needs for catalog compilation. Puppet users can focus on collaborating on modules and let Conjur handle the heavy lifting for secrets management.
The Conjur Puppet module is open source and published to the Puppet Forge.
Try it out today with our free Conjur tutorial.
Questions? Connect with us on the CyberArk Commons.
Staff Writer
CyberArk uses a collection of staff writers and practitioners to support the DevOps Security