Короткий ответ

Сначала докажите, что DC отвечает на LDAP 389 и LDAPS 636 с имени, которое прописано в приложении. Потом разделите: неверный DN/пароль (LDAP 49), нет маршрута/firewall, требование LDAP signing / channel binding, просроченный сертификат на DC01. Не отключайте LDAP signing и CBT глобально, чтобы «сайт заработал».

Интерактивный логон Windows при этом может быть жив: приложение часто делает simple bind по IP, без Kerberos и без SAN в сертификате.

Симптомы и как отличить

  • Веб/сервис: invalid credentials, Can't contact LDAP server, SSL handshake failed.
  • Windows-пользователи входят, bind из Java/PHP/Nginx auth_ldap — нет.
  • По IP на 389 проходит, по FQDN на 636 — нет.
СимптомКуда
Нет 389 ни у когоDC недоступен
SRV/имя не резолвитсяDNS
Clock skew, LDAP естьвремя
Учётка lock после bindблокировка

Код LDAP 49 / Windows 52e — учётные данные или DN. 81 — сервер недоступен. Не лечите 49 перезапуском NTDS.

Возможные причины

  1. Неверный Bind DN (cn=app,ou=svc,dc=contoso,dc=example vs UPN).
  2. Приложение ходит на старый IP DC или на VIP без LDAP.
  3. LDAPS: нет слушателя 636, сертификат не на тот CN/SAN, истек, клиент не доверяет CA.
  4. Требуются LDAP signing и channel binding, клиент делает unsigned simple bind.
  5. Firewall: 389/636/3268/3269 (GC) закрыты от VLAN приложения.
  6. Приложение биндится к GC-порту обычным DN без GC.
  7. Учётка приложения истекла, disabled, нет права читать нужный OU.

Диагностика

1. Порты с хоста приложения и с админской станции

Test-NetConnection DC01.contoso.example -Port 389
Test-NetConnection DC01.contoso.example -Port 636
Test-NetConnection DC01.contoso.example -Port 3268
Resolve-DnsName DC01.contoso.example
nltest /dsgetdc:contoso.example /force

Если ping есть, 636 False — это не «AD упал», это TLS-порт или фильтр.

2. Живой LDAP без приложения

$root = [ADSI]'LDAP://DC01.contoso.example/DC=contoso,DC=example'
$root.distinguishedName
Get-ADUser 'svc-ldap' -Properties Enabled, PasswordExpired, LockedOut, LastLogonDate |
  Format-List Enabled, PasswordExpired, LockedOut, DistinguishedName

Simple bind тем же DN, что в конфиге приложения (пароль не логируйте):

$cred = Get-Credential -Message 'Bind DN приложения (не логируйте пароль)'
$de = New-Object System.DirectoryServices.DirectoryEntry(
  'LDAP://DC01.contoso.example',
  $cred.UserName,
  $cred.GetNetworkCredential().Password
)
$de.RefreshCache()
$de.distinguishedName

Практичнее: ldp.exe против DC01.contoso.example Connection → Bind. Ошибка 49 в ldp при тех же DN — конфиг приложения, не сертификат.

3. Сертификат LDAPS

На DC01:

Get-ChildItem Cert:\LocalMachine\My |
  Where-Object { $_.HasPrivateKey } |
  Format-List Subject, NotAfter, Thumbprint, DnsNameList
Get-WinEvent -LogName System -MaxEvents 20 |
  Where-Object { $_.ProviderName -match 'Schannel' }

Имя в SAN должно совпадать со строкой подключения приложения (DC01.contoso.example, не голый IP). Для IP в URL нужен SAN IP — обычно его нет.

4. Signing / CBT

Клиенты после политик Microsoft по LDAP signing ломаются на unsigned simple bind. В журнале Directory Service на DC будут отказы LDAP. Не выключайте требование signing лесом; чините клиент: TLS (636) или SASL/Kerberos.

Решение

Сценарий A. Неверные учётные данные или DN

