Comment déployer votre infrastructure Azure en toute sécurité avec Terraform

Lorsque l’on déploie une infrastructure en utilisant du code (on parle aussi d’Infrastructure As Code), la sécurité est bien souvent négligée et cela peut avoir de lourdes conséquences.
En effet, une infrastructure contient des accès, des mots de passe, etc. autant d’informations sensibles qui ne doivent pas tomber dans de mauvaises mains, cela pourrait avoir de très graves conséquences sur les performances des applications, des données des utilisateurs ou des clients, ou bien même la totalité de l’infrastructure.
Le fait de “coder” cette infrastructure impose donc de correctement protéger votre code ainsi que les données qui y sont répertoriées.
Dans un précédent article sur le provisionnement d’une infrastructure Azure avec Terraform, nous avons vu comment utiliser les principales fonctionnalités de Terraform avec un exemple simple de code.
Cependant, en regardant ce code d’un peu plus près, on peut se rendre compte que certaines données sensibles peuvent être mieux protégées, nous verrons donc dans cet article comment les protéger. Nous parlerons également du fichier tfstate pour mieux comprendre le fonctionnement de Terraform dans la protection de données sensibles.
Les informations d’authentification Azure
Dans l’article précédent, nous avions montré comment définir les informations d’authentification du Service principal Azure dans le provider :
Ce code pose un certain nombre de problèmes de sécurité. Toute personne qui peut accéder au code, que ce soit par un accès direct au fichier ou par un contrôleur de source et surtout si le code est sur GitHub, peut manipuler toutes les ressources de votre abonnement Azure. Mais comment protéger ces accès ?
Solution 1 : Restreindre la portée des permissions du Service principal
Dans la mesure du possible, si le code Terraform n’a pas besoin de créer de nouveaux resources group, mais manipule des ressources dans un resource group déjà existant, alors dans ce cas il convient de donner les droits de Contributeurs au Service principal uniquement sur le resource group existant.
Dans ce cas, la personne qui a ces informations ne peut pas avoir accès aux autres ressources externes.
Solution 2 : Utiliser des variables d’environnement
Il s’agit de la meilleure solution, en effet, au lieu de renseigner les informations d’authentification directement dans le code, on va déclarer dans des variables d’environnement spécifique à Terraform comme spécifier dans la documentation officielle.
Voici leur correspondance :
- L’Id de la subscription – ARM_SUBSCRIPTION_ID
- Le Client ID – ARM_CLIENT_ID
- Le Client Secret – ARM_CLIENT_SECRET
- Le Tenant ID – ARM_TENANT_ID
En voici un exemple pour Linux :
Et en déclarant ces variables le code Terraform du provider n’a plus besoin de ces informations :
Et ainsi moins de crainte que ces informations soient visibles même dans un gestionnaire de code source.
Un autre avantage de cette solution est que les informations d’authentifications sont protégées en local mais aussi dans vos pipeline CI / CD de façon à les rendre dynamiques selon les environnements.
Le fichier d’état Tfstate
Quand on exécute les commandes terraform plan et apply (ou destroy), Terraform possède un mécanisme qui lui permet d’identifier quelles sont les ressources à mettre jour, à ajouter ou à supprimer. Terraform maintient un fichier .tfstate au format Json qui contient tous les détails des ressources provisionnées par Terraform.
Ce fichier tfstate est créé lors du premier Terraform plan et il est mis à jour à chaque action (apply/destroy).
Voici un exemple de tfstate et de son contenu :
Ce fichier apporte de nouvelles problématiques, en voici d’ailleurs quelques-unes :
- Des informations sensibles sur mes ressources y sont mentionnées en clair.
- Dans le cas où l’on travaille à plusieurs, il faut que ce fichier soit partagé, or par défaut il est créé sur le poste local.
- Même s’il est archivé dans le gestionnaire de code source, une fois récupéré sur le poste en local, il ne permet pas le travail à plusieurs sur le même fichier.
- Avec ce fichier en local, gérer du multi-environnements peut vite s’avérer compliqué et risqué.
- Toute suppression de ce fichier local ou une mauvaise édition manuelle peut rendre votre code Terraform, et donc votre infrastructure in-déployable.
Pour répondre à toutes ces problématiques, il existe une solution qui réside en l’utilisation d’un “Remote Backend“. C’est-à-dire stocker ce fichier dans un espace partagé et sécurisé.
Dans Terraform, et selon les providers, il existe plusieurs types de remotes backend, pour Azure on utilisera le stockage blob d’un Azure Storage Account.
Créer d’un Remote Backend pour Terraform avec Azure Storage Account
On va donc créer un Storage Account (dans un resource group déjà existant de notre suscription) qu’on appellera, par exemple, “remoteterraform” et à l’intérieur un conteneur de blob “tfbackends“. Il faut aussi lui donner le rôle Contributeur à notre Service principal.
Puis, nous allons configurer Terraform pour l’utiliser, en rajoutant la portion de code suivante :
Cette configuration contient les informations du fichier tfstate à utiliser.
Remarques :
- Si votre service principal n’a pas les permissions sur ce storage account, vous pouvez spécifier la variable d’environnement ARM_ACCESS_KEY avec la Acces Key du storage account.
- Pour une meilleure organisation des fichiers tfstate (si par exemple vous avez plusieurs environnements) pour pouvez spécifier des répertoires dans la valeur key , par exemple : dev/monappli.tfstate
Lors de l’exécution de la commande terraform init, Terraform va désormais se connecter au tfstate spécifié, ce fichier n’est donc plus dépendant du poste en local et tous les membres de l’équipe pourront travailler sur le même tfstate. Tout ça en profitant de tous les avantages d’un Azure storage account (snapshoot, backups, etc.).
Comment protéger les données sensibles du code source ?
Après avoir protégé vos informations d’identifications et les fichiers tfstates, il y a toujours le problème des données sensibles qui se trouvent dans le code Terraform qui provisionne vos ressources Azure. Ces données sensibles sont, par exemple, des mots de passe des VM, des bases de données SQL Server ou MySql, etc.
En effet, toutes ces données sont insérées dans le code Terraform, ce code sera archivé et versionné dans un gestionnaire de code source (git, GitHub, etc.) et cela peut donc donner accès au contenu de vos ressources (disque de VM, data de base de données, etc. ) et cela peut être critique.
Voici un exemple de code Terraform qui crée une Virtual Machine (Linux) :
A la ligne 25, on peut remarquer que le mot de passe est inscrit en clair.
L’une des solutions qui permet de protéger ces données consiste en l’utilisation d’un coffre fort de mot de passe avec par exemple Azure Key Vault.
L’objectif est de stocker les mots de passes dans un Azure Key Vault et Terraform doit récupérer la valeur du secret désiré et utiliser le résultat dans la VM. La première étape va nous permettre de stocker le mot de passe dans un secret d’un Azure Key Vault.
Remarque: Le Service Principal doit avoir les permissions sur cet Azure Key Vault.
Puis, dans le code Terraform, utiliser des objets data “azurerm_key_vault” et “azurerm_key_vault_secret”, comme ci-dessous :
Le azurerm_key_vault_secret retourne la valeur du secret qui contient le mot de passe de la VM (documentation).
Enfin, la dernière étape consiste à utiliser la valeur retournée par le azurerm_key_vault_secret dans la resource VM en ayant modifié son code de création.
La ligne 7 montre bien comment on ne met plus de mot de passe dans le code Terraform en récupérant la valeur stockée dans l’Azure Key Vault.
Attention: La ressource data “azurerm_key_vault_secret” qui récupère le mot de passe stocke la valeur retournée dans le fichier tfstate en clair, il est donc très important de protéger votre fichier tfstate avec comme nous l’avons vu un peu plus haut, l’utilisation d’un remote backend sécurisé.
Conclusion
Les accès d’authentification, le fichier tfstate et les données sensibles sont des éléments qui peuvent, s’ils sont mal sécurisés, nuires à votre déploiement et même à votre infrastructure et aux données de vos applications.
Nous avons vu que Terraform possède beaucoup d’options pour les sécuriser et les protéger, il est donc recommandé de les mettre en place et de les appliquer.