La gestion des secrets dans un projet est loin d’être l’étape la plus triviale. Aujourd’hui nous allons voir comment on peut stocker des secrets à l’intérieur d’un dépôt git sans compromettre la sécurité. Pour cela ces secrets seront chiffrés avant d’être commités dans le dépôt.

Dans une seconde partie, nous mettrons en place un pipeline avec GitLab-CI dans lequel nous verrons comment déchiffrer ces secrets.

sops et age

Pour gérer le chiffrement et le déchiffrement des secrets, nous nous appuyons sur les deux outils suivants :

  • age, est un outil de chiffrement moderne comparable à gpg mais avec un format de clés plus simple à manipuler.

  • sops, abréviation de Secret OPerationS, est un outil de la fondation Mozilla permettant de piloter le chiffrement ou déchiffrement de fichiers complets ou de sous-parties de fichiers à l’aide de différents formats de clés : AWS KMS, GCP KMS, Azure Key Vault, age, and PGP.

Afin de se simplifier la vie, nous allons nous servir de l’image Docker xian310/encryption-toolbox qui contient déjà les deux outils.

Générer des clés au format age

La première étape consiste à générer des clés avec age-keygen :

docker run -ti xian310/encryption-toolbox age-keygen > key.txt

Le fichier généré contient une clé privé et une clé publique :

$ cat key.txt 
# created: 2022-10-25T20:39:09Z
# public key: age16wx32x2p2n20df5q74sq2e4smtdpk248tt67uqw6xjdg7p7xgeqsqrgm5u
AGE-SECRET-KEY-1Y0HJ40LHPF6ZHQMQ9MXVDNJPCW8WLYWRGSNY09WFM9VPEXZWVPUQLHKM3W

Attention à ne jamais commiter ce fichier dans le dépôt git, il permettrait à n’importe qui de déchiffrer vos données confidentielles.

La clé publique va permettre de chiffrer les données et la clé privée sera nécessaire pour les déchiffrer.

Mise en place des pré-requis au chiffrement

Afin d’éviter de devoir passer des paramètres sur la ligne de commande lorsque nous allons invoquer la commande sops, nous pouvons créer un fichier de configuration pour sops, nommé .sops.yaml, avec le contenu suivant :

creation_rules:
- path_regex: .
  age: age16wx32x2p2n20df5q74sq2e4smtdpk248tt67uqw6xjdg7p7xgeqsqrgm5u

ou :

  • path_regex indique que la méthode de chiffrement définie sera utilisé pour tous les fichiers du répertoire courant.
  • age indique que le chiffrement sera réalisé avec age et la valeur correspond à la clé publique générée précédemment.

Ensuite il faut exporter la variable SOPS_AGE_KEY_FILE avec comme valeur le nom du fichier généré tout à l’heure :

export SOPS_AGE_KEY_FILE=key.txt

Et on est bon !

Création du secret

Nous pouvons maintenant créer un fichier contenant les secrets, à l’aide de la commande suivante :

sops secrets.env

Ceci ouvre un éditeur dans lequel nous remplaçons les lignes proposées en exemple par les lignes suivantes :

username=bob
password="321 aZeRtY!!!"

Enregistrer le fichier. Si on regarde maintenant le contenu du fichier on remarque son contenu a été chiffré :