Выровняйте Bind DN с Get-ADUser. UPN svc-ldap@contoso.example и DN — разные строки. Сбросьте пароль учётки приложения только если 49 подтверждён в ldp, затем обновите все инстансы.

Сценарий B. Сеть

Откройте 389/636 (и GC, если нужен) с VLAN приложения до DC. Не отключайте firewall профиля Domain на DC целиком.

Сценарий C. LDAPS

Выпустите шаблон Kerberos Authentication / Domain Controller Authentication с FQDN в SAN, доверьте CA клиенту. Проверка:

Test-NetConnection DC01.contoso.example -Port 636

В приложении URL ldaps://DC01.contoso.example:636. Не ldaps://10.0.10.10, пока в сертификате нет этого IP.

Сценарий D. Signing

Переведите bind на LDAPS или Negotiate. Временное ослабление LDAP signing на DC — только осознанное исключение в тикете изменений с датой отката, не «галка навсегда».

Сценарий E. Поиск и GC

Приложение ищет пользователя по всему лесу, но биндится на порт 389 обычного DC без GC: объекты других доменов не находятся, это выглядит как «LDAP auth failed». Проверьте base DN: DC=contoso,DC=example vs DC=child,DC=contoso,DC=example. Для леса используйте 3268/3269 и GC, либо явный DC нужного домена.

Таймаут: TCP 389 True, bind висит — смотрите LDAP query logging на DC и сетевой MTU, не «перезапустить NTDS». Фильтры (objectClass=*) без лимита с приложения — отдельная нагрузка, не отказ аутентификации.

Проверка сертификата с клиента (имя должно совпасть):

Test-NetConnection DC01.contoso.example -Port 636

В логе приложения сравните host в URL с SAN. После смены сертификата DC перезапуск NTDS обычно не нужен, но Schannel мог кэшировать старый: новый сеанс с хоста приложения обязателен.

Как проверить, что проблема устранена

С хоста приложения: TCP 389/636 True. ldp bind кодом 0. Приложение логинит тестового пользователя. Повторный bind не плодит 4740. Сертификат NotAfter в будущем, имя совпадает.

Test-NetConnection DC01.contoso.example -Port 636
Get-ADUser 'svc-ldap' -Properties LastLogonDate, LockedOut

Если не помогло

  • 636 открыт, handshake нет: клиент TLS 1.0 vs политика DC; чините стек приложения, не «включайте SSL 2.0».
  • Работает только с одного хоста: локальный hosts/прокси/другой CA store.
  • StartTLS на 389 vs LDAPS 636 — разные режимы; конфиг часто путает.
  • Пора проверять сам DC (NTDS, диск) по статье недоступности.

Профилактика

  • Приложения: LDAPS или Kerberos, не simple bind по паролю пользователя в логах.
  • Мониторинг 636 и срока сертификата DC.
  • Отдельная учётка bind с минимальными правами чтения, не Domain Admin.
  • Фиксация DC через SRV/сайт, не один захардкоженный IP без runbook смены.
  • Перед патчем DC — baseline.

FAQ

LDAP 389 проходит, LDAPS нет. Можно оставить 389?

Для приложений с паролем в канале — нет, это cleartext. Для SASL/Kerberos 389 бывает достаточно. Simple bind на 389 в 2026 году не должен быть целевой моделью.

Нужен ли отдельный сертификат на каждый DC?

На каждом DC, который принимает LDAPS, должен быть свой сертификат с его FQDN (и доп. имена, если клиенты ходят на них). Один файл на все узлы с чужим CN ломает проверку имени.

Порт 3269 — это что?

GC over SSL. Поиск по лесу. Обычный доменный DN на 3269 без понимания GC даёт пустые/неожиданные результаты.

Почему Windows-логон жив, а сайт нет?

Логон использует Kerberos/Netlogon. Сайт — свой bind. Разные протоколы и часто разные учётки.

Event ID выдумывать не буду — где смотреть отказ bind?

Security 4625 на DC для simple bind; Directory Service — LDAP errors; у приложения — его лог с кодом 49/81. Не отключайте аудит, чтобы «стало тише».