Playbook Ansible - Déploiement SSH Sécurisé
Contexte
Ce playbook automatise le durcissement de l'authentification SSH sur l'ensemble des serveurs Linux de l'infrastructure. Il désactive l'authentification par mot de passe et impose l'utilisation de clés publiques, pour les utilisateurs réels (simon, wassim, david) ainsi que pour l'utilisateur de liaison ansible.
Les clés publiques des utilisateurs sont stockées sur le serveur bdd dans le répertoire /home/bdd/clefssh. Le playbook les récupère depuis cette source centrale avant de les déployer sur chaque hôte.
Prérequis
Avant d'exécuter ce playbook, les conditions suivantes doivent être réunies :
- Le fichier d'inventaire
/etc/ansible/hostsest à jour avec tous les serveurs cibles - L'utilisateur
ansibledispose des droitssudosans mot de passe sur tous les hôtes (via/etc/sudoers.d/ansible) - Les clés publiques des utilisateurs sont déposées sur
bdddans/home/bdd/clefssh/ - La clé publique d'Ansible (
ansible.pub) est également présente dans ce répertoire
Fichiers de configuration
Inventaire /etc/ansible/hosts
L'inventaire recense tous les serveurs cibles. Chaque hôte déclare son IP, l'utilisateur Ansible de connexion (ansible_user=ansible) et l'utilisateur réel du système (user_reel).
[local]
127.0.0.1 ansible_connection=local
[serveurs]
bdd ansible_host=10.10.120.7 user_reel=bdd ansible_user=ansible
reverse-proxy ansible_host=10.10.120.80 ansible_user=ansible user_reel=user
reverse-proxy-sec ansible_host=10.10.120.90 ansible_user=ansible user_reel=user
web ansible_host=10.10.120.11 ansible_user=ansible user_reel=user
dns-autorite ansible_host=10.10.120.8 ansible_user=ansible user_reel=user
dns-autorite-sec ansible_host=10.10.120.18 ansible_user=ansible user_reel=user
dns-resolver ansible_host=10.10.120.9 user_reel=user ansible_user=ansible
dns-resolver-sec ansible_host=10.10.120.19 user_reel=user ansible_user=ansible
ca-autorite ansible_host=10.10.120.12 user_reel=certificat ansible_user=ansible
docker ansible_host=10.10.120.15 ansible_user=ansible user_reel=user
graylog ansible_host=10.10.120.16 ansible_user=ansible user_reel=user
datanode ansible_host=10.10.120.17 ansible_user=ansible user_reel=user
proxmox ansible_host=10.10.120.50 ansible_user=root
shop ansible_host=10.10.120.21 ansible_user=ansible user_reel=user
radius ansible_host=10.10.120.22 ansible_user=ansible user_reel=user
slam2 ansible_host=10.10.120.100 ansible_user=ansible user_reel=user
[all_linux:children]
serveurs
[all_linux:vars]
ansible_python_interpreter=/usr/bin/python3
Sudoers /etc/sudoers.d/ansible
L'utilisateur ansible doit pouvoir exécuter des commandes en tant que root sans mot de passe sur chaque machine cible. Ce fichier doit être présent sur chaque serveur de l'infrastructure avant le premier lancement du playbook.
La création de ce fichier se fait manuellement lors de l'ajout d'un nouveau serveur, avant toute exécution de playbook sur cette machine. Utiliser visudo pour éviter les erreurs de syntaxe :
Coller la ligne suivante :
Vérifier que le fichier est bien pris en compte :
La sortie doit contenir (ALL) NOPASSWD: ALL.
Attention : ne jamais éditer ce fichier avec
nanoouvimdirectement. Une erreur de syntaxe dans un fichier sudoers peut bloquer tous les accès sudo sur la machine. Toujours passer parvisudo.
Configuration SSH résultante /etc/ssh/sshd_config.d/01-ssh-secure.conf
C'est le fichier déployé par le playbook sur chaque serveur. Il désactive l'authentification par mot de passe et active uniquement l'authentification par clé :
Playbook ssh_secure.yml
Variables
| Variable | Valeur | Description |
|---|---|---|
clefs_users |
[simon.pub, wassim.pub, david.pub] |
Clés publiques des utilisateurs réels |
clef_ansible |
ansible.pub |
Clé publique de l'utilisateur ansible |
clef_dir |
/home/bdd/clefssh |
Répertoire source des clés sur bdd |
sshd_conf_file |
/etc/ssh/sshd_config.d/01-keyonly.conf |
Fichier de configuration SSH déployé |
Déroulement des tâches
Le playbook s'exécute sur le groupe serveurs avec remote_user: ansible et gather_facts: no.
1. Récupération des clés depuis BDD
Les deux premières tâches utilisent ansible.builtin.slurp avec delegate_to: bdd et run_once: true pour lire les fichiers de clés sur le serveur BDD sans les copier localement.
- name: Récupérer les clés SSH des utilisateurs depuis la BDD
ansible.builtin.slurp:
src: "{{ clef_dir }}/{{ item }}"
register: ssh_keys_users
loop: "{{ clefs_users }}"
delegate_to: bdd
become: yes
run_once: true
- name: Récupérer la clé SSH ansible depuis la BDD
ansible.builtin.slurp:
src: "{{ clef_dir }}/{{ clef_ansible }}"
register: ssh_key_ansible
delegate_to: bdd
become: yes
run_once: true
2. Configuration de l'utilisateur ansible
Création du répertoire ~/.ssh pour l'utilisateur ansible avec les permissions appropriées, puis ajout de sa clé publique dans authorized_keys :
- name: Créer ~/.ssh pour l'utilisateur ansible
ansible.builtin.file:
path: "/home/ansible/.ssh"
state: directory
mode: "0700"
owner: ansible
group: ansible
- name: Ajouter uniquement la clé ansible à l'utilisateur ansible
ansible.posix.authorized_key:
user: ansible
key: "{{ ssh_key_ansible.content | b64decode }}"
state: present
3. Configuration de l'utilisateur réel
Même logique pour l'utilisateur réel (défini par user_reel dans l'inventaire). Les trois clés (simon, wassim, david) sont ajoutées en boucle :
- name: Créer ~/.ssh pour l'utilisateur réel
ansible.builtin.file:
path: "/home/{{ user_reel }}/.ssh"
state: directory
mode: "0700"
owner: "{{ user_reel }}"
group: "{{ user_reel }}"
become: yes
- name: Ajouter les clés de simon, wassim, david à l'utilisateur réel
ansible.posix.authorized_key:
user: "{{ user_reel }}"
key: "{{ item.content | b64decode }}"
state: present
loop: "{{ ssh_keys_users.results }}"
become: yes
4. Durcissement SSH
Création du répertoire sshd_config.d si absent, puis dépôt du fichier de configuration qui désactive l'authentification par mot de passe :
- name: Créer le répertoire de configuration SSH si nécessaire
ansible.builtin.file:
path: /etc/ssh/sshd_config.d
state: directory
owner: root
group: root
mode: "0755"
become: yes
- name: Forcer authentification par clé
ansible.builtin.copy:
content: |
PasswordAuthentication no
PubkeyAuthentication yes
dest: "{{ sshd_conf_file }}"
owner: root
group: root
mode: "0644"
become: yes
5. Redémarrage SSH
Redémarrage du service SSH pour appliquer la nouvelle configuration :
Exécution
Depuis le serveur Ansible :
Pour cibler un seul hôte :
Pour effectuer un dry-run avant d'appliquer :
Vérification post-déploiement
Sur un serveur cible, vérifier que le fichier a bien été créé :
Résultat attendu :
Tester que la connexion par clé fonctionne depuis le serveur Ansible :
Tenter une connexion par mot de passe depuis un poste tiers doit être refusée.
Points d'attention
- Le serveur
proxmoxutiliseansible_user=rootet n'a pas de variableuser_reel. Les tâches utilisantuser_reelsont commentées avec unwhen: inventory_hostname != 'bdd'ou similaire pour les cas particuliers. - Le serveur
bddest à la fois source des clés (viadelegate_to) et hôte cible du playbook. Les tâches de déploiement surbddlui-même sont actives, les conditionswhencorrespondantes ayant été désactivées. - La valeur
run_once: truesur les tâchesslurpgarantit que la lecture des clés sur BDD n'est effectuée qu'une seule fois, même si le playbook tourne sur de nombreux hôtes.
Playbook complet ssh_secure.yml
Fichier prêt à copier-coller dans /etc/ansible/playbooks/ssh_secure.yml :
---
- hosts: serveurs
gather_facts: no
remote_user: ansible
vars:
clefs_users:
- simon.pub
- wassim.pub
- david.pub
clef_ansible: ansible.pub
clef_dir: /home/bdd/clefssh
sshd_conf_file: /etc/ssh/sshd_config.d/01-keyonly.conf
tasks:
# -------------------------------------------------------
# Récupération des clés depuis le serveur BDD
# -------------------------------------------------------
- name: Récupérer les clés SSH des utilisateurs depuis la BDD
ansible.builtin.slurp:
src: "{{ clef_dir }}/{{ item }}"
register: ssh_keys_users
loop: "{{ clefs_users }}"
delegate_to: bdd
become: yes
run_once: true
- name: Récupérer la clé SSH ansible depuis la BDD
ansible.builtin.slurp:
src: "{{ clef_dir }}/{{ clef_ansible }}"
register: ssh_key_ansible
delegate_to: bdd
become: yes
run_once: true
# -------------------------------------------------------
# Configuration pour l'utilisateur ansible
# -------------------------------------------------------
- name: Créer ~/.ssh pour l'utilisateur ansible
ansible.builtin.file:
path: "/home/ansible/.ssh"
state: directory
mode: "0700"
owner: ansible
group: ansible
- name: Ajouter uniquement la clé ansible à l'utilisateur ansible
ansible.posix.authorized_key:
user: ansible
key: "{{ ssh_key_ansible.content | b64decode }}"
state: present
# -------------------------------------------------------
# Configuration pour l'utilisateur réel - via sudo depuis ansible
# -------------------------------------------------------
- name: Créer ~/.ssh pour l'utilisateur réel
ansible.builtin.file:
path: "/home/{{ user_reel }}/.ssh"
state: directory
mode: "0700"
owner: "{{ user_reel }}"
group: "{{ user_reel }}"
become: yes
- name: Ajouter les clés de simon, wassim, david à l'utilisateur réel
ansible.posix.authorized_key:
user: "{{ user_reel }}"
key: "{{ item.content | b64decode }}"
state: present
loop: "{{ ssh_keys_users.results }}"
become: yes
# -------------------------------------------------------
# Durcissement SSH
# -------------------------------------------------------
- name: Créer le répertoire de configuration SSH si nécessaire
ansible.builtin.file:
path: /etc/ssh/sshd_config.d
state: directory
owner: root
group: root
mode: "0755"
become: yes
- name: Forcer authentification par clé
ansible.builtin.copy:
content: |
PasswordAuthentication no
PubkeyAuthentication yes
dest: "{{ sshd_conf_file }}"
owner: root
group: root
mode: "0644"
become: yes
# -------------------------------------------------------
# Redémarrage SSH
# -------------------------------------------------------
- name: Redémarrer SSH
ansible.builtin.service:
name: ssh
state: restarted
become: yes