/dev/oops

fiddyspence's blog

Encrypted Facts and Inventory Service

Puppet extensions like hiera-gpg provide some entry points to enable a Puppet administrator to protect static data that feed into generating configurations. There are however occasions where one might want existing potentially sensitive data to be moved between managed nodes, for example via storeconfigs. Another example of this might be facts that are passed into the inventory service for storage that needs to be consumed within a class but needs to be obscured against casual viewing. I encountered this last requirement first when dealing with things like pre-shared keys (ceph volume keys, for example) that are easiest to generate on the node and then shared to other nodes via a fact - however I didn’t particularly want that data to be easily viewable.

So, a while back I hacked on a module to provide some RSA symmetric key encryption/decryption methods - first released at https://github.com/fiddyspence/puppet-certencryption - this post is a follow up to that to demonstrate how that library might be used to move data about reasonably securely - the code from this post is also in that repo.

The pre-requisites for this method are to have a public/private RSA keypair that you distribute to nodes. I’d suggest you use Puppet to distribute the Public key part to the nodes that need to encrypt data. The private portion would live on the nodes that do catalog compiles (Puppet masters). To generate a keypair is as easy as:

1
puppet cert generate mycertencryptionkeypair 

The data I want to export from a node needs to be in the form of a custom fact

1
2
3
4
5
6
7
8
require 'puppet/util/certencryption'
require 'base64'
Facter.add("encfact") do
  a=Puppet::Util::Certencryption.new
  setcode do
    Base64.encode64(a.encrypt('/etc/puppetlabs/puppet/ssl/public_keys/puppet1.spence.org.uk.local.pem','/etc/passwd'))
  end
end

Note that I’m Base64 encoding the output here - facter and puppet don’t deal with binary data very well, so we turn it into ASCII.

Then, I need to consume that data in a Puppet resource somehow - usually this would be via the inventory service, so I’d call out to PuppetDB to retrieve the encrypted value of the fact and then decode it, but this example just runs on the one node:

1
2
3
4
class foo {
  $foovar = decrypt('/etc/foo/privatekey.pem',$::encfact')
  notify { $foovar: }
}

The sample decrypt parser function takes care of Base64 decoding the data from the fact, and then passing it into the decryptstring function, and returns the original data. You could then use this as the value of a content attribute etc.

The data in the inventory service is secured: