# Migrer vos secrets applicatifs dans Vault (étape 1)

> Sortir les secrets de la configuration de votre application vers HashiCorp Vault, avec Terraform et sans toucher au code. Étape 1 de la série.

2019-03-28 · Vault, HashiCorp, Secrets management, Terraform

Créer, stocker ou transférer des secrets au sein d’une application a toujours été un challenge. Aujourd’hui encore les mauvaises pratiques subsistent, comme l’envoi de mail avec les credential, fragilisant la sécurité de vos applications. [Vault](https://www.vaultproject.io/), avec l’aide de [Terraform](https://www.terraform.io/) permet de répondre aux problématiques de stockage et de diffusion des secrets. Nous verrons dans ce tutoriel comment Vault vous permet de sécuriser la gestion de vos mots de passe et secrets… en les faisant disparaître.

Le sujet étant vaste, il sera traité en trois articles visant à présenter les étapes d’intégration du Vault au sein d’une application. Chaque étape majeure constitue un article, et chacune d’entre elles se suit :

- [Article 1 : Migration des secrets statiques](https://mehdilaruelle.com/fr/posts/2019/03/migrer-vos-secrets-applicatif-dans-vault-etape-1/)
- [Article 2 : Migration des secrets dynamiques (Secret as a Service)](https://mehdilaruelle.com/fr/posts/2019/03/migrer-vos-secrets-applicatif-dans-vault-etape-2/)
- [Article 3 : Encryption as a service](https://mehdilaruelle.com/fr/posts/2019/03/migrer-vos-secrets-applicatif-dans-vault-etape-3/)

Nous verrons ainsi comment implémenter chacune de ces étapes au sein d’une application, ce que cela change pour l’application, et les points de vigilance sur cette migration.

Pour commencer, nous allons présenter brièvement Vault et Terraform, puis l’environnement applicatif de démonstration, et ses problématiques de diffusion/stockage de secret.

## Pré-requis

Si vous ne connaissez pas Vault, je vous invite à essayer le tutoriel interactif sur le site officiel [Vault](https://www.vaultproject.io/#/demo/0) afin de vous faire une première idée. Je vous conseille également de lire l’article concernant les [méthodes d’authentification de Vault](https://mehdilaruelle.com/fr/posts/2018/09/comment-choisir-sa-methode-dauthentification-auth-backend-avec-vault/) afin de mieux cerner et implémenter l’intégration de Vault au niveau applicatif.

Nous utiliserons également [Docker](https://www.docker.com/) et [Docker compose](https://docs.docker.com/compose/) afin de simplifier nos démonstrations.

Nous utiliserons ici **Vault conjointement avec Terraform**, les deux outils ayant une bonne intégration. Nous allons voir ce qu’apporte chacun d’entre eux, et **pourquoi il est intéressant de les combiner**.

## Le binôme Vault et Terraform

### Vault : gestion et protection des secrets

Vault est une solution de centralisation des secrets multi-environnement (cloud, hybride, intranet, etc) servant au **stockage, à l’accès et à la distribution des secrets**. Ceux-ci peuvent être de tous types : username/password, certificat, clé de chiffrement, etc.

L’interaction avec l’outil peut se faire via UI, CLI ou encore API REST permettant ainsi une intégration avec les outils devops ou applications. Vault se distingue, par rapport aux autres outils, par sa **gestion du cycle de vie des secrets** :

- Les [secrets statiques](https://www.vaultproject.io/docs/secrets/kv/index.html) stockés sous la forme de [key/value](https://www.vaultproject.io/docs/secrets/kv/index.html) permettant de stocker n’importe quelle forme de secret et que l’on retrouve dans la plupart des outils. L’intégration des secrets statiques est simple mais demande d’implémenter une méthode de rotation de mot de passe.
- Les secrets dynamiques ou autrement appelés [Secret as a Service](https://www.vaultproject.io/docs/secrets/). Dans ce cas, les secrets ont une durée de vie courte déterminée par le temps d’accès au service.
Prenons l’exemple d’une application souhaitant accéder à une base de données ([database secret engine](https://www.vaultproject.io/docs/secrets/databases/index.html))
<img src="/vault-secure-your-app/vault_db.png" loading="lazy" decoding="async" width="439" height="292" alt="Secret engine database de Vault" class="left" />
L’application demande un accès à une base de données à Vault. Vault crée cet accès, puis fournit les credentials à l’application. Une fois reçus, l’application se connecte à la base de données pour interagir avec celle-ci. Puis l’application demande à Vault de révoquer les accès à la base de données et celui-ci supprime enfin les credentials. Chaque secret dynamique a une **durée de vie par défaut**, dans le cas où l’application ne contacte pas Vault pour révoquer les accès, alors celui-ci se chargera de les révoquer.
Ainsi, les secrets sont fournis à la demande avec une durée définie ce qui offre l’avantage de **ne plus avoir de rotation de mot de passe**. À chaque connexion, l’application utilise Vault comme intermédiaire.

- Enfin, Vault est capable de faire de l’[Encryption as a Service](https://learn.hashicorp.com/vault/encryption-as-a-service/eaas-transit), pour **chiffrer la data en transit ou au repos**. Ceci permet de **réduire la complexité applicative liée à la gestion de clé de chiffrement** (stockage, cycle de vie, échange de la clé, etc) mais aussi aux opérations de chiffrement/déchiffrement.

### Terraform : planifier, créer, modifier et versionner l’infrastructure

[Terraform](https://www.terraform.io/) permet la planification, la création, la modification et le versioning d’infrastructure. Basé sur le langage HCL ([Hashicorp Configuration Language](https://github.com/hashicorp/hcl)), Terraform permet de transformer son infrastructure en **Infrastructure as Code** (IaC).

Dans notre contexte, Terraform aura un rôle un peu plus particulier que dans son usage habituel. Il jouera le rôle de **facilitateur** auprès de Vault aussi bien dans sa configuration que de son intégration côté application.

D’une part, il nous permettra de configurer le service Vault avec le [provider Terraform dédié](https://www.terraform.io/docs/providers/vault/index.html) et d’[intégrer les policies Vault de façon continue](https://www.hashicorp.com/blog/continuously-integrating-policy-into-vault).

D’autre part, il nous permettra d’intégrer Vault de façon simple et sécurisée auprès des applications. Celui-ci aura le rôle d’une partie tierce de confiance que nous utiliserons avec la méthode [Approle](https://www.vaultproject.io/docs/auth/approle.html) de Vault. Dans le cadre de cet article, nous ne pourrons pas intégrer pleinement la bonne pratique de cette méthode mais vous pouvez vous référer à la [documentation de Vault](https://learn.hashicorp.com/vault/developer/iam-authentication).

Bien évidemment, l’ensemble de ces actions peuvent se faire sans Terraform, mais l’intégration de Terraform avec Vault nous simplifie grandement la tâche.

## Mise en place de l’environnement

Maintenant que nous avons fait le tour des outils, nous allons **mettre en place notre environnement à sécuriser avec Vault**.

Nous allons prendre **comme exemple un site web PHP** sous apache utilisant une base de données MySQL (LAMP stack). Afin de simplifier les exemples mais aussi pour mieux nous concentrer sur l’usage de Vault, nous allons minimiser notre application.

Pour ce faire, nous aurons besoin de:

- **Vault** en version 1.0.3
- **Terraform** en version 0.11.10
- Une application web **PHP 7.2 et apache**
- Une base de données qui sera ici **MySQL** en version 5.7

Vous trouverez les **sources** permettant de reproduire cet environnement ici.

Regardons de plus près le **[docker-compose.yml](https://github.com/mehdilaruelle/vault-demo/blob/master/step0/docker-compose.yml)** qui représente ici notre application qui se découpe en trois services:

- **Vault**: Le serveur Vault lancé en mode développeur.
- **Web**: Notre application web PHP embarquée par le serveur web apache. Nous créons en amont notre image web, via le dockerfile, afin d’y installer mysqli qui permettra d’interagir avec la base de données. Enfin, le dossier web courant contiendra nos pages web: ici index.php uniquement.
- **Db**: La base de données MySQL où nous avons:
  - 2 utilisateurs créés: **root** et **dev**.
  - Une base créée nommée: **test**

Enfin, pour terminer ce tour, mettons en place notre environnement avec la commande suivante:

```bash
$ docker-compose up
```

En accédant au site web via l’adresse suivante: [http://127.0.0.1:8080](http://127.0.0.1:8080), nous devrions nous retrouver avec la page suivante :
<img src="/vault-secure-your-app/web_site_hu_23827dafcd49a6ce.webp" loading="lazy" decoding="async" srcset="/vault-secure-your-app/web_site_hu_5a3557138f43fc75.webp 480w, /vault-secure-your-app/web_site_hu_23827dafcd49a6ce.webp 576w" sizes="(max-width: 800px) 100vw, 800px" width="576" height="518" alt="Notre site web" class="left" />

L’action faite par l’application web PHP est plutôt simple:

1. L’application se connecte à la base de données **test** avec l’utilisateur **dev**.
2. Supprime la table **test** si celle-ci existe.
3. Crée la table **test**.
4. Insère 3 valeurs dans la table: **1**, **2** et **3**.
5. Récupère l’ensemble des valeurs dans la table **test**.
6. Affiche le résultat des valeurs récupérées.

L’ensemble de ces actions sont définies dans le fichier **[web/index.php](https://github.com/mehdilaruelle/vault-demo/blob/master/step0/web/index.php)**. Pour finir, nous avons aussi accès à l’interface graphique du Vault à l’adresse suivante: [http://127.0.0.1:8200](http://127.0.0.1:8200)
<img src="/vault-secure-your-app/vault_login_hu_f13410d96e0c1350.webp" loading="lazy" decoding="async" srcset="/vault-secure-your-app/vault_login_hu_f47fe029d259245c.webp 480w, /vault-secure-your-app/vault_login_hu_3732a7b377a9e4a2.webp 800w, /vault-secure-your-app/vault_login_hu_f13410d96e0c1350.webp 1088w" sizes="(max-width: 800px) 100vw, 800px" width="1088" height="602" alt="Portail Vault" class="left" />
Vous pouvez vous identifier avec le token suivant: **root**

### Identifier les secrets non protégés au sein de l’application web

Comme nous avons pu le voir, l’environnement est assez simple. Notre objectif, dans un premier temps, est d’**identifier les secrets au sein de l’application web qui peuvent être soumis à une divulgation**. Ainsi nous ne prendrons pas en compte Vault ou la base de données MySQL qui sont lancés en mode “dev” afin de simplifier l’environnement.

Commençons donc à regarder notre service web au niveau **[docker-compose.yml](https://github.com/mehdilaruelle/vault-demo/blob/master/step0/docker-compose.yml)**:

```yaml
web:
  build: .
  container_name: php
  environment:
    - DB_HOST=${MYSQL_HOST}
    - DB_NAME=${MYSQL_DATABASE}
    - DB_USER=${MYSQL_USER}
  depends_on:
    - db
  volumes:
    - ./web/:/var/www/html/
  ports:
    - "8080:80"
```

L’ensemble des variables d’environnement ne contient aucune information sensible: nom de la base de données, de l’utilisateur et nom de l’host. Cependant, du côté du fichier **[web/index.php](https://github.com/mehdilaruelle/vault-demo/blob/master/step0/web/index.php)** nous remarquons que **la variable pass contient le mot de passe de l’utilisateur de la base de données**:

```php
$host   = getenv('DB_HOST');
$dbname = getenv('DB_NAME');
$user   = getenv('DB_USER');
$pass   = "dev";
```

Ici, l’utilisation d’un fichier de credentials ou encore la déclaration de la variable dans le docker-compose ne résout pas notre problématique car le secret sera toujours visible.

Dans le cas où l’application est hébergée sur un repository de type [Git](https://git-scm.com/) et utilisée par une pipeline CI/CD, il devient critique de masquer ce secret afin que les différents acteurs ne puissent en prendre connaissance. Ainsi, nous devons **supprimer la visibilité de ce secret dans le code**.

### Migration de l’application web vers Vault avec des secrets statiques

Pour répondre à cette problématique, nous migrerons les secrets de l’application web dans Vault avec le [Secret Engine k/v version 2](https://www.vaultproject.io/docs/secrets/kv/kv-v2.html).

L’utilisation de ce secret engine permet d’**intégrer Vault au sein de l’application web sans impacter le code en lui-même**. C’est aussi la méthode la plus simple à implémenter.

Concernant l’authentification de l’application auprès du Vault, nous utiliserons [Approle](https://www.vaultproject.io/docs/auth/approle.html).

Vous trouverez les sources permettant de mettre en place cette solution sur l’environnement précédent [ici](https://github.com/d2si/vault-demo/tree/master/step1).

Voici donc ce que nous allons **ajouter au processus de déploiement de l’application web**:

1. Configuration du Vault, via Terraform (cf: dossier terraform), afin de mettre en place:
  - Le backend d’authentification: **Approle**
  - Configuration de l’Approle pour l’application web afin que celui-ci puisse s’authentifier auprès de Vault
  - Création et mise en place des policies applicatives en lecture seule sur le path suivant: **secret/data/web**
2. Le passage en variable d’environnement du Role ID et du Secret ID via Docker. Pour simplifier la démonstration, nous n’utilisons pas de pipeline CI/CD, cette étape sera donc manuelle.
3. Authentification et récupération des secrets auprès de Vault au démarrage de l’application. Ici, le script **[vault.sh](https://github.com/mehdilaruelle/vault-demo/blob/master/step1/vault.sh)** effectuera ces actions à l’entrypoint de notre container applicatif.

Les secrets seront en variables d’environnement, sans impacter le code en lui-même.

### Ce qui change

Premier changement, le secret n’est plus en clair dans le code, on le récupère au sein du code via des variables d’environnements :

```php
$host   = getenv('DB_HOST');
$dbname = getenv('DB_NAME');
$user   = getenv('DB_USER');
$pass   = getenv('DB_PASSWORD');
```

Le second changement concerne la stack du docker-compose où nous avons retiré l’application pour la placer dans une stack isolée nommée **[app.yml](https://github.com/mehdilaruelle/vault-demo/blob/master/step1/app.yml)**, afin de pouvoir séparer les cycles de vie d’infrastructure et applicatif.

Par rapport à notre service web, deux changements notables:

1. L’ajout d’un entrypoint pour l’exécution du script **[vault.sh](https://github.com/mehdilaruelle/vault-demo/blob/master/step1/vault.sh)**
2. L’ajout de deux variables d’environnement liées à Vault **VLT_ADDR** (l’adresse de notre Vault) et **VLT_PATH** (le chemin de nos secrets applicatifs). Dans notre cas, ces variables nous permettent de simplifier notre démonstration.

```yaml
entrypoint: ["vault.sh"]
environment:
  - DB_HOST=${MYSQL_HOST}
  - DB_NAME=${MYSQL_DATABASE}
  - VLT_ADDR=http://vault:8200
  - VLT_PATH=secret/data/web
```

Enfin, nous avons ajouté le script **[vault.sh](https://github.com/mehdilaruelle/vault-demo/blob/master/step1/vault.sh)**. Celui-ci sera joué à l’initialisation de notre container et effectuera les étapes suivantes:

1. Vérification et récupération des variables attendues: **Role_ID** et **Secret_ID**
2. Authentification au Vault avec la méthode Approle en utilisant le **Role_ID** et le **Secret_ID**. Celui-ci retournera un token Vault qui permettra à notre application de récupérer ses secrets.
3. Récupération des secrets dans le path: **secret/data/web**
4. Passage des variables d’environnements **DB_USER** et **DB_PASSWORD**
5. Lancement de notre serveur Apache

### Tester notre exemple

Maintenant que nous avons fait les modifications, il est temps pour nous de tester notre application.

Commençons par mettre en place notre infrastructure:

1. Initialisation du dossier **terraform** afin de récupérer les bons providers:
`$ docker run --rm -v $(pwd)/terraform:/app/ -w /app/ hashicorp/terraform:light init`
2. Déploiement de notre infrastructure:
`$ 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)

Comme nous n’avons pas de pipeline CI/CD, certaines étapes sont manuelles. Commençons par stocker notre secret dans le Vault:

1. Accéder au Vault via l’interface UI: [http://127.0.0.1:8200](http://127.0.0.1:8200/)
2. Utiliser l’authentification Token avec le token suivant: **root**
3. Sélectionner le secret engine ‘secret’ puis créer un secret avec comme path: **web**
4. Insérer le secret comme dans la capture d’écran suivante et puis cliquer sur **Save**

<img src="/vault-secure-your-app/vault_create_secret.png" loading="lazy" decoding="async" width="425" height="474" alt="Création d&#39;un secret dans Vault" class="left" />

Notre secret est maintenant bien présent dans notre Vault. Il ne nous reste plus qu’à lancer notre application avec le bon **Role_ID** et **Secret_ID**:

```bash
$ role_id=$(docker run --rm -v $(pwd)/terraform:/app/ -w /app/ hashicorp/terraform:light output approle_role_id)
$ secret_id=$(docker run --rm -v $(pwd)/terraform:/app/ -w /app/ hashicorp/terraform:light output approle_secret_id)
$ docker-compose -f app.yml run -e VLT_ROLE_ID=$role_id -e VLT_SECRET_ID=$secret_id --service-ports web
```

Notre application est de nouveau disponible sur l’adresse [http://127.0.0.1:8080](http://127.0.0.1:8080) et le résultat devrait être le même que l’étape précédente:

<img src="/vault-secure-your-app/result_hu_23827dafcd49a6ce.webp" loading="lazy" decoding="async" srcset="/vault-secure-your-app/result_hu_5a3557138f43fc75.webp 480w, /vault-secure-your-app/result_hu_23827dafcd49a6ce.webp 576w" sizes="(max-width: 800px) 100vw, 800px" width="576" height="518" alt="Résultat de la migration dans Vault" class="left" />
Nous avons bien réussi à intégrer Vault à notre application sans impacter le code applicatif.

Enfin, pour clean, 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 sur cette migration

- Le code applicatif n’a pas changé (à l’exception de la suppression du secret)
- Un script a été rajouté en entrypoint afin d’interagir avec le Vault pour récupérer les secrets et les stocker en variable d’environnement
- Avant que l’application ne soit déployée, le Vault doit être configuré pour autoriser l’authentification Approle de l’application et que le secret de la base de données y soit stocké. Cette étape doit être automatisée côté Ops

L’intégration du Vault est transparente côté développeur, mais cette méthode ne résout pas la problématique de la **rotation du secret**, ou encore la connaissance du secret par les ops.

**[Dans notre prochain article, nous verrons comment résoudre ces problématiques par l’intermédiaire des secrets dynamiques (Secret as a Service).](https://mehdilaruelle.com/fr/posts/2019/03/migrer-vos-secrets-applicatif-dans-vault-etape-2/)**

<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
			<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/RjlZlrm06Qw?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
		</div>

---

Publié à l’origine sur https://mehdilaruelle.com/fr/posts/2019/03/migrer-vos-secrets-applicatif-dans-vault-etape-1/
