Stocker des secrets dans un dépôt Git
Éviter d’envoyer ses secrets sur Git, nous devons toujours être vigileant avant un quelconque Push. Et c’est justement le but de Sops (Secrets OPerationS) qui va nous aider à stocker nos informations sur le dépôt.. mais en les chiffrant.
Celui-ci est compatible avec de nombreux gestionnaires de secret comme :
- Hashicorp Vault
- GCP KMS
- PGP
- Age (Celui que nous allons utiliser)
Astuce
Age est un outil en Go simple et moderne. Celui-ci propose un format qui semble être validé par de nombreux experts. .
Créer notre clé Age
Information
Vous pouvez installer Age en suivant les instructions sur le dépôt officiel ici
Nous allons donc créer notre propre clé avec age.
mkdir -p ~/.keys/
age-keygen -o ~/.keys/ma-cle
En inspectant le contenu du fichier ~/.keys/ma-cle
, nous remarquons un schéma que l’on connait bien : une clé publique, et une clé privée.
# created: 2023-02-15T07:50:20+01:00
# public key: age1220x7zmnp0j8du3vxk67a4mdkr3gqn9djjn7f7gamjclr3em7g2sxpns35
AGE-SECRET-KEY-1JY9Q0NWNRK4DCT9J3D2H0Z9D5ZY0XHV8EJ39JKKK2PW6SUH9FTFSN9T6HF
Et pour que Sops utilise cette clé, nous allons créer la variable d’environnement SOPS_AGE_KEY_FILE
dans notre ~/.bashrc
ou ~/.zshrs
.
export SOPS_AGE_KEY_FILE=~/.keys/ma-cle
Maintenant, nous pouvons passer au niveau supérieur : créer notre premier fichier de secret.
Sops, en pratique
Vous pouvez installer Sops sur un système Amd64 en suivant ces instructions:
wget https://github.com/mozilla/sops/releases/download/v3.7.3/sops-v3.7.3.linux.amd64 -O /usr/bin/sops
chmod +x /usr/bin/sops
Notre système d’authentification est déjà créé : c’est notre couple de clé AGE. Ce que nous allons faire maintenant, c’est créer un secret qui sera déchiffrable uniquement par notre clé privée.
Première chose que nous allons faire, c’est créer notre fichier .sops.yaml
.
Ce fichier permet de définir quels fichiers devront être manoeuvrés par SOPS et surtout : quels clés ont accès à ces fichiers.
# .sops.yaml
creation_rules:
- path_regex: secret.*\.ya?ml
encrypted_regex: "^(username|password)$"
key_groups:
- age:
- age1220x7zmnp0j8du3vxk67a4mdkr3gqn9djjn7f7gamjclr3em7g2sxpns35
Créons maintenant un fichier secret.dev.yml
:
username: "thebidouilleur"
password: jadorelabidouille
url: "https://thebidouilleur.xyz"
QI: 7.2
et affichons ce même fichier en le chiffrant avec sops via l’argument -e
(encrypt).
➜ sops -e secret.dev.yml
username: ENC[AES256_GCM,data:8KUxRrhWLWsbxzJqxRQ=,iv:qJQYUgCQ6wv9fmn+scJ3ui7tFD6lpoRH0qpC+n58sF8=,tag:RJdluEfMdnXy6Zhpxn2AyQ==,type:str]
password: ENC[AES256_GCM,data:dm9t60SH/4/wqy3Ww5RxaDU=,iv:ch0ZRbhN6+ouCNXzgWO63GHK9ewgOMpfJMzjYxIq8h4=,tag:EKRHJBwRV8ZRRpDMaGG4sQ==,type:str]
url: https://thebidouilleur.xyz
QI: 7.2
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age14ysm820ajay8wqslnkjqcewvq4tmeucth3a88qk4a7hl0mnwkfaqmj6xx5
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBHZU9xNklSTkRTU0p0SFV3
NE1tV2M2Wjk3SDl2a1lJWk8wQi96Yjk2eUVjCi9qSWREUkZqTFpOa1luZEFlb2lK
dHBBNDllSFlhL2cycW82SGl4bDU2YXcKLS0tIHd5aCsyK1BHT2dDTGpZWUxITE83
MFd5MlowTHNIekVXTzJWbXNuUmxGRWsKp1o+kh9lbWBLh6rZ4845c31rxowb9uX+
/a01TYbiWfn2lWmUJ+gXq0nQZxqo3iDEI+mrG+n+c79rmq6BGPYVPw==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2023-02-15T11:44:32Z"
mac: ENC[AES256_GCM,data:cUtxnG/ycha3Zk0xNrmeioeBB9SiH3U4ENbnGtkpJmM9SBOFVZGKikaDZwdk1c2aflC07kELIoN0BxspgJseCLNvA3nsTYEEjHe53zJZUaDYn7u0D1+th3XjYdU17zdx9ECN5SjExvOIDLmQ4j512/LCN+lBVi4SxaJWDqzzva0=,iv:vhrbuibyInOxcYihgMVZN8c0v05GdPXB+EbACQijg9s=,tag:GHLv+agLCXcTiUDq8gBEkA==,type:str]
pgp: []
encrypted_regex: ^(username|password)$
version: 3.7.3
Ni l’URL, ni mon QI n’ont été chiffrés. Gardez également en tête que la commande ne fait qu’afficher un output du même fichier chiffré, le fichier n’a pas été modifié.
Si on souhaite ré-écrire le fichier, il faut rajouter l’argument -i
: sops -e -i secret.dev.yml
Pour déchiffrer le fichier, il suffit de faire la même commande en changeant -e
par -d
(decrypt).
Si jamais je chiffre mon fichier secret.dev.yml
et que je change de clé, nous serons dans l’incapacité de le chiffrer :
➜ sops -d -i secret.dev.yml
Failed to get the data key required to decrypt the SOPS file.
Group 0: FAILED
age14ysm820ajay8wqslnkjqcewvq4tmeucth3a88qk4a7hl0mnwkfaqmj6xx5: FAILED
- | no age identity found in "~/.keys/ma-cle" that
| could decrypt the data
Recovery failed because no master key was able to decrypt the file. In
order for SOPS to recover the file, at least one key has to be successful,
but none were.
Pour ajouter une clé pouvant déchiffrer les fichiers, rajoutez-là dans votre .sops.yaml
.
En déchiffrant puis rechiffrant les fichiers, la nouvelle clé y aura accès.
Chiffrer avant de commiter
Mais comme Skynet (ou ChatGPT) n’a pas encore remplacé les informaticiens : nous restons humains. Il est donc obligatoire de trouver un moyen pour ne jamais oublier de chiffrer nos secrets avant de commit. Et la solution est simple : pre-commit
Remarque
Pre-Commit est un utilitaire en Python permettant d’automatiser certaines taches avant de commit votre code. Il est ainsi possible de faire votre propre CI en testant vos fichiers ou … en chiffrant vos secrets.
L’installation de pre-commit se fait avec pip
(Si vous n’êtes pas à l’aise avec Python, je vous invite à suivre cette documentation pour l’installer.).
pip install pre-commit
Astuce
Si vous avez installé pip mais que vous n’arrivez pas à le lancer car commande introuvable, c’est surement car celui-ci ne s’est pas mis dans votre $PATH. Il faudra ajouter le dossier ~/.local/bin dans votre fichier .bashrc ou .zshrc.
export PATH="$PATH:$HOME/.local/bin
Une fois pre-commit installé, il faudra l’activer dans un dossier gérer par Git:
➜ pre-commit install
pre-commit installed at .git/hooks/pre-commit
Nous allons maintenant créer le fichier .pre-commit-config.yaml
. avec le contenu suivant:
repos:
- repo: local
hooks:
- id: encrypt files
name: encrypt files
entry: .pre-commit-sops.sh
language: script
pass_filenames: true
Cette configuration permet à pre-commit d’exécuter le script .pre-commit-sops.sh
avant chaque commit. Et voici le contenu de ce fichier :
#!/bin/bash
log_file="./pre-commit.log"
for filename in "$@"
do
echo "Checking.... $filename"
if [[ "$filename" =~ ^secret\..*\.ya?ml$ ]]; then
echo "$filename matches pattern, encrypting..." >> $log_file
sops -e -i $filename
git add $filename
else
echo "$filename does not match pattern and will not be encrypted" >> $log_file
fi
done
Donc si le fichier correspond au pattern identifiant les fichiers contenants les secrets, nous le chiffrons, et nous actualisons son contenu avant de commit.
Pensez également à rendre votre fichier exécutable.
On teste ça, le fichier secret.dev.yml
devrait être chiffré après mon commit.
TheBidouilleur:~/Documents/GitOps$ cat secret.dev.yaml
username: bigusername
password: bigpassword
chiffre-pas-ça: coucou2
TheBidouilleur:~/Documents/GitOps$ git add secret.dev.yaml
TheBidouilleur:~/Documents/GitOps$ git commit -m "Je veux chiffrer mes secrets"
encrypt files............................................................Passed
[main 7ffcf4a] Je veux chiffrer mes secrets
1 file changed, 23 insertions(+), 25 deletions(-)
rewrite secret.dev.yaml (75%)
TheBidouilleur:~/Documents/GitOps$ cat secret.dev.yaml
username: ENC[AES256_GCM,data:wrK2xpPUBYE9dEo=,iv:B2KKu9Y3BeT9Cj8cgZFmxeK70ZOiJhE7wtRkYk0hY4U=,tag:MKgFQqkSOD9tCVp1PlnVOA==,type:str]
password: ENC[AES256_GCM,data:qCTcjaUbPpzzMUc=,iv:q3vmXf2YbZtSgm1vCj5tpdu+Ug4Hby9IOo2/y7zBwQI=,tag:0TvqVAIDxxPLk1GsEDKXcQ==,type:str]
chiffre-pas-ça: coucou2
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age14ysm820ajay8wqslnkjqcewvq4tmeucth3a88qk4a7hl0mnwkfaqmj6xx5
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSByTFFQMU95dVc3dkk5Y2VB
TFQ3czc5WDliNXpOV001b3FaNWdGbW82ampVCitzVGFmemFvdC9kVnF4SlBNMGdi
Yjc3ZERiMWVKZXoveFdHbVRWU3pRc1kKLS0tIDFCOTh1U0F6QmtsaG1WTElDbjRz
TXdCeVpER0h6WG9XZm93bGI2Ni83SXMKCPLM5tTUYkhjirljDcNitID/2NXbOx4y
i5/f79+ulVkWm9hx+AmNBxdbzbopJOk+/y1UmWF64y7ovPjdFy5BSg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2023-02-15T21:22:28Z"
mac: ENC[AES256_GCM,data:2PlCiFkxCl84odqZ6Qxo74VLhAe2Tqa7pUDbYJTshpG+WKYj/mN9xvfU3Pz8NNe5vWoone2PV2nLvrLQlDQEjnM5PybLRVcSDiSPHF61VfdiI4G25DTScrHWmucSxIv7JFlagVH8JOjii43oe4Ul+coVhhSn3PClIS3X22ZqjUI=,iv:mSZ6Pl0Q7RT3hSFsP0pmF77XnzNyZ6c50G2lsIw6zOU=,tag:z6RH/DDhpRk7rtigdc3mXA==,type:str]
pgp: []
encrypted_regex: ^(username|password)$
version: 3.7.3
Et maintenant.. aucun risque d’oublier de chiffrer !