32.5. 轻型目录访问协议(LDAP)

轻型目录访问协议(LDAP)是一种应用层协议,用于使用分布式目录信息服务访问、修改和验证对象。可以把它想象成一本电话或唱片簿,它存储了几个层次的分层、同质信息。它用于 Active Directory 和 OpenLDAP 网络,允许用户使用单个帐户访问多个级别的内部信息。例如,电子邮件身份验证、提取员工联系信息和内部网站身份验证都可能使用 LDAP 服务器记录库中的单个用户帐户。

本节提供在 FreeBSD 系统上配置 LDAP 服务器的快速入门指南。它假定管理员已经有一个设计计划,其中包括要存储的信息类型、该信息将用于什么目的、哪些用户应有权访问该信息,以及如何防止未经授权的访问。

32.5.1. LDAP 术语和结构

LDAP 使用几个术语,在开始配置之前应了解这些术语。所有目录条目都由一组 属性 组成。这些属性集中的每一个都包含一个称为 可分辨名称(DN)的唯一标识符,该标识符通常由其他几个属性(如公用或 相对可分辨名称(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

此示例条目显示 dnmailcnuidtelephoneNumber 属性的值。cn 的属性是 RDN。

有关 LDAP 及其术语的更多信息,请参阅 http://www.openldap.org/doc/admin24/intro.html

32.5.2. 配置 LDAP 服务器

FreeBSD 不提供内置的 LDAP 服务器。通过软件包或 port net/openldap 来安装服务器软件以开始配置:

# pkg install openldap-server

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

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

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

下一阶段是配置证书颁发机构。以下命令必须从 /usr/local/etc/openldap/private 执行。这一点很重要,因为文件权限需要受到限制,并且用户不应有权访问这些文件。有关证书及其参数的更多详细信息,请参阅 OpenSSL。要创建证书颁发机构,请从以下命令开始,然后按照提示操作:

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

提示的条目可能是通用的, Common Name 条目必须与系统主机名 不同。如果这是自签名证书,请在主机名前面加上“证书颁发机构”前缀 CA

下一个任务是创建证书签名请求和私钥。输入以下命令并按照提示操作:

# 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

请记住在出现提示时使用相同的 Common Name 属性。完成后,确保通过一连串命令总共生成了 8 个新文件。

运行 OpenLDAP 服务器的守护程序已 过期。它的配置是通过 slapd.ldif 执行的:旧的 slapd.conf 已被 OpenLDAP 弃用。

slapd.ldif配置示例可用,也可以在 /usr/local/etc/openldap/slapd.ldif.sample 中找到。选项记录在 slapd-config(5)中。与所有其他 LDAP 属性集一样,slapd.ldif 的每个部分都通过 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 之前,以后任何人都无法修改 全局配置 部分。

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

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

服务器仍必须受信任。如果以前从未这样做过,请按照以下说明进行操作。使用软件包或 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 中添加以下行:

slapd_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

当被要求时,提供在 配置后端 部分选择的密码。用户名不是必需的:在这里,cn=config 代表要修改的数据库部分的 DN。另外,使用 ldapmodify 来删除数据库中的某一行,ldapdelete 来删除整个条目。

如果出现问题,或者全局超级用户无法访问配置后端,则可以删除并重新写入整个配置:

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

然后可以编辑并再次导入 slapd.ldif。请仅在没有其他可用解决方案时才执行此过程。

这只是服务器的配置。同一台计算机还可以托管 LDAP 客户端,并具有自己单独的配置。