OpenLDAP 의 권한은 access to 규칙(정적 slapd.conf) 또는 olcAccess 속성(동적 cn=config)으로 정한다. 형식은 같다.
access to <무엇을>
by <누가> <어느 수준>
by <누가> <어느 수준>
by * none
두 가지만 기억하면 대부분의 사고를 피할 수 있다.
to * 가 잡아 버리면 도달하지 않는다. 좁은 규칙을 위에, 넓은 규칙을 아래에 둔다.by 절이 적용된다. 이후 절은 보지 않는다. 계속 평가시키려면 break 또는 continue 를 쓴다.| 수준 | 포함 범위 |
|---|---|
none |
접근 불가 |
disclose |
존재 여부만 오류 메시지로 노출 |
auth |
인증(바인드) 용도로만 사용 |
compare |
값 비교 |
search |
필터 적용 |
read |
값 읽기 |
write |
추가 · 수정 |
manage |
전부. 관리 작업 포함 |
아래 수준은 위 수준을 포함한다. 그러므로 manage 는 꼭 필요한 계정에만 준다.
| 표기 | 의미 |
|---|---|
dn.base="..." / dn.exact="..." |
그 항목 하나 |
dn.one="..." |
바로 아래 한 단계 |
dn.subtree="..." |
그 항목과 모든 하위 |
dn.children="..." |
하위만. 자기 자신 제외 |
attrs=userPassword |
특정 속성 |
* |
전부 |
by 쪽에도 같은 표기를 쓴다. dn.exact 는 그 DN 하나, dn.subtree 는 그 아래 모든 사용자를 뜻하므로 권한을 줄 때 의미가 크게 달라진다. self 는 자기 자신의 항목, anonymous 는 미인증 연결, users 는 인증된 모든 사용자다.
# 비밀번호는 인증에만 쓰고 아무도 읽지 못하게 한다
access to attrs=userPassword,shadowLastChange
by self write
by anonymous auth
by * none
# 관리자에게 전체 권한
access to *
by dn.exact="uid=admin,ou=people,dc=example,dc=com" manage
by dn.exact="cn=admin,dc=example,dc=com" manage
by users read
by * none
userPassword 규칙이 맨 위에 있어야 한다. 아래에 두면 access to * 가 먼저 잡아 해시가 노출된다. by anonymous auth 는 아직 인증하지 않은 연결이 비밀번호를 대조만 할 수 있게 하는 것으로, 이것이 없으면 simple bind 자체가 실패한다.
하위 트리에만 권한을 주면 항목을 추가할 수 없는 경우가 있다.
# 이것만 있으면 부모 탐색이 막혀 추가 · 수정이 실패한다
access to dn.subtree="ou=team,dc=example,dc=com"
by dn.exact="uid=haedong,ou=people,dc=example,dc=com" manage
by * none
OpenLDAP 의 ACL 은 상속되지 않는다. 트리를 따라 내려가려면 상위 항목에 대한 읽기 · 검색 권한이 따로 있어야 하고, 항목을 추가하려면 부모 항목에 대한 write 가 필요하다.
access to dn.base="dc=example,dc=com"
by dn.exact="uid=haedong,ou=people,dc=example,dc=com" read
by users read
by * none
access to dn.subtree="ou=team,dc=example,dc=com"
by dn.exact="uid=haedong,ou=people,dc=example,dc=com" manage
by * none
# 현재 규칙 확인. 데이터베이스 번호를 먼저 본다
ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config \
"(objectClass=olcDatabaseConfig)" dn olcSuffix olcRootDN olcAccess
# acl.ldif - 전체 교체
dn: olcDatabase={2}mdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {1}to * by dn.exact="uid=admin,ou=people,dc=example,dc=com" manage by users read by * none
sudo ldapmodify -Y EXTERNAL -H ldapi:/// -f acl.ldif
{0} · {1} 은 평가 순서다. 생략하면 뒤에 붙는다. add 로 한 줄만 넣으면 기존 규칙 뒤에 들어가 to * 뒤에 가려질 수 있으므로, 순서가 중요한 변경은 replace 로 전체를 다시 쓰는 편이 안전하다.
cn=config 수정은 반드시 root 로 ldapi:/// 에 EXTERNAL 인증해야 한다. 자세한 것은 OpenLDAP cn=config 전환과 EXTERNAL 인증 에 있다.
# 적용된 규칙
ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b "olcDatabase={2}mdb,cn=config" olcAccess
# 해당 계정으로 실제 읽기 · 쓰기 시험
ldapwhoami -x -D "uid=haedong,ou=people,dc=example,dc=com" -W
ldapsearch -x -D "uid=haedong,ou=people,dc=example,dc=com" -W -b "dc=example,dc=com" dn
ldapadd -x -D "uid=haedong,ou=people,dc=example,dc=com" -W -f test.ldif
의도대로 걸리지 않으면 ACL 처리 과정을 로그로 본다.
dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: acl stats
slapd.conf 를 쓰는 구성이면 slaptest -f /etc/openldap/slapd.conf -v 로 문법을 먼저 검사한다.
access to * by * read 한 줄로 끝내면 userPassword 해시까지 익명에게 공개된다. 편의를 위해 임시로 넣었다가 그대로 남는 사례가 많다.rootdn(olcRootDN)은 ACL 을 우회한다. 이 계정으로는 모든 시험이 성공하므로 권한 확인은 반드시 일반 계정으로 한다.by group.exact="cn=ldap-admins,ou=groups,dc=example,dc=com" manage 형태이며 대상 그룹은 groupOfNames 여야 한다.