ansible-hardening-linux
Configuration et durcissement de serveurs Linux — Debian 12/13, Ubuntu 22.04/24.04,
RHEL / Rocky / AlmaLinux 8 et 9 — par Ansible. Reprise du dépôt de scripts
prerequis-install-deb (Debian uniquement, copie de fichiers à la main) sous
forme de rôles idempotents, multi-distributions, avec CI/CD Gitea.
Objectif : à la fin d'un passage, tout serveur est configuré, à jour, supervisé,
et n'est joignable à distance qu'en SSH par clé publique ; l'accès local
(console, su -) continue de fonctionner comme avant.
Correspondance avec les scripts d'origine
| Script d'origine | Rôle | Ce qui change |
|---|---|---|
01-ssh (sshd_config, ssh_config, banner, authorized_keys, 00-basic) |
ssh |
Drop-in sshd_config.d/00-durcissement.conf au lieu d'écraser tout le fichier ; clé publique obligatoire (AuthenticationMethods publickey) ; AllowUsers calculé depuis la liste des comptes ; clés centralisées dans /etc/ssh/authorized_keys/<user> (le lien symbolique de l'ancien script est remplacé par des fichiers appartenant à root) ; bannière dans /etc/issue.net et /etc/issue ; MOTD 00-basic conservé, rendu portable, et équivalent profile.d sous Red Hat ; vérification d'une nouvelle connexion SSH avant de rendre la main |
02-apt (proxy + full-upgrade) |
proxy, updates |
Proxy APT et DNF, plus profile.d ; mises à jour de sécurité automatiques (unattended-upgrades / dnf-automatic) ; signalement du besoin de redémarrage |
04-agent-glpi |
glpi_agent (+ common pour le certificat) |
Installateur perl officiel, version pilotée par variable, configuration dans conf.d/10-ansible.cfg ; le certificat racine va dans le magasin système (update-ca-certificates / update-ca-trust) et sert à tout le monde, pas seulement à l'agent |
06-snmp |
snmp |
Communautés v2c par source de supervision, lues depuis le vault ; SNMPv3 disponible ; vue en lecture seule |
07-DNS |
dns |
Respecte le gestionnaire réel : systemd-resolved (drop-in), NetworkManager (dns=none puis resolv.conf), ou resolv.conf direct |
08-NRPE |
nrpe |
Installation et configuration : allowed_hosts, dont_blame_nrpe=0, commandes de base |
03-cortex, 05-manageengine |
— | Ignorés à la demande |
| (absent) | firewall |
nftables (Debian/Ubuntu) ou firewalld (Red Hat), politique drop : SSH depuis les réseaux d'administration, ICMP, SNMP/NRPE depuis la supervision |
| (absent) | fail2ban |
Bannissement des sources en échec sur SSH, backend systemd |
| (absent) | os_hardening |
sysctl, core dumps, login.defs, auditd, services et paquets inutiles |
| (absent) | common |
Contrôle de la distribution, paquets de base, fuseau, certificats racine |
Ce qui garantit que l'accès local reste possible
- Aucun mot de passe n'est verrouillé, aucun compte n'est désactivé, PAM n'est pas modifié.
PermitRootLogin prohibit-password: root n'est joignable à distance que par clé, mais son mot de passe reste valable sur la console (Proxmox, KVM, iLO…) et poursu -.- Le pare-feu ne filtre que le réseau ; la console n'est pas concernée.
- fail2ban ne bannit que des adresses IP distantes ; les réseaux d'administration sont
dans
ignoreip. - Deux garde-fous contre l'auto-verrouillage : le rôle
sshrefuse une configuration où le compte utilisé par Ansible n'aurait pas de clé ou ne serait pas dansAllowUsers, et le rôlefirewallrefuse une listefirewall_ssh_sourcesqui exclurait l'adresse depuis laquelle Ansible est connecté. Après chaque redémarrage de sshd et après chaque application du pare-feu, une nouvelle session SSH est ouverte depuis le contrôleur ; si elle échoue, le playbook s'arrête sans fermer la session courante.
Arborescence
Même organisation qu'ansible-centreon : playbook et group_vars à la racine,
inventaire et vault versionnés, collections déclarées dans collections/.
.
├── ansible.cfg
├── site.yml # point d'entrée
├── collections/requirements.yml # ansible.posix, community.general (installées par Semaphore)
├── inventory/hosts.yml # inventaire versionné
├── group_vars/all/
│ ├── main.yml # réseaux, DNS, proxy, comptes, supervision, activation des rôles
│ └── vault.yml.example # à copier vers vault.yml, chiffrer, puis versionner
├── files/
│ ├── keys/*.pub # clés publiques SSH, attribuées par ssh_users[].keys
│ └── ca/*.crt # certificats racine internes (versionnés)
├── roles/ # common, proxy, dns, updates, ssh, firewall, fail2ban,
│ # os_hardening, snmp, nrpe, glpi_agent
├── .gitea/workflows/ansible-lint.yml
└── CICD.md # chaîne Gitea (contrôle) -> Semaphore (exécution)
Mise en route
- Inventaire : renseigner
inventory/hosts.yml. Deux modes de connexion :- compte de service
ansible+ sudo (becomeest activé dansansible.cfg) ; - root par clé, comme sur les VM Terraform :
ansible_user: root,becomeest alors sans effet.
- compte de service
- Clés publiques : fichiers
*.pubà plat dansfiles/keys/, attribués aux comptes parssh_users[].keysdansgroup_vars/all/main.yml. Le compte utilisé par Ansible (root) doit y avoir la clé de Semaphore. - Certificat racine (facultatif) :
files/ca/root-ca.crtau format PEM. - Secrets :
cp group_vars/all/vault.yml.example group_vars/all/vault.yml, renseigner, puisansible-vault encrypt group_vars/all/vault.yml. - Variables : relire
group_vars/all/main.yml— en particulierfirewall_ssh_sources(réseaux d'administration),supervision_sources,dns_nameservers,proxy_url,ssh_users,glpi_agent_server. - Simulation :
ansible-playbook site.yml --check --diff --ask-vault-pass - Application :
ansible-playbook site.yml --ask-vault-pass
Un seul rôle : --tags ssh, --tags firewall, etc. Un rôle peut aussi être
désactivé par variable (enable_<rôle>: false) au niveau global, d'un groupe ou d'un hôte.
Ordre d'exécution et raison
common → proxy → dns → updates → ssh → firewall → fail2ban →
os_hardening → snmp → nrpe → glpi_agent.
Le proxy et le DNS viennent d'abord (sans eux, aucun paquet ne se télécharge) ; le SSH
est durci avant le pare-feu (la règle SSH doit exister avant que la politique passe à
drop) ; la supervision et l'agent GLPI viennent en dernier, une fois le système stable.
Points d'attention par distribution
- Ubuntu 22.10+ : sshd est activé par socket (
ssh.socket). Le rôle bascule surssh.servicepour quePortsoit pris en compte depuissshd_config. - Red Hat / Rocky / Alma : les algorithmes SSH ne sont pas définis dans
sshd_config(ils viennent decrypto-policies) ; fail2ban, NRPE et les greffons Nagios viennent d'EPEL, installé parcommon. SELinux : le port SSH est déclaré si différent de 22. - Red Hat 8 (OpenSSH 8.0) : pas de directive
Include; le durcissement est inséré en tête desshd_configentre des marqueurs, et les directives contradictoires du fichier d'origine sont commentées. - VM issues de
terraform-proxmox-serveur-debian-12: le jeu nftables et le drop-in sshd sont compatibles avec ceux posés par cloud-init (même structure, même répertoire/etc/nftables.d/). Le drop-in Ansible00-durcissement.confpasse avant99-durcissement.confde cloud-init : c'est Ansible qui a le dernier mot.
CI/CD
Voir CICD.md : Gitea contrôle le code, Semaphore l'exécute depuis la branche valide.
Vérifications après un premier passage
# Depuis le contrôleur : nouvelle session, clé uniquement
ssh -o BatchMode=yes -o ControlPath=none ansible@<hôte> 'sudo sshd -T | grep -Ei "^(permitrootlogin|passwordauthentication|authenticationmethods|allowusers)"'
# Pare-feu : seuls SSH, ICMP et les ports de supervision sont ouverts
ssh ansible@<hôte> 'sudo nft list chain inet filter input' # Debian/Ubuntu
ssh ansible@<hôte> 'sudo firewall-cmd --list-all' # Red Hat
# fail2ban actif sur sshd
ssh ansible@<hôte> 'sudo fail2ban-client status sshd'