Sécuriser votre toolchains DevOps avec Vault

D’après le survey de Fortinet en 2019, les entreprises sont principalement confrontés aux challenges d’intégration de la sécurité dans une approche Devops. Juste 19% parmi les tous les Lead Devops qui ont répondu au survey disposent d’une stratégie d’intégration de mesures de sécurité dès le début du cycle de développement des applications. 

Sécuriser une chaîne DevOps, passe par beaucoup d’étapes dont la gestion des secrets qui reste une étape assez importante. Les processus DevOps requièrent souvent différents types de secrets comme des mots de passe, des clés d’API, des clés SSH pour interagir avec d’autres systèmes afin de générer, tester et déployer des applications. Ceci fait que, l’un des défis de ces processus est de savoir comment stocker les secrets. C’est également un challenge dans des environnements comme Kubernetes où les secrets sont encodés en base64, mais peuvent être facilement décodés. Ainsi, l’idéal serait de faire reposer sa stratégie sur des outils de Secret Management pour gérer le chiffrement, le stockage sécurisé et la rotation fréquente. Quelques outils sur le marché dont HashiCorp Vault permettent justement de sécuriser les secrets utilisés dans les toolchains DevOps. 

Par exemple, chaque équipe de développement utilise des outils comme Jenkins pour mettre en place leur pipeline CI/CD. Pour assurer une sécurité dans nos chaînes CI/CD,  on peut en effet intégrer Vault à nos pipelines jenkins pour assurer la gestion des secrets.

Qu’est-ce que Vault?

 Vault est une sorte de coffre fort qui stocke des secrets et permet d’y accéder en toute sécurité. 

Autrement dit, Vault permet de sécuriser, stocker et contrôler l’accès aux tokens, mots de passe, certificats, clés API et autres secrets. Sur le marché, Vault devient de plus en plus la plateforme standard de gestion des secrets dans les environnements qui s’appuient sur une approche DevOps.  

Vault est capable de gérer des secrets statiques comme des mots de passe en assurant ainsi le stockage et la gestion.

Vault peut également fonctionner en tant que Secret As a Service. Dans ce cas là, les secrets ne sont plus statiques mais dynamiques. Vault permet de créer le secret, de vérifier les accès et de le supprimer. Par exemple, pour accéder à un service AWS, Vault est capable de générer des Access Key AWS et de les supprimer dès sa fin de validité. Vault peut aussi fonctionner en tant que service de chiffrement grâce au module transit. Avec ce module, Vault chiffre, signe et vérifie des données sans les stocker.

Vault fournit une interface unifiée à tout secret, tout en offrant un contrôle d’accès et en enregistrant un journal d’audit détaillé.

Que propose Vault?

Vault propose différents modules qui permettent de gérer les secrets, les backends de stockage,  l’authentification, le contrôle d’accès et également l’audit de ces accès. Ces modules peuvent être répartis comme suit:

Secrets Engines

Les secrets engines sont responsables de la gestion des secrets. Ce sont des composants Vault qui stockent, génèrent ou chiffrent des données. 

Chaque secret engine gère un type de secret. Vault propose environ une quinzaine de secrets engines. Parmi ces secrets engine, nous pouvons citer en guise d’exemple: 

  • KV: pour le stockage simple des secrets de type Key/Value.
  • AWS: pour générer des accès key AWS de manière dynamique en fonction des stratégies IAM.
  • Consul: pour générer des tokens d’API Consul de manière dynamique en fonction des Consul ACL policies.

Selon les secrets engines utilisés, les secrets peuvent être associés à un Time to live (TTL). Ce TTL correspond au bail du secret. A l’expiration de ce bail, Vault va automatiquement supprimer le secret.

Stockage des secrets

Vault propose plusieurs supports de stockage communément appelés backends de stockage pour l’emplacement durable des données stockées. Chaque backend a des avantages et des compromis. Par exemple, certains backends prennent en charge la haute disponibilité Vault, tandis que d’autres fournissent un processus de sauvegarde et de restauration plus robuste. Le choix d’un backend de stockage dépend principalement de l’usage et des objectifs. 

Parmi les backends de stockage Vault, nous avons:

  • MYSQL: utilisé pour conserver les données de Vault dans un serveur ou un cluster MySQL. 
  • Consul: utilisé pour conserver les données de Vault dans Consul. Il enregistre Vault en tant que service dans Consul avec une vérification de l’état par défaut. 
  • S3: utilisé pour conserver les données de Vault dans un bucket S3.

Quel que soit le backend utilisé, les données de Vault sont toujours stockées chiffrées.

Authentification

