FreeIPA & trust Active Directory¶
Rôle¶
FreeIPA fournit l'annuaire, l'authentification Kerberos et les services LDAP/DNS
pour un royaume dédié ipa.infra.indio, distinct du domaine Active Directory
existant infra.indio (voir Durcissement Active Directory).
Une relation de confiance (trust) inter-royaumes relie les deux annuaires pour
l'authentification croisée : les comptes et groupes AD (y compris les groupes
intégrés) sont résolus côté FreeIPA et pilotent désormais l'accès SSH à
45 des 47 serveurs Linux de la flotte via une règle HBAC dédiée.
Le même dépôt Terraform/Ansible héberge également le bastion d'accès distant
Guacamole (VM INDIDEN001, hors périmètre applicatif FreeIPA — voir
Bastion Guacamole).
Demande initiale complètement traitée depuis le 2026-07-17
Trust établi et vérifié, flotte enrôlée, HBAC SSH actif. Cette page documente l'état réel après une histoire mouvementée (voir l'encadré « Chronologie » ci-dessous) — les versions précédentes de cette page décrivaient un trust encore à reconstruire manuellement ; ce n'est plus le cas.
Architecture¶
| VM | Rôle | IP | Notes |
|---|---|---|---|
| INDIDEN001 | Bastion Guacamole | 10.100.2.244 | Installé manuellement, hors Ansible (cf. Bastion Guacamole) — inclus comme client IPA (ipa_clients_admin) |
| INDIDEN004 | FreeIPA serveur primaire | 10.100.2.245 | ipa_primary, ipa-server-4.13.1-3.el9_8.2, DNS intégré (--setup-dns), adtrust actif (smb/winbind) |
| INDIDEN005 | FreeIPA réplica | 10.100.2.246 | ipa_replica, DNS intégré, pas d'adtrust (smb/winbind absents — voir Points d'attention) |
| INDIDEN006 / INDIDEN007 | VM génériques seg-identite | 10.100.2.247 / 10.100.2.248 | Provisionnées, aucun rôle serveur dédié, mais enrôlées comme clients IPA (ipa_clients_admin) |
- Segment réseau :
seg-identite,10.100.2.240/28, gateway10.100.2.254, zone Admin (DFW default-ALLOW intra-zone), templatetmpl-rocky96-hardened. Les deux contrôleurs de domaine AD (INDIDEN00810.100.2.241,INDIDEN00910.100.2.242, voir Durcissement Active Directory) sont sur ce même segment L2 que FreeIPA — pas de routage inter-segment pour la relation de confiance elle-même. - Domaine/royaume FreeIPA :
ipa.infra.indio/IPA.INFRA.INDIO(sous-domaine du domaine AD, choisi précisément pour éviter toute collision de royaume Kerberos avecinfra.indio/INFRA.INDIO). - Choix d'architecture assumé (« option B ») : les hôtes FreeIPA restent
membres DNS du domaine AD
infra.indio, mais le royaume Kerberos/IPA est distinct — d'où--no-host-dnsà l'installation (le A record du serveur est géré côté AD, pas par FreeIPA). - Clients IPA enrôlés (45/47 serveurs Linux de la flotte, cf.
inventory.iniréorganisé en 3 sous-groupes zonésipa_clients_admin/_dmz/_soc) : toute la zone admin sauf 2 nœuds Kubernetes injoignables en réseau (INDSERV020/021, problème préexistant sans rapport), les 4 proxies DMZ (INDPRXY001/002,INDPRXD001/002, via leur NIC admin dédié — voir Convention de nommage), et 16 hôtes SOC. - INDIDEN002/003 (première installation FreeIPA) ont été détruites le 2026-07-07 et non reconstruites sous cette identité (voir « Chronologie »).
Chronologie (pour comprendre l'état actuel)¶
Trois cycles avant l'état opérationnel actuel
- 2026-07-05 : première installation,
ipa-node01/ipa-node02(INDIDEN002/003). Aucun trust AD établi (ipa trust-findvide). - 2026-07-07 : les deux VM sont détruites à la demande explicite du
user (
terraform destroy -target), nettoyage complet effectué (NetBox,populate.py, inventaire Ansible). Le trust n'existant pas encore, rien à nettoyer côté AD. Les 4 proxies DMZ perdent leur enrôlement IPA (désenrôlés proprement le jour même). - 2026-07-11 : 4 VM nues provisionnées en seg-identite
(
INDIDEN004-007), sans FreeIPA (commentairevariables.tfde l'époque : « VM nues (pas de FreeIPA) »). - 2026-07-16 : le user installe FreeIPA manuellement sur
INDIDEN004/005(même pattern que Guacamole/MISP/TheHive/Cortex — installation hors Ansible). Bug critique introduit à cette occasion : le royaume Kerberos utilisé estINFRA.INDIO(identique au royaume AD) au lieu deIPA.INFRA.INDIOprévu — un trust est structurellement impossible entre deux royaumes Kerberos identiques (collision dekrbtgt/basedn, pas juste déconseillé). - 2026-07-17 : le mauvais royaume est détecté avant tout dégât
(0 client enrôlé sur ce royaume). Désinstallation complète
(réplica puis primaire,
ipa-server-install --uninstall) suivie d'une réinstallation viaansible-playbook site.yml— cette fois avec le bon royaume et--setup-dns(DNS intégré à IPA, absent de l'installation manuelle précédente). Trust établi, flotte enrôlée, HBAC activé (voir sections dédiées ci-dessous).
Topologie du trust et flux DNS/HBAC¶
flowchart TB
subgraph SEG["seg-identite — 10.100.2.240/28 (zone Admin, même segment L2)"]
subgraph ADB["Active Directory — infra.indio / INFRA.INDIO"]
DC1["INDIDEN008\n10.100.2.241\nDC primaire, tous rôles FSMO"]
DC2["INDIDEN009\n10.100.2.242\nDC #2, Global Catalog"]
end
subgraph IPAB["FreeIPA — ipa.infra.indio / IPA.INFRA.INDIO"]
P["INDIDEN004 primaire\n10.100.2.245\n+ smb/winbind (adtrust)"]
R["INDIDEN005 réplica\n10.100.2.246\n(pas d'adtrust)"]
end
end
DC1 -. "délégation DNS NS zone ipa\n+ A records indiden004/005" .-> P
P == "ipa trust-add --type=ad infra.indio\n(résultat : Established and verified)" ==> DC1
P -. "réplication LDAP 389/636" .-> R
GRP["AD: GG_Infrastructure,\nAdmins du domaine (RID 512)"] -.->|"external groups IPA"| POSIX["ad-linux-admins (POSIX)"]
POSIX --> HBAC["hbacrule allow-ssh-linux-admins\nhostgroup linux-servers (45 hôtes), service sshd"]
sequenceDiagram
participant U as Utilisateur AD
participant SSH as sshd (hôte cible)
participant SSSD as SSSD (client IPA)
participant IPA as FreeIPA (résolution trust + HBAC)
U->>SSH: ssh utilisateur@infra.indio@hôte
SSH->>SSSD: authentification PAM
SSSD->>IPA: résolution identité via trust AD + évaluation HBAC
alt Membre de ad-linux-admins (ex. pmartin, via GG_Infrastructure)
IPA-->>SSSD: allow-ssh-linux-admins MATCH
SSSD-->>SSH: authentication success + accepted
SSH-->>U: session ouverte
else Hors groupe HBAC (ex. hrichard, service Support)
IPA-->>SSSD: aucune règle ne matche (allow_all désactivé)
SSSD-->>SSH: Access denied by PAM account configuration
SSH-->>U: Permission denied
end
Règles DFW dédiées (zones deny-all)¶
La zone admin (seg-identite incluse) est en DFW default-ALLOW — aucune règle n'est nécessaire pour que le trafic intra-zone admin atteigne FreeIPA. En revanche, les zones DMZ et SOC sont en deny-all scopé (voir NSX) et nécessitent des règles explicites, ajoutées le 2026-07-17 :
| Règle | Fichier | Source | Destination | Services |
|---|---|---|---|---|
dmz-to-freeipa |
dfw_dmz.tf |
seg-proxy, seg-proxy-dns |
seg-identite |
svc-LDAP (Kerberos 88+464 TCP/UDP, LDAP 389/636 TCP), DNS (TCP/UDP), HTTP, HTTPS |
soc-to-freeipa |
dfw_soc.tf |
seg-soc-admin, seg-soc |
seg-identite |
idem |
Le service personnalisé svc-LDAP (services.tf) existe car les services
NSX prédéfinis ne couvrent pas correctement Kerberos : il ajoute
explicitement le port 464 (kpasswd, changement de mot de passe) en TCP
et UDP en plus du 88 (authentification), aux côtés du LDAP/LDAPS standard.
Provisioning Terraform¶
Fichiers : main.tf, variables.tf, outputs.tf, versions.tf.
- Providers
hashicorp/vsphere(clonage VM) ete-breuninger/netbox(réservation/lecture d'IP par nom DNS viadata.netbox_ip_addresses, filtrée surdns_nameen minuscule). - Une ressource
vsphere_virtual_machine.vmenfor_eachsurvar.nodes(listename/cpu/ram, valeurs actuelles :INDIDEN001,004,005,006,007— 2 vCPU/4096 Mo chacune), clonée depuistmpl-rocky96-hardened, personnalisée (customize) avec hostname, domaine, IP/masque/gateway/DNS. lifecycle.ignore_changessurannotation,template_uuid,customizeetdisk[0].io_share_countpour éviter un drift Terraform sur des VM déjà personnalisées manuellement après clonage (essentiel ici : FreeIPA et Guacamole modifient abondamment ces VM après le clone).outputs.tfexpose la map nom → IP des VM créées.- Le commentaire du fichier
variables.tfqualifiait encore récemment INDIDEN004-007 de « VM nues (pas de FreeIPA) » — obsolète depuis l'installation du 2026-07-16 pour 004/005 ; non corrigé dans le code à ce jour (voir Points d'attention).
Configuration Ansible¶
inventory.ini: groupesipa_primary,ipa_replica,ipa:children,guacamole, etipa_clientsréorganisé en 3 sous-groupes zonés (ipa_clients_admin/_dmz/_soc) pour permettre un rollout par lots (--limit) avec vérification entre chaque lot.site.yml: deux plays séquentiels — serveur primaire (rôlesfreeipa_common+freeipa_primary) puis réplica (freeipa_common+freeipa_replica). Le bastion Guacamole est explicitement exclu de ce playbook (installation manuelle) : c'est ce mêmesite.yml, jusque-là jamais utilisé pour une installation réelle, qui a servi à la réinstallation propre du 2026-07-17.enroll-clients.yml: playbook séparé qui applique le rôleipa_clientau groupeipa_clients.group_vars/all.yml: domaines/royaumes IPA et AD, forwarders DNS (10.100.2.241/242), liste des ports firewalld à ouvrir (dont le bloc adtrust : SMB/RPC/Global Catalog — 135, 138, 139, 445/tcp+udp, 3268, 3269, 49152-65535/tcp). Les mots de passeipa_dm_passwordetipa_admin_passwordsont stockés chiffrés (!vault/$ANSIBLE_VAULT) — valeurs gérées séparément, non reproduites ici.ansible.cfgréférence un fichier de mot de passe vault dédié (/root/.identity_vault_pass).
Rôles¶
| Rôle | Contenu |
|---|---|
freeipa_common |
Hostname FQDN, résolution /etc/hosts des deux serveurs IPA, installation des paquets ipa-server, ipa-server-dns, ipa-server-trust-ad, ouverture firewalld des ports IPA + adtrust |
freeipa_primary |
ipa-server-install --unattended avec --setup-dns, --setup-adtrust, --netbios-name=IPA, --no-host-dns, --no-reverse, --no-dnssec-validation, --no-ntp (exécution asynchrone, poll jusqu'à 30 min) |
freeipa_replica |
Résolveur pointé vers le primaire, enrôlement explicite comme client IPA (domaine/royaume/serveur imposés, pour éviter la découverte SRV automatique qui trouverait l'AD), puis ipa-replica-install --setup-ca --setup-dns — nécessite --principal admin --admin-password pour la promotion |
ipa_client |
Résolution locale, installation ipa-client, désenrôlement préalable d'un éventuel ancien client obsolète (garde-fou failed_when: false), puis ipa-client-install --mkhomedir --force-join |
Ce que ces rôles ne font pas, et qui reste manuel (voir section
suivante) : établissement du trust lui-même, délégation DNS côté AD, et
toute la configuration HBAC (groupes, règles, désactivation de allow_all).
Procédure manuelle¶
Ces étapes ne sont couvertes par aucun playbook Ansible ni ressource Terraform — reconstituées ici comme procédure numérotée reproductible à partir de l'état vérifié le 2026-07-17.
1. Délégation DNS côté AD (préalable indispensable au trust)¶
ipa.infra.indio est un sous-domaine de infra.indio, zone dont l'AD est
déjà autoritaire — un simple conditional forwarder est rejeté par
Windows DNS dans ce cas précis (Add-DnsServerConditionalForwarderZone
échoue avec WIN32 9604 / DNS_ERROR_ZONE_CONFIGURATION_ERROR : un DC ne
peut pas créer de forwarder conditionnel pour un sous-domaine d'une zone
dont il est déjà autoritaire). Il faut une vraie délégation de zone :
# Sur un DC AD (INDIDEN008), PowerShell DnsServer
Add-DnsServerZoneDelegation -Name infra.indio -ChildZoneName ipa `
-NameServer indiden004.infra.indio -IPAddress 10.100.2.245
Add-DnsServerResourceRecord -ZoneName infra.indio -NS -Name ipa `
-NameServer indiden005.infra.indio
# A records des serveurs IPA eux-mêmes, absents malgré la mention
# "hors Terraform, tâche user" dès la conception initiale du projet
Add-DnsServerResourceRecordA -ZoneName infra.indio -Name indiden004 -IPv4Address 10.100.2.245
Add-DnsServerResourceRecordA -ZoneName infra.indio -Name indiden005 -IPv4Address 10.100.2.246
Vérification bidirectionnelle : _kerberos._tcp.ipa.infra.indio doit se
résoudre depuis un DC AD, _ldap._tcp.infra.indio doit se résoudre depuis
un serveur IPA (forward configuré par ipa_forwarders dans group_vars).
2. Établissement du trust¶
# Sur INDIDEN004 (primaire IPA), après kinit admin@IPA.INFRA.INDIO
ipa trust-add --type=ad infra.indio --admin Administrateur --password
# (nom localisé FR — ce domaine AD est installé en français, cf.
# Durcissement Active Directory) -> "Trust status: Established and verified"
Vérifié fonctionnel immédiatement, sans retry, y compris pendant la promotion simultanée du réplica (pas de race constatée entre écriture LDAP sur le primaire et réplication en cours).
3. Enrôlement de la flotte¶
ansible-playbook enroll-clients.yml --limit ipa_clients_admin
ansible-playbook enroll-clients.yml --limit ipa_clients_dmz
ansible-playbook enroll-clients.yml --limit ipa_clients_soc
Par lots zonés avec vérification entre chaque (et non un seul run global) — c'est ce séquencement qui a permis d'isoler le trou DFW SOC décrit en Points d'attention plutôt que d'échouer en bloc sans diagnostic clair.
4. Configuration HBAC (restriction de l'accès SSH aux groupes AD attendus)¶
# Groupe hôte couvrant toute la flotte Linux
ipa hostgroup-add linux-servers --desc="Flotte Linux Indio (SSH via trust AD)"
ipa hostgroup-add-member linux-servers --hosts=indserv002.infra.indio,...
# Groupes externes = pointeurs vers des groupes AD (côté trust)
ipa group-add ad-gg-infrastructure-external --external
ipa group-add ad-domain-admins-external --external
ipa group-add ad-linux-admins --desc="Comptes AD autorisés en SSH sur la flotte"
# ATTENTION : chaque commande *-add-member ci-dessous est interactive par
# défaut (voir piège CLI en Points d'attention) — à exécuter une par une,
# jamais en pipe multi-lignes vers un même stdin.
ipa group-add-member ad-gg-infrastructure-external --external "INFRA.INDIO\GG_Infrastructure"
ipa group-add-member ad-domain-admins-external --external "INFRA.INDIO\Admins du domaine"
ipa group-add-member ad-linux-admins --groups=ad-gg-infrastructure-external
ipa group-add-member ad-linux-admins --groups=ad-domain-admins-external
# Règle HBAC dédiée, puis désactivation de la règle par défaut
ipa hbacrule-add allow-ssh-linux-admins
ipa hbacrule-add-user allow-ssh-linux-admins --groups=ad-linux-admins
ipa hbacrule-add-host allow-ssh-linux-admins --hostgroups=linux-servers
ipa hbacrule-add-service allow-ssh-linux-admins --hbacsvcs=sshd
ipa hbacrule-disable allow_all
T0-Admins (groupe AD du volet durcissement, vide au moment du choix) a été
écarté du mapping ; GG_Infrastructure et Admins du domaine retenus car
seuls groupes avec des membres réels à ce moment (pmartin, sbernard,
Administrateur).
Procédure de déploiement¶
terraform apply(racine du repo) — provisionne les VM surseg-identite.ansible-playbook site.yml— installe le serveur FreeIPA primaire (INDIDEN004,--setup-dnsinclus), puis le réplica (INDIDEN005), dans cet ordre (dépendance).- Délégation DNS côté AD (Procédure manuelle, étape 1) — préalable au trust.
ipa trust-add --type=ad infra.indio(Procédure manuelle, étape 2).ansible-playbook enroll-clients.yml --limit <groupe>par lots zonés (Procédure manuelle, étape 3) — vérifier au préalable que les règles DFWdmz-to-freeipa/soc-to-freeipasont appliquées côté NSX si les cibles sortent de la zone admin.- Configuration HBAC (Procédure manuelle, étape 4).
- Bastion Guacamole (INDIDEN001) : installation manuelle séparée, voir Bastion Guacamole.
Contrôle de santé / Vérification¶
# État du trust
ipa trust-show infra.indio
# -> Trust status: Established and verified
# Résolution d'identité cross-royaume (utilisateur + groupes AD, y compris
# noms localisés FR)
id pmartin@infra.indio
# État de la règle HBAC et confirmation qu'allow_all est bien désactivée
ipa hbacrule-show allow-ssh-linux-admins
ipa hbacrule-show allow_all # Enabled: FALSE attendu
# Vérification décisive (pas seulement le comportement du client SSH,
# ambigu) : lire les logs PAM/SSSD sur l'hôte cible
journalctl -u sshd -u sssd --since "-10min"
# Cas autorisé : "Accepted password" + session ouverte
# Cas refusé : "Access denied ... by PAM account configuration"
# (distinct sans ambiguïté d'un échec d'authentification)
Le compte local indio-adm (pam_unix) n'est jamais concerné par ces
vérifications — HBAC ne s'applique qu'aux identités IPA/AD via SSSD.
Pour la santé du domaine AD sous-jacent (réplication, services core, PKI), voir le contrôle de santé documenté dans Durcissement Active Directory.
Points d'attention¶
Royaume Kerberos erroné lors de l'installation manuelle du 07-16
L'installation manuelle du 2026-07-16 avait utilisé le royaume
INFRA.INDIO (identique à l'AD) au lieu de IPA.INFRA.INDIO prévu —
un trust est structurellement impossible entre deux royaumes
Kerberos identiques (collision krbtgt/basedn, pas un simple défaut de
configuration). Détecté avant tout dégât (0 client enrôlé sur ce
royaume), corrigé par un cycle complet désinstall/réinstall via le
playbook site.yml — la leçon généralisable : toujours vérifier
/etc/ipa/default.conf et /etc/krb5.conf après toute installation
manuelle d'un service Kerberos, avant de supposer que la
configuration correspond à ce que documente le dépôt IaC.
- Piège CLI FreeIPA généralisable : toute commande
ipa <resource>-add-member/-add-user/-add-host/-add-servicedéclenche 4 prompts interactifs séquentiels dès qu'aucun de--users/--groups/--services/--idoverrideusersn'est fourni explicitement — même avec--external=..., qui ne supprime pas ces prompts contrairement à l'intuition. Exécutée via un script multi-lignes piped en une seule fois vers un shell distant (cat script.sh | ssh host "bash -s"), ces prompts consomment silencieusement les lignes suivantes du script comme fausses réponses interactives — plusieurs commandes ultérieures ne sont alors jamais exécutées, sans erreur visible autre qu'un « Failed members » contenant des bouts de texte du script lui-même. Toujours exécuter une commandeipa *-add-memberpar appel SSH séparé. - Piège découvert en testant le HBAC : la politique de mot de passe AD
durcie exige 14 caractères minimum (voir
Durcissement Active Directory) — un mot de passe de
13 caractères échoue silencieusement côté
Set-ADAccountPassword($ErrorActionPreferencepar défautContinue, aucune exception levée), puis fait échouer le SSH suivant avec unPreauthentication failedtrompeur qui ressemble à un refus HBAC. Toujours vérifier la longueur du mot de passe et entourer les cmdlets AD sensibles detry/catch -ErrorAction Stop. - Trou DFW rencontré en conditions réelles lors de l'enrôlement SOC : les
règles
dmz-to-freeipa/soc-to-freeipaposées initialement omettaient le port HTTP (80/tcp), qu'ipa-client-installexige en plus de HTTPS. Les proxies DMZ avaient réussi malgré tout via un chemin détourné (règle HTTP générale préexistante non scopée), ce qui a masqué le trou jusqu'à l'échec explicite des 10 premiers hôtes SOC (sans route de secours équivalente). Corrigé en ajoutant/infra/services/HTTPaux deux règles. - Pas de haute disponibilité du trust lui-même :
ipa-adtrust-installn'a pas été exécuté sur le réplica (INDIDEN005,smb/winbindabsents de sonipactl status) — le trust fonctionne via le primaire seul ; sa panne interromprait la résolution d'identité AD côté FreeIPA (le LDAP/ Kerberos IPA de base resterait néanmoins disponible via le réplica). - HBAC limité à SSH (
sshd) comme demandé explicitement — aucune règlesudoou autre service créée à ce jour. - Le commentaire
variables.tf(« VM nues, pas de FreeIPA ») pour INDIDEN004-007 est resté obsolète dans le code après l'installation du 07-16 — à corriger si ce repo est repris, pour éviter qu'un futur lecteur ne se fie au commentaire plutôt qu'à l'état live. INDSERV020/INDSERV021(nœuds Kubernetes, voir Cluster k8s) restent injoignables réseau et non enrôlés — problème préexistant sans rapport avec ce projet.- Les mots de passe Directory Manager et admin IPA sont chiffrés via
ansible-vault dans
group_vars/all.yml— jamais en clair dans le dépôt.