Files
ansible-hardening-linux/README.md
T
hcornet b307a35eb4
Ansible - controle du code / controle (push) Successful in 3m42s
Ansible - controle du code / promouvoir (push) Failing after 3s
fix 004
2026-09-15 17:43:07 +02:00

127 lines
8.3 KiB
Markdown

# 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 pour `su -`.
- 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 `ssh` refuse une configuration
où le compte utilisé par Ansible n'aurait pas de clé ou ne serait pas dans `AllowUsers`,
et le rôle `firewall` refuse une liste `firewall_ssh_sources` qui 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
1. **Inventaire** : renseigner `inventory/hosts.yml`. Deux modes de connexion :
- compte de service `ansible` + sudo (`become` est activé dans `ansible.cfg`) ;
- root par clé, comme sur les VM Terraform : `ansible_user: root`, `become` est alors sans effet.
2. **Clés publiques** : fichiers `*.pub` à plat dans `files/keys/`, attribués aux comptes
par `ssh_users[].keys` dans `group_vars/all/main.yml`. Le compte utilisé par Ansible
(root) **doit** y avoir la clé de Semaphore.
3. **Certificat racine** (facultatif) : `files/ca/root-ca.crt` au format PEM.
4. **Secrets** : `cp group_vars/all/vault.yml.example group_vars/all/vault.yml`,
renseigner, puis `ansible-vault encrypt group_vars/all/vault.yml`.
5. **Variables** : relire `group_vars/all/main.yml` — en particulier
`firewall_ssh_sources` (réseaux d'administration), `supervision_sources`,
`dns_nameservers`, `proxy_url`, `ssh_users`, `glpi_agent_server`.
6. **Simulation** : `ansible-playbook site.yml --check --diff --ask-vault-pass`
7. **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 sur
`ssh.service` pour que `Port` soit pris en compte depuis `sshd_config`.
- **Red Hat / Rocky / Alma** : les algorithmes SSH ne sont pas définis dans `sshd_config`
(ils viennent de `crypto-policies`) ; fail2ban, NRPE et les greffons Nagios viennent
d'EPEL, installé par `common`. 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 de `sshd_config` entre 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 Ansible `00-durcissement.conf` passe avant
`99-durcissement.conf` de 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
```bash
# 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'
```