Avant qu’un client puisse accéder au serveur Vault, il doit tout d’abord s’authentifier. Vault met à notre disposition différentes méthodes d’authentification comme l’utilisation des:

  • Tokens: qui permettent aux utilisateurs de s’authentifier à l’aide d’un token, ainsi que de créer de nouveaux tokens, de révoquer des secrets par token.
  • Username/Password: permettant aux utilisateurs de s’authentifier auprès de Vault à l’aide d’une combinaison de nom d’utilisateur et de mot de passe.
  • LDAP: qui permet l’authentification à l’aide d’un serveur LDAP existant et des credentials d’utilisateur/mot de passe.
  • Kubernetes: utilisée pour s’authentifier auprès de Vault à l’aide d’un token de compte de service Kubernetes appelé service account. Cette méthode d’authentification facilite l’introduction d’un token Vault dans un pod Kubernetes.
  • AppRole: qui permet aux machines ou aux applications de s’authentifier avec des rôles définis par Vault.

Ces différentes méthodes peuvent être utilisés suivant un use case défini pour les utilisateurs ou les applications qui doivent accéder à Vault. 

Lors de l’authentification, un token est généré. Ce Token est conceptuellement similaire à un ID de session sur un site Web. Le token peut avoir une stratégie attachée, qui est mappée au moment de l’authentification. 

Audit

Vault nous permet d’obtenir des logs sur tous les appels d’API selon le module d’audit activé via du:

  • File: en ajoutant les journaux d’audit dans un fichier. Il s’agit d’un dispositif d’audit très simple.
  • Syslog: en écrivant les journaux d’audit dans syslog.
  • Socket: où les journaux d’audit sont écrits sur un socket TCP, UDP ou UNIX.

Ces modules d’audit sont des composants Vault qui conservent un journal détaillé de toutes les appels d’API à Vault. Étant donné que chaque opération avec Vault est une demande/réponse d’API, le journal d’audit contient toutes les interactions authentifiées avec Vault, y compris les erreurs.

Comment sécuriser nos pipelines Jenkins avec Vault?

Souvent nous construisons des systèmes qui ont besoin que des secrets leur soient transmis au moment de build. Nous pouvons utiliser Jenkins Credentials Store pour conserver les secrets mais ceci peut ne pas être forcément sécurisé. Il peut donc être un vecteur d’attaque potentielle. En plus de ça, au fur et à mesure que le nombre de secrets que nous devons gérer pour les builds augmente, nous nous rendons compte d’un besoin d’un système de Secret Management plus robuste. De ce fait, une solution comme Vault pour un stockage sécurisé des secrets serait une bonne chose. Ce qui va nous permettre d’avoir un stockage centralisé des secrets et un accès basé sur les rôles. 

C’est ce qui nous amène à penser à un scénario dans lequel une équipe DevOps peut configurer Jenkins pour récupérer les secrets stockés dans Vault afin de pouvoir injecter les secrets dans les variables d’environnement d’une application par exemple MYSQL au moment du déploiement.

Ainsi, la question qui se pose est comment est-ce qu’un serveur Jenkins peut demander un token afin de pouvoir lire les secrets de Vault?

Pour ce faire, nous allons dans un premier temps assurer l’authentification de Jenkins auprès de Vault et ensuite nous pouvons extraire les secrets Vault dans un pipeline Jenkins.

Comment authentifier Jenkins auprès de Vault?

Pour extraire des secrets dans Vault, il faut que Jenkins s’authentifie auprès de Vault. Nous allons donc activer une méthode d’authentification afin que le serveur Jenkins puisse obtenir un token Vault. 

La méthode d’authentification la plus appropriée est la méthode AppRole qui est un mécanisme d’authentification permettant aux machines ou aux applications de s’authentifier avec des rôles définis par Vault. Il utilise un RoleID et un SecretID pour la connexion, semblable à un username et  un password respectivement.

Étant donné que chaque rôle défini par cette méthode est mappé à des stratégies attachées, nous pouvons écrire des stratégies limitant quelle application peut accéder à quel chemin.

Cette méthode va ainsi nous permettre de configurer un ensemble credentials pour chaque application et de limiter ces credentials aux seuls chemins secrets auxquels l’application doit avoir accès. 

La figure ci-dessous nous montre le workflow de base de la méthode d’authentification AppRole.

Source : https://learn.hashicorp.com/vault/identity-access-management/approle

Pour effectuer l’authentification de Jenkins avec Vault, nous allons suivre les étapes suivantes:

Activer la méthode d’authentification Approle

Comme beaucoup d’autres méthodes d’authentification, AppRole doit être activé avant de pouvoir être utilisé. 

Nous pouvons l’activer directement sur l’UI Vault ou bien utiliser la commande suivante:

Copy to Clipboard

Comme nous pouvons le voir sur la figure ci-dessous de l’UI Vault, la méthode AppRole a été bien activée sur le serveur Vault.

En plus de ça, il faut également activer le secret engine dans lequel nous stockons des secrets. Ces secrets pourront être extraits par Jenkins pour les builds, une fois l’authentification effectuée.

Nous avons ainsi activé le secret engine KV sur le chemin d’accès secret/ comme le montre la figure ci-dessous. C’est-à-dire que tous les secrets que nous allons créer auront comme préfixe secret/.

Créer un rôle avec une stratégie attachée

Maintenant que nous avons activé la méthode d’authentification AppRole et le secret engine KV sur le chemin secret/, nous allons créer un fichier dans lequel nous définissons une stratégie sur ce chemin pour définir les autorisations appropriées. 

Ensuite, nous créons d’abord la stratégie avant de créer le rôle. 

Avec la déclaration de stratégie ci-dessus, les tokens affectés à la stratégie auraient l’autorisation de lire un secret uniquement sur le chemin secret/mysql.

La figure ci-dessous nous montre la stratégie nommée “jenkins” qui vient d’être créée.

Maintenant, nous pouvons créer un rôle qui générera un token associé à cette stratégie “jenkins” pour l’authentification au serveur Vault.

Pour créer un rôle avec une stratégie associée, nous utilisons la commande suivante.

Copy to Clipboard

Cette commande crée un rôle nommé “jenkins” avec la stratégie déclarée précédemment. Le TTL du token est défini sur 1 heure et peut être renouvelé jusqu’à 4 heures après sa première création.

Obtenir le RoleID et le SecretID

Le RoleID et le SecretID sont comme un username et un password qu’une machine ou une application utilise pour s’authentifier.

Pour récupérer le RoleID et le SecretID, nous invoquons les endpoints suivants: auth/approle/role/<ROLE_NAME>/role-id & auth/approle/role/<ROLE_NAME>/secret-id.

Dans notre cas, le ROLE_NAME correspond au rôle créé nommé “jenkins”. 

Sur la figure ci-dessous, nous avons les commandes exécutées pour effectuer la récupération des RoleID et SecretID.

Nous avons maintenant toutes les informations nécessaires pour que Jenkins puisse s’authentifier auprès de Vault.

Ainsi, nous avons stocké un ensemble de secrets dans Vault auxquels Jenkins pourra lire. Nous avons pris comme exemple des credentials de base de données Mysql. Ces secrets sont stockés sur le chemin d’accès secret/mysql/webapp comme nous le montre la figure suivante.

Connecter Jenkins à Vault

Il est important de savoir que Vault fonctionne comme une application client/serveur. Vu que toute la configuration côté serveur Vault a été faite, Jenkins étant le client Vault utilise le RoleID et SecretID générés ci-dessus pour s’authentifier. 

Pour ce faire, nous avons besoin d’installer le plugin Jenkins Hashicorp Vault pour créer un crédential de type Vault App Role où nous allons ajouter le RoleID et le SecretID générés.

De ce fait, nous pouvons maintenant utiliser Vault dans nos pipelines étant donné que toutes les configurations ont été faites.

Comment extraire des secrets de Vault dans un pipeline?

Comme nous devons récupérer un secret spécifique pour notre build, nous avons utilisé la fonction du plugin withVault pour extraire les secrets et les définir sur des variables.

Il faut définir le chemin où sont stockés les secrets, la version du secret engine KV, ainsi que les keyname des secrets.

Pour la connexion au serveur, il nous faut l’URL du serveur Vault de même que l’ID du credential de type Vault App Role créé plus haut.

Copy to Clipboard

 

Ce pipeline est assez simple. Après son exécution, nous obtenons les résultats suivants:

Nous pouvons remarquer que Jenkins s’est bien connecté sur le serveur Vault et parvient à récupérer les secrets stockés.

En résumé, comme nous pouvons le voir, l’intégration de vault dans un pipeline Jenkins n’est pas si complexe.

L’utilisation de la méthode d’authentification Approle et du plugin Jenkins Hashicorp Vault sont les principaux éléments qui nous ont permis de réaliser cette intégration. Il fallait d’abord générer des credentials  pour que Jenkins puisse s’authentifier auprès de Vault, ensuite définir un rôle attaché à une stratégie pour les autorisations. Ce qui nous a permis de limiter l’accès aux seuls secrets auxquels l’équipe doit avoir accès. Et donc, nous avons pu extraire nos secrets d’un système beaucoup plus robuste tel que Vault.

Les outils de Secret Magement sont d’une importance capitale dans une approche DevOps. Ils permettent non seulement un stockage sécurisé et centralisé des secrets mais également un contrôle d’ accès sécurisé et un audit complet.