Vous utilisez peut-être SOPS pour gérer vos secrets dans vos dépôts Git, peut-être même déjà avec Ansible ? Mais est-ce que vous vous simplifiez vraiment la vie ? Je vais vous présenter une structure de projet et une méthode qui tend vers une logique GitOps.
Structure du projet
Avec Ansible, il y a des structures recommandées pour les fichiers et moi je vous recommande la version alternative : cela permet en spécifiant juste le dossier d’inventaire d’utiliser les bons hosts et les variables qui vont avec :
inventories/
production/
hosts # inventory file for production servers
group_vars/
group1.yaml # here we assign variables to particular groups
group2.yaml
host_vars/
hostname1.yaml # here we assign variables to particular systems
hostname2.yaml
staging/
hosts # inventory file for staging environment
group_vars/
group1.yaml # here we assign variables to particular groups
group2.yaml
host_vars/
stagehost1.yaml # here we assign variables to particular systems
stagehost2.yaml
library/
module_utils/
filter_plugins/
site.yaml
webservers.yaml
dbservers.yaml
roles/
common/
webtier/
monitoring/
fooapp/
La structure alternative recommandée par Ansible
Configuration pour intégrer SOPS à Ansible
Si vous voulez utiliser des fichiers de variables chiffrés avec SOPS, il va vous falloir plusieurs choses :
- Installer de la collection communautaire Ansible, qui contient
community.sops.sops:
ansible-galaxy collection install community.general
- Activer le plugin
community.sops.sopsdans le fichier de configurationansible.cfg:
# ansible.cfg
[defaults]
vars_plugins_enabled = host_group_vars,community.sops.sops
# Au passage je definis mon environnement par défaut, le dev
inventory = ./environments/dev
Un fichier .sops.yaml à la racine du projet :
# .sops.yaml
creation_rules:
- path_regex: "environments/prod/.*.sops.yaml"
key_groups:
- age:
- "agekey_user1"
- path_regex: "environments/dev/.*.sops.yaml"
key_groups:
- age:
- "agekey_user1"
- "agekey_user2"
- path_regex: "environments/common/.*.sops.yaml"
key_groups:
- age:
- "agekey_user1"
- "agekey_user2"
Là j’ai fais le choix d’utiliser des clés age pour le chiffrement/déchiffrement, mais vous pouvez utiliser GPG ou encore un vault.
Cela me permet d’avoir une seule source de vérité pour mon infra. Si je dois rollback quelque chose cela m’évite de modifier de la configuration en plusieurs endroits (c’est à peu presque du GitOps).
Si vous voulez générer une nouvelle paire de clés et qu’elle soit utilisée par SOPS, une fois age installé :
mkdir -p $HOME/.config/sops/age/
echo '# name: my new age key' >> $HOME/.config/sops/age/keys.txt
age-keygen | tee >> $HOME/.config/sops/age/keys.txt
Vous pourrez ensuite créer votre structure de dossier comme ceci :
environments/
dev/
hosts
group_vars/all/
main.yaml
main.sops.yaml
prod/
hosts
group_vars/all/
main.yaml
main.sops.yaml
En utilisant sops environments/dev/group_vars/all/main.sops.yaml pour les fichiers chiffrés.
Le fichier hosts est votre inventaire dans le format de votre choix (ça peut même être un inventaire dynamique)
J’ai appelé les fichiers de variables main, mais vous pouvez les nommer comme bon vous semble,
faites juste attention à respecter vos règles de création définies dans .sops.yaml
Variables communes à plusieurs environnements
Dans .sops.yaml, vous avez vu la référence à un dossier common.
C’est pratique pour définir des variables partagées entre tous les environnements.
Je vais donc créer un dossier common avec dedans deux fichier, un chiffré un en clair.
mkdir -p environments/common/group_vars/all/
touch environments/common/group_vars/all/000_common.yaml
sops environments/common/group_vars/all/000_common.sops.yaml
Le préfixe 000 garantit que ces fichiers sont lus en premier par Ansible (ordre alphanumérique : 0→9→a→z).
Ensuite, je veux que ces fichiers soient pris en compte dans mes autres environnements, je vais avoir recours à des liens symboliques !
ln -sf environments/common/group_vars/all/* environments/dev/group_vars/all/
ln -sf environments/common/group_vars/all/* environments/prod/group_vars/all/
Et voilà à quoi va ressembler la structure finale :
environments/
common/
group_vars/all/
000_common.yaml
000_common.sops.yaml
dev/
hosts
group_vars/all/
000_common.yaml --> `../../../common/group_vars/all/000_common.yaml`
000_common.sops.yaml `../../../common/group_vars/all/000_common.sops.yaml`
main.yaml
main.sops.yaml
prod/
hosts
group_vars/all/
000_common.yaml --> `../../../common/group_vars/all/000_common.yaml`
000_common.sops.yaml `../../../common/group_vars/all/000_common.sops.yaml`
main.yaml
main.sops.yaml
Vous pouvez ensuite utiliser le dossier au complet avec vos playbooks !
# Déployer en dev (par défaut, ansible.cfg)
ansible-playbook deploy.yml
# Déployer en production
ansible-playbook -i environments/prod deploy.yml
Conclusion
Il existe mille façons d’organiser un projet Ansible et d’utiliser SOPS. J’aime cette approche car elle est simple : un dossier = un environnement.
Libre à vous ensuite de :
- créer plus de dossiers par groupe de machines
- séparer par brique applicative
- ou affiner par rôle
Bref, adaptez selon vos besoins 😗