Короткий ответ
Сначала докажите, что 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.
Возможные причины
- Неверный Bind DN (
cn=app,ou=svc,dc=contoso,dc=examplevs UPN). - Приложение ходит на старый IP DC или на VIP без LDAP.
- LDAPS: нет слушателя 636, сертификат не на тот CN/SAN, истек, клиент не доверяет CA.
- Требуются LDAP signing и channel binding, клиент делает unsigned simple bind.
- Firewall: 389/636/3268/3269 (GC) закрыты от VLAN приложения.
- Приложение биндится к GC-порту обычным DN без GC.
- Учётка приложения истекла, 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, DistinguishedNameSimple 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. Не отключайте аудит, чтобы «стало тише».