22 Feb 2023

Gestion de secrets dynamiques dans Puppet

Sommaire


Introduction

Dans le monde du config management l’une des plus grandes difficultés reste la gestion des secrets, et notamment de manière dynamique. Bien qu’il comment à y avoir des solutions intéressantes, notamment dans vault, ce ne fût pas le cas pendant très longtemps.

Il y a maintenant presque 10 ans, des personnes se posaient déjà la question pour l’intégration avec Puppet. Malheureusement, le choix fût assez limité, vault que je citais n’existait pas par exemple.

Trocla

Après réflexion trocla fût choisi. Il s’agit d’une simple librairie ruby qui permet de faire de la gestion clé/valeur. Ce choix a été fait pour plusieurs raisons:

  • La crĂ©ation de secret; la librairie contient tout ce qu’il faut pour directement crĂ©er des secrets, ce qui fait un seul point d’entrĂ©e pour gĂ©nĂ©rer et consommer les secrets.
  • La gestion de plusieurs backends; dans la version simple de trocla le contenue du keystore est intĂ©gralement en mĂ©moire (si votre initialisation Trocla n’existe plus, il n’y a plus rien dans le keystore), mais il est tout Ă  fait possible d’utiliser un autre store: moneta. Dans le mĂŞme principe de clĂ©/valeur, moneta est un wrapper pour stocker des donnĂ©es dans plusieurs backends (fichier, mysql, postgresql, redis…). Il est donc possibile de faire un stockage centralisĂ© avec plusieurs instances trocla. Il est Ă©galement possible depuis la version 0.4.0 d’utiliser un backend kv vault.
  • Le chiffrement des donnĂ©es; un point essentiel dans la gestion de secrets. Bien que les secrets soient en claire par default, il existe la possibilitĂ© de chiffrer le contenue si votre backend ne le gère pas. Le chiffrement, bien qu’assez basique avec un certificat x509, permet un niveau de sĂ©curitĂ© nĂ©cessaire en cas de corruption du backend de stockage.
  • Plusieurs formats disponibles; en plus d’une gestion en mode raw, il est possible de dĂ©finir des formats Ă  votre clĂ©. Cela permettra de gĂ©rer plus facilement diffĂ©rent types de donnĂ©es telles que mysql, md5crypt, les clĂ©s ssh ou encore les certificat x509
  • L’expiration des clĂ©s; ce qui peut Ă©ventuellement permettre de gĂ©rer le cas oĂą l’on veux renouveler rĂ©gulièrement et automatiquement des secrets.
  • L’intĂ©gration avec Puppet; et c’est lĂ  des points les plus importants: tout est dĂ©jĂ  fait pour ĂŞtre consommer directement depuis Puppet.

Intégration dans Puppet

L’intégration dans Puppet se fait par le module duriton/trocla. En plus de pouvoir gérer l’installation de votre contexte Puppet, cela va permettre d’utiliser des helpers spécifiques à Puppet. Pour l’installation cela se fera en deux étapes, l’installation puis la configuration.

Comme dit précédemment, le module contient également des helpers. Le plus intéressant est trocla(KEY, FORMAT, [optional options]). Celui-ci va nous permettre de récupérer le secret et de le créer si ce dernier n’existe pas. Couplé d’une option d’expiration de votre secret, on se retrouve avec un secret qui effectue des rotations de manière totalement autonome.

Évidemment il est très intéressant d’utiliser trocla dans le dsl Puppet avec vos classes de profiles afin de pouvoir faire des secrets propres à un agent ou bien à un facts de cluster (notamment pour les mots de passe de bdd). Mais il est également possible de faire des appels directement depuis vos hiera avec un petit bout de code en plus dans votre configuration hiera:

---
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'

Les secrets seront accessibles par la suite de cette manière: "%{lookup('trocla_lookup::<format de la cle>::<nom de la cle>')}". Deux cas spécifiques sont à traiter:

  • Le cas oĂą il y a des . dans le nom de la clĂ©, caractère interdit qui est utilisĂ© pour dĂ©finir des clĂ©s de hash au format dot
  • Le cas oĂą on veut passer des options spĂ©cifiques

Par chance les deux cas se résolvent de la même manière, un exemple regroupant les deux où je souhaite récupérer la clé privée la clé my.key au format x509 (je stockerais le contenue dans la clé hiera my_variable:

# I use a fake trocla key with 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 specifique format to use for this options
    render:
        keyonly: true

Intégration d’une Api

Par la suite une interaction de la part des ops et de certains outils internes ce sont vite fait resentir. Une première version d’un API fût réalisé en interne, mais très restreinte à un cas d’usage. En 2016 j’avais dont initié de refaire une nouvelle API REST afin de pouvoir apprendre le ruby. Bien que je n’ai jamais réussis à finir de coder quelques options (notamment sur la gestion d’acl) j’ai fait le code de troclapi, ce qui a permis aux ops de pouvoir interagir avec trocla sans avoir besoin d’avoir d’accès particuliers (à l’époque backend mysql + chiffrement x509), mais aussi de pouvoir utiliser trocla avec ansible.

Il n’y a pas de ci sur la troclapi car tout était fait sur un Gitlab interne, il y a donc tout ce qu’il faut en gitlab-ci pour faire des tests unitaires, une image docker, de la documentation, une charts helm… (avec intégration harbor)


Tags: