LDAP 결과 코드는 RFC 4511 이 정의하며 서버 종류와 무관하게 같다. 메시지 뒤 괄호 안 숫자를 먼저 본다.
| 코드 | 이름 | 흔한 원인 |
|---|---|---|
| 0 | success | 성공 |
| 1 | operationsError | 순서 위반. 바인드 전에 다른 연산을 보냄 |
| 10 | referral | 다른 서버로 넘기라는 응답. AD 다중 도메인에서 흔하다 |
| 32 | noSuchObject | 대상 DN 없음. base DN · 상위 항목 확인 |
| 34 | invalidDNSyntax | DN 문법 오류 |
| 49 | invalidCredentials | 바인드 DN 또는 비밀번호 틀림 |
| 50 | insufficientAccess | 권한 부족 |
| 51 | busy | 서버가 처리 불가 상태 |
| 53 | unwillingToPerform | 서버가 거부. 정책 위반 · 읽기 전용 · 평문 바인드 금지 등 |
| 64 | namingViolation | DN 과 속성값 불일치 |
| 65 | objectClassViolation | 필수 속성 누락 또는 objectClass 조합 오류 |
| 68 | entryAlreadyExists | 같은 DN 이 이미 있음 |
| 19 | constraintViolation | 제약 위반. unique overlay · 비밀번호 정책 |
클라이언트 라이브러리가 내는 음수 코드는 서버 응답이 아니라 클라이언트 쪽 상태다. -1(LDAP_SERVER_DOWN)은 연결 자체가 안 된 것이고, Windows LDAP API 의 81 도 같은 뜻이다. 이 경우 권한이나 DN 이 아니라 이름 해석 · 포트 · TLS 협상을 본다.
ldapwhoami -x -D "uid=haedong,ou=people,dc=example,dc=com" -W
sudo ldapwhoami -Y EXTERNAL -H ldapi:///
cn=config 를 고치는 중이라면 root 로 ldapi:/// EXTERNAL 인증이어야 한다. cn=config 전환과 EXTERNAL 인증 참고.to * 규칙에 먼저 걸렸을 가능성이 높다. 접근 제어(ACL) 작성 참고.AD 에서 계정을 만들다 나오는 problem 4003 (INSUFF_ACCESS_RIGHTS) 도 같은 성격이다. 바인드 계정에 해당 OU 의 객체 생성 권한이 있어야 하고, AD 는 기본적으로 사용자 생성을 위임받은 계정만 허용한다. AD 는 또 비밀번호를 설정하는 연산을 암호화된 연결에서만 받으므로, ldaps:// 나 StartTLS 없이 계정을 만들면 생성은 되고 비밀번호만 실패하는 형태로 나타난다.
dc=example,dc=com 과 dc=exmaple,dc=com.dc=example,dc=com → ou=people,... 순서로 만든다.cn=config 쪽이면 데이터베이스 번호가 다르다.# 트리가 실제로 어떻게 생겼는지
ldapsearch -x -H ldap://localhost -b "dc=example,dc=com" -s base dn
ldapsearch -x -H ldap://localhost -b "dc=example,dc=com" -s one dn
필수 속성이 빠졌거나 구조적 objectClass 를 두 개 넣은 경우다.
dn: uid=haedong,ou=people,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
cn: haedong
sn: kang
uid: haedong
uidNumber: 10001
gidNumber: 10001
homeDirectory: /home/haedong
loginShell: /bin/bash
inetOrgPerson 은 cn 과 sn, posixAccount 는 uid · uidNumber · gidNumber · homeDirectory 를 요구한다. SSSD 로 리눅스 로그인을 시키려면 posixAccount 가 필요하다.
필요한 스키마가 적재돼 있어야 한다. cn=config 라면 cn=schema,cn=config 아래를 확인한다.
ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=schema,cn=config dn
필터는 괄호로 감싸야 한다. uid=haedong 이 아니라 (uid=haedong) 이다.
ldapsearch -x -b "dc=example,dc=com" "(uid=haedong)"
ldapsearch -x -b "dc=example,dc=com" "(&(objectClass=posixAccount)(uid=haedong))"
값에 ( ) * \ NUL 이 들어가면 이스케이프한다(\28 \29 \2a \5c). 애플리케이션 로그에 Unexpected problem with the provided LDAP config 와 함께 필터 파싱 스택이 찍힌다면, 사용자 입력을 그대로 필터에 넣어 특수문자가 깨진 경우를 의심한다. 이는 LDAP 인젝션 경로이기도 하므로 반드시 이스케이프한다.
{0} 같은 자리표시자를 쓰는 제품((sAMAccountName={0}))은 그 표기를 그대로 두어야 하며, 직접 값을 넣어 시험할 때만 치환한다.
systemctl status slapd
journalctl -u slapd -n 100 --no-pager
ss -lntp | grep -E ':389|:636'
dn: olcDatabase={2}mdb,cn=config
changetype: modify
replace: olcDbIndex
olcDbIndex: objectClass eq
olcDbIndex: uid,cn,mail eq,sub
olcDbIndex: uidNumber,gidNumber eq
olcDbIndex: member,memberUid eq
mdb 백엔드는 olcDbMaxSize 만큼 파일을 미리 잡는다. 이 값을 넘기면 쓰기가 실패하므로 여유를 둔다.stats 이상으로 두면 디스크를 빠르게 채운다.dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: stats
nc -vz ldap.example.com 389
openssl s_client -connect ldap.example.com:636 -showcerts </dev/null | head -20
ldapsearch -x -H ldap://ldap.example.com -b "" -s base namingContexts
마지막 명령(RootDSE 조회)은 인증 없이도 대개 응답하므로 연결 확인에 좋다. 여기서 실패하면 자격 증명 문제가 아니다.
LDAPS 에서 unable to get CN from peer certificate 나 TLS: hostname does not match CN 이 나오면 접속 이름과 인증서 이름이 다른 것이다. 클라이언트 쪽 ldap.conf 에 CA 를 지정한다.
# /etc/openldap/ldap.conf
URI ldaps://ldap.example.com
BASE dc=example,dc=com
TLS_CACERT /etc/pki/tls/certs/corp-ca.crt
시험 단계에서만 LDAPTLS_REQCERT=allow 로 검증을 건너뛸 수 있다. 운영에 남기지 않는다.