Directory Service คืออะไร?
ลองนึกภาพ สมุดโทรศัพท์ — เปิดหา “สมชาย” ก็เจอเบอร์โทร, ที่อยู่, แผนกที่ทำงาน ข้อมูลเรียงเป็นโครงสร้าง หาง่าย
Directory Service ก็คือสมุดโทรศัพท์ดิจิทัล — ระบบที่เก็บข้อมูลเป็นโครงสร้าง (hierarchical) ออกแบบมาเพื่อ อ่านเร็ว มากกว่าเขียน:
Directory Service = สมุดโทรศัพท์ดิจิทัล
┌─────────────────────────────────────────┐
│ example.com (องค์กร) │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ People │ │ Groups │ │
│ │ │ │ │ │
│ │ alice │ │ devops │ │
│ │ bob │ │ hr │ │
│ │ charlie │ │ finance │ │
│ └──────────┘ └──────────┘ │
│ │
│ "หา alice" → ได้ email, เบอร์, แผนก │
│ "หาคนในแผนก devops" → ได้ทั้ง list │
└─────────────────────────────────────────┘
ลักษณะเด่นของ Directory Service:
- Read-heavy — ถูกออกแบบให้อ่านเร็วมาก เพราะข้อมูล (ชื่อ, email, แผนก) ไม่ค่อยเปลี่ยน แต่ถูกค้นหาบ่อย
- Hierarchical — จัดข้อมูลเป็นต้นไม้ (tree) ไม่ใช่ตาราง (table)
- Distributed — กระจาย replicate ไปหลาย servers ได้ เพื่อรองรับ load
LDAP คืออะไร?
LDAP (Lightweight Directory Access Protocol) คือ protocol สำหรับเข้าถึง directory service — เหมือน HTTP เป็น protocol สำหรับเข้าถึง web server LDAP ก็เป็น protocol สำหรับเข้าถึง directory
สำคัญ: LDAP เป็น protocol ไม่ใช่ database ตัว database ที่อยู่ข้างหลังเรียกว่า directory server หรือ LDAP server ส่วน LDAP เป็นภาษาที่ใช้คุยกับมัน
เหมือน SQL เป็น language สำหรับคุยกับ database — LDAP เป็น protocol สำหรับคุยกับ directory
LDAP Implementations ที่นิยม
| Implementation | ผู้พัฒนา | จุดเด่น | License |
|---|---|---|---|
| OpenLDAP | Community | เบา, ยืดหยุ่น, ใช้ทั่วไปใน Linux | Open source |
| Active Directory | Microsoft | เชื่อมกับ Windows ecosystem ดีมาก | Commercial |
| 389 Directory Server | Red Hat | enterprise-grade, มี GUI | Open source |
| Apache Directory | Apache | เขียนด้วย Java, มี Studio UI | Open source |
| FreeIPA | Red Hat | ครบวงจร (LDAP + Kerberos + DNS + CA) | Open source |
LDAP vs Database
คำถามที่พบบ่อย: “ทำไมไม่ใช้ database ปกติแทน LDAP?”
| LDAP | Relational Database (SQL) | |
|---|---|---|
| โครงสร้าง | Tree (hierarchical) | Table (relational) |
| Optimized สำหรับ | Read-heavy workload | Read + Write balanced |
| Schema | ยืดหยุ่น, entries ไม่จำเป็นต้องมี attributes เหมือนกัน | เข้มงวด, ทุก row ใน table มี columns เดียวกัน |
| Query | LDAP filter (cn=alice) | SQL SELECT * FROM users WHERE name='alice' |
| Replication | Built-in multi-master | ต้อง config เพิ่ม |
| Standards | RFC 4511 (ทุก implementation ใช้ protocol เดียวกัน) | SQL dialect ต่างกันในแต่ละ DB |
| เหมาะกับ | User directory, org hierarchy, address book | Transaction, e-commerce, analytics |
Rule of thumb: ถ้าข้อมูลถูก อ่านบ่อย เขียนน้อย และมีโครงสร้างเป็น hierarchy → LDAP ถ้าข้อมูลถูก อ่านและเขียนเท่าๆ กัน หรือต้องการ JOIN ข้าม tables → SQL database
โครงสร้าง DIT (Directory Information Tree)
LDAP เก็บข้อมูลเป็น tree เรียกว่า DIT (Directory Information Tree) — ทุก entry มีตำแหน่งในต้นไม้นี้:
dc=example,dc=com ← root (domain component)
│
├── ou=People ← Organizational Unit (กลุ่ม)
│ │
│ ├── cn=alice ← entry (คน)
│ │ ├── mail: alice@example.com
│ │ ├── uid: alice
│ │ └── title: Developer
│ │
│ └── cn=bob
│ ├── mail: bob@example.com
│ ├── uid: bob
│ └── title: Manager
│
├── ou=Groups
│ │
│ ├── cn=devops
│ │ └── member: cn=alice,ou=People,dc=example,dc=com
│ │
│ └── cn=hr
│ └── member: cn=bob,ou=People,dc=example,dc=com
│
└── ou=Services
└── cn=mail-server
└── description: Internal mail service
ศัพท์ที่ต้องรู้
| คำ | ย่อจาก | คืออะไร | ตัวอย่าง |
|---|---|---|---|
| DN | Distinguished Name | “ที่อยู่เต็ม” ของ entry ไม่ซ้ำกันในทั้ง tree | cn=alice,ou=People,dc=example,dc=com |
| RDN | Relative Distinguished Name | ชื่อของ entry (เฉพาะ level นี้) | cn=alice |
| DC | Domain Component | ส่วนของ domain name | dc=example, dc=com |
| OU | Organizational Unit | กลุ่ม/แผนก | ou=People, ou=Groups |
| CN | Common Name | ชื่อที่ใช้เรียก | cn=alice, cn=devops |
| UID | User ID | username | uid=alice |
DN เปรียบเทียบกับ file path: ถ้า file path คือ
/com/example/People/alice→ DN คือcn=alice,ou=People,dc=example,dc=com(เขียนกลับจากล่างขึ้นบน)
Entry Structure
แต่ละ entry ใน LDAP ประกอบด้วย:
- DN — ที่อยู่เต็มของ entry
- objectClass — “ประเภท” ของ entry กำหนดว่าต้องมี/อาจมี attributes อะไรบ้าง
- Attributes — ข้อมูลจริงๆ เป็นคู่ key-value
objectClass ที่ใช้บ่อย
| objectClass | ใช้แทน | attributes ที่ต้องมี | attributes ที่เพิ่มได้ |
|---|---|---|---|
| inetOrgPerson | คน (user) | cn, sn | mail, uid, title, telephoneNumber |
| organizationalUnit | แผนก/กลุ่ม | ou | description |
| groupOfNames | group ของ users | cn, member | description |
| organization | องค์กร | o | description |
| dcObject | domain component | dc | — |
sn = Surname (นามสกุล), cn = Common Name (ชื่อเต็ม), o = Organization
LDIF Format
LDIF (LDAP Data Interchange Format) คือ text format สำหรับแสดง/นำเข้า LDAP entries — เหมือน CSV สำหรับ database แต่ LDIF สำหรับ LDAP:
# สร้าง root
dn: dc=example,dc=com
objectClass: dcObject
objectClass: organization
dc: example
o: Example Inc.
# สร้าง OU สำหรับ People
dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
ou: People
# สร้าง user alice
dn: cn=alice,ou=People,dc=example,dc=com
objectClass: inetOrgPerson
cn: alice
sn: Wonderland
mail: alice@example.com
uid: alice
title: Senior Developer
userPassword: {SSHA}encrypted_password_here
แต่ละ entry คั่นด้วยบรรทัดว่าง แต่ละ attribute เป็น key: value
LDAP Operations
LDAP มี operations หลักๆ สำหรับจัดการข้อมูล:
| Operation | ทำอะไร | เปรียบเทียบกับ SQL |
|---|---|---|
| Bind | ยืนยันตัวตน (login กับ LDAP server) | CONNECT + authenticate |
| Search | ค้นหา entries ตาม filter | SELECT ... WHERE ... |
| Add | เพิ่ม entry ใหม่ | INSERT |
| Modify | แก้ไข attributes ของ entry | UPDATE |
| Delete | ลบ entry | DELETE |
| Compare | เช็คว่า attribute มีค่าตามที่ระบุไหม | SELECT ... WHERE attr='value' |
| ModifyDN | ย้าย/เปลี่ยนชื่อ entry | UPDATE ... SET name=... (+ move) |
| Unbind | ปิดการเชื่อมต่อ | DISCONNECT |
Search Filter Syntax
Search เป็น operation ที่ใช้บ่อยที่สุด filter syntax ของ LDAP หน้าตาแบบนี้:
# หา user ที่ชื่อ alice
(cn=alice)
# หา user ที่ email ลงท้ายด้วย @example.com
(mail=*@example.com)
# หา user ที่เป็น developer AND อยู่ใน devops
(&(title=Developer)(memberOf=cn=devops,ou=Groups,dc=example,dc=com))
# หา user ที่เป็น developer OR manager
(|(title=Developer)(title=Manager))
# หาทุกคนที่ไม่ใช่ admin
(!(title=Admin))
| Operator | ความหมาย | ตัวอย่าง |
|---|---|---|
= | เท่ากับ | (cn=alice) |
=* | มีค่า (ไม่ว่างเปล่า) | (mail=*) |
* (ใน value) | wildcard | (cn=a*) = ขึ้นต้นด้วย a |
& | AND | (&(a=1)(b=2)) |
| | OR | (|(a=1)(b=2)) |
! | NOT | (!(a=1)) |
Schema
Schema คือ “กฎ” ของ directory — กำหนดว่า:
- มี objectClass อะไรบ้าง
- แต่ละ objectClass ต้องมี attributes อะไร (MUST) และมีได้ (MAY)
- แต่ละ attribute เป็น data type อะไร (string, integer, boolean)
ตัวอย่าง: inetOrgPerson schema กำหนดว่า:
- MUST:
cn,sn(ต้องมี ถ้าไม่มี → error) - MAY:
mail,uid,title,telephoneNumber(มีหรือไม่มีก็ได้)
objectClass: inetOrgPerson
│
├── MUST (ต้องมี)
│ ├── cn (Common Name)
│ └── sn (Surname)
│
└── MAY (มีได้)
├── mail
├── uid
├── title
├── telephoneNumber
├── jpegPhoto
└── ... (อีกหลาย attributes)
ข้อดี: schema ทำให้ data มีมาตรฐาน — ทุก user entry รับประกันว่ามี cn และ sn เสมอ app ที่อ่านข้อมูลไม่ต้องเช็คว่า field นี้มีหรือเปล่า
Schema สามารถ extend ได้ — ถ้า attributes มาตรฐานไม่พอ ก็สร้าง custom objectClass เพิ่ม เช่น เพิ่ม employeeID หรือ department
Use Cases
1. Centralized Authentication
use case ที่พบบ่อยที่สุด — ทุก app ในองค์กรเช็ค username/password กับ LDAP ที่เดียว:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ App A │ │ App B │ │ App C │
│ (web) │ │ (VPN) │ │ (email) │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└───────────────┼───────────────┘
│
▼
┌──────────────┐
│ LDAP Server │ ← user database กลาง
│ │
│ alice ✓ │
│ bob ✓ │
│ charlie ✓ │
└──────────────┘
ข้อดี: เพิ่ม/ลบ user ที่เดียว มีผลกับทุก app ทันที ไม่ต้องไปสร้าง account ใน app แต่ละตัว
2. Address Book & Organization Chart
เก็บข้อมูลพนักงาน — ชื่อ, email, เบอร์โทร, แผนก, หัวหน้า — ค้นหาได้เร็วมาก email client เช่น Thunderbird รองรับ LDAP address book โดยตรง
3. Service Configuration
เก็บ config ของ services เป็นศูนย์กลาง เช่น printer settings, DNS records, mail routing rules
LDAP + Keycloak Federation
LDAP มักถูกใช้คู่กับ Keycloak ในรูปแบบ User Federation — Keycloak เป็น front-end ที่รองรับ modern protocols (OAuth 2.0, OIDC) ส่วน LDAP เป็น backend เก็บ users:
┌──────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐
│ User │────▶│ OAuth2 Proxy │────▶│ Keycloak │────▶│ LDAP │
│ │ │ (หน้า app) │ │ (IdP) │ │ (user DB)│
│ │◀────│ │◀────│ │◀────│ │
└──────┘ └──────────────┘ └──────────────┘ └──────────┘
│
ออก JWT token
ให้ app ใช้
Federation Modes
| Mode | วิธีทำงาน | ข้อดี | ข้อเสีย |
|---|---|---|---|
| Read-only | Keycloak อ่าน users จาก LDAP อย่างเดียว | LDAP ยังเป็น source of truth | สร้าง user ผ่าน Keycloak ไม่ได้ |
| Writable | Keycloak อ่าน + เขียนกลับ LDAP | สร้าง/แก้ user จากทั้ง 2 ทางได้ | ต้องระวัง conflict |
| Import | Copy users จาก LDAP มา Keycloak | เร็วกว่า (query local DB) | ข้อมูลอาจ outdated ถ้า sync ไม่ทัน |
| On-demand | Query LDAP ทุกครั้งที่ login | ข้อมูล real-time เสมอ | ช้ากว่า ถ้า LDAP ล่ม login ไม่ได้ |
ทำไมต้อง Keycloak + LDAP? เพราะ LDAP เก่ง centralized user management แต่ไม่รองรับ modern protocols อย่าง OAuth 2.0/OIDC โดยตรง Keycloak ทำหน้าที่แปล — app ที่ต้องการ OAuth 2.0 คุยกับ Keycloak ส่วน Keycloak ไปดึง users จาก LDAP
สนใจเรื่อง Keycloak เพิ่มเติม อ่านต่อได้ที่ Keycloak: ทำความรู้จัก Identity & Access Management ตั้งแต่พื้นฐาน
OpenLDAP
OpenLDAP เป็น implementation ที่ได้รับความนิยมมากที่สุดในโลก open-source:
- เบา — ใช้ resource น้อย เหมาะกับ VPS
- ยืดหยุ่น — config ผ่าน
cn=config(LDAP-based config) ไม่ต้อง restart เมื่อเปลี่ยน config - Replication — รองรับ multi-provider replication สำหรับ HA
- Backend — ใช้ LMDB (Lightning Memory-Mapped Database) เร็วมากสำหรับ read operations
- Community — ใช้มานานกว่า 20 ปี documentation เยอะ
ทางเลือกอื่นที่น่าสนใจ:
- LLDAP — lightweight LDAP server เขียนด้วย Rust มี web UI เหมาะกับ self-hosting ที่ต้องการแค่ user management
- GLAuth — tiny LDAP server ที่ config ด้วย file เดียว เหมาะกับ small team
Security
Threats ที่ต้องระวัง
| Threat | คืออะไร | ป้องกันด้วย |
|---|---|---|
| Eavesdropping | ดักฟัง traffic ระหว่าง client กับ LDAP server | LDAPS (LDAP over TLS) |
| LDAP Injection | ใส่ filter พิเศษเพื่อดึงข้อมูลที่ไม่ควรเห็น | Escape/validate input |
| Unauthorized access | เข้าถึง entries ที่ไม่มีสิทธิ์ | ACL (Access Control Lists) |
| Password theft | ขโมย password จาก directory | Hash passwords (SSHA, PBKDF2) |
| DoS | ส่ง query หนักๆ ทำให้ server ล่ม | Rate limiting, size limits |
แนวทางป้องกัน
1. LDAPS (LDAP over TLS)
- ใช้ LDAPS (port 636) หรือ StartTLS (port 389 + upgrade) เสมอ
- ห้ามส่ง password ผ่าน plain-text LDAP
2. ACL (Access Control Lists)
- กำหนดว่า user ไหนอ่าน/เขียน entry ไหนได้
- ตัวอย่าง: user ธรรมดาอ่านได้แค่ข้อมูลตัวเอง admin อ่านได้ทุกคน
3. Password Hashing
- เก็บ password เป็น hash (SSHA, PBKDF2, bcrypt) ไม่เก็บ plain-text
- LDAP server จะเทียบ hash เวลา login ไม่เคยเก็บ password จริง
4. Network Isolation
- LDAP server ไม่ควรเปิดให้เข้าถึงจาก internet โดยตรง
- เข้าถึงได้เฉพาะจาก internal network หรือ VPN
5. Bind DN ที่เหมาะสม
- Application ที่เชื่อม LDAP ควรใช้ service account ที่มีสิทธิ์น้อยที่สุด (least privilege)
- ไม่ใช้ admin DN สำหรับ read-only operations
สรุป
| หัวข้อ | สิ่งสำคัญ |
|---|---|
| LDAP คืออะไร | Protocol สำหรับเข้าถึง directory service (ไม่ใช่ database) |
| เหมาะกับ | Read-heavy, hierarchical data เช่น user directory |
| โครงสร้าง | DIT (tree), แต่ละ entry มี DN เป็น unique address |
| Entry | ประกอบด้วย objectClass + attributes กำกับด้วย schema |
| Operations | Bind, Search, Add, Modify, Delete — Search ใช้บ่อยที่สุด |
| Implementations | OpenLDAP (open source), Active Directory (Microsoft) |
| คู่กับ Keycloak | LDAP เก็บ users, Keycloak แปลเป็น OAuth 2.0/OIDC |
| Security | LDAPS (TLS), ACL, password hashing, network isolation |
LDAP อาจดูเป็นเทคโนโลยีเก่า แต่ยังเป็นมาตรฐานสำหรับ centralized user management ในองค์กร — Active Directory ที่ใช้กันทั่วโลกก็ทำงานบน LDAP ถ้ากำลังสร้าง infrastructure ที่ต้องจัดการ users หลายระบบ การเข้าใจ LDAP จะช่วยให้เลือกเครื่องมือได้ถูกต้อง