Aller au contenu

Sécurité Ansible

Gérer les secrets avec Ansible Vault et OpenBao pour ne jamais stocker de données sensibles en clair.


Ansible Vault

Ansible Vault permet de chiffrer des fichiers ou des variables directement dans le dépôt Git. Les fichiers chiffres sont stockes au format AES-256 et peuvent être commites sans risque.

Commandes essentielles

# Chiffrer un fichier existant
ansible-vault encrypt group_vars/production/vault.yml

# Creer un nouveau fichier chiffre directement
ansible-vault create group_vars/production/vault.yml

# Modifier un fichier chiffre (dechiffrement temporaire en memoire)
ansible-vault edit group_vars/production/vault.yml

# Dechiffrer un fichier sur place (deconseille en production)
ansible-vault decrypt group_vars/production/vault.yml

# Changer le mot de passe de chiffrement
ansible-vault rekey group_vars/production/vault.yml

# Chiffrer une seule variable inline
ansible-vault encrypt_string 'MonMotDePasseSecret123' --name 'vault_db_password'

Utiliser Vault dans un playbook

# Demander le mot de passe interactivement
ansible-playbook site.yml --ask-vault-pass

# Utiliser un fichier contenant le mot de passe
ansible-playbook site.yml --vault-password-file ~/.vault_pass

Fichier de mot de passe Vault

Ne jamais committer le fichier de mot de passe Vault dans Git. L'ajouter dans .gitignore :

.vault_pass
*.vault_pass
.vault_pass_*

En CI/CD, stocker le mot de passe dans les secrets du pipeline (GitHub Actions secrets, GitLab CI variables protegees) et l'injecter via une variable d'environnement ou un fichier temporaire créé à la volee.

Organisation recommandee

Séparer les variables en clair des variables chiffrees, un fichier par niveau d'inventaire :

group_vars/
├── all/
│   ├── vars.yml        # variables communes en clair
│   └── vault.yml       # secrets communs (chiffres)
├── production/
│   ├── vars.yml        # variables de production en clair
│   └── vault.yml       # secrets de production (chiffres)
└── staging/
    ├── vars.yml        # variables de staging en clair
    └── vault.yml       # secrets de staging (chiffres)
# group_vars/production/vars.yml
db_host: db.production.example.com
db_port: 5432
db_user: app_user
db_password: "{{ vault_db_password }}"   # reference la variable vault

api_endpoint: https://api.example.com
api_key: "{{ vault_api_key }}"
# group_vars/production/vault.yml (chiffre avec ansible-vault)
vault_db_password: "MonMotDePasseSecret123"
vault_api_key: "sk-abcdef1234567890"
vault_smtp_password: "MotDePasseSMTP!"

Le préfixe vault_ permet d'identifier immédiatement les variables sensibles sans avoir a dechiffrer le fichier.


Vault IDs

Les Vault IDs permettent de gérer plusieurs mots de passe Vault en parallèle, un par environnement. Cela applique le principe de moindre privilege : une compromission du mot de passe de staging ne compromet pas la production.

Chiffrer avec un Vault ID spécifique

# Chiffrer le vault de production avec son propre ID
ansible-vault encrypt group_vars/production/vault.yml \
    --vault-id prod@prompt

# Chiffrer le vault de staging avec un fichier de mot de passe
ansible-vault encrypt group_vars/staging/vault.yml \
    --vault-id dev@~/.vault_pass_dev

Exécuter un playbook avec plusieurs Vault IDs

# Fournir les deux mots de passe lors de l'execution
ansible-playbook site.yml \
    --vault-id prod@prompt \
    --vault-id dev@~/.vault_pass_dev

# En CI/CD avec des fichiers de mots de passe distincts
ansible-playbook site.yml \
    --vault-id prod@/run/secrets/vault_prod \
    --vault-id dev@/run/secrets/vault_dev

Quand utiliser les Vault IDs

Préférer les Vault IDs dans les cas suivants :

  • Plusieurs environnements (dev, staging, prod) avec des équipes d'accès différentes
  • Rotation indépendante des mots de passe par environnement
  • Pipelines CI/CD multi-environnements avec des secrets distincts

Pour un projet simple mono-environnement, un seul mot de passe Vault suffit.


OpenBao

OpenBao est un fork open source de HashiCorp Vault, maintenu par la Linux Foundation après le changement de licence de Vault en 2023. Il offre une API entièrement compatible et une gestion centralisee des secrets avec audit des accès, rotation automatique et contrôle d'accès granulaire.

Installation

# Linux : telecharger le binaire depuis GitHub Releases
wget https://github.com/openbao/openbao/releases/download/v2.0.0/bao_2.0.0_linux_amd64.zip
unzip bao_2.0.0_linux_amd64.zip
sudo mv bao /usr/local/bin/
bao version

# macOS via Homebrew
brew install openbao

Démarrer un serveur de développement

Le mode dev démarré un serveur en mémoire, non persistant. Suffisant pour tester l'intégration Ansible.

bao server -dev -dev-root-token-id="root"

# Dans un autre terminal, configurer les variables d'environnement
export BAO_ADDR='http://127.0.0.1:8200'
export BAO_TOKEN='root'

# Verifier que le serveur repond
bao status

Stocker et lire des secrets (KV v2)

# Activer le moteur de secrets KV version 2
bao secrets enable -path=secret kv-v2

# Stocker un secret avec plusieurs champs
bao kv put secret/ansible/database \
    password="MonMotDePasseSecret123" \
    user="app_user" \
    host="db.example.com"

# Lire l'ensemble des champs d'un secret
bao kv get secret/ansible/database

# Lire uniquement un champ specifique
bao kv get -field=password secret/ansible/database

# Mettre a jour un seul champ sans ecraser les autres
bao kv patch secret/ansible/database password="NouveauMotDePasse!"

Intégration avec Ansible

OpenBao partage la même API REST que HashiCorp Vault. Le plugin community.hashi_vault est directement compatible sans modification.

# Installer la collection Ansible
ansible-galaxy collection install community.hashi_vault

# Installer le client Python requis par le plugin
pip install hvac
# Recuperer un secret depuis OpenBao dans un playbook
- name: Recuperer les credentials de base de donnees
  ansible.builtin.set_fact:
    db_password: "{{ lookup('community.hashi_vault.hashi_vault',
      'secret/data/ansible/database:password',
      url='http://vault.example.com:8200',
      token=lookup('ansible.builtin.env', 'BAO_TOKEN')
    ) }}"
  no_log: true

- name: Configurer la base de donnees
  ansible.builtin.template:
    src: templates/db.conf.j2
    dest: /etc/app/db.conf
    owner: app
    group: app
    mode: '0600'
  no_log: true

Politiques et tokens

Créer des tokens avec des permissions minimales pour Ansible :

# Creer une politique limitee en lecture seule sur le chemin ansible/
bao policy write ansible-readonly - <<EOF
path "secret/data/ansible/*" {
  capabilities = ["read"]
}
path "secret/metadata/ansible/*" {
  capabilities = ["list"]
}
EOF

# Creer un token avec cette politique et une TTL limitee
bao token create \
    -policy=ansible-readonly \
    -ttl=24h \
    -display-name="ansible-pipeline"

# Verifier les capacites du token sur un chemin
bao token capabilities secret/data/ansible/database

Méthodes d'authentification

Méthode Usage typique
Token Usage direct, scripts simples, tests
AppRole Applications et pipelines CI/CD
Kubernetes Pods Kubernetes via service account JWT
LDAP Authentification utilisateurs enterprise

OpenBao vs Ansible Vault : quand choisir lequel

  • Ansible Vault : solution simple et intégrée, adaptée aux secrets statiques versiones avec le code. Aucun serveur a opérer. Pas de rotation automatique ni de journal d'audit. Recommande pour les projets de taille modérée avec une équipe réduite.
  • OpenBao : solution centralise avec audit complet, rotation automatique des secrets, revocation granulaire et accès multi-équipes. Indispensable pour les environnements multi-projets, les architectures microservices ou les contextes soumis à des exigences de conformité (PCI-DSS, ISO 27001).

Autres sources de secrets externes

Ansible propose des plugins de lookup pour intégrer les principaux gestionnaires de secrets du marche :

Source Plugin Ansible
OpenBao / HashiCorp Vault community.hashi_vault.hashi_vault
AWS Secrets Manager amazon.aws.aws_secret
GCP Secret Manager google.cloud.gcp_secret_manager
Azure Key Vault azure.azcollection.azure_keyvault_secret

Dans tous les cas, ajouter no_log: true sur les tâches qui récupèrent ou utilisent ces secrets.


Sécurité d'exécution

Permissions sur les fichiers déployés

Toujours définir explicitement owner, group et mode sur les fichiers déployés. Ne jamais laisser Ansible utiliser les valeurs par défaut du système.

- name: Deployer le fichier de configuration applicative
  ansible.builtin.template:
    src: templates/app.conf.j2
    dest: /etc/app/app.conf
    owner: app
    group: app
    mode: '0640'

- name: Deployer une cle privee
  ansible.builtin.copy:
    content: "{{ vault_private_key }}"
    dest: /etc/app/private.key
    owner: app
    group: app
    mode: '0600'
  no_log: true

Limiter become au niveau de la tâche

Éviter become: true au niveau du play sauf si toutes les tâches du play necessitent les privileges root. Préférer l'elevation au niveau de la tâche pour limiter la surface d'exposition.

# Mauvaise pratique : become au niveau du play
- name: Configurer le serveur
  hosts: webservers
  become: true          # toutes les taches s'executent en root, meme si inutile
  tasks:
    - name: Installer nginx
      ansible.builtin.apt:
        name: nginx
        state: present

    - name: Deployer la config applicative
      ansible.builtin.template:
        src: app.conf.j2
        dest: /etc/app/app.conf

# Bonne pratique : become uniquement sur les taches qui en ont besoin
- name: Configurer le serveur
  hosts: webservers
  tasks:
    - name: Installer nginx
      ansible.builtin.apt:
        name: nginx
        state: present
      become: true          # elevation uniquement pour cette tache

    - name: Deployer la config applicative
      ansible.builtin.template:
        src: app.conf.j2
        dest: /etc/app/app.conf
        owner: app
        group: app
        mode: '0640'
        # pas de become : l'utilisateur Ansible a les droits suffisants

Masquer les secrets dans les logs

Utiliser no_log: true sur toute tâche qui manipule des données sensibles. Ansible affiche par défaut les parametres des tâches dans la sortie standard.

- name: Configurer le mot de passe de la base de donnees
  ansible.builtin.lineinfile:
    path: /etc/app/db.conf
    regexp: '^DB_PASSWORD='
    line: "DB_PASSWORD={{ vault_db_password }}"
  no_log: true          # empeche l'affichage du mot de passe dans les logs

- name: Creer l'utilisateur applicatif avec mot de passe
  ansible.builtin.user:
    name: app_user
    password: "{{ vault_app_user_password | password_hash('sha512') }}"
    state: present
  no_log: true

Préférer les clés SSH aux mots de passe

Déployer les clés SSH autorisees plutôt que de distribuer des mots de passe. Le module ansible.posix.authorized_key géré le fichier ~/.ssh/authorized_keys de façon idempotente.

- name: Deployer la cle SSH de l'utilisateur de deploiement
  ansible.posix.authorized_key:
    user: deploy
    state: present
    key: "{{ lookup('ansible.builtin.file', '~/.ssh/id_ed25519.pub') }}"

- name: S'assurer que seules les cles autorisees sont presentes
  ansible.posix.authorized_key:
    user: deploy
    state: present
    key: "{{ lookup('ansible.builtin.file', 'files/deploy_keys/{{ inventory_hostname }}.pub') }}"
    exclusive: true      # supprime toute autre cle non listee

- name: Desactiver l'authentification par mot de passe SSH
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^PasswordAuthentication'
    line: 'PasswordAuthentication no'
    validate: 'sshd -t -f %s'
  become: true
  notify: Redemarrer sshd

Validation avant application

Utiliser le parametre validate des modules ansible.builtin.template et ansible.builtin.lineinfile pour les fichiers de configuration système critiques (sshd, sudoers, nginx). Une configuration invalide peut rendre un serveur inaccessible.

- name: Configurer sudoers pour l'utilisateur deploy
  ansible.builtin.template:
    src: templates/sudoers_deploy.j2
    dest: /etc/sudoers.d/deploy
    owner: root
    group: root
    mode: '0440'
    validate: 'visudo -cf %s'
  become: true