$ cat secrets.env 
username=ENC[AES256_GCM,data:LHLi,iv:xUsB41CMbdNplOX52NIvm0ayKzIZDHV3xPs9el5EPCA=,tag:5wtLmKbclqf4huleRSA8+g==,type:str]
password=ENC[AES256_GCM,data:lm8rgy8VafGO7M3YOyIn,iv:RgHp0U9vMxjq9428z+uawS6SxyVGJj7G3mbHIxr98rs=,tag:sa5kwvTG4nCOQT+/7Zx9QA==,type:str]
sops_age__list_0__map_recipient=age16wx32x2p2n20df5q74sq2e4smtdpk248tt67uqw6xjdg7p7xgeqsqrgm5u
sops_lastmodified=2022-10-26T09:26:28Z
sops_unencrypted_suffix=_unencrypted
sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWdHJzMXdKYmt1WGpIUklD\ncytad05LcThCRjMzUlhjQnFySUhDN2dBUmxrCkhSUkQzY1hVUzlpUVFjZVVNQ0Y3\nN1VMZHJDTW9ubDB5MTVHUGIxK2RZQ00KLS0tIHhyZy9icVFYTnZIYmcya0EvUDVV\neUxTUGFrMVBvUUpVSHVObEZBMDBUNmcKvHFodm8IJXcvONPcVpNyEIxcyKQCXEc0\nSZzc18RaGOFfbrERLacsqnXhvDirRRCnEnlQ6NTysqG9zgfTavbI+w==\n-----END AGE ENCRYPTED FILE-----\n
sops_mac=ENC[AES256_GCM,data:ZKdV+UNjQkLufkbiudGz/2qcgZey88rwXS/0TXTPFFxRWYbCCZYNQyPFSpTpOPhenRIBGiCucKKyEyC+chx0rOo8YC4Fw+2DPPyFist0Fm8MWO2FsUV/fVUCnHVj5H7nrjbt3RCmapMTXFV5bfrVHGanmqctUCvzT+eoulc5k+M=,iv:ufOMax3BAHOenSxl3ihxm5TZ/RUgQdaRcp6tbjn2NYI=,tag:yp4l6nr7Cn5xxiL2OtxvKQ==,type:str]
sops_version=3.7.3

En regardant de plus près on peut constater que sops a déduit en s’appuyant sur l’extension du fichier (.env) que nous voulions chiffrer un fichier contenant des variables d’environnement, les noms des variables sont toujours affichés en clair mais les valeurs ont été chiffrées.

Sops est en effet capable de s’adapter à différents types de fichiers : yaml, json, env, ini et binary de manière à en chiffrer intelligement certaines sous-parties seulement.

Le fichier secrets.env est donc désormais chiffré, il devient désormais possible de le commiter sans danger dans le dépôt git.

Utilisation avec GitLab-CI

Dans cette seconde partie, nous allons créer un pipeline dans lequel nous allons exploiter le fichier de secrets créé précedemment.

Importer la clé privée age dans une variable GitLab-CI

Le pipeline doit avoir un moyen de déchiffrer le fichier contenant les secrets, de manière à pouvoir les exploiter. Pour cela il faut dans un premier temps créer une variable de projet pour stocker la clé age :

Dans le projet GitLab, aller sur Settings > CI/CD > Variables, cliquer sur Expand et ajouter une nouvelle variable avec les paramètres ci-dessous en cliquant sur Add variable :

  • Key :

    SOPS_AGE_KEY_FILE
    
  • Value :

    # created: 2022-10-25T20:39:09Z
    # public key: age16wx32x2p2n20df5q74sq2e4smtdpk248tt67uqw6xjdg7p7xgeqsqrgm5u
    AGE-SECRET-KEY-1Y0HJ40LHPF6ZHQMQ9MXVDNJPCW8WLYWRGSNY09WFM9VPEXZWVPUQLHKM3W
    
  • Type :

    File
    

Ceci va mettre à disposition de chaque job du pipeline un fichier contenant la clé age spécifiée dans le champ Value. Le chemin vers ce fichier sera défini dans la variable d’environnement nommée SOPS_AGE_KEY_FILE ce qui permettra à sops de retrouver l’emplacement de la clé.

Créer un pipeline

Ajoutons ensuite un fichier .gitlab-ci.yml dans lequel nous spécifions le pipeline suivant :

stages:
  - test

decrypt:
  image: xian310/encryption-toolbox
  stage: test
  script:
    - sops --version
    - age --version
    - ls -l $SOPS_AGE_KEY_FILE
    - sops -d secrets.env > secrets.decrypted.env
    - source secrets.decrypted.env
    - echo "Bonjour $username, votre mot de passe est '$password'"

Ici on utilise la commande sops -d secrets.env pour demander à sops de déchiffrer le fichier de secrets.

Une fois le fichier commité et poussé sur le dépôt, un nouveau pipeline est créé contenant un unique job nommé decrypt.

Si on examine les logs du job, on constate que le fichier secrets.env a correctement été déchiffré, et que le job peut donc exploiter les secrets en clair pour en faire ce qu’il veut ! :

...
Bonjour bob, votre mot de passe est '321 aZeRtY!!!'
...

Et voilà !


Liens utiles :