중소기업 OpenLDAP 통합 인증 구축 – 04. 커스텀 스키마 설계: groupPosixAux로 STRUCTURAL 충돌 해결하기

2026.10.16

·

왜 커스텀 스키마가 필요한가

표준 LDAP 스키마는 일반적인 디렉토리 서비스를 위해 설계되어 있습니다.
inetOrgPerson으로 이름·이메일·전화번호를 담을 수 있고, posixAccount로 Linux 계정 정보를 담을 수 있습니다.

하지만 HR 관리 시스템으로 LDAP을 쓰려면 표준 스키마만으로는 부족합니다.

  • 부서 코드 (단순 텍스트와 다른 구조화된 코드 체계)
  • 재직 상태 (재직 / 휴직 / 퇴직)
  • 고용 형태 (정규직 / 계약직 / 프리랜서)

이것들을 기존 속성(description, ou 등)에 우겨넣을 수도 있지만, 그러면 검색 필터 작성이 복잡해지고 의미가 불명확해집니다.
명시적인 속성 이름을 가진 커스텀 스키마를 정의하는 것이 훨씬 낫습니다.

Sponsored

그리고 또 하나의 문제가 있습니다 — 그룹 설계에서 발생하는 STRUCTURAL 클래스 충돌입니다.


STRUCTURAL 충돌: groupOfNames + posixGroup

OpenLDAP에서 그룹을 만들 때 두 가지 요구사항이 충돌합니다.

요구사항objectClass필요한 속성
멤버를 DN으로 관리 (RFC 2256)groupOfNamesmember (DN)
Linux/NAS 그룹 ID 관리 (RFC 2307)posixGroupgidNumber, memberUid

Synology NAS는 memberUid(uid 문자열)로 그룹 멤버를 확인합니다.
그룹웨어는 member(full DN)로 멤버를 확인합니다.
둘 다 필요합니다.

그래서 자연스럽게 이렇게 쓰고 싶어집니다.

# 하고 싶었던 구성 (오류 발생)
dn: cn=dept-dev,ou=Groups,dc=ldap,dc=example,dc=com
objectClass: top
objectClass: groupOfNames   # STRUCTURAL
objectClass: posixGroup     # STRUCTURAL ← 충돌!
cn: dept-dev
gidNumber: 4013
member: uid=john,ou=people,dc=ldap,dc=example,dc=com
memberUid: john

실행하면 오류가 납니다.

ldap_add: Object class violation (65)
  additional info: invalid structural object class chain (posixGroup/groupOfNames)

왜 충돌하는가

LDAP 스키마에서 objectClass는 세 종류입니다.

타입설명예시
STRUCTURAL엔트리의 기본 구조를 정의. 하나만 허용groupOfNames, posixGroup, person
AUXILIARY기존 엔트리에 속성을 추가. 여러 개 허용posixAccount(일부), 커스텀 클래스
ABSTRACT추상 기반 클래스. 직접 사용 불가top

groupOfNames와 posixGroup은 둘 다 STRUCTURAL입니다.
RFC는 하나의 엔트리에 STRUCTURAL 클래스를 하나만 허용합니다(상속 관계가 아니면).


해결책: groupPosixAux AUXILIARY 클래스

해결 방법은 posixGroup의 속성(gidNumber, memberUid)만 AUXILIARY 클래스로 분리하는 것입니다.

groupOfNames (STRUCTURAL)  +  groupPosixAux (AUXILIARY)
     ↓                              ↓
  member (DN)              gidNumber, memberUid

AUXILIARY 클래스는 STRUCTURAL 클래스와 충돌 없이 함께 쓸 수 있습니다.


커스텀 스키마 정의

두 클래스를 하나의 스키마 파일에 정의합니다.

# 이 스키마는 cn=config OLC 방식으로 런타임에 로드합니다
dn: cn=custom-hr,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: custom-hr

# ─── 속성 정의 ───────────────────────────────────────────────

# 부서 코드 (예: DEPT001)
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.1.1
  NAME 'hrDepartmentCode'
  DESC 'HR department code'
  EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE )

# 재직 상태: active / on-leave / resigned
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.1.2
  NAME 'hrEmploymentStatus'
  DESC 'Employment status: active, on-leave, resigned'
  EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE )

# 고용 형태: regular / contract / freelance
olcAttributeTypes: ( 1.3.6.1.4.1.99999.1.1.3
  NAME 'hrEmploymentType'
  DESC 'Employment type: regular, contract, freelance'
  EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  SINGLE-VALUE )

# ─── objectClass 정의 ─────────────────────────────────────────

# HR 사용자 확장 (AUXILIARY — inetOrgPerson에 추가)
olcObjectClasses: ( 1.3.6.1.4.1.99999.2.1.1
  NAME 'customHRPerson'
  DESC 'Auxiliary class for HR person attributes'
  AUXILIARY
  MAY ( hrDepartmentCode $ hrEmploymentStatus $ hrEmploymentType ) )

# groupOfNames에 POSIX 그룹 속성 추가 (AUXILIARY)
olcObjectClasses: ( 1.3.6.1.4.1.99999.2.1.2
  NAME 'groupPosixAux'
  DESC 'Auxiliary class to add POSIX group attributes to groupOfNames'
  AUXILIARY
  MAY ( gidNumber $ memberUid ) )

OID 선택에 대해

OID(1.3.6.1.4.1.99999.x.x.x)에서 99999는 예시용 Private Enterprise Number입니다.
실제 운영 환경에서는 IANA에 자신의 번호를 등록하거나, 내부 서비스 전용이라면 99999처럼 충돌 없는 번호를 정해서 사용합니다.
중요한 것은 다른 스키마의 OID와 겹치지 않아야 한다는 점입니다.


스키마 로드 방법

osixia/openldap 컨테이너는 cn=config(OLC) 방식으로 런타임에 스키마를 추가할 수 있습니다.
컨테이너를 재시작할 필요 없습니다.

처음 스키마 추가

# 스키마 LDIF를 컨테이너로 복사
docker cp custom-hr-schema.ldif openldap:/tmp/

# 컨테이너 내부에서 cn=config에 로드
docker exec openldap ldapadd -Y EXTERNAL -H ldapi:/// -f /tmp/custom-hr-schema.ldif

로드 확인

docker exec openldap ldapsearch -Y EXTERNAL -H ldapi:/// \
  -b "cn=schema,cn=config" \
  "(cn=*custom-hr*)" cn

결과에 cn: {9}custom-hr 같은 항목이 나오면 성공입니다.

groupPosixAux만 추가 (기존 스키마 수정)

이미 스키마가 로드된 상태에서 groupPosixAux만 추가하려면 modify 방식을 씁니다.

# add-posixaux.ldif
dn: cn={9}custom-hr,cn=schema,cn=config
changetype: modify
add: olcObjectClasses
olcObjectClasses: ( 1.3.6.1.4.1.99999.2.1.2
  NAME 'groupPosixAux'
  DESC 'Auxiliary class to add POSIX group attributes to groupOfNames'
  AUXILIARY
  MAY ( gidNumber $ memberUid ) )
docker exec -i openldap ldapmodify -Y EXTERNAL -H ldapi:/// < add-posixaux.ldif

실제 사용 예시

사용자 엔트리

customHRPerson을 추가하면 HR 속성을 쓸 수 있습니다.

dn: uid=john,ou=people,dc=ldap,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: customHRPerson        # ← HR 속성 추가
objectClass: sambaSamAccount
uid: john
cn: John Doe
sn: Doe
mail: john@example.com
uidNumber: 10001
gidNumber: 4013
homeDirectory: /home/john
loginShell: /bin/bash
hrDepartmentCode: DEPT013          # ← 커스텀 속성
hrEmploymentStatus: active         # ← 커스텀 속성
hrEmploymentType: regular          # ← 커스텀 속성

그룹 엔트리

groupPosixAux를 추가하면 groupOfNames에 gidNumber와 memberUid를 같이 쓸 수 있습니다.

dn: cn=dept-dev,ou=Groups,dc=ldap,dc=example,dc=com
objectClass: top
objectClass: groupOfNames          # STRUCTURAL
objectClass: groupPosixAux         # AUXILIARY ← 충돌 없음
cn: dept-dev
gidNumber: 4013
member: uid=john,ou=people,dc=ldap,dc=example,dc=com
memberUid: john

검색 예시

커스텀 속성으로 필터링할 수 있습니다.

ADM="cn=admin,dc=ldap,dc=example,dc=com"
PW="your_admin_password"
BASE="dc=ldap,dc=example,dc=com"

# 재직 중인 사용자만 조회
docker exec openldap ldapsearch -x -H ldap://localhost:389 \
  -D "$ADM" -w "$PW" \
  -b "ou=people,$BASE" \
  "(hrEmploymentStatus=active)" uid cn hrEmploymentStatus

# 특정 부서 코드 사용자 조회
docker exec openldap ldapsearch -x -H ldap://localhost:389 \
  -D "$ADM" -w "$PW" \
  -b "ou=people,$BASE" \
  "(hrDepartmentCode=DEPT013)" uid cn

# groupPosixAux가 있는 그룹 (Synology용 필터)
docker exec openldap ldapsearch -x -H ldap://localhost:389 \
  -D "$ADM" -w "$PW" \
  -b "ou=Groups,$BASE" \
  "(objectClass=groupPosixAux)" cn gidNumber memberUid

스키마 롤백

스키마를 잘못 추가했을 때 제거하는 방법입니다.

# rollback-schema.ldif
dn: cn={9}custom-hr,cn=schema,cn=config
changetype: delete
docker exec -i openldap ldapmodify -Y EXTERNAL -H ldapi:/// < rollback-schema.ldif

단, 해당 스키마를 사용하는 엔트리가 있으면 삭제되지 않습니다.
먼저 해당 objectClass를 쓰는 모든 엔트리에서 속성을 제거한 뒤 스키마를 삭제해야 합니다.


여기까지의 상태

  • customHRPerson: HR 관리용 사용자 속성 (부서코드, 재직상태, 고용형태)
  • groupPosixAux: groupOfNames에 POSIX 그룹 속성 추가, STRUCTURAL 충돌 해결
  • 사용자와 그룹 엔트리 모두 커스텀 스키마 적용 완료

다음 편에서는 실제 사용자와 그룹을 생성하고, Samba SMB 파일 서버 연동에 필요한 sambaSamAccount 설정과 NT 해시 생성 문제(Node.js 20의 MD4 비활성화)를 다룹니다.


MORE POSTS

다른 글 보기

테크 랩

PMBOK 완독기 – 00. 오리엔테이션: 왜 정석을 완독하나 + 6판·7판 지도

PMBOK을 목차대로 완독하며 IT 현장에 번역하는 시리즈 시작. 6판(10지식영역)·7판(원칙·테일러링) 지도와 15편 로드맵.
2026.10.22
guniq 인사이트 썸네일 — 중소기업 OpenLDAP 통합 인증 구축 – 07. 백업·로그·운영 자동화: 장애 없는 LDAP 서버 유지하기
테크 랩

중소기업 OpenLDAP 통합 인증 구축 – 07. 백업·로그·운영 자동화: 장애 없는 LDAP 서버 유지하기

LDAP 서버는 설치 후가 더 중요합니다. 데이터 유실을 막는 백업 전략(daily/weekly/monthly), slapd 로그 관리와 logrotate 설정, 헬스체크 자동화까지 — 실제 운영 중인 스크립트를 바탕으로 LDAP 서버를 안정적으로 유지하는 방법을 정리합니다.
2026.10.21
guniq 인사이트 썸네일 — 중소기업 OpenLDAP 통합 인증 구축 – 06. Synology NAS LDAP 연동: DSM 설정과 그룹 권한 매핑
테크 랩

중소기업 OpenLDAP 통합 인증 구축 – 06. Synology NAS LDAP 연동: DSM 설정과 그룹 권한 매핑

Synology DSM의 LDAP 클라이언트를 커스텀 프로파일로 설정하는 방법을 정리합니다. groupPosixAux 필터, memberUid 매핑, LDAPS 인증서 처리까지 — 표준을 지킨 LDAP 구조가 NAS 연동을 얼마나 단순하게 만드는지 확인합니다.
2026.10.20

프로젝트 문의 환영합니다

기획부터 개발, 운영까지 함께 만들어 드립니다.

무료 3분 자가진단

우리 회사, 자체 클라우드가 답일까?

AWS vs 자체 인프라 · 11개 항목 3분 체크