Durcissement Active Directory (ad-hardening)

Rôle

Package de scripts PowerShell de durcissement du domaine Active Directory infra.indio (INFRA.INDIO), basé sur le modèle de tiering Microsoft (Tier0/Tier1/Tier2) : OU dédiées, groupes d'administration isolés, GPO de sécurité, comptes de service applicatifs, et depuis le 2026-07-16 un volet PKI/RDP qui s'appuie sur le cluster Vault interne. Le dépôt est livré comme un package à revoir puis appliquer manuellement (RSAT AD-PowerShell + GPMC requis, compte Domain Admins/Enterprise Admins) — rien n'est exécuté automatiquement contre les contrôleurs de domaine par un agent.

Architecture

Contrairement aux autres dépôts identité, ad-hardening ne contient ni Terraform ni Ansible : c'est un ensemble de scripts PowerShell organisés comme suit.

config/TieringConfig.psd1     configuration centrale (DN, noms, tiers, comptes de service, bloc VaultPKI)
common/Common-Functions.ps1   logging, helpers idempotents, écriture GptTmpl/audit.csv, fonctions Vault/PKI
scripts/00-Deploy-All.ps1     orchestrateur complet
scripts/01-04                 OU, groupes de tiering, délégation, GPO deny-logon croisée
scripts/05-06                 renommage Administrator, rotation krbtgt (FORT IMPACT)
scripts/10-15                 GPO de durcissement (password/PSO, Kerberos, NTLM/SMB, audit avancé+WEF, LAPS, AppLocker)
scripts/16-Link-GPOsToOUs.ps1 liaison effective des GPO aux OU
scripts/20/22/23              comptes de service (bind LDAP Horizon/NSX/vCenter/GitLab, GPO deny-logon dédiée, jointure vSAN File Service)
scripts/21                    compte admin nominatif paramétrable (1 par personne et par tier)
scripts/30-33                 PKI Vault : publication des CA + certificat KDC, durcissement transport RDP, auto-enrollment certificat RDP, exigence smart-card T0 (FORT IMPACT, gate)
payload/trust/                copies statiques des CA publiques (bootstrap Vault + Indio Root/Intermediate)
payload/*.ps1                 scripts déployés par GPO (RDP) ou à exécuter manuellement (smart-card)
external-package/Deploy-ADOrganization.ps1   script tiers : structure d'OU/groupes type PME, adaptée à l'organisation Indio (Direction/Infrastructure/SOC/Support/RnD)
gpo-backups/                  destination des exports Backup-GPO
ACCOUNTS.md                   inventaire exhaustif des comptes créés + procédures de raccordement produit

Modèle de tiering (OU=Tiering,DC=infra,DC=indio) : Tier0 (Admins, Groups, PAW, ServiceAccounts), Tier1 (Servers, Admins, Groups, PAW, ServiceAccounts, File-Services), Tier2 (Workstations, Users, Groups, PAW, ServiceAccounts), plus OU=GPO-Staging pour tester avant liaison large. OU=Domain Controllers est conservée telle quelle (Tier 0 intégré, non déplacé).

Un compte/groupe d'un tier n'administre que son propre tier (délégation d'OU) et ne peut pas ouvrir de session sur un autre tier (GPO deny-logon croisées). Les comptes T0 sont membres de Protected Users.

Le fichier de configuration note explicitement que la structure d'OU réelle et vivante du domaine, depuis son rebuild du 2026-07-15, ne correspond plus entièrement à l'arborescence OU=Tiering de ce dépôt (le domaine utilise une autre organisation, OU=Indio/OU=Admin, déployée par ailleurs) — les GPO RDP/PKI (30-33) sont donc liées via une liste distincte d'OU cibles réelles (RDPLinkTargets), pas via Get-TierOUPath.

Structure d'OU réellement vivante sur le domaine aujourd'hui (constatée par audit LDAP le 2026-07-16), à ne pas confondre avec l'arborescence OU=Tiering du dépôt décrite plus haut :

flowchart TD
    ROOT["DC=infra,DC=indio"] --> INDIO["OU=Indio (racine réelle du domaine)"]
    INDIO --> ADMIN["OU=Admin"]
    INDIO --> UTIL["OU=Utilisateurs"]
    INDIO --> GRP["OU=Groupes"]
    INDIO --> ORD["OU=Ordinateurs (Fixes/Portables)"]
    INDIO --> CS["OU=Comptes-Service"]
    ADMIN --> T0["OU=Tier0 (Accounts/Groups + PAW)"]
    ADMIN --> T1["OU=Tier1 (Accounts/Groups) + Servers-T1"]
    ADMIN --> T2["OU=Tier2 (Accounts/Groups) + Workstations-T2"]
    UTIL --> DIR["Direction"]
    UTIL --> INFRA["Infrastructure"]
    UTIL --> SOC["SOC"]
    UTIL --> SUP["Support"]
    UTIL --> RND["RnD"]
    T0 -. GPO deny-logon croisée .-> T1
    T1 -. GPO deny-logon croisée .-> T2
    T2 -. GPO deny-logon croisée .-> T0

Flux d'émission d'un certificat RDP/KDC via Vault (scripts 30/32)

Les deux enrôlements (certificat KDC sur un DC, certificat serveur RDP) suivent exactement le même schéma sign-only, constaté directement dans scripts/30-Deploy-VaultPKITrust.ps1 et payload/Invoke-VaultRDPCertEnrollment.ps1 :

sequenceDiagram
    participant W as certreq (CSP local, poste/DC)
    participant AD as AD (ticket Kerberos machine)
    participant V as Vault (auth/kerberos + pki_int)
    W->>W: certreq -new (CSR local, clé privée non exportable)
    W->>AD: ticket Kerberos (identité SYSTEM du compte machine)
    W->>V: POST /v1/auth/kerberos/login (-UseDefaultCredentials, SPNEGO)
    V-->>W: client_token (policies mappées via identity group AD)
    W->>V: POST /v1/pki_int/sign/ROLE {csr, common_name, ttl}
    Note over V: role = infra-indio (RDP) ou infra-indio-kdc (KDC)<br/>jamais pki_int/issue/* (principe sign-only)
    V-->>W: certificat signé (PEM)
    W->>W: certreq -accept (import, associe la clé privée locale en attente)
    W->>W: Win32_TSGeneralSetting.SSLCertificateSHA1Hash = thumbprint ; .Put()
    Note over W: bind listener RDP-Tcp (script 32 uniquement — le cert KDC<br/>n'a besoin que d'être présent dans LocalMachine\My)

Provisioning Terraform

Non applicable — ce dépôt ne provisionne aucune VM ; il s'exécute contre des contrôleurs de domaine déjà existants (gérés ailleurs dans l'IaC).

Configuration Ansible

Non applicable — durcissement 100% PowerShell/GPO côté Windows, sans Ansible.

Procédure de déploiement

Ordre recommandé (détaillé dans README.md) :

  1. Valider chaque GPO sur OU=GPO-Staging avant toute liaison large (idéalement sur un AD de lab/snapshot).
  2. 01 → OU ; 02 → groupes de tiering (ajout automatique des groupes T0 à Protected Users) ; 03 → délégation par OU ; 04 → GPO deny-logon croisées (créées, non liées).
  3. 10 à 15 → GPO de durcissement (password/PSO, Kerberos, NTLM/SMB, audit avancé + WEF, LAPS, AppLocker en mode audit) — créées, non liées.
  4. Revue manuelle des GPO créées, puis 16 → liaison effective aux OU (l'impact réel commence ici).
  5. 20 → comptes de service bind LDAP (Horizon/NSX/vCenter/GitLab) ; 23 → compte de jointure de domaine du vSAN File Service ; 22 → GPO deny-logon pour ces comptes de service, puis re-liaison via 16.
  6. 21 → comptes admin nominatifs, à invoquer une fois par personne et par tier (aucune création en masse).
  7. Étapes séparées à fort impact, après confirmation explicite : 05 (renommage/désactivation du compte Administrator intégré) puis 06 (rotation krbtgt en 2 phases espacées d'au moins 24h).
  8. Volet PKI/RDP (ajouté le 2026-07-16, prérequis Vault réalisés hors de ce repo) : 30 publie les 3 CA (bootstrap Vault + Indio Root + Indio Intermediate) dans les conteneurs AD distribués (Trusted Root, AIA, NTAuth) et enrôle le certificat KDC sur le DC courant (à rejouer sur chaque DC) ; 31 GPO de durcissement du transport RDP (NLA, TLS, chiffrement fort) ; 32 GPO d'auto-enrollment/renouvellement du certificat RDP via Vault (Kerberos/SPNEGO, policy sign-only) ; 16 relie 31/32 aux OU réelles ; 33 (gate, fort impact) exige la carte à puce pour T0-Admins — jamais exécuté automatiquement.
  9. Configuration côté produit (Horizon/NSX/vCenter/GitLab/vSAN) des comptes de service créés : manuelle, procédures détaillées dans ACCOUNTS.md.

00-Deploy-All.ps1 enchaîne automatiquement 1-4, 10-16, 20, 22-23 et 30-31-32 (-SkipHighImpact vrai par défaut : n'exécute jamais 05 ni la rotation krbtgt). Les comptes admin nominatifs (21) et l'exigence smart-card (33) restent volontairement hors de l'orchestrateur. L'ensemble est idempotent (vérifications d'existence systématiques dans Common-Functions.ps1) ; un compte déjà existant ne voit jamais son mot de passe réinitialisé par une ré-exécution.

Contrôle de santé (2026-07-17)

Un contrôle de santé complet du domaine (dcdiag /q + repadmin /replsummary + statut des services core NTDS/DNS/Netlogon/KDC/ W32Time) a été réalisé sur les deux contrôleurs de domaine actuels — INDIDEN008 (primaire, tous les rôles FSMO) et INDIDEN009 (promu 2ᵉ DC le 2026-07-16, Global Catalog). Cœur AD sain : 0/5 échec de réplication dans les deux sens, tous les services core RUNNING, SYSVOL identique sur les deux DC (9 dossiers GPO, mêmes GUID).

Trois points relevés dans les avertissements dcdiag, dont deux résolus :

Point État Détail
Relation d'approbation cassée sur INDSERV015 Connu, non bloquant Machine jamais rejointe depuis le rebuild du domaine du 2026-07-15 (nouveau SID) — comportement AD normal (rejet d'un secret de canal obsolète), pas un défaut du domaine. Fix hors périmètre de ce dépôt : rejoindre le domaine ou Reset-ComputerMachinePassword sur cette VM si elle redevient pertinente.
Blip DFSR à la promotion d'INDIDEN009 Auto-résolu Événement 1202 transitoire au moment du Install-ADDSDomainController, normal dans les premières minutes avant stabilisation Netlogon/DNS SRV. Aucune action nécessaire.
URL OCSP Vault erronée (event Schannel 36928 en boucle) Corrigé pki_int/config/urls/pki/config/urls pointaient vers l'IP brute du nœud Vault backend plutôt que vault.infra.indio — cosmétique (LDAPS/KDC/RDP restaient fonctionnels, Windows soft-fail sur OCSP injoignable) mais polluait le journal System en continu sur les deux DC.

Fix OCSP appliqué (vault write pki_int/config/urls issuing_certificates=... crl_distribution_points=... ocsp_servers=https://vault.infra.indio:8200/v1/pki_int/ocsp, config équivalente sur pki). L'URL étant embarquée dans chaque certificat au moment de l'émission (pas relue dynamiquement), les certificats KDC, RDP et LDAPS des deux DC ont été réenrôlés de force après le fix plutôt que d'attendre le renouvellement naturel. Vérifié par extension X.509 réelle (AIA/CDP) sur les nouveaux certificats et par handshake TLS réel (openssl s_client -connect <ip>:636) pour confirmer le certificat LDAPS effectivement servi — une vérification par simple présence dans le store aurait été ambiguë (plusieurs certificats ServerAuth de même Subject cohabitent sur un DC multi-rôles PKI).

NTAuth résolu sur les deux DC : la cause profonde était la confusion entre deux magasins distincts fusionnés à l'affichage — l'objet AD NTAuthCertificates (source « Enterprise », peuplé correctement depuis le 07-16) et le registre local HKLM\SOFTWARE\Microsoft\SystemCertificates\NTAuth\Certificates (source que PKINIT consulte réellement côté DC), que la synchronisation automatique attendue ne remplissait jamais. Fix : certutil -f -addstore NTAuth <fichier> (sans -enterprise) écrit directement la seconde source ; Publish-CertToNTAuth dans Common-Functions.ps1 corrigée en conséquence (commit 47eb21a) pour que tout futur DC traité par le script 30 obtienne NTAuth peuplé localement dès le premier passage. Plus aucun gap bloquant connu côté infrastructure PKI pour un futur script 33 (smart-card) — il reste néanmoins gaté par design.

Points d'attention

Étapes irréversibles à fort impact

Quatre opérations nécessitent une confirmation humaine explicite et ne sont jamais déclenchées automatiquement :

  • 05 renommage/désactivation du compte Administrator intégré.
  • 06 rotation krbtgt (2 phases espacées ≥24h, invalide tous les tickets Kerberos en circulation).
  • 33 exigence de logon par carte à puce pour T0-AdminsSmartcardLogonRequired réinitialise automatiquement le mot de passe du compte (comportement natif AD) : l'accès par mot de passe est perdu tant qu'un certificat carte valide n'a pas été enrôlé au préalable pour chaque compte concerné.
  • Toute liaison de GPO deny-logon croisée (04/16) sans test préalable sur OU=GPO-Staging.
  • Volet PKI/RDP (30-33) : la CA vit entièrement dans Vault (pas d'AD CS natif). Principe central — Vault ne fait que signer des CSR générés localement par certreq (clé privée jamais transmise sur le réseau), jamais émettre. Plusieurs prérequis doivent être réalisés une fois, hors de ce dépôt, sur l'environnement Vault/AD vivant : DNS vault.infra.indio, certificats TLS des nœuds Vault avec le bon SAN, LDAPS actif sur les DC, compte de service Kerberos dédié + keytab, groupes AD dédiés aux rôles de signature, mount auth/kerberos configuré. Chacun de ces prérequis a fait l'objet d'un blocage réel documenté lors de la mise en œuvre du 2026-07-16 — voir le README du dépôt pour le détail.
  • NTAuth nécessite une double publication (conteneur AD et store registre local de chaque DC individuellement) — la propagation automatique ne s'est jamais déclenchée en pratique dans cet environnement. Résolu le 2026-07-17 (voir « Contrôle de santé » ci-dessous) : Publish-CertToNTAuth écrit désormais explicitement les deux sources.
  • Mots de passe générés (comptes de service, comptes admin nominatifs, compte leurre Administrator) : jamais commités ni journalisés, écrits une seule fois dans un fichier local hors dépôt (chemin configurable via ServiceAccountSecretsPath), à basculer immédiatement dans le coffre-fort de secrets Indio puis à supprimer.
  • Comptes de service bind LDAP (svc-horizon-bind, svc-nsx-bind, svc-vcenter-bind, svc-gitlab-bind) : lecture seule stricte sur OU=Tiering et descendants, aucune appartenance à un groupe privilégié, logon interactif/RDP refusé par GPO dédiée. Détail complet des comptes et procédures de raccordement produit dans ACCOUNTS.md.
  • Windows Event Forwarding (13) : ne configure que la GPO côté clients (pointeur vers le collecteur) ; le serveur WEC et le pont vers ELK/syslog-ng restent à construire séparément.
  • AppLocker (15) est déployé en mode audit uniquement — aucune application n'est bloquée tant que le mode Enforce n'est pas activé manuellement après analyse des logs.
  • external-package/Deploy-ADOrganization.ps1 est un script distinct (structure d'OU/groupes générique type PME, modèle AGDLP), adapté à l'organisation réelle Indio mais non intégré à l'orchestrateur 00-Deploy-All.ps1 ni au modèle de tiering du reste du dépôt.