Redmine 은 관리 → LDAP 인증에서 디렉터리 서버를 등록한다. Active Directory 를 대상으로 할 때 값을 잘못 넣기 쉬운 자리가 정해져 있다.
| 항목 | AD 기준 값 |
|---|---|
| 호스트 · 포트 | ad.example.com · 389 (LDAPS 는 636) |
| 계정 | 검색용 바인드 계정의 DN. CN=svc_redmine,OU=service,DC=example,DC=com |
| 검색 기준 DN | DC=example,DC=com |
| 로그인 속성 | sAMAccountName |
| 이름 속성 | givenName |
| 성 속성 | sn |
| 메일 속성 | mail |
OpenLDAP 의 관례를 따라 로그인 속성에 uid 를 넣어 두면 AD 에서는 매칭되지 않는다. AD 는 sAMAccountName 또는 userPrincipalName 을 쓴다.
"온디맨드 사용자 생성" 을 켜면 LDAP 인증에 성공한 사용자가 Redmine 계정 없이도 첫 로그인 때 만들어진다. 끄면 미리 만들어 둔 계정만 로그인할 수 있다.
디렉터리 전체가 아니라 특정 그룹 구성원만 들여보내려면 LDAP 필터를 쓴다. 사용자와 그룹이 서로 다른 OU 에 있어도 상관없다. 필터는 DN 값으로 비교하므로 사용자가 어느 OU 에 있든 그룹 소속만 맞으면 통과한다.
(|(memberOf=CN=redmine_users,OU=system,DC=example,DC=com)(memberOf=CN=redmine_admins,OU=system,DC=example,DC=com))
AD 에서 그룹 안에 그룹이 들어 있는 구조(중첩 그룹)라면 위 필터로는 직접 소속만 잡힌다. 중첩까지 따라가려면 AD 전용 매칭 규칙 OID 를 붙인다.
(|(memberOf:1.2.840.113556.1.4.1941:=CN=redmine_users,OU=system,DC=example,DC=com)(memberOf:1.2.840.113556.1.4.1941:=CN=redmine_admins,OU=system,DC=example,DC=com))
1.2.840.113556.1.4.1941 은 LDAP_MATCHING_RULE_IN_CHAIN 으로, 소속 관계를 재귀적으로 평가한다. AD 전용이므로 OpenLDAP 에서는 쓰지 못한다.
Redmine 의 "연결 테스트" 는 바인드 계정으로 접속이 되는지만 본다. 즉 테스트 성공은 필터가 맞다는 뜻이 아니다. 로그인만 실패한다면 원인은 셋 중 하나다.
가장 흔하다. 쉼표 자리에 마침표가 들어가는 실수가 잦고, 이 경우 오류 없이 조용히 매칭에 실패한다.
dc=example.dc=com 잘못됨
dc=example,dc=com 맞음
실제 DN 을 서버에서 읽어 그대로 복사해 넣는다.
ldapsearch -x -LLL -H ldap://ad.example.com \
-D "CN=svc_redmine,OU=service,DC=example,DC=com" -W \
-b "DC=example,DC=com" "(cn=redmine_users)" dn
AD 는 memberOf 를 자동으로 채워 주지만 OpenLDAP 은 기본적으로 채우지 않는다(memberof 오버레이를 켜야 한다). 대상 사용자에게 실제로 그 속성이 붙어 있는지 확인한다.
ldapsearch -x -LLL -H ldap://ad.example.com \
-D "CN=svc_redmine,OU=service,DC=example,DC=com" -W \
-b "DC=example,DC=com" "(sAMAccountName=testuser)" memberOf
여기에 그룹이 나오지 않으면 memberOf 기반 필터는 어떤 형태로 써도 통과하지 못한다. OpenLDAP 이라면 그룹 쪽에서 찾는 형태로 바꾼다.
(member=CN=%{login},OU=users,DC=example,DC=com)
필터를 아주 단순하게 바꿔 로그인 자체가 되는지부터 확인한다. 이걸로 되면 문제는 100% 그룹 필터다.
(sAMAccountName=%{login})
# 1. 바인드가 되는가
ldapwhoami -x -H ldap://ad.example.com \
-D "CN=svc_redmine,OU=service,DC=example,DC=com" -W
# 2. 필터로 사용자가 실제로 걸리는가
ldapsearch -x -LLL -H ldap://ad.example.com \
-D "CN=svc_redmine,OU=service,DC=example,DC=com" -W \
-b "DC=example,DC=com" \
"(&(sAMAccountName=testuser)(memberOf:1.2.840.113556.1.4.1941:=CN=redmine_users,OU=system,DC=example,DC=com))" dn
2번이 비어 나오면 Redmine 이 아니라 필터 문제다. Redmine 쪽 로그는 컨테이너 표준 출력이나 log/production.log 에서 본다.
LDAP 설정 화면의 입력란이 겹쳐 보이거나 레이아웃이 무너지면 설정이 아니라 정적 리소스가 로드되지 않은 것이다. 리버스 프록시 뒤에 두었을 때 /assets 경로가 전달되지 않거나, 서브 경로로 붙였는데 RAILS_RELATIVE_URL_ROOT 가 맞지 않으면 그렇게 된다.
curl -sI https://redmine.example.com/stylesheets/application.css
docker exec -it redmine ls public/assets | head