22 Feb 2023

Dynamic secret management in Puppet

Table of contents


Introduction

In the world of config management, one of the greatest difficulties is the secret management, and especially in the dynamic way. Of course, new solutions have emerged, like vault, but for a very long time this was not the case.

Almost 10 years ago, some people were already wondering how to integrate with Puppet. Unfortunately, the choices were quite limited. For example, Vault, that I mentioned above did not exist.

The choice of the solution: Trocla

After reflection, they chose trocla, which is a simple ruby librairy to have a key -> value management. This choice was made for several reasons:

  • The secret generation; this librairy contains everything you need to directly create a secret, we use the same code to generate and get secrets.
  • Multiple backend choices; in the most simple trocla usage, the content is entirely stored in memory (if your Trocla initialisation doesn’t exist anymore, the keystore is destroyed), but it is possible to set another keystore: moneta. Also, in the same key <-> value way, moneta is a wrapper to stock data in multiple backends (file, mysql, postgresql, redis…). It is therefore possible to make a centralized storage with multiple trocla instance. Since the release of version 0.4.0, you can also use a kv storage on Vault.
  • Encryption of datas; this is the most important point in secret management. There is the possibility to encrypt the content if your backend does not manage it. The data encryption uses a simple x509 certificat, which allows a necessary level of security in case of corruption of the storage backend.
  • Having multiple formats; In addition to management in raw mode, it is possible to define formats to your key. This will make it easier to manage different types of data such as mysql, md5crypt, ssh keys or even x509 certificates.
  • Key expiration; very usefull if you want to have a self renew of your secret
  • Puppet integration; and this is the most important point: everything is already done to be consumed directly from Puppet.

Puppet integration

The puppet integration is made with duriton/trocla module. In addition to being able to manage the installation of your Puppet context, this will allow you to use helpers specific to Puppet. For the installation this will be done in two steps, the installation then the configuration.

Like I said before, the module contain some helpers. The most intersting is trocla(KEY, FORMAT, [optional options]). This will allow us to retrieve the secret and create it if it does not exist. Coupled with an expiration option for your secret, we end up with a secret that performs completely self-managed rotations.

Obviously it is very interesting to use trocla in the dsl Puppet with your profile classes in order to be able to create secrets specific to an agent or to a cluster facts (especially for database passwords). But it is also possible to make calls directly from your hiera with a little extra code in your hiera configuration:

---
version: 5
defaults:
  datadir: '/etc/puppetlabs/code/hieradata'
  data_hash: yaml_data

hierarchy:
  - name: Common
    path: common.yaml
  - name: trocla
    lookup_key: trocla_lookup_key
    options:
      trocla_hierarchy:
        - defaults
      config: '/etc/puppetlabs/puppet/troclarc.yaml'

The secrets will after be accessible in this way:"%{lookup('trocla_lookup::<key format>::<key name>')}". Two specific cases are to be treated:

  • The case where there is a. in the key name, forbidden character which is used to define hash keys in dot format.
  • The case where we want to pass specific options Luckily the two cases are solved in the same way, an example grouping the two where I want to recover the private key the keymy.key in x509 format : I would store the content in the key hiera
# I use a fake trocla key with substitute name my_key
my_variable: "%{hiera('trocla_lookup::x509::my_key')}"
# I set some option on my fake key
trocla_options::my_key:
  x509:  # I use a specific format for this option
    render:
        keyonly: true

Api integration

Very quickly the ops and some internal tools have to interact with Trocla. A first API release was made, but it was very restricted to one specific use case. In 2016 I decied to create a new REST API to learn ruby language. Although I never managed to finish coding some options (in particular on the management of acl) I’ve released troclapi. This allowed the ops to be able to interact with trocla without needing to have any special access (at the time mysql backend + x509 encryption), but also to be able to use ansible trocla integration.

Troclapi doesn’t have any ci because everything was done on an internal Gitlab, so there is everything you need on gitlab-ci to do unit tests, a docker image, documentation, a helm charts… (with harbor integration).


Tags: