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 NameDN),这个辨识码会由数个其他的属性,如:常用或相对辨识名称(Relative Distinguished NameRDN)所组成,这就像目录有绝对路径与相对路径,可以把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

这个示例项目会显示dnmailcnuid以及telephoneNumber属性的数值。而cn属性则是RDN

更多有关LDAP以及其术语的信息可在http://www.openldap.org/doc/admin24/intro.html找到。

29.5.2. 设定LDAP服务器

FreeBSD 没有提供内置的 LDAP服务器。安装 net/openldap-server 开始配置:

# pkg install openldap-server

package中有大量的默认选项。通过运行pkg info openldap-server来检查它们。如果它们还不够 (例如需要 SQL 支持), 请考虑使用适当的 框架 重新编译 port。

安装过程中将创建/var/db/openldap-data目录来保存数据。同时必须创建存储证书的目录:

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

The next phase is to configure the Certificate Authority. The following commands must be executed from /usr/local/etc/openldap/private. This is important as the file permissions need to be restrictive and users should not have access to these files. More detailed information about certificates and their parameters can be found in 第 13.6 节 “OpenSSL”. To create the Certificate Authority, start with this command and follow the prompts:

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

The entries for the prompts may be generic except for the Common Name. This entry must be different than the system hostname. If this will be a self signed certificate, prefix the hostname with CA for Certificate Authority.

接下来的工作是建立一个凭证签署的请求及私钥。请输入以下指令然后依提示操作:

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

在证书生成过程中,一定要正确设置Common Name属性。证书签名请求必须与证书管理局签署,才能作为有效证书使用:

# 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

Remember to use the same Common Name attribute when prompted. When finished, ensure that a total of eight (8) new files have been generated through the proceeding commands.

运行OpenLDAP服务器的守护进程是slapd。它的配置文件是slapd.ldif:旧的slapd.conf已经被OpenLDAP所淘汰。

slapd.ldif的配置示例可用,也可以在/usr/local/etc/openldap/slapd.ldif.samplease中找到。选项在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

这里必须指定证书机构、服务器证书和服务器私钥文件。建议让客户端选择安全密码,并省略选项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模式:它们是必不可少的。

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之前,无法修改global configuration

最后一部分是关于数据库后端:

#######################################################################
# 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

# /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,请按照以下说明进行操作。从 package 或 port 安装 OpenSSL:

# 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.logdmesg -a/var/log/messages

下面的例子将组team和用户john添加到domain.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

需要时,提供configuration backend部分中选择的密码。用户名不是必须的:这里,cn=config代表要修改的数据库部分的 DN。或者,使用ldapmodify删除数据库的单行,使用ldapdelete删除整个条目。

如果出现问题,或者管理员无法访问控制页面,则可以删除并重新写入整个配置:

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

然后可以再次编辑和导入slapd.ldif。请仅在没有其他解决方案可用时使用此操作。

这仅是服务器的配置。同一台机器也可以托管一个LDAP客户端,有自己独立的配置。

本文档和其它文档可从这里下载: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题,请先阅读 文档,如不能解决再联系 <questions@FreeBSD.org>.

关于本文档的问题请发信联系 <doc@FreeBSD.org>.