데이터를 넣는 방법은 둘이고, 고르는 기준은 slapd 가 떠 있느냐다.
| 도구 | 조건 | 쓰는 자리 |
|---|---|---|
ldapadd · ldapmodify |
slapd 기동 중 | 운영 중 추가·수정. 접근 제어와 overlay 가 적용된다 |
slapadd |
slapd 중지 상태 | 초기 대량 적재. 검증과 overlay 를 건너뛰어 훨씬 빠르다 |
slapadd 를 slapd 가 떠 있는 상태에서 돌리면 데이터베이스가 깨진다. 예외 없다.
상위 항목이 먼저 와야 한다. ou=People 없이 그 아래 사용자를 넣으면 No such object 로 끊긴다.
dn: dc=example,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: Example Corp
dc: example
dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
ou: People
dn: uid=testuser,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
cn: Test User
sn: User
uid: testuser
uidNumber: 10001
gidNumber: 10001
homeDirectory: /home/testuser
loginShell: /bin/bash
mail: testuser@example.com
userPassword: {SSHA}${HASHED_PASSWORD}
리눅스 로그인(SSSD · nss)에 쓰려면 posixAccount 와 uidNumber · gidNumber · homeDirectory 가 반드시 있어야 한다. 이것이 빠지면 LDAP 에서 검색은 되는데 id 명령에는 안 잡히는 상태가 된다.
비밀번호 해시는 slappasswd 로 만든다.
slappasswd -s '${PASSWORD}'
파일은 UTF-8 로 저장한다. LDIF 는 UTF-8 을 전제하므로 다른 인코딩이면 한글 속성값이 깨진다. 값에 줄바꿈이나 비ASCII 가 섞이면 base64 로 인코딩해 :: 로 구분해 적어야 한다.
ldapadd -x -H ldap://localhost:389 \
-D "cn=admin,dc=example,dc=com" -W \
-f users.ldif
| 옵션 | 뜻 |
|---|---|
-x |
simple bind (SASL 대신) |
-D |
bind DN |
-W |
비밀번호를 프롬프트로 입력. 스크립트라면 -y <파일> 을 쓴다 |
-H |
LDAP URI |
-f |
LDIF 파일 |
-c |
오류가 나도 계속 진행. 일부만 이미 있는 경우에 쓴다 |
이미 있는 항목이 섞여 있으면 ldapadd 는 그 줄에서 멈춘다. -c 를 주면 건너뛰고 계속한다. 기존 항목을 갱신해야 한다면 ldapmodify 로 changetype: modify 를 쓴다.
비밀번호를 명령줄에 -w 로 적지 않는다. 셸 히스토리와 프로세스 목록에 남는다.
systemctl stop slapd
slapadd -F /etc/openldap/slapd.d -n <DB번호> -l users.ldif
chown -R ldap:ldap /var/lib/ldap
systemctl start slapd
-n 에 넣을 DB 번호는 환경마다 다르므로 확인하고 쓴다. cn=config 구성에서 {0}config · {1}monitor 다음에 실제 데이터 DB 가 오는 것이 흔하지만 전제할 수 없다.
slapcat -F /etc/openldap/slapd.d -n 0 | grep -E '^dn: olcDatabase'
출력에서 olcDatabase={2}mdb,cn=config 처럼 나오면 그 숫자가 -n 값이다.
chown 을 빠뜨리면 slapd 가 다시 뜨지 않는다. slapadd 를 root 로 돌렸으므로 데이터 파일이 root 소유가 되기 때문이다. 이것이 초기 적재 후 기동 실패의 첫 번째 원인이다.
데이터 디렉터리를 PVC 로 마운트했다면, LDIF 를 PVC 에 직접 넣을 생각을 하지 않는다. PVC 안에는 LDIF 가 아니라 MDB 데이터 파일이 들어 있다. slapd 에게 LDIF 를 처리하게 하면 결과가 자연히 PVC 에 남는다.
kubectl -n <ns> get pods
kubectl -n <ns> cp users.ldif openldap-0:/tmp/users.ldif
kubectl -n <ns> exec -it openldap-0 -- \
ldapadd -x -H ldap://localhost:389 \
-D "cn=admin,dc=example,dc=com" -W \
-f /tmp/users.ldif
kubectl cp 는 컨테이너 안에 tar 가 있어야 동작한다. 최소 이미지에는 없을 수 있는데, 그때는 표준 입력으로 밀어 넣는다.
kubectl -n <ns> exec -i openldap-0 -- \
ldapadd -x -H ldap://localhost:389 \
-D "cn=admin,dc=example,dc=com" -w "${LDAP_ADMIN_PASSWORD}" \
< users.ldif
초기화 시점에만 적재하면 되는 경우라면, 이미지에 따라 /docker-entrypoint-initdb.d/ 같은 디렉터리에 LDIF 를 두면 첫 기동 때 자동으로 읽는 것도 있다. 데이터가 이미 있으면 실행되지 않으므로 운영 중 추가에는 쓸 수 없다.
# 항목이 들어갔는지
ldapsearch -x -H ldap://localhost:389 \
-D "cn=admin,dc=example,dc=com" -W \
-b "dc=example,dc=com" "(uid=testuser)"
# 그 계정으로 인증이 되는지
ldapwhoami -x -H ldap://localhost:389 \
-D "uid=testuser,ou=People,dc=example,dc=com" -W
# 전체 건수
ldapsearch -x -H ldap://localhost:389 \
-D "cn=admin,dc=example,dc=com" -W \
-b "dc=example,dc=com" "(objectClass=posixAccount)" dn | grep -c '^dn:'
| 증상 | 원인 |
|---|---|
No such object |
상위 DN 이 아직 없다. LDIF 순서를 확인한다 |
Already exists |
같은 DN 이 이미 있다. -c 로 건너뛰거나 ldapmodify 로 바꾼다 |
Object class violation |
필수 속성이 빠졌다. inetOrgPerson 은 cn 과 sn 이 필수다 |
Invalid syntax |
속성 값 형식이 스키마와 맞지 않는다. 앞뒤 공백도 문제가 된다 |
Undefined attribute type |
스키마가 적재돼 있지 않다. cosine · nis · inetorgperson 스키마를 먼저 넣는다 |
| SSSD 에서 사용자가 안 보인다 | posixAccount 가 없거나 ldap_user_search_base 가 다르다 |
| 적재 후 slapd 가 안 뜬다 | slapadd 후 /var/lib/ldap 소유권을 되돌리지 않았다 |
| 한글 속성이 깨진다 | LDIF 가 UTF-8 이 아니다 |
ldapadd 가 권한으로 막힐 때 볼 곳.