Ce document est le premier fichier à lire pour configurer la connexion AD du collecteur. Public cible : admin AD sans expérience préalable d'audit tooling. À la fin vous aurez un fichier JSON d'audit en moins de 15 minutes.
Pour les détails techniques par mode (LDAP/LDAPS/StartTLS), voir ad-connection-modes.md. Pour extraire/installer les certificats TLS, voir ad-tls-certificates.md. Pour les erreurs, voir ad-troubleshooting.md.
Cochez mentalement les réponses - elles déterminent votre chemin.
| # | Question | Réponse OUI | Réponse NON |
|---|---|---|---|
| Q1 | Avez-vous AD Certificate Services (AD CS) installé dans la forêt ? | LDAPS direct, méthode standard | Cert auto-signé sur chaque DC (cas plus rare) |
| Q2 | Le DC répond-il sur le port 636 (LDAPS) ? | LDAPS direct | Tentez StartTLS sur 389 ou installez LDAPS |
| Q3 | Avez-vous un DNS interne qui résout dc01.example.com depuis la machine du collecteur ? |
Utilisez le FQDN | Utilisez l'IP (mais le cert TLS doit avoir une IP SAN) |
| Q4 | Avez-vous un compte AD avec read sur tout le domaine ? | OK | Créez svc-etc-collector (cf. §6) |
| Q5 | Le collecteur tourne-t-il sur Windows ou Linux ? | Détermine où installer le truststore | - |
Si vous avez répondu OUI aux 4 premières questions → suivez le Scénario 1 (cas typique 70% des installations).
┌─────────────────────────────────────┐
│ Avez-vous une AD CS dans la forêt ? │
└───────────────┬─────────────────────┘
OUI ◄─┴─► NON
│ │
│ └──► Scénario 2 (cert auto-signé)
▼
┌────────────────────┐
│ DC répond sur 636 ?│
└────────┬───────────┘
OUI ┴─► NON
│ │
│ └──► Scénario 3 (StartTLS sur 389)
▼
┌──────────────────┐
│ DNS résout FQDN ?│
└────────┬─────────┘
OUI ┴─► NON
│ │
│ └──► Scénario 1bis (utiliser IP + désactiver TLS verify)
▼
┌──────────────────┐
│ ▶ Scénario 1 │
│ LDAPS standard │
└──────────────────┘
Contexte : domaine avec AD CS, DC sur Windows Server 2016+, DNS interne fonctionnel.
Sur le DC (PowerShell admin) :
# Trouve la root CA dans le truststore local
$rootCa = Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -match "<NomDeVotreCA>" } | Select-Object -First 1
# L'exporte en PEM
$bytes = $rootCa.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)
$b64 = [Convert]::ToBase64String($bytes, [Base64FormattingOptions]::InsertLineBreaks)
"-----BEGIN CERTIFICATE-----`n$b64`n-----END CERTIFICATE-----`n" | Set-Content -Path "C:\rootca.pem" -NoNewline
Write-Host "Cert exporté ($($bytes.Length) bytes)"Comment trouver
<NomDeVotreCA>: lancezGet-ChildItem Cert:\LocalMachine\Root | Format-Table Subject- la CA de votre forêt est typiquementCN=<domaine>-<hostname>-CA. Exemple sur dc01 example.com :CN=example-dc01-CA.
- Linux/macOS :
scp administrator@dc01:C:/rootca.pem /etc/etc-collector/rootca.pem - Windows : copie via SMB ou
xcopy
# Vérification réseau + TLS uniquement
openssl s_client -connect dc01.example.com:636 -CAfile /etc/etc-collector/rootca.pem -verify_return_error </dev/null
# Doit afficher "Verify return code: 0 (ok)"etc-collector audit ad \
--ldap-url "ldaps://dc01.example.com:636" \
--ldap-bind-dn "CN=svc-etc-collector,CN=Users,DC=example,DC=com" \
--ldap-bind-password "<mot de passe>" \
--ldap-base-dn "DC=example,DC=com" \
--ldap-ca-cert /etc/etc-collector/rootca.pem \
-o /tmp/audit-result.jsonSi succès : fichier JSON ~3 MB avec ~270 findings (variable selon votre AD).
🔧 Si ça plante : voir §10 ci-dessous ou directement ad-troubleshooting.md.
Vous n'avez pas de DNS qui résout dc01.example.com depuis la machine du collecteur - vous voulez utiliser l'IP.
Problème : le certificat du DC contient le FQDN dans son SAN, pas l'IP. La validation TLS échoue avec LDAP_TLS_IP_SAN_MISSING.
3 solutions :
-
Recommandé : ajouter une entrée
/etc/hosts(Linux) ouC:\Windows\System32\drivers\etc\hosts(Windows) :10.0.0.10 dc01.example.comPuis utilisez le FQDN comme dans le Scénario 1 normal.
-
Compromise : désactiver la vérification TLS - connexion chiffrée mais cert non vérifié, vulnérable au MITM :
etc-collector audit ad \ --ldap-url "ldaps://10.0.0.10:636" \ --ldap-tls-verify=false \ ... (autres flags identiques) -
Régénérer le cert du DC avec une IP SAN : opération admin AD CS, hors scope. Voir ad-tls-certificates.md §8.
Le DC présente son propre certificat auto-signé. Le "CA" est en fait le cert serveur lui-même.
Depuis n'importe quelle machine :
echo | openssl s_client -connect dc01.example.com:636 -showcerts 2>/dev/null \
| awk '/-----BEGIN/,/-----END/' \
| head -30 \
> /tmp/dc-cert.pem
openssl x509 -in /tmp/dc-cert.pem -noout -subject -issuer -dates
# Si Issuer == Subject → auto-signéetc-collector audit ad \
--ldap-url "ldaps://dc01.example.com:636" \
--ldap-bind-dn "..." --ldap-bind-password "..." --ldap-base-dn "..." \
--ldap-ca-cert /tmp/dc-cert.pem \
-o /tmp/audit-result.json
⚠️ Si vous avez plusieurs DCs avec des certs auto-signés différents, il faudra concaténer leurs PEM dans un seul fichier.
Le DC ne répond pas sur 636 mais accepte StartTLS sur 389 (rare en AD natif - AD CS active LDAPS par défaut).
Sur n'importe quelle machine avec OpenSSL :
openssl s_client -starttls ldap -connect dc01.example.com:389 -showcerts 2>/dev/null | head -20
# Si vous voyez un cert s'afficher → StartTLS supportéetc-collector audit ad \
--ldap-url "ldap://dc01.example.com:389" \
--ldap-start-tls \
--ldap-ca-cert /etc/etc-collector/rootca.pem \
--ldap-bind-dn "..." --ldap-bind-password "..." --ldap-base-dn "..." \
-o /tmp/audit-result.jsonLe flag
--ldap-start-tlsupgrade la connexionldap://(cleartext) en TLS chiffré juste après la connexion TCP.
À créer : un compte dédié svc-etc-collector (mot de passe long, non-rotatif, marqué "Account is sensitive and cannot be delegated").
# Sur un DC ou un poste avec RSAT
New-ADUser -Name "svc-etc-collector" `
-SamAccountName "svc-etc-collector" `
-UserPrincipalName "svc-etc-collector@example.com" `
-AccountPassword (Read-Host -AsSecureString "Password") `
-Enabled $true `
-PasswordNeverExpires $true `
-CannotChangePassword $true
# Marquer "sensitive"
Set-ADAccountControl -Identity svc-etc-collector -AccountNotDelegated $true| Capacité | Permission AD requise | Disponible par défaut ? |
|---|---|---|
| Lecture utilisateurs/groupes/computers | Authenticated Users | ✅ Oui |
| Lecture des ACL (DCSync, AdminSDHolder) | Authenticated Users | ✅ Oui (lecture seule, pas d'écriture) |
| Lecture du SYSVOL (audit GPO) | Accès SMB sur \\<domaine>\SYSVOL |
✅ Oui par défaut pour Authenticated Users |
| Lecture audit policy depuis registre DC | Lecture sur HKLM:\System\CurrentControlSet\... |
✅ Oui |
→ Aucun privilège élevé nécessaire. Le compte ne doit PAS être membre de Domain Admins, Enterprise Admins ou Backup Operators.
Le collecteur accepte 3 formats équivalents pour --ldap-bind-dn :
| Format | Exemple | Quand l'utiliser |
|---|---|---|
| Distinguished Name (DN) | CN=svc-etc-collector,CN=Users,DC=example,DC=com |
✅ Recommandé - explicite, pas d'ambiguïté |
| UPN (User Principal Name) | svc-etc-collector@example.com |
✅ Pratique si vous connaissez l'UPN, fonctionne pareil |
| NetBIOS (legacy) | EXAMPLE\svc-etc-collector |
Mot de passe : --ldap-bind-password "valeur". Pour éviter de l'exposer en CLI, utilisez le mode server avec un fichier de config YAML (voir modes/standalone.md).
Une fois tout en place :
etc-collector audit ad \
--ldap-url "ldaps://dc01.example.com:636" \
--ldap-bind-dn "CN=svc-etc-collector,CN=Users,DC=example,DC=com" \
--ldap-bind-password "$LDAP_PASSWORD" \
--ldap-base-dn "DC=example,DC=com" \
--ldap-ca-cert /etc/etc-collector/rootca.pem \
--format json-pretty \
-o /tmp/audit-$(date +%F).json
echo "Findings: $(jq '.audit.summary.risk.findings.total // 0' /tmp/audit-*.json)"
echo "Score: $(jq '.audit.summary.risk.score // 0' /tmp/audit-*.json)"
echo "Frameworks compliance:"
jq -r '.audit.summary.complianceScores[]? | " \(.framework): \(.score) (\(.rating))"' /tmp/audit-*.jsonSortie attendue (exemple - les chiffres varient selon votre AD ; commande revérifiée contre un vrai DC le 2026-09-02, v3.2.0, voir Annexe B) :
Findings: 270
Score: 33
Frameworks compliance:
ANSSI_PA099: 40.4 (high)
ANSSI_BP039: 25 (critical)
ANSSI_GUIDE_HYGIENE: 44.4 (high)
HDS_v1_1: 27.3 (critical)
RGPD: 25 (critical)
NIS2_FR: 14.3 (critical)
CIS_v8: 25 (critical)
NIST_800_53: 0 (critical)
DISA_STIG: 0 (critical)
Avant de lancer un audit complet, vous pouvez vérifier la connexion + lister les assets :
etc-collector discover ad \
--ldap-url "ldaps://dc01.example.com:636" \
--ldap-bind-dn "..." --ldap-bind-password "..." --ldap-base-dn "..." \
--ldap-ca-cert /etc/etc-collector/rootca.pem \
-o /tmp/discover.jsonRenvoie un inventaire (OUs, users, computers, groups) sans aucun détecteur de sécurité - utile pour vérifier les permissions du compte sans charger le DC.
Si votre DC a LdapEnforceChannelBinding=2 (forcé en hardening NIST/CIS récent), le collecteur en v3.1.12 ne supporte pas encore Channel Binding. Symptôme :
Error: LDAP connection failed:
[LDAP_CHANNEL_BINDING_REQUIRED] DC requires LDAP channel binding (LdapEnforceChannelBinding=2)
Workaround temporaire : passer LdapEnforceChannelBinding=1 (mode "supported when possible") sur le DC le temps de l'audit. Vérifiez la valeur :
Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters" | Select LdapEnforceChannelBindingLe support natif est planifié pour une release future (nécessite SASL/GSSAPI côté collecteur).
| Code ETC | Cause | Fix express |
|---|---|---|
LDAP_TLS_UNKNOWN_AUTHORITY |
CA non trouvée | Passez --ldap-ca-cert /chemin/rootca.pem (cf. §3.1) |
LDAP_TLS_IP_SAN_MISSING |
Connexion par IP, cert n'a que des DNS SAN | Utilisez le FQDN (cf. §3bis) |
LDAP_TLS_HOSTNAME_MISMATCH |
URL utilise un nom différent du SAN du cert | Utilisez exactement le nom listé dans le cert |
LDAP_CA_CERT_FILE_NOT_FOUND (v3.1.12) |
--ldap-ca-cert pointe vers un fichier absent |
Vérifier le chemin avec Test-Path ou ls -l |
LDAP_CA_CERT_INVALID_PEM (v3.1.12) |
Fichier passé est binaire (DER) au lieu de texte (PEM) | Convertir : openssl x509 -inform der -in cert.der -out cert.pem |
LDAP_TLS_INVALID_MIN_VERSION (v3.1.12) |
--ldap-tls-min-version n'est pas dans {1.0, 1.1, 1.2, 1.3} |
Utiliser une valeur valide ou omettre le flag |
LDAP_BIND_INVALID_CREDENTIALS |
bindDN ou mot de passe invalide | Vérifiez le DN (essayez UPN), retapez le mot de passe |
LDAP_REFERRAL_BAD_BASE_DN (v3.1.12 fix) |
--ldap-base-dn ne correspond pas au domaine du DC |
(Get-ADDomain).DistinguishedName pour récupérer le bon DN |
dial tcp ...:636: connection refused |
DC n'écoute pas sur 636 | Vérifiez avec Test-NetConnection -ComputerName dc01 -Port 636 ; passez en StartTLS (cf. Scénario 3) |
Pour le runbook complet des 21 codes d'erreur structurés, voir ad-troubleshooting.md.
Sur le DC (PowerShell admin) :
# Snapshot complet de la conf LDAP/LDAPS du DC
$ports = Get-NetTCPConnection -LocalPort 389,636 -State Listen | Select LocalPort
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.EnhancedKeyUsageList.FriendlyName -contains "Server Authentication" } | Select -First 1
$ntds = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters" -ErrorAction SilentlyContinue
$adcs = Get-WindowsFeature -Name AD-Certificate -ErrorAction SilentlyContinue
Write-Host "DC: $env:COMPUTERNAME.$((Get-ADDomain).DNSRoot)"
Write-Host " Listening LDAP : $($ports | Where LocalPort -eq 389 | Select -Expand LocalPort)"
Write-Host " Listening LDAPS: $($ports | Where LocalPort -eq 636 | Select -Expand LocalPort)"
Write-Host " Server cert : $($cert.Subject) (expires $($cert.NotAfter))"
Write-Host " CA issuer : $($cert.Issuer)"
Write-Host " LDAP signing : $($ntds.LDAPServerIntegrity) (1=optional, 2=required)"
Write-Host " Channel binding: $($ntds.LdapEnforceChannelBinding) (0/empty=off, 1=supported, 2=required)"
Write-Host " AD CS installed: $($adcs.Installed)"Sortie type (exemple - script relu et cohérent avec un DC réel testé le 2026-09-02, v3.2.0, voir Annexe B) :
Correction (2026-09-08) : le script tel qu'il était documenté filtrait
Cert:\LocalMachine\Mypar$_.Subject -match "$env:COMPUTERNAME"
- sur un DC dont l'Enterprise CA suit la convention de nommage standard décrite au §3.1 (
CN=<domaine>-<hostname>-CA), le nom d'hôte apparaît aussi dans le CN de la CA, qui peut donc matcher avant le vrai certificat serveur selon l'ordre du store - rejoué en direct contre DC01 (Windows Server 2022, CAexample-DC01-CA) : le script original affichait bien le CN de la CA en « Server cert », pas celui du DC. Corrigé : filtre sur$_.EnhancedKeyUsageList.FriendlyName -contains "Server Authentication"au lieu du nom d'hôte - rejoué avec le correctif, résultat conforme à l'exemple ci-dessous (Server cert=CN=dc01.example.com,CA issuer= la CA).
DC: dc01.example.com
Listening LDAP : 389
Listening LDAPS: 636
Server cert : CN=dc01.example.com (expires 11/17/2026 ...)
CA issuer : CN=example-dc01-CA, DC=example, DC=cc
LDAP signing : 1 (1=optional, 2=required)
Channel binding: (0/empty=off, 1=supported, 2=required)
AD CS installed: True
Doc revalidée le 2026-09-02 contre un vrai DC de lab (Windows Server
2022, AD CS installé, hostname réel dans le certificat), binaire pro compilé
depuis le code courant, conteneur jetable sur un hôte de démo dédié. Remplace
la validation précédente (2026-04-22, v3.1.12) - quatre mois s'étaient
écoulés, avec l'édition unique, la bascule de licence, la fusion générique du
registre et d'autres changements dans l'intervalle. Détail complet
(commandes, sorties collées) : docs/rejeu-doc/docs/BILAN-T140.md.
La date d'ensemble (2026-09-02) reste valable : 17 des 18 scénarios sont inchangés. Seul le scénario 9 a un résultat différent depuis, daté à part dans sa propre note ci-dessous - un correctif du classifieur (2026-09-03), pas une nouvelle divergence d'environnement.
| # | Scénario | Résultat | Code ETC |
|---|---|---|---|
| 1 | LDAPS IP :636 + tls-verify=false |
✅ succès | - |
| 2 | LDAPS IP :636 + tls-verify=true |
❌ rejeté | LDAP_TLS_IP_SAN_MISSING |
| 3 | LDAPS FQDN :636 + tls-verify=true (system trust) |
✅ succès | - |
| 4 | LDAPS FQDN :636 + --ldap-ca-cert rootca.pem |
✅ succès | - |
| 5 | LDAPS FQDN + chemin ca-cert invalide | ❌ rejeté | LDAP_CA_CERT_FILE_NOT_FOUND |
| 6 | LDAP plain :389 (DC avec LDAPServerIntegrity=1) |
✅ succès | - |
| 7 | LDAP+StartTLS :389 + tls-verify=false |
✅ succès | - |
| 8 | LDAP+StartTLS :389 FQDN + ca-cert |
✅ succès | - |
| 9 | LDAPS + --ldap-tls-min-version 1.3 |
❌ rejeté (voir note) | LDAP_TLS_VERSION_MISMATCH |
| 10 | LDAPS + --ldap-tls-min-version 1.0 |
✅ succès | - |
| 11 | LDAPS + --ldap-tls-min-version xxx (invalide) |
❌ rejeté | LDAP_TLS_INVALID_MIN_VERSION |
| 12 | bindDN format DN complet | ✅ succès | - |
| 13 | bindDN format UPN (admin@domaine) |
✅ succès | - |
| 14 | bindDN format NetBIOS (DOMAINE\admin) |
- | |
| 15 | Mot de passe invalide | ❌ rejeté | LDAP_BIND_INVALID_CREDENTIALS (AD code 52e) |
| 16 | Base DN correct | ✅ (= test 1) | - |
| 17 | Base DN inexistant DC=fake,DC=domain |
❌ rejeté | LDAP_REFERRAL_BAD_BASE_DN |
| 18 | Base DN sub-tree (CN=Users,DC=...) |
✅ succès (audit partiel) | - |
→ 17/18 réussissent tels que documentés ; les 6 scénarios ❌ sont des rejets voulus (config invalide ou refusée par le DC), pas des échecs de la doc - chacun ressort avec le code structuré attendu, sortie collée dans le BILAN.
Scénario 9 - divergence trouvée le 2026-09-02, corrigée dans le
code, rejouée ici le 2026-09-03 : l'annexe v3.1.12 affirmait
« ✅ (DC supporte 1.3) ». Sur ce DC réel (Windows Server 2022), forcer
--ldap-tls-min-version 1.3 échoue toujours - vérifié indépendamment du
collecteur (openssl s_client -tls1_3 contre le DC : reset de connexion,
-tls1_2 : négociation TLS 1.2 normale). Ce DC ne propose pas TLS 1.3 sur
ce listener LDAPS, fait d'environnement inchangé depuis.
Ce qui a changé : le 2026-09-02, ce refus produisait [LDAP_UNKNOWN_ERROR] le classifieur ne reconnaissait que les alertes TLS propres
(tls: protocol version not supported), jamais le reset TCP brut que ce DC
émet réellement. Corrigé côté code
(ClassifyHandshake, internal/providers/ldap/errors.go) ; rejoué le
2026-09-03 avec la même commande, contre le même DC :
[LDAP_TLS_VERSION_MISMATCH] cette fois, comme documenté dans
ad-troubleshooting.md
(voir la portée exacte du correctif à cet endroit - il ne couvre qu'une
version forcée explicitement et une coupure pendant la poignée de main,
pas un refus de version en général).