Windows Server 2025 로 새로 배포한 도메인 컨트롤러는 "Domain controller: LDAP server signing requirements enforcement" 정책에 따라 기본적으로 서명된 LDAP 통신을 요구한다. Channel binding 기본값은 "When supported" 다. 2019 · 2022 는 기본값이 None 이라 서명 없는 bind 가 통했지만 2025 는 거부한다.
| 방식 | 2025 DC 에서 |
|---|---|
| LDAPS(636), StartTLS, SASL(Kerberos/NTLM) signing | 동작 |
| 389 평문 + Simple Bind(서명 없음) | 거부. 레거시 어플라이언스 · 방화벽 · 프린터가 여기 걸린다 |
호환성 때문에 서명 강제를 풀어야 한다면 Default Domain Controllers Policy 의 Computer Configuration → Policies → Windows Settings → Security Settings → Local Policies → Security Options 에서 신규 정책("LDAP server signing requirements enforcement") 을 Disabled 로 두고 기존 "LDAP server signing requirements" 를 None 으로, channel binding 을 "When Supported" 로 둔다. 보안상 임시 조치로만 쓴다.
전환 전에는 NTDS 진단 로깅을 켜고 Event ID 2889 · 3039 · 3074 · 3075 로 서명 없이 bind 하는 클라이언트 · 계정 · IP 를 식별한다.
ldap_bind: Strong(er) authentication required (8)
00002028: LdapErr: DSID-0C09035C, comment: The server requires binds to turn on
integrity checking if SSL\TLS are not already active on the connection, data 0, v65f4
DSID-0C09035C 는 AD 가 LDAP signing 정책 위반을 잡아낸 코드 위치다. 계정이나 비밀번호 문제가 아니라 정책상 평문 Simple Bind 를 거부한 것이다. 다른 DSID 와 data 조합은 다음과 같다.
| DSID + data | 의미 |
|---|---|
DSID-0C09042F + data 52e |
비밀번호 오류 |
DSID-0C090334 + data 525 |
사용자 없음 |
DSID-0C090453 + data 533 |
계정 비활성화 |
DSID-0C0903A9 + data 775 |
계정 잠김 |
ldapsearch 결과 코드는 0 Success, 49 Invalid credentials, 8 Strong(er) auth required(서버가 서명 요구), 32 No such object(base DN 오류) 로 갈린다.
# 1) 연결성 + RootDSE (익명)
ldapsearch -x -H ldap://<DC>:389 -s base -b "" "(objectclass=*)" namingContexts defaultNamingContext
# 2) 평문 Simple Bind — 2025 DC 또는 signing 강제 환경에서는 실패가 정상
ldapsearch -x -H ldap://<DC>:389 -D "<bind-user>@<domain>" -W \
-b "DC=<...>,DC=<...>" "(sAMAccountName=<user>)" dn sAMAccountName userPrincipalName memberOf
# 3) LDAPS
ldapsearch -x -H ldaps://<dc-fqdn>:636 -D "CN=<bind>,OU=<..>,DC=<..>" -W \
-b "DC=<...>" "(sAMAccountName=<user>)" dn
# 4) StartTLS (389 위에서 TLS 로 업그레이드)
ldapsearch -x -ZZ -H ldap://<DC>:389 -D "CN=<bind>,OU=<..>,DC=<..>" -W \
-b "DC=<...>" "(sAMAccountName=<user>)" dn
# 5) 인증서 검증만 우회해 원인 분리 (테스트 전용)
LDAPTLS_REQCERT=never ldapsearch -x -ZZ -H ldap://<DC>:389 -D "CN=<bind>,..." -W \
-b "DC=<...>" "(sAMAccountName=<user>)" dn
# 6) 어디서 죽는지
ldapsearch -x -H ldaps://<DC>:636 -d 1 -D "CN=<bind>,..." -W -b "DC=<...>" "(sAMAccountName=<user>)" dn 2>&1 | head -50
-Z 는 StartTLS 를 시도하고 실패하면 평문으로 계속 진행하지만, -ZZ 는 TLS 협상이 성공해야만 진행하고 실패하면 즉시 중단한다. 평문으로 비밀번호가 새지 않도록 운영에서는 항상 -ZZ 를 쓴다.
이 사례에서는 LDAPS(636) 가 Can't contact LDAP server (-1) 로 막혀 있었고(호스트별 방화벽 차이), 389 + StartTLS + LDAPTLS_REQCERT=never 조합으로 result: 0 Success 를 받았다. 중간에 LDAPTLS_REQUEST=never 라는 존재하지 않는 변수명을 써서 검증이 계속 걸렸다. 정확한 이름은 LDAPTLS_REQCERT 다.
서버(DC) 쪽에 서버 인증서가 있어야 636 이 listen 된다. AD CS(Enterprise CA) 와 자동 등록이 있으면 관리자가 따로 하지 않아도 DC 가 인증서를 받아 LDAPS 가 활성화된다. 인증서가 없으면 636 자체가 열리지 않아 클라이언트에는 Can't contact LDAP server 로 보인다.
클라이언트 쪽은 DC 인증서를 검증만 한다. 우리 인증서를 만들 필요는 없고, 사내 CA 루트를 신뢰 저장소에 등록하면 된다.
openssl s_client -connect <DC-IP>:636 -showcerts </dev/null 2>&1 | grep -E "subject=|issuer=|Verify return code"
subject= 가 보이면 DC 인증서가 있는 것이고, Verify return code: 21 (unable to verify) 는 CA 신뢰만 없는 상태다. 운영에서는 다음처럼 등록한다.
openssl x509 -inform DER -in ad-root-ca.cer -out ad-root-ca.pem
cp ad-root-ca.pem /etc/openldap/certs/
echo "TLS_CACERT /etc/openldap/certs/ad-root-ca.pem" >> /etc/openldap/ldap.conf
echo "TLS_REQCERT demand" >> /etc/openldap/ldap.conf
IP 로 LDAPS 에 붙으면 DC 인증서가 FQDN 으로 발급돼 있어 CN/SAN 불일치로 실패할 수 있다. 운영에서는 FQDN 을 쓴다.
kubectl exec -it <pod> -n <ns> -- /bin/bash
which ldapsearch || dnf install -y openldap-clients # 또는 apt-get install ldap-utils, apk add openldap-clients
# 설치 권한이 없으면 디버그 파드
kubectl run ldap-test --rm -it --restart=Never --image=alpine:latest -n <ns> -- sh -c "apk add openldap-clients bind-tools && sh"
# 순서: DNS → 포트 → bind
nslookup <dc-fqdn> || getent hosts <dc-fqdn>
nc -zv <dc-fqdn> 389
nc -zv <dc-fqdn> 636
파드는 호스트의 /etc/resolv.conf 를 직접 보지 않고 CoreDNS 를 거치며, CoreDNS 는 클러스터 외 도메인을 노드의 /etc/resolv.conf 로 포워딩한다. 노드가 AD 도메인을 못 풀면 파드도 못 푼다. 내부 이름은 풀리는데 AD 도메인만 실패하면 CoreDNS 업스트림 문제다. 조치는 노드 /etc/resolv.conf 에 AD DNS 추가, CoreDNS ConfigMap 에 도메인 전용 forwarder 추가, 파드의 hostAliases, 파드의 dnsConfig 순으로 검토한다.
example.local:53 {
errors
cache 30
forward . <ad-dns-ip-1> <ad-dns-ip-2>
}
kubectl -n kube-system edit configmap coredns
kubectl -n kube-system rollout restart deployment coredns
Cloudera Manager 환경에서는 hue.ini 를 직접 고치면 덮어써지므로 Safety Valve 로 넣는다.
[desktop]
[[ldap]]
ldap_url=ldaps://<dc-fqdn>:636
ldap_cert=/etc/hue/conf/ad-root-ca.pem
ldap_validate_certs=true
base_dn="DC=<...>,DC=<...>"
search_bind_authentication=true
create_users_on_login=true
bind_dn="CN=<bind-acct>,OU=<OU>,DC=<...>,DC=<...>"
bind_password_script=/etc/hue/conf/bind_pw.sh
[[[users]]]
user_filter="objectclass=user"
user_name_attr="sAMAccountName"
[[[groups]]]
group_filter="objectclass=group"
group_name_attr="cn"
group_member_attr="member"
StartTLS 로 갈 때는 ldap_url=ldap://<dc-fqdn>:389 에 use_start_tls=true 를 더한다. CA 인증서는 모든 Hue 노드에 배포하고 소유자를 hue 로 둔다.
CDSW 는 외부 인증(LDAP/AD/SAML) 설정 실수로 전원이 로그인하지 못하는 상황을 대비해 디버그 로그인 URL 을 제공한다. http://cdsw.<도메인>/login?debug=1 로 접속하면 최초 사인업 때 만든 로컬 admin 계정으로 들어갈 수 있고, 비밀번호를 잊었으면 /forgot-password 에 직접 접근한다. 다만 이 디버그 경로를 비활성화하는 옵션이 있으므로, 설정을 바꾸기 전에 디버그 로그인이 실제로 되는지와 로컬 admin 비밀번호를 먼저 확인한다. 변경은 기존 admin 세션을 열어 둔 채 새 창에서 테스트한다.