29.5. 輕量級目錄存取協定 (LDAP)

Originally contributed by Tom Rhodes.
Updates by Rocky Hotas.

輕量級目錄存取協定 (Lightweight Directory Access Protocol, LDAP) 是一個利用分散式目錄資訊服務來做到存取、修改與認証物件的應用層通訊協定,可以想像成是一本可以儲存數個階層、同質資訊的電話簿或記錄簿。它用在 Active Directory 及 OpenLDAP 網路,允許使用者利用一個帳號來存取數個階層的內部資訊,例如:電子郵件認証、取得員工聯絡資訊及內部網站的認証皆可使用 LDAP 伺服器資料庫中的單一使用者帳號來存取。

本章節將介紹在 FreeBSD 系統上如何快速的設定一個 LDAP 伺服器。本章節假設管理者已做好規劃,這包含:要儲存何種類型的資訊、這些資訊要來做什麼、那些使用者擁有存取這些資訊的權限以及如何確保這些資訊不會被未經授權存取。

29.5.1. LDAP 術語與結構

LDAP 使用了數個術語在開始設置之前必須先了解。所有的目錄項目由一群屬性 (attributes) 所組成,每個屬性集皆有一個獨特的辨識碼稱為辨識名稱 (Distinguished Name, DN),這個辨識碼會由數個其他的屬性,如:常用或相對辨識名稱 (Relative Distinguished Name, RDN) 所組成,這就像目錄有絕對路徑與相對路徑,可以把 DN 當做絕對路徑,RDN 當做相對路徑。

LDAP 項目的例子如下。這個例子會搜尋指定使用者帳號 (uid)、組織單位 (ou) 及組織的項目 (o):

% ldapsearch -xb "uid=trhodes,ou=users,o=example.com"
# extended LDIF
#
# LDAPv3
# base <uid=trhodes,ou=users,o=example.com> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# trhodes, users, example.com
dn: uid=trhodes,ou=users,o=example.com
mail: trhodes@example.com
cn: Tom Rhodes
uid: trhodes
telephoneNumber: (123) 456-7890

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

這個範例項目會顯示 dn, mail, cn, uid 以及 telephoneNumber 屬性的數值。而 cn 屬性則是 RDN

更多有關 LDAP 以及其術語的資訊可在 http://www.openldap.org/doc/admin24/intro.html 找到。

29.5.2. 設定 LDAP 伺服器

FreeBSD 並未提供內建的 LDAP 伺服器,要開始設定前請先安裝 net/openldap-server 套件或 Port:

# pkg install openldap-server

套件中已開啟了許多的預設選項,可以透過執行 pkg info openldap-server 來查看已開啟的選項,若有不足的地方 (例如需要開啟 SQL 的支援),請考慮使用適當的方式重新編譯該 Port。

安裝程序會建立目錄 /var/db/openldap-data 來儲存資料,同時需要建立儲存憑證的目錄:

# mkdir /usr/local/etc/openldap/private

接下來是設定憑証機構 (Certificate authority)。以下指令必須在 /usr/local/etc/openldap/private 下執行,這很重要是由於檔案權限須要被限制且其他使用者不應有這些檔案的存取權限,更多有關憑証的詳細資訊以及相關的參數可在 節 13.6, “OpenSSL” 中找到。要建立憑証授權,需先輸人這個指令並依提示操作:

# openssl req -days 365 -nodes -new -x509 -keyout ca.key -out ../ca.crt

提示輸入的項目除了通用名稱 (Common Name) 外其他是可以一樣的,這個項目必須使用跟系統主機名稱 不同 的名稱。若這是一個自行簽署的憑証 (Self signed certificate),則在憑証機構 CA 的前面加上主機名稱。

接下來的工作是建立一個伺服器的憑証簽署請求與一個私鑰。請輸入以下指令然後依提示操作:

# openssl req -days 365 -nodes -new -keyout server.key -out server.csr

在憑証產生程序的過程中請確認 Common Name 屬性設定正確。憑証簽署請求 (Certificate Signing Request) 必須經過憑証機構簽署後才會成為有效的憑証:

# openssl x509 -req -days 365 -in server.csr -out ../server.crt -CA ../ca.crt -CAkey ca.key -CAcreateserial

在憑証產生程序的最後一步是產生並簽署客戶端憑証:

# openssl req -days 365 -nodes -new -keyout client.key -out client.csr
# openssl x509 -req -days 3650 -in client.csr -out ../client.crt -CA ../ca.crt -CAkey ca.key

記得當提示時要使用同樣的 Common Name 屬性。完成之後,請確認執行的指令產生了 8 個新檔案。

OpenLDAP 伺服器所執行的 Daemon 為 slapd,OpenLDAP 是透過 slapd.ldif 來做設定, OpenLDAP 官方已停止採用舊的 slapd.conf 格式。

這裡有些 slapd.ldif設定檔範例 可以使用,同時您也可以在 /usr/local/etc/openldap/slapd.ldif.sample 找到範例資訊。相關可用的選項在 slapd-config(5) 文件會有說明。slapd.ldif 的每個段落,如同其他 LDAP 屬性設定一樣會透過獨一無二 DN 來辨識,並請確保 dn: 描述與其相關屬性之間沒有空行。以下的範例中會實作一個使用 TLS 的安全通道,首先是全域的設定:

#
# See slapd-config(5) for details on configuration options.
# This file should NOT be world readable.
#
dn: cn=config
objectClass: olcGlobal
cn: config
#
#
# Define global ACLs to disable default read access.
#
olcArgsFile: /var/run/openldap/slapd.args
olcPidFile: /var/run/openldap/slapd.pid
olcTLSCertificateFile: /usr/local/etc/openldap/server.crt
olcTLSCertificateKeyFile: /usr/local/etc/openldap/private/server.key
olcTLSCACertificateFile: /usr/local/etc/openldap/ca.crt
#olcTLSCipherSuite: HIGH
olcTLSProtocolMin: 3.1
olcTLSVerifyClient: never

這個檔案中必須指定憑証機構 (Certificate Authority)、伺服器憑証 (Server Certificate) 與伺服器私鑰 (Server Private Key),建議可讓客戶端決定使用的安全密碼 (Security Cipher),略過 olcTLSCipherSuite 選項 (此選項不相容 openssl 以外的 TLS 客戶端)。選項 olcTLSProtocolMin 讓伺服器可要求一個安全等級的最低限度,建議使用。伺服器有進行驗証的必要,但客戶端並不需要,因此可設定 olcTLSVerifyClient: never

第二個部份是設定後端要採用的模組有那些,可使用以下方式設定:

#
# Load dynamic backend modules:
#
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulepath:	/usr/local/libexec/openldap
olcModuleload:	back_mdb.la
#olcModuleload:	back_bdb.la
#olcModuleload:	back_hdb.la
#olcModuleload:	back_ldap.la
#olcModuleload:	back_passwd.la
#olcModuleload:	back_shell.la

第三個部份要載入資料庫所需的 ldif 綱要 (Schema),這個動作是必要的。

dn: cn=schema,cn=config
objectClass: olcSchemaConfig
cn: schema

include: file:///usr/local/etc/openldap/schema/core.ldif
include: file:///usr/local/etc/openldap/schema/cosine.ldif
include: file:///usr/local/etc/openldap/schema/inetorgperson.ldif
include: file:///usr/local/etc/openldap/schema/nis.ldif

接下來是前端設定的部份:

# Frontend settings
#
dn: olcDatabase={-1}frontend,cn=config
objectClass: olcDatabaseConfig
objectClass: olcFrontendConfig
olcDatabase: {-1}frontend
olcAccess: to * by * read
#
# Sample global access control policy:
#	Root DSE: allow anyone to read it
#	Subschema (sub)entry DSE: allow anyone to read it
#	Other DSEs:
#		Allow self write access
#		Allow authenticated users read access
#		Allow anonymous users to authenticate
#
#olcAccess: to dn.base="" by * read
#olcAccess: to dn.base="cn=Subschema" by * read
#olcAccess: to *
#	by self write
#	by users read
#	by anonymous auth
#
# if no access controls are present, the default policy
# allows anyone and everyone to read anything but restricts
# updates to rootdn.  (e.g., "access to * by * read")
#
# rootdn can always read and write EVERYTHING!
#
olcPasswordHash: {SSHA}
# {SSHA} is already the default for olcPasswordHash

再來是設定後端的部份,之後唯一能夠存取 OpenLDAP 伺服器設定的方式是使用全域超級使用者。

dn: olcDatabase={0}config,cn=config
objectClass: olcDatabaseConfig
olcDatabase: {0}config
olcAccess: to * by * none
olcRootPW: {SSHA}iae+lrQZILpiUdf16Z9KmDmSwT77Dj4U

預設的管理者使用者名稱是 cn=config,可在 Shell 中輸入 slappasswd,決定要使用的密碼並將其產生的編碼放到 olcRootPW 欄位中。若這個選項在這時沒有設定好,在匯入 slapd.ldif 之後將沒有任何人有辦法修改全域的設定

最後一個部份是有關資料庫後端的設定:

#######################################################################
# LMDB database definitions
#######################################################################
#
dn: olcDatabase=mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: mdb
olcDbMaxSize: 1073741824
olcSuffix: dc=domain,dc=example
olcRootDN: cn=mdbadmin,dc=domain,dc=example
# Cleartext passwords, especially for the rootdn, should
# be avoided.  See slappasswd(8) and slapd-config(5) for details.
# Use of strong authentication encouraged.
olcRootPW: {SSHA}X2wHvIWDk6G76CQyCMS1vDCvtICWgn0+
# The database directory MUST exist prior to running slapd AND
# should only be accessible by the slapd and slap tools.
# Mode 700 recommended.
olcDbDirectory:	/var/db/openldap-data
# Indices to maintain
olcDbIndex: objectClass eq

這裡指定的資料庫即實際用來保存 LDAP 目錄的資料,也可以使用 mdb 以外的項目,資料庫的超級使用者可在這裡設定 (與全域的超級使用者是不同的東西):olcRootDN 需填寫使用者名稱 (可自訂),olcRootPW 需填寫該使用者編碼後的密碼,將密碼編碼可使用 slappasswd 如同前面所述。

這裡有個檔案庫內有四個 slapd.ldif 的範例,要將現有的 slapd.conf 轉換成 slapd.ldif 格式,可參考此頁 (注意,這裡面的說明也會介紹一些不常用的選項)。

當設定完成之後,需將 slapd.ldif 放在一個空的目錄當中,建議如以下方式建立:

# mkdir /usr/local/etc/openldap/slapd.d/

匯入設定資料庫:

# /usr/local/sbin/slapadd -n0 -F /usr/local/etc/openldap/slapd.d/ -l /usr/local/etc/openldap/slapd.ldif

啟動 slapd Daemon:

# /usr/local/libexec/slapd -F /usr/local/etc/openldap/slapd.d/

選項 -d 可以用來除錯使用,如同 slapd(8) 中所說明的,若要檢驗伺服器是否正常執行與運作可以:

# ldapsearch -x -b '' -s base '(objectclass=*)' namingContexts
# extended LDIF
#
# LDAPv3
# base <> with scope baseObject
# filter: (objectclass=*)
# requesting: namingContexts
#

#
dn:
namingContexts: dc=domain,dc=example

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

伺服器端仍必須受到信任,若在此之前未做過這個動作,請依照以下指示操作。安裝 OpenSSL 套件或 Port:

# pkg install openssl

進入 ca.crt 所在的目錄 (以這邊使用的例子來說則是 /usr/local/etc/openldap),執行:

# c_rehash .

現在 CA 與伺服器憑証可以依其用途被辨識,可進入 server.crt 所在的目錄執行以下指令來檢查:

# openssl verify -verbose -CApath . server.crt

slapd 已正在執行,就重新啟動它。如同 /usr/local/etc/rc.d/slapd 所述,要讓 slapd 開機時可正常執行,須要加入以下行到 /etc/rc.conf

lapd_enable="YES"
slapd_flags='-h "ldapi://%2fvar%2frun%2fopenldap%2fldapi/
ldap://0.0.0.0/"'
slapd_sockets="/var/run/openldap/ldapi"
slapd_cn_config="YES"

開機啟動 slapd 並不會提供除錯的功能,您可以檢查 /var/log/debug.log, dmesg -a/var/log/messages 檢確認是否有正常運作。

以下範例會新增群組 team 及使用者 johndomain.example LDAP 資料庫,而該資料庫目前是空的。首先要先建立 domain.ldif 檔:

# cat domain.ldif
dn: dc=domain,dc=example
objectClass: dcObject
objectClass: organization
o: domain.example
dc: domain

dn: ou=groups,dc=domain,dc=example
objectClass: top
objectClass: organizationalunit
ou: groups

dn: ou=users,dc=domain,dc=example
objectClass: top
objectClass: organizationalunit
ou: users

dn: cn=team,ou=groups,dc=domain,dc=example
objectClass: top
objectClass: posixGroup
cn: team
gidNumber: 10001

dn: uid=john,ou=users,dc=domain,dc=example
objectClass: top
objectClass: account
objectClass: posixAccount
objectClass: shadowAccount
cn: John McUser
uid: john
uidNumber: 10001
gidNumber: 10001
homeDirectory: /home/john/
loginShell: /usr/bin/bash
userPassword: secret

請查看 OpenLDAP 說明文件取得更詳細的資訊,使用 slappasswd 來將純文字的密碼 secret 更改為已編碼的型式來填寫 userPassword 欄位。在 loginShell 所指定的路徑,必須在所有可讓 john 登入的系統中存在。最後是使用 mdb 管理者修改資料庫:

# ldapadd -W -D "cn=mdbadmin,dc=domain,dc=example" -f domain.ldif

要修改全域設定只能使用全域的超及使用者。例如,假設一開始採用了 olcTLSCipherSuite: HIGH:MEDIUM:SSLv3 選項,但最後想要把它移除,可以建立一個有以下內容的檔案:

# cat global_mod
dn: cn=config
changetype: modify
delete: olcTLSCipherSuite

然後套用修改內容:

# ldapmodify -f global_mod -x -D "cn=config" -W

當提示輸入密碼時,提供當時在設定後端一節所設定的密碼,在這裡無須填寫使用者名稱,cn=config 代表要修改資料庫資料的位置。也可以使用 ldapmodify 刪除其中一行屬性,或是 ldapdelete 刪除整筆資料。

若有問題無法正常執行,或是全域的超級使用者無法存取後端的設定,可以刪除並重建整個後端設定:

# rm -rf /usr/local/etc/openldap/slapd.d/

可以修改 slapd.ldif 後再重新匯入一次。請注意,這個步驟只在沒有其他方式可用時才使用。

本章節的設定說明只針對伺服器端的部份,在同一台主機中也可以同時有安裝 LDAP 客戶端但需要額外做設定。

本文及其他文件,可由此下載: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/

若有 FreeBSD 方面疑問,請先閱讀 FreeBSD 相關文件,如不能解決的話,再洽詢 <questions@FreeBSD.org>。

關於本文件的問題,請洽詢 <doc@FreeBSD.org>。