OpenLDAP 구축 가이드입니다. OpenLDAP 2.6의 동적 설정인 cn=config와 MDB backend를 기준으로 TLS, 최소 권한 ACL, 인덱스, 백업·복구와 syncrepl 고가용성을 순서대로 설명합니다. 사용자 인증 기반 서비스이므로 설치 성공보다 암호화, 권한, 정합성과 복구 가능성을 먼저 검증해야 합니다.
Rocky Linux 9에서는 서버 패키지의 제공처와 디렉터리 배치가 선택한 vendor·빌드 방식에 따라 달라질 수 있습니다. 따라서 이 글은 출처가 검증된 OpenLDAP 2.6 패키지 설치를 전제로 하고, 패키지별 경로는 먼저 확인한 뒤 cn=config에 LDAP 연산으로 변경합니다. 호스트 로그인 연동은 nss-pam-ldapd 대신 SSSD의 LDAP provider를 검토하십시오.

OpenLDAP 구축 구성 요소와 설계
| 구성 요소 | 역할 | 운영 기준 |
|---|---|---|
| slapd | LDAP 요청 처리 | 비특권 서비스 계정, systemd, TLS listener |
| cn=config | 실행 중인 동적 설정 DIT | LDAPI + SASL EXTERNAL로 변경 |
| MDB | directory 데이터 저장 | map size, checkpoint, filesystem 용량 |
| ACL | attribute·DN별 권한 | 구체 규칙 우선, 기본 거부 |
| TLS | bind·검색·복제 암호화 | CA 신뢰, hostname, 만료 관리 |
| syncrepl | provider 간 변경 동기화 | 고유 serverID·rid, 시간 동기화 |
| slapcat/slapadd | 논리 백업·복구 | config와 data를 함께 보관 |
OpenLDAP 구축 전 패키지와 경로 확인
패키지 이름을 추측하지 말고 서비스가 사용하는 binary, 설정 DIT, 데이터 디렉터리와 인증서 경로를 기록합니다. OpenLDAP Project의 source build를 선택했다면 configure·test·install 결과와 의존성 버전을 보관하고, vendor 패키지는 GPG 서명과 지원 정책을 확인합니다.
slapd -VVV 2>&1 | head -n 40
ldapsearch -VV 2>&1 | head -n 20
command -v slapd ldapsearch ldapmodify slapcat slapadd slaptest
systemctl cat slapd
systemctl show slapd -p User -p Group -p ExecStart
findmnt -T /var/lib/ldap
df -hT /var/lib/ldap
df -i /var/lib/ldap
OpenLDAP 구축: cn=config와 MDB 확인
LDAPI 로컬 관리자 연결
sudo ldapwhoami -Q -Y EXTERNAL -H ldapi:///
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=config -s base dn cn
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=config '(&(objectClass=olcMdbConfig)(olcSuffix=*))' dn olcSuffix olcRootDN olcDbDirectory olcDbMaxSize olcDbIndex
-Q -Y EXTERNAL은 Unix socket의 peer credential을 사용하므로 cn=config 관리 비밀번호를 command line에 남기지 않습니다. 원격에서 cn=config를 노출하지 말고, 변경 전 현재 entry를 LDIF로 백업합니다. database DN의 {번호}는 설치마다 다르므로 고정값을 추측하지 않습니다.
현재 cn=config 백업
sudo install -d -m 0700 /var/backups/openldap
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=config '(objectClass=*)' '*' '+' | sudo tee "/var/backups/openldap/cn-config-before-$(date +%F-%H%M%S).ldif" >/dev/null
sudo chmod 0600 /var/backups/openldap/*.ldif
sudo sha256sum /var/backups/openldap/*.ldif
OpenLDAP 구축: suffix와 관리자 DN
예시는 dc=example,dc=com과 cn=admin을 사용합니다. 실제 조직 이름, DNS 소유권과 naming 정책을 먼저 확정하십시오. rootDN은 애플리케이션 계정이 아니며 모든 ACL을 우회하므로 긴급 복구용으로만 사용하고 일상 변경은 위임된 관리자 group으로 수행합니다.
MDB database DN 확인과 rootPW 해시 생성
MDB_DN=$(sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=config '(&(objectClass=olcMdbConfig)(olcSuffix=*))' dn | awk '/^dn: / {print substr($0,5); exit}')
printf 'MDB_DN=%s
' "$MDB_DN"
test -n "$MDB_DN"
# 비밀번호는 대화형으로 입력되고 셸 이력에 남지 않습니다.
ROOT_HASH=$(slappasswd)
test -n "$ROOT_HASH"
suffix·rootDN·rootPW 변경
sudo install -m 0600 /dev/null /root/openldap-database.ldif
sudo tee /root/openldap-database.ldif >/dev/null <<EOF
dn: ${MDB_DN}
changetype: modify
replace: olcSuffix
olcSuffix: dc=example,dc=com
-
replace: olcRootDN
olcRootDN: cn=admin,dc=example,dc=com
-
replace: olcRootPW
olcRootPW: ${ROOT_HASH}
EOF
unset ROOT_HASH
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f /root/openldap-database.ldif
sudo shred -u /root/openldap-database.ldif
OpenLDAP 구축: 기본 DIT와 사용자
base DN·조직 단위 생성
install -m 0600 /dev/null base.ldif
cat > base.ldif <<'LDIF'
dn: dc=example,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: Example Directory
dc: example
dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
ou: People
dn: ou=Groups,dc=example,dc=com
objectClass: organizationalUnit
ou: Groups
LDIF
ldapadd -x -W -D 'cn=admin,dc=example,dc=com' -H ldapi:/// -f base.ldif
shred -u base.ldif
테스트 사용자와 관리자 group
USER_HASH=$(slappasswd)
install -m 0600 /dev/null people.ldif
cat > people.ldif <<EOF
dn: uid=alice,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
uid: alice
cn: Alice Example
sn: Example
mail: alice@example.com
userPassword: ${USER_HASH}
dn: cn=ldap-admins,ou=Groups,dc=example,dc=com
objectClass: groupOfNames
cn: ldap-admins
member: uid=alice,ou=People,dc=example,dc=com
EOF
unset USER_HASH
ldapadd -x -W -D 'cn=admin,dc=example,dc=com' -H ldapi:/// -f people.ldif
shred -u people.ldif
사용자 비밀번호는 평문으로 LDIF·Git·티켓에 저장하지 않습니다. 애플리케이션 service account를 개인 계정과 분리하고, groupOfNames의 첫 member를 삭제할 때 schema 제약을 고려합니다. uid와 mail uniqueness는 애플리케이션만 믿지 말고 overlay 또는 provisioning workflow에서 보장합니다.
OpenLDAP 구축: TLS 강제와 검증
simple bind는 TLS 없이 사용하면 비밀번호를 보호하지 못합니다. 서버 인증서의 SAN에는 클라이언트가 실제 접속하는 DNS 이름을 넣고, key는 slapd service 계정만 읽게 합니다. 조직 CA 또는 공인 CA의 chain을 모든 LDAP client와 복제 노드에 배포합니다.
인증서 파일 권한과 유효성
sudo install -d -o root -g ldap -m 0750 /etc/openldap/certs
sudo install -o root -g ldap -m 0640 ldap01.key /etc/openldap/certs/
sudo install -o root -g ldap -m 0644 ldap01.crt /etc/openldap/certs/
sudo install -o root -g ldap -m 0644 org-ca.crt /etc/openldap/certs/
openssl x509 -in /etc/openldap/certs/ldap01.crt -noout -subject -issuer -dates -ext subjectAltName
openssl verify -CAfile /etc/openldap/certs/org-ca.crt /etc/openldap/certs/ldap01.crt
cn=config에 TLS 경로 적용
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// <<'LDIF'
dn: cn=config
changetype: modify
replace: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/openldap/certs/org-ca.crt
-
replace: olcTLSCertificateFile
olcTLSCertificateFile: /etc/openldap/certs/ldap01.crt
-
replace: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/openldap/certs/ldap01.key
-
replace: olcTLSProtocolMin
olcTLSProtocolMin: 3.3
LDIF
sudo systemctl restart slapd
sudo systemctl status slapd --no-pager
StartTLS와 LDAPS hostname 검증
openssl s_client -connect ldap01.example.com:636 -servername ldap01.example.com -CAfile /etc/openldap/certs/org-ca.crt -verify_return_error </dev/null
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapwhoami -x -ZZ -H ldap://ldap01.example.com -D 'uid=alice,ou=People,dc=example,dc=com' -W
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -H ldaps://ldap01.example.com -b 'dc=example,dc=com' '(uid=alice)' dn cn mail
OpenLDAP 구축: 최소 권한 ACL
OpenLDAP ACL은 위에서 아래로 평가되고 첫 번째로 일치한 to와 by 규칙이 결정합니다. 따라서 userPassword 같은 민감 attribute를 먼저 처리하고 일반 규칙을 마지막에 둡니다. 기본 공개 read에 기대지 말고 anonymous는 bind에 필요한 auth만 허용합니다.
sudo tee /root/openldap-acl.ldif >/dev/null <<EOF
dn: ${MDB_DN}
changetype: modify
replace: olcAccess
olcAccess: {0}to attrs=userPassword by self =xw by anonymous auth by * none
olcAccess: {1}to dn.subtree="ou=People,dc=example,dc=com" attrs=entry,children,cn,sn,uid,mail by self write by group.exact="cn=ldap-admins,ou=Groups,dc=example,dc=com" write by users read by * none
olcAccess: {2}to dn.subtree="ou=Groups,dc=example,dc=com" by group.exact="cn=ldap-admins,ou=Groups,dc=example,dc=com" write by users read by * none
olcAccess: {3}to * by users read by * none
EOF
sudo chmod 0600 /root/openldap-acl.ldif
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f /root/openldap-acl.ldif
sudo shred -u /root/openldap-acl.ldif
ACL 허용·거부 시험
# anonymous가 userPassword를 읽지 못해야 합니다.
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -ZZ -H ldap://ldap01.example.com -b 'uid=alice,ou=People,dc=example,dc=com' userPassword
# 본인 bind와 일반 attribute 읽기를 확인합니다.
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -ZZ -H ldap://ldap01.example.com -D 'uid=alice,ou=People,dc=example,dc=com' -W -b 'uid=alice,ou=People,dc=example,dc=com' cn mail
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b "$MDB_DN" olcAccess
OpenLDAP 구축: MDB 인덱스와 상태
인덱스는 실제 search filter와 bind attribute에서 시작합니다. 모든 attribute에 eq·sub를 붙이면 쓰기 비용과 DB 크기가 증가합니다. 변경 전에 access log나 애플리케이션 query를 분석하고, 기존 데이터가 있으면 유지보수 창에서 slapindex를 실행합니다.
sudo tee /root/openldap-index.ldif >/dev/null <<EOF
dn: ${MDB_DN}
changetype: modify
replace: olcDbIndex
olcDbIndex: objectClass eq
olcDbIndex: entryUUID,entryCSN eq
olcDbIndex: uid,mail eq
olcDbIndex: cn,sn eq,sub
EOF
sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f /root/openldap-index.ldif
sudo shred -u /root/openldap-index.ldif
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b "$MDB_DN" olcDbIndex olcDbMaxSize olcDbCheckpoint
OpenLDAP 구축: slapcat 백업과 복구 훈련
백업에는 cn=config와 application suffix가 모두 필요합니다. 단일 노드라면 유지보수 창에 slapd를 중지하고, HA라면 한 replica를 load balancer에서 제외한 뒤 해당 노드에서 일관된 LDIF를 만듭니다. 파일은 암호화해 별도 시스템으로 복사하고 checksum을 기록합니다.
BACKUP_DIR="/var/backups/openldap/$(date +%F-%H%M%S)"
sudo install -d -m 0700 "$BACKUP_DIR"
sudo systemctl stop slapd
sudo slapcat -F /etc/openldap/slapd.d -n 0 | sudo tee "$BACKUP_DIR/cn-config.ldif" >/dev/null
sudo slapcat -F /etc/openldap/slapd.d -b 'dc=example,dc=com' | sudo tee "$BACKUP_DIR/example-data.ldif" >/dev/null
sudo systemctl start slapd
sudo chmod 0600 "$BACKUP_DIR"/*.ldif
sudo sha256sum "$BACKUP_DIR"/*.ldif | sudo tee "$BACKUP_DIR/SHA256SUMS"
sudo journalctl -u slapd --since '-5 min' --no-pager
복구 리허설 검증 항목
sudo slaptest -F /etc/openldap/slapd.d -u
sudo slapcat -F /etc/openldap/slapd.d -n 0 | head
sudo slapcat -F /etc/openldap/slapd.d -b 'dc=example,dc=com' | head
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -ZZ -H ldap://ldap01.example.com -D 'cn=admin,dc=example,dc=com' -W -b 'dc=example,dc=com' '(objectClass=*)' dn | tail
- 격리 호스트의 빈 slapd.d와 데이터 디렉터리에 config·data 순서로 slapadd합니다.
- 패키지 service 계정으로 소유권과 SELinux context를 복원합니다.
- slaptest 후 LDAPI, StartTLS·LDAPS와 hostname 검증을 수행합니다.
- base DN, entry 수, 주요 group member와 password bind를 샘플 검증합니다.
- 애플리케이션 계정의 허용 작업과 anonymous 거부를 다시 시험합니다.
- 복구 시간과 누락 데이터를 기록해 RPO·RTO를 조정합니다.
OpenLDAP 구축: syncrepl과 HA
두 provider의 multiprovider 구성은 두 곳에 무조건 동시에 쓰라는 뜻이 아닙니다. 충돌과 split brain을 피하려면 load balancer가 한 시점에 하나의 활성 쓰기 경로만 선택하고, 읽기 분산·health check·failback을 별도로 설계합니다. 각 serverID와 syncrepl rid는 고유해야 합니다.
syncprov overlay와 checkpoint
sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// <<EOF
dn: olcOverlay=syncprov,${MDB_DN}
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpCheckpoint: 100 10
olcSpSessionlog: 1000
EOF
provider별 syncrepl 구조 예시
dn: cn=config
changetype: modify
replace: olcServerID
olcServerID: 1 ldaps://ldap01.example.com
olcServerID: 2 ldaps://ldap02.example.com
dn: olcDatabase={MDB_NUMBER}mdb,cn=config
changetype: modify
add: olcSyncrepl
olcSyncrepl: rid=001 provider=ldaps://ldap02.example.com bindmethod=simple binddn="uid=replicator,ou=People,dc=example,dc=com" credentials=<REPLICATION_SECRET> searchbase="dc=example,dc=com" type=refreshAndPersist retry="5 5 300 +" timeout=1 tls_reqcert=demand
-
add: olcMultiProvider
olcMultiProvider: TRUE
복제 정합성과 failover 확인
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -H ldaps://ldap01.example.com -b 'dc=example,dc=com' contextCSN
LDAPTLS_CACERT=/etc/openldap/certs/org-ca.crt ldapsearch -LLL -x -H ldaps://ldap02.example.com -b 'dc=example,dc=com' contextCSN
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=Monitor '(objectClass=monitorOperation)' cn monitorOpInitiated monitorOpCompleted
timedatectl status
chronyc tracking
같은 테스트 entry의 add·modify·delete가 반대편에 도착하는지 확인하고, 한 provider를 load balancer에서 제외한 뒤 읽기·쓰기·재가입을 시험합니다. contextCSN이 다르다는 사실만으로 즉시 장애로 단정하지 말고 로그, pending operation, 연결 상태와 수렴 시간을 함께 봅니다.
OpenLDAP 구축 운영 점검
systemctl --no-pager --full status slapd
ss -lntp | grep -E ':(389|636)\b'
sudo journalctl -u slapd --since '-30 min' --no-pager
sudo ldapsearch -LLL -Q -Y EXTERNAL -H ldapi:/// -b cn=Monitor -s base '*' '+'
df -hT /var/lib/ldap /var/log
df -i /var/lib/ldap /var/log
findmnt -T /var/lib/ldap
- systemd exit code와 최초 오류 시각을 확인합니다.
- 389·636 listener가 의도한 주소에만 열렸는지 확인합니다.
- CA chain, SAN, 유효 기간과 client의 hostname 검증을 확인합니다.
- bind DN·base DN·filter·scope와 ACL 순서를 최소 query로 재현합니다.
- MDB map size, filesystem·inode, checkpoint와 장기 query를 확인합니다.
- syncrepl 연결, serverID·rid, contextCSN과 시간 동기화를 확인합니다.
- 최근 slapcat checksum과 격리 복구 훈련 결과를 확인합니다.
자주 하는 실수와 대안
| 실수 | 영향 | 대안 |
|---|---|---|
| 평문 simple bind | 비밀번호 노출 | StartTLS critical 또는 LDAPS + CA 검증 |
| rootDN을 애플리케이션에 사용 | ACL 전체 우회 | service DN과 최소 권한 group |
| anonymous 전체 read | 개인정보·조직도 노출 | password auth만 허용 후 기본 거부 |
| database DN 번호 고정 | 다른 설치의 entry 변경 | cn=config 검색으로 실제 DN 확인 |
| 실행 중 MDB 파일 복사 | 일관성 없는 백업 | slapcat과 복구 시험 |
| 두 provider 동시 무제한 쓰기 | 충돌·split brain | 단일 활성 쓰기 경로와 failover 제어 |
| TLS 검증 생략 | 위조 서버 연결 | CA·SAN·hostname·만료 검증 |
관련 자료
- OpenLDAP 2.6 공식 Administrator’s Guide
- OpenLDAP 공식 Access Control 가이드
- OpenLDAP 공식 Replication 가이드
- Red Hat SSSD LDAP·TLS 클라이언트 가이드
- 리눅스 디스크·inode 부족 진단 가이드
- DRBD·Pacemaker·STONITH HA 가이드
- Ansible 플레이북·Vault·롤링 배포 가이드
OpenLDAP 구축 정리
OpenLDAP 구축의 완료 조건은 bind 성공 한 번이 아닙니다. cn=config와 MDB 경로를 확인하고, TLS hostname 검증, attribute 단위 ACL, 실제 query 기반 인덱스, config·data 동시 백업과 격리 복구를 검증해야 합니다. 고가용성에서는 고유 serverID·rid, 시간 동기화와 단일 활성 쓰기 경로를 지켜야 인증 기반 서비스를 예측 가능하게 운영할 수 있습니다.