# Réduire sa dépendance code avec Vault Agent

> Garder HashiCorp Vault hors du code avec Vault Agent : auto-auth, templates et cache de token, avec un exemple AppRole sur Docker.

2021-02-12 · Vault, HashiCorp, Secrets management

Nous vous avons déjà parlé de l’outil [HashiCorp](https://www.hashicorp.com/) [Vault](https://www.vaultproject.io/) pour la centralisation des secrets statiques, dynamiques ou encore pour l’Encryption as a Service. Dans cet article, nous allons nous intéresser à Vault Agent, qui va nous permettre de réduire la dépendance de HashiCorp Vault au niveau du code. Ce premier article est une introduction au sujet, avant de voir en détail comment intégrer Vault Agent dans une application.

## Prérequis

**Précision** : au moment où nous écrivons cet article, Vault est en **version 1.6.2**.

Si vous ne connaissez pas encore Vault, je vous invite à essayer le tutoriel interactif sur le [site officiel Vault d’HashiCorp](https://learn.hashicorp.com/collections/vault/getting-started) afin de vous faire une première idée.

Enfin, cet article fait suite à la trilogie d’articles sur comment faire disparaître les secrets applicatifs avec Vault et Terraform :
- [Partie 1](https://mehdilaruelle.com/fr/posts/2019/03/migrer-vos-secrets-applicatif-dans-vault-etape-1/)
- [Partie 2](https://mehdilaruelle.com/fr/posts/2019/03/migrer-vos-secrets-applicatif-dans-vault-etape-2/)
- [Partie 3](https://mehdilaruelle.com/fr/posts/2019/03/migrer-vos-secrets-applicatif-dans-vault-etape-3/)

## Découverte du Vault Agent

### Qu'est-ce que Vault Agent ?

Vault Agent n’est autre que le binaire Vault lancé en mode Agent. Il permet en l’occurrence de bénéficier de certaines fonctionnalités nous facilitant l’interaction avec Vault, notamment :

- [Auto-Auth](https://developer.hashicorp.com/vault/docs/agent-and-proxy/autoauth): Prend en charge l’authentification avec Vault définie par une **méthode**. Une fois authentifié, celui-ci dépose le token Vault dans un **Sinks**.
  - [Methods](https://developer.hashicorp.com/vault/docs/agent-and-proxy/autoauth/methods): Il s’agit de la configuration côté Vault Agent nous permettant de nous authentifier avec Vault. À savoir que TOUTES les méthodes d’authentification que l’on retrouve côté Vault ne sont pas forcément disponibles via le Vault Agent. Si le token Vault expire, Vault agent est en capacité de renouveler celui-ci.
  - [Sinks](https://developer.hashicorp.com/vault/docs/agent-and-proxy/autoauth/sinks/file): Il s’agit de la configuration permettant d’indiquer à l’Agent où déposer le token Vault ou encore chiffrer celui-ci si besoin. Pour le moment, seul **File** (stockage fichier) est supporté.
- [Caching](https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent/caching): Permet de mettre en cache les réponses de Vault (token, lease, etc). Le caching est utilisé le plus souvent lors d’un usage plus intensif de Vault et n’entrera pas dans le cadre de cet article.
- [Templating](https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent/template): Anciennement [Consul-template](https://github.com/hashicorp/consul-template), le templating permet de transformer un fichier template source en un fichier de destination contenant nos secrets et a lieu une fois que le token Vault a été généré par l’Auto-Auth. Le templating se base sur [Consul Template markup](https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent/template). Enfin, celui-ci permet de gérer le renouvellement dynamique de nos secrets en remettant à jour notre fichier de destination (toujours en se basant sur le template).

### Vault Agent, pourquoi ?

Dans les précédents articles, nous avons vu [comment intégrer Vault dans une application](https://github.com/mehdilaruelle/vault-demo). Cependant, dans chacune des actions entreprises, nous avons constaté que l’intégration pouvait impacter le code et créer des dépendances avec Vault.

On note **deux dépendances** :

1. Au niveau de l’authentification et renouvellement du token Vault :
   1. Premièrement, l’application doit être “consciente” de la méthode d’authentification (plus d’information sur l’article: [Comment choisir sa méthode d’authentification](https://mehdilaruelle.com/fr/posts/2018/09/comment-choisir-sa-methode-dauthentification-auth-backend-avec-vault/)) afin d’obtenir son token.
   2. Deuxièmement, le token fourni par Vault a une durée d’expiration et l’application doit être en capacité de renouveler celui-ci si besoin
2. Si le secret est dynamique, l’application doit être en capacité de le renouveler.

Pour résumer, nous avons donc les dépendances suivantes, en prenant l’exemple d’une application sur AWS :

<img src="/reduce-code-dependency/dependency_hu_40e969a4986e1f06.webp" loading="lazy" decoding="async" srcset="/reduce-code-dependency/dependency_hu_2ccab833bb321abe.webp 480w, /reduce-code-dependency/dependency_hu_40e969a4986e1f06.webp 768w" sizes="(max-width: 800px) 100vw, 800px" width="768" height="270" alt="Dépendances de Vault" class="left" />

[Vault Agent](https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent) permet de s’affranchir des dépendances. Il prend en charge les différentes tâches, notamment:

- [Auto-Auth](https://developer.hashicorp.com/vault/docs/agent-and-proxy/autoauth): S’authentifier auprès de Vault et renouveler le token fourni par Vault si celui-ci expire (si spécifié)
- [Template](https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent/template): Récupérer les secrets et renouveler les secrets dynamiques via le token généré par la méthode Auto-Auth. Anciennement [Consul-template](https://github.com/hashicorp/consul-template)

Si nous reprenons l’exemple précédent, voici ce que ça donnerait :
<img src="/reduce-code-dependency/vault_agent_hu_6cfd5ebf90d70b9a.webp" loading="lazy" decoding="async" srcset="/reduce-code-dependency/vault_agent_hu_f38fd11ec889bcc4.webp 480w, /reduce-code-dependency/vault_agent_hu_6cfd5ebf90d70b9a.webp 768w" sizes="(max-width: 800px) 100vw, 800px" width="768" height="257" alt="La solution avec Vault Agent" class="left" />

Le Vault Agent nous permet dans un premier temps de s’authentifier auprès du Vault avec la [méthode d’authentification AWS](https://developer.hashicorp.com/vault/docs/agent-and-proxy/autoauth/methods/aws) et de renouveler le token si la durée de vie de celui-ci arrive à expiration.

Dans un second temps, il permet de récupérer les secrets pour les déposer dans un [fichier template](https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent/template) et enfin renouveler ses secrets si besoin.

Si vous êtes sur Kubernetes, il est possible de passer par le [Vault Agent Sidecar Injector](https://www.vaultproject.io/docs/platform/k8s/injector), sujet qui ne sera pas abordé ici.

## Mise en place de notre environnement

Afin de rendre l’intégration la plus transparente possible pour notre application, notre intégration du Vault Agent doit se faire en 2 parties:

1. Mettre en place la méthode d’authentification et valider la récupération du token Vault. Au besoin, renouveler celui-ci
2. Récupérer les secrets ainsi que le templating de notre fichier contenant nos secrets. Au besoin, vérifier que nos secrets se renouvellent

Dans notre exemple, nous allons nous baser sur :

- **Vault** en version 1.6.2
- **Terraform** en version 0.14.6
- Une base de données qui sera ici **MySQL en version 5.7**
- Une application web **PHP 7.2** et **Apache**
  - Avec des **secrets** de type **statiques** ([secret engine K/V](https://www.vaultproject.io/docs/secrets/kv)) et **dynamiques** ([secret engine Database](https://www.vaultproject.io/docs/secrets/databases))
  - Une méthode d’authentification [Approle](https://www.vaultproject.io/docs/auth/approle) sera utilisée pour authentifier l’application à Vault

Vous trouverez les sources permettant de reproduire cet environnement [ici](https://github.com/mehdilaruelle/vault-demo/tree/master/step4b).

L’environnement se base sur des containers docker **afin que vous puissiez reproduire l’exemple de votre côté**.

Concernant la stack docker (docker-compose.yml, app.yml, etc), vous pouvez en retrouver les détails dans les [précédents articles](https://mehdilaruelle.com/fr/posts/2019/03/migrer-vos-secrets-applicatif-dans-vault-etape-1/) ou encore dans le [readme du projet](https://github.com/mehdilaruelle/vault-demo/blob/master/README.md).

### Intégrer le Vault Agent pour la méthode d’authentification

Commençons par installer Vault Agent.

Pour ce faire, il suffit de télécharger le binaire Vault ou de le packager avec l’application. Vous trouverez toutes les [instructions d’installation dans la documentation officielle HashiCorp](https://www.vaultproject.io/docs/install).

Dans notre cas, nous ajoutons celui-ci dans notre [dockerfile](https://github.com/mehdilaruelle/vault-demo/blob/master/step4b/dockerfile#L8) pour télécharger le Vault Agent au build de notre image.

Une fois l’installation faite, nous pouvons nous pencher sur l’authentification qui est de type Approle avec le Vault Agent.

Dans notre cas, dans un cadre de test, nous allons injecter directement le role_id et secret_id dans notre container via les variables d’environnements. Dans le cas d’un pipeline ou encore une CI/CD, l’implémentation de la méthode Approle de façon sécurisée est plus technique et mériterait un article entier sur le sujet. Vous pouvez retrouver un HashiCorp Learn sur le sujet [AppRole With Terraform & Chef](https://learn.hashicorp.com/tutorials/vault/approle-trusted-entities?in=vault/auth-methods).

Pour que notre Vault Agent puisse s’authentifier en [Auto-Auth](https://developer.hashicorp.com/vault/docs/agent-and-proxy/autoauth) nous devons lui fournir un fichier de configuration comprenant: la [méthode d’authentification](https://developer.hashicorp.com/vault/docs/agent-and-proxy/autoauth/methods) et le [Sinks](https://developer.hashicorp.com/vault/docs/agent-and-proxy/autoauth/sinks/file) (où doit-il déposer le token Vault ?), actuellement n’ayant que pour seule possibilité un [fichier](https://developer.hashicorp.com/vault/docs/agent-and-proxy/autoauth/sinks/file).

Dans notre cas, nous aurons la [configuration suivante](https://github.com/mehdilaruelle/vault-demo/blob/master/step4b/vault/config.hcl) :


```hcl
pid_file = "./pidfile"

auto_auth {
  method "approle" {
    config = {
      role_id_file_path = "/root/role-id"
      secret_id_file_path = "/root/.secret-id"
      remove_secret_id_file_after_reading = true
    }
  }

  sink "file" {
    config = {
      path = "/var/www/.vault-token"
      mode = 0644
    }
  }
}

template {
  source = "/var/www/secrets.tpl"
  destination = "/var/www/secrets.json"
}
```

Côté **method Approle**, nous devons:

- Préciser où se situe le **role_id** et **secret_id**. Dans notre cas, ces valeurs sont passées en variable d’environnement cependant Vault Agent n’est en mesure de lire ces valeurs que via un fichier. Il nous faudra donc, au niveau de l’entrypoint, créer ces fichiers avec les valeurs attendues.
- L’option **remove_secret_id_file_after_reading** permet de supprimer le secret_id après la lecture du fichier par le Vault Agent. Une méthode sécurisée afin de garantir que personne d’autre que le Vault Agent ne soit en capacité de s’authentifier à nouveau.

Côté **sink**, la configuration est plutôt simple:

- Le **path** sur lequel le Vault Agent ira écrire le token Vault.
- Le **mode** sur lequel le Vault Agent ira écrire les droits du fichier. Ici nous avons mis 644 pour que le serveur apache soit en capacité de lire le token. Nous pouvons aussi très bien mettre 600 afin de garantir que seul le Vault Agent est en capacité de lire et modifier celui-ci.

Enfin, les derniers éléments de notre configuration:
- **L’adresse du Vault**: nous passons la variable d’environnement **VAULT_ADDR** contenant l’adresse de notre Vault. Le Vault Agent est en capacité de se référer à celle-ci. Dans le cas inverse, il faudra renseigner l’[adresse du Vault directement dans le fichier de configuration](https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent#vault-stanza).
- **pid_file**: est le path sur lequel l’ID du process Vault Agent est écrit. Sachant que le Vault Agent fonctionnera en background, il peut être pertinent de se référer à ce fichier afin de kill le process si besoin.

Afin de finaliser notre intégration, nous devons rajouter un entrypoint à notre container afin de:

- Provisionner les fichiers role_id et secret_id au travers de nos variables d’environnement
- Démarrer le processus du Vault Agent en background
- Et enfin lancer notre serveur apache

Ce qui nous donne comme [entrypoint](https://github.com/mehdilaruelle/vault-demo/blob/master/step4b/vault/entrypoint.sh) :

```bash
#! /bin/bash

echo "$VLT_ROLE_ID" >> /root/role-id
echo "$VLT_SECRET_ID" >> /root/.secret-id


vault agent -config=/root/config.hcl &
apache2-foreground
```

À ce stade, le Vault Agent est en mesure de s’authentifier auprès de Vault et de renouveler le token Vault.

### Intégrer le Vault Agent pour la récupération de secrets

Comme nous l’avons vu précédemment, le Vault Agent se base sur un [fichier template](https://developer.hashicorp.com/vault/docs/agent-and-proxy/agent/template) afin de transformer celui-ci en fichier contenant nos secrets.

Pour notre template, nous allons récupérer notre **secret dynamique de type database** mais il est tout à fait possible de faire la même chose pour les autres secrets dynamiques ou statiques.

Voulant que les secrets soient stockés dans un format JSON, nous avons donc le [template suivant](https://github.com/mehdilaruelle/vault-demo/blob/master/step4b/vault/secrets.tpl) :

```jinja2
{
  {{ with secret (env "VAULT_PATH") }}
  "username":"{{ .Data.username }}",
  "password":"{{ .Data.password }}"
  {{ end }}
}
```

Nous récupérons la valeur de **VAULT_PATH**, une variable d’environnement qui contient comme valeur **database/creds/web**, étant notre path afin de récupérer notre secret.

Vault Agent devrait, en se basant sur notre template, créer notre fichier de secret qui ressemblera à ceci :

```json
{
  "username":"vault-1430158508-126",
  "password":"132ae3ef-5a64-7499-351e-bfe59f3a2a21"
}
```

Si vous souhaitez en savoir plus sur le langage de templating utilisé par Vault Agent, vous pouvez vous référer à la [documentation de Consul Template](https://github.com/hashiCorp/consul-template/blob/master/docs/templating-language.md).

Il ne nous reste plus qu’à indiquer à notre Vault Agent, dans son [fichier de configuration](https://github.com/mehdilaruelle/vault-demo/blob/master/step4b/vault/config.hcl), le fichier source template et le fichier de destination des secrets :
```hcl
template {
  source = "/var/www/secrets.tpl"
  destination = "/var/www/secrets.json"
}
```

Le fichier de template des secrets ainsi que le fichier de configuration du Vault Agent doivent être copiés au sein du [dockerfile](https://github.com/mehdilaruelle/vault-demo/blob/master/step4b/dockerfile).

Pour finir, il ne nous reste plus qu’à [indiquer à notre application](https://github.com/mehdilaruelle/vault-demo/blob/master/step4b/web/index.php) de lire le fichier de secret et de stocker les secrets en variables si ce n’est pas déjà le cas :
```php
if (file_exists("/var/www/secrets.json")) {
  $secrets_json = file_get_contents("/var/www/secrets.json", "r");
  $user = json_decode($secrets_json)->{'username'};
  $pass = json_decode($secrets_json)->{'password'};
}
else{
  echo "Secrets not found.";
  exit;
}
```

### Tester notre exemple

Une fois que le Vault Agent est en capacité de s’authentifier et de récupérer ses secrets, il ne nous reste plus qu’à tester notre application. Le [README](https://github.com/mehdilaruelle/vault-demo/blob/master/step4b/README.md) du projet explique en détail chaque étape du test et un [Makefile](https://github.com/mehdilaruelle/vault-demo/blob/master/step4b/Makefile) est à votre disposition.

Commençons par notre infrastructure :
```bash
$ docker run --rm -v $(pwd)/terraform:/app/ -w /app/ hashicorp/terraform:light init
$ docker-compose up
```

L’infrastructure étant opérationnelle, nous pouvons accéder à notre Vault via cette adresse: [http://127.0.0.1:8200](http://127.0.0.1:8200)

Du côté de notre application :
```bash
$ docker-compose -f app.yml build
$ role_id=$(docker run --rm -v $(pwd)/terraform:/app/ -w /app/ hashicorp/terraform:light output -raw approle_role_id)
$ secret_id=$(docker run --rm -v $(pwd)/terraform:/app/ -w /app/ hashicorp/terraform:light output -raw approle_secret_id)
$ docker-compose -f app.yml run -e VLT_ROLE_ID=$role_id -e VLT_SECRET_ID=$secret_id --service-ports web
```

L’application est maintenant disponible sur l’adresse suivante: [http://127.0.0.1:8080](http://127.0.0.1:8080)

La page suivante devrait apparaître :
<img src="/reduce-code-dependency/result_1_hu_a0456da9ac4eb381.webp" loading="lazy" decoding="async" srcset="/reduce-code-dependency/result_1_hu_836226d46aad0d1e.webp 480w, /reduce-code-dependency/result_1_hu_a0456da9ac4eb381.webp 768w" sizes="(max-width: 800px) 100vw, 800px" width="768" height="307" alt="Vault résultat 1" class="left" />

Si nous rafraichissons la page, seule la valeur chiffrée via l’EaaS (Encryption as a Service) de Vault change et notre application ré-utilise bien notre secret de base de données :
<img src="/reduce-code-dependency/result_2_hu_7488c73c9bb234a8.webp" loading="lazy" decoding="async" srcset="/reduce-code-dependency/result_2_hu_5f635f0acdefd197.webp 480w, /reduce-code-dependency/result_2_hu_7488c73c9bb234a8.webp 768w" sizes="(max-width: 800px) 100vw, 800px" width="768" height="293" alt="Vault résultat 2" class="left" />

Pour tester le bon fonctionnement de la rotation des secrets et du token Vault, nous pouvons révoquer les leases :
<img src="/reduce-code-dependency/lease_1_hu_ed4db5dbc99c82a3.webp" loading="lazy" decoding="async" srcset="/reduce-code-dependency/lease_1_hu_92ad9565186911e9.webp 480w, /reduce-code-dependency/lease_1_hu_ed4db5dbc99c82a3.webp 768w" sizes="(max-width: 800px) 100vw, 800px" width="768" height="118" alt="Lease AppRole dans Vault" class="left" />
côté token Vault: **auth/approle/login**

<img src="/reduce-code-dependency/lease_2_hu_4699d8a26b9a533e.webp" loading="lazy" decoding="async" srcset="/reduce-code-dependency/lease_2_hu_2ea6d58ed98f55c9.webp 480w, /reduce-code-dependency/lease_2_hu_4699d8a26b9a533e.webp 768w" sizes="(max-width: 800px) 100vw, 800px" width="768" height="93" alt="Lease database dans Vault" class="left" />
côté secrets Vault: **database/creds/web**

Et voir ainsi les logs côtés Vault Agent :
<img src="/reduce-code-dependency/logs_hu_15d619fef3e34cfc.webp" loading="lazy" decoding="async" srcset="/reduce-code-dependency/logs_hu_d2adb459e3c139f1.webp 480w, /reduce-code-dependency/logs_hu_15d619fef3e34cfc.webp 768w" sizes="(max-width: 800px) 100vw, 800px" width="768" height="313" alt="Journaux de Vault" class="left" />

Comme nous pouvons le voir:

- **Si le token Vault expire** : le Vault Agent s’authentifie à nouveau
- **Si les secrets expirent** : le Vault Agent récupère les nouveaux secrets puis met à jour notre fichier de secret

Enfin, à des fins de nettoyage, n’oubliez pas d’exécuter les commandes suivantes :
```bash
$ docker-compose down
$ docker-compose -f app.yml down
$ rm terraform/terraform.tfstate
```

## Ce qu’il faut retenir

- Si votre application consomme ses secrets au travers d’un fichier, alors l’utilisation du Vault Agent permet d’intégrer Vault de façon transparente. Sinon, si celui-ci utilise les variables d’environnement il existe une possibilité d’intégration avec [envconsul](https://github.com/hashicorp/envconsul). Pour ceux qui souhaitent tester cette méthode, vous pouvez consulter le [repository GitHub qui se base sur notre exemple](https://github.com/mehdilaruelle/vault-demo/tree/master/step4).
- Le Vault Agent s’occupe de la rotation de vos secrets (s’ils sont dynamiques) et du token Vault.
- Le Vault Agent se sert d’un fichier template pour créer le fichier contenant nos secrets. Vous pouvez en apprendre plus sur la [documentation de Consul Template](https://github.com/hashicorp/consul-template/blob/master/docs/templating-language.md).
- Nous ne modifions plus le code de notre application pour intégrer Vault à l’exception de l’EaaS (Encryption as a Service). L’application est en capacité de récupérer un token Vault qui est renouvelé par le Vault Agent pour l’EaaS.
- Dans notre exemple, nous utilisons un [rôle dynamique pour le secret engine Database](https://www.vaultproject.io/docs/secrets/databases#database-capabilities). Il est tout à fait possible d’utiliser des [rôles statiques](https://www.vaultproject.io/docs/secrets/databases#static-roles) si l’on souhaite éviter de générer un grand nombre d’utilisateurs de base de données à la volée.
- Nous avons modifié l’entrypoint de notre application pour y lancer le Vault Agent mais il est possible de le lancer au démarrage de notre container via systemd ou autre system/service manager afin d’éviter de toucher à l’entrypoint.

Comme nous l’avons vu dans cet article, **nous avons retiré les dépendances entre notre code applicatif et HashiCorp Vault** au travers du Vault Agent. Ceci apporte aussi une rotation automatique du token Vault et des secrets faite par le Vault Agent.

L’intégration du Vault au sein de notre application devient transparente et de même pour la gestion des secrets par les différentes équipes (dev, ops, etc).

Cependant, afin de rendre l’intégration du Vault transparente de bout en bout, il reste une question que nous n’avons pas abordée dans cet article : **Comment rendre l’intégration de Vault transparente pour le déploiement d’une application via un pipeline ?**

Ce point fera certainement l’objet d’un autre article !

---

Publié à l’origine sur https://mehdilaruelle.com/fr/posts/2021/02/reduire-sa-dependance-code-avec-vault-agent/
