Configuration de base des VM (baseline)¶
Rôle¶
Deux dépôts complémentaires appliquent et maintiennent la configuration de base de la flotte de VM Indio, à deux niveaux différents :
/root/baseline(Ansible,indio/iac/baseline) : gestion des comptes (rotation du mot de passeroot/indio-adm), déploiement/maintenance du dépôt interne sur des VM existantes, agents de supervision et d'inventaire (Zabbix, OCS), relais des logs (rsyslog → syslog-ng, plugin d'audit)./root/indio-baseline-config(paquet RPM noarch,indio/iac/indio-baseline-config) : paquet consolidé, installé directement par le template doré Packer (tmpl-rocky96-hardened), qui pousse en une fois les dépôts internes (Nexus), le client NTP interne, le proxy Squid par défaut et la confiance de la CA interne Vault PKI.
Ces deux projets se recouvrent partiellement dans le temps : le RPM a été créé pour consolider ce
que /root/baseline/ansible/repo.yml faisait déjà partiellement (dépôt interne, en version réduite
à 3 sections) et pour combler des manques qui n'existaient dans aucun des deux avant (NTP, proxy
actif par défaut, CA interne).
Architecture¶
/root/baseline (Ansible)¶
baseline/ansible/
├── ansible.cfg # inventory.ini, remote_user indio-adm, become=sudo, vault_password_file
├── inventory.ini # groupe [fleet] : VM existantes gérées par ce dépôt (nom IND<SEG><NNN> + IP admin)
├── group_vars/fleet.yml # variables communes (Zabbix HA, OCS, proxy Squid, syslog central...)
├── set-passwords.yml # rotation mdp root/indio-adm
├── repo.yml # dépôt interne (indio.repo à 3 sections)
├── agents.yml # agents Zabbix + OCS sur toute la flotte
├── syslog-forward.yml # drop-in rsyslog -> syslog-ng central
├── audit-syslog.yml # relais auditd -> syslog (plugin audisp-syslog)
├── reboot-batch.yml # reboot séquentiel par lot (serial 1)
├── files/ # indio.repo, drop-in rsyslog
└── roles/
├── ocs_agent/ # agent OCS Inventory NG (compilé depuis les sources)
├── zabbix_agent/ # agent Zabbix classique
├── zabbix_agent2_mysql/ # bascule agent2 + plugin MySQL natif
└── zabbix_agent2_postgresql/ # bascule agent2 + plugin PostgreSQL natif
group_vars/fleet.yml contient notamment l'IP du cluster Zabbix HA, le FQDN/IP du serveur OCS, la
VIP du proxy Squid, le FQDN/port du serveur syslog central, ainsi qu'une variable chiffrée par
ansible-vault (mysql_zbx_password, déchiffrée via vault_password_file). Aucun rôle dédié au
hardening SSH n'existe dans ce dépôt (voir Points d'attention).
/root/indio-baseline-config (RPM)¶
indio-baseline-config/
├── indio-baseline-config.spec # sources statiques + scriptlets %post/%postun idempotents
├── files/
│ ├── indio.repo # 6 sections : baseos/appstream/extras/crb/epel/custom (Nexus)
│ ├── indio-proxy.sh # /etc/profile.d, proxy pour les shells interactifs
│ ├── 10-indio-proxy.conf # /etc/systemd/system.conf.d, DefaultEnvironment (tous services)
│ ├── indio-ntp.conf # /etc/chrony.d, 3 serveurs NTP internes
│ ├── indio-root-ca.crt / indio-intermediate-ca.crt # CA Vault PKI, /etc/pki/ca-trust
│ └── RPM-GPG-KEY-EPEL-9
├── dist/ # RPM déjà construits : 1.0.2, 1.0.3, 1.0.4
└── README.md
Versions successives (voir %changelog du spec) : 1.0.0 (initiale : dépôts Nexus/EPEL, NTP,
proxy, CA) → 1.0.1 (ajout section indio-custom) → 1.0.2 (fix d'un typo de domaine sur
indio-epel + ajout de proxy=_none_ sur chaque section indio-*, faute de quoi le proxy global
casse l'accès aux dépôts internes) → 1.0.3 (désactivation dynamique des dépôts stock Rocky,
sinon dnf tente systématiquement mirrors.rockylinux.org) → 1.0.4 (suppression pure des
dépôts stock Rocky, plus seulement désactivés).
Consommé par /root/packer/scripts/internal-repo.sh : bootstrap minimal du dépôt indio-custom
puis dnf install indio-baseline-config, qui dépose lui-même la configuration définitive — voir
packer-template.md.
Schéma — quelle voie pour quelle VM¶
flowchart TD
VM["Nouvelle VM (terraform apply, /root/vmware)"] --> Q{"Clonée depuis quel<br/>template ?"}
Q -->|"tmpl-rocky96-hardened<br/>courant (RPM déjà intégré<br/>depuis le 2026-07-11)"| DONE["Déjà configurée au 1er boot<br/>dépôts + NTP + proxy + CA"]
Q -->|"VM antérieure au rebuild<br/>du template (07-11)"| CATCHUP["Rattrapage manuel requis"]
CATCHUP --> C1["rpm -q indio-baseline-config<br/>(IP ADMIN, pas IP service —<br/>hôtes DMZ multi-NIC)"]
C1 --> C2{"Cas particulier ?"}
C2 -->|"Serveur NTP lui-même<br/>(INDSERV005/006/007)"| C3["NE PAS installer le RPM complet<br/>(bouclerait sur lui-même) :<br/>fichiers proxy seuls +<br/>désactivation dynamique repos stock"]
C2 -->|VM normale| C4["Patcher indio.repo existant<br/>(proxy=_none_) AVANT le RPM"]
C4 --> C5["dnf install indio-baseline-config"]
DONE --> ROT["set-passwords.yml si rotation décidée"]
C5 --> ROT
C3 --> ROT
ROT --> AGENTS["agents.yml + syslog-forward.yml + audit-syslog.yml<br/>(toute la flotte, indépendant du RPM)"]
Provisioning Terraform¶
Non applicable. Ni /root/baseline (Ansible) ni /root/indio-baseline-config (RPM) ne
provisionnent de VM — les deux agissent sur des VM déjà déployées par
/root/vmware (rattrapage post-clonage) ou déjà présentes dans le template doré
au clonage (voir packer-template.md).
Configuration Ansible (/root/baseline)¶
set-passwords.yml: playbook générique de rotation (hosts: fleet), moduleansible.builtin.usersurrootetindio-adm. Le hash du nouveau mot de passe n'est jamais stocké dans le dépôt : il est fourni en extra-var au lancement, par exempleansible-playbook set-passwords.yml -e @<fichier externe non versionné> -e new_password_hash='<hash>'. La valeur elle-même est gérée séparément (Vault / non versionnée).repo.yml: dépose l'entrée/etc/hostspourrepo.infra.infra, copiefiles/indio.repo(version à 3 sections : baseos/appstream/extras), rafraîchit le cachednfdu dépôt interne et journalise le statut de la résolution.agents.yml+ rôles associés : déploiement de l'agent Zabbix classique et de l'agent OCS Inventory NG sur toute la flotte, avec bascule verszabbix-agent2(plugin natif) sur les hôtes MySQL/PostgreSQL qui le nécessitent (les clésmysql.*/pgsql.*n'existent que dans l'agent2). Le rôleocs_agentinstalle la CA Vault dans le trust store système pour la vérification TLS du serveur OCS, et compile l'agent depuis les sources (pas de RPM officiel EL9).syslog-forward.yml(hosts: all) : drop-in rsyslogomfwdvers le serveur syslog-ng central (RFC5424/syslog-protocol, indispensable pour que syslog-ng détecte le bon format — voir syslogng.md), avec file d'attente disque et retry infini en cas de coupure réseau (action.resumeRetryCount="-1",queue.type="linkedList").audit-syslog.yml(hosts: all) : relais des événementsauditden deux destinations parallèles — (1)audispd-plugins+ activation du pluginsysloggénérique (rejoint le flux rsyslog standard ci-dessus) ; (2)rsyslog-kafka(moduleomkafka) + un module SELinux dédié (files/indio_rsyslog_kafka.pp, chargé viasemodule -i) pour autorisersyslogd_tà joindre le port Kafka 9092 (bloqué nativement par la policy de référence, aucun label dédié Kafka) — sans ce module, le trafic est silencieusement rejeté (AVC denial). Le second volet alimente le topic Kafkalogs-auditconsommé ensuite par le pipeline Logstash → Elasticsearch. Piège important : le template durci active le mode audit immuable (auditctl -s→enabled 2) etauditdrefuse unrestartmanuel — l'activation du plugin ne prend effet qu'après un reboot, jamais à chaud.reboot-batch.yml: playbook générique de reboot par lot (serial: 1,--limit <groupe>) pour appliquer ce reboot en respectant les contraintes de chaque cluster ; vérifie après coup que le plugin audit est bien actif (grep "^active = yes" /etc/audit/plugins.d/syslog.conf).- Secret ansible-vault :
group_vars/fleet.ymlcontientmysql_zbx_passwordchiffré ($ANSIBLE_VAULT), jamais consulté en clair pour cette documentation.
Packaging RPM (indio-baseline-config)¶
%installplace chaque fichier statique à son emplacement final (yum.repos.d,profile.d,systemd/system.conf.d,chrony.d,pki/ca-trust,pki/rpm-gpg), avec%config(noreplace)sur les fichiers de configuration.%post(idempotent, vérifie avant d'ajouter) :- Ajoute
repo.infra.infraà/etc/hosts. - Ajoute une ligne
proxy=globale dans/etc/dnf/dnf.conf(section[main]— dnf4 n'a pas de drop-in pour cette section). - Active
confdir /etc/chrony.ddanschrony.confet neutralise (commente) toutpool/serverpublic préexistant, pour que les 3 serveurs NTP internes soient prioritaires. update-ca-trust extract(intègre les 2 CA + la clé EPEL).systemctl daemon-reexec(propageDefaultEnvironmentaux services déjà démarrés).- Sauvegarde puis supprime les fichiers
.repostock Rocky non préfixésindio-(rocky.repoet ses variantes-extras/-devel/-security/-addons) dans/var/lib/indio-baseline-config/stock-repos-removed/. %postun(si désinstallation complète) : retire la ligne/etc/hosts, la ligneproxy=dednf.conf, restaurepool/serverdanschrony.conf, restaure les dépôts stock sauvegardés, régénère le trust store.- Build recommandé dans un conteneur
rockylinux:9(podman) plutôt qu'en natif, pour éviter tout écart de format RPM avec l'hôte de build. - Régénération des certificats CA (si besoin) : requête sur les endpoints PEM non authentifiés de la CA Vault, puis bump de version et rebuild — voir le README du dépôt.
Procédure de déploiement¶
VM nouvelles (clonées depuis le template Packer courant) : la configuration (dépôts, NTP,
proxy, CA) est déjà présente au premier boot, le RPM étant intégré au template — aucune action
/root/baseline n'est nécessaire pour ce volet. Seule une éventuelle rotation de mot de passe
fleet-wide (set-passwords.yml) reste pertinente si une nouvelle rotation est décidée après le
build.
VM existantes, antérieures à l'intégration du RPM dans le template : rattrapage manuel.
- Vérifier la présence du paquet (requête RPM en lecture seule) sur l'IP d'administration de la VM — pas l'IP de service, pour les hôtes multi-NIC en DMZ.
- Cas général : si un
/etc/yum.repos.d/indio.repopréexistant a été déposé parrepo.yml(Ansible), le patcher pour ajouterproxy=_none_à chaque section avant d'installer le RPM —%config(noreplace)ne remplace pas un fichier déjà présent, et le proxy global casserait sinon l'accès aux dépôts internes. - Cas particulier des serveurs NTP internes eux-mêmes : ne jamais leur installer le RPM complet
(cela les ferait boucler sur eux-mêmes et perdre leur seule source de temps externe) — déployer
uniquement les fichiers proxy (
profile.d+ drop-in systemd) et désactiver dynamiquement les dépôts stock, sans toucher àchrony.conf. - Revérifier le service métier après coup (un
dnf makecacheréel), pas seulement le code de sortie de l'installation.
Les playbooks agents.yml, syslog-forward.yml et audit-syslog.yml s'exécutent sur toute la
flotte indépendamment de la présence du RPM : ils couvrent un périmètre différent (supervision,
inventaire, logs).
Contrôle de santé / Vérification¶
# Présence et version du RPM (sur l'IP admin, pas l'IP de service pour les hôtes DMZ)
ansible fleet -m command -a "rpm -q indio-baseline-config"
# Le dépôt interne répond réellement (pas seulement "RPM installé")
ansible fleet -m command -a "dnf -q --disablerepo=* --enablerepo=indio-* makecache"
# Synchronisation NTP effective sur les 3 serveurs internes
ansible fleet -m command -a "chronyc sources"
# Plugin audit actif après reboot (audit-syslog.yml + reboot-batch.yml)
ansible fleet -m command -a "grep '^active = yes' /etc/audit/plugins.d/syslog.conf"
Toujours revérifier le service métier réel (un dnf makecache qui aboutit, pas seulement le
code de sortie de l'installation du RPM) — c'est cette vérification qui a permis de détecter le
bug 1.0.0 (proxy global cassant l'accès aux dépôts internes) lors de sa découverte initiale.
Points d'attention¶
- Le hardening SSH n'est pas appliqué par
/root/baseline. Le drop-in/etc/ssh/sshd_config.d/00-hardening.conf(PermitRootLogin no, chiffrement restreint, etc.) est intégré au template doré au moment du build Packer (scripts/hardening.sh, voir packer-template.md) et hérité par toute VM clonée./root/baselinese limite à la rotation du mot de passe des comptes existants (set-passwords.yml), pas à la configuration SSH elle-même. - Mots de passe : aucune valeur n'est stockée en clair dans
/root/baseline; le hash utilisé parset-passwords.ymlest fourni en extra-var au lancement, depuis un fichier externe non versionné. Toute valeur observée ailleurs est gérée séparément (Vault / non versionnée) et ne doit jamais figurer dans la documentation. - Secret ansible-vault :
group_vars/fleet.ymlcontient une variable chiffrée (mysql_zbx_password) ; le fichier de mot de passe du vault est référencé dansansible.cfgmais n'est lui-même jamais versionné. - Redondance non résolue :
files/indio.repo(Ansible, 3 sections) est une version obsolète par rapport à celle livrée par le RPM (6 sections, avecproxy=_none_) — dette technique identifiée dans le README du RPM, pas encore dédupliquée. - Le RPM active le proxy Squid par défaut sur toutes les VM (changement de posture assumé), y
compris pour les services systemd (
DefaultEnvironment) : ses propres dépôts internes doivent systématiquement être exemptés (proxy=_none_) sous peine de casser l'accès à Nexus. - Les scriptlets
%post/%postundu RPM manipulent des fichiers potentiellement bind-montés (/etc/hosts) pargrep -v ... > tmp && cat tmp > fichierplutôt quesed -i, qui échouerait sur un bind-mount. - Vérifier, selon la version du RPM en place, que le dépôt
indio-epelpointe bien vers le miroir Nexus (repo.infra.infra) et non vers un upstream public direct — ce comportement a varié entre les premières versions du paquet.