32.5. 轻型目录访问协议(LDAP) ============================== 轻型目录访问协议(LDAP)是一种应用层协议,用于使用分布式目录信息服务访问、修改和验证对象。可以把它想象成一本电话或唱片簿,它存储了几个层次的分层、同质信息。它用于 Active Directory 和 OpenLDAP 网络,允许用户使用单个帐户访问多个级别的内部信息。例如,电子邮件身份验证、提取员工联系信息和内部网站身份验证都可能使用 LDAP 服务器记录库中的单个用户帐户。 本节提供在 FreeBSD 系统上配置 LDAP 服务器的快速入门指南。它假定管理员已经有一个设计计划,其中包括要存储的信息类型、该信息将用于什么目的、哪些用户应有权访问该信息,以及如何防止未经授权的访问。 32.5.1. LDAP 术语和结构 ----------------------- LDAP 使用几个术语,在开始配置之前应了解这些术语。所有目录条目都由一组 *属性* 组成。这些属性集中的每一个都包含一个称为 *可分辨名称*\ (DN)的唯一标识符,该标识符通常由其他几个属性(如公用或 *相对可分辨名称*\ (RDN))构建。与目录具有绝对路径和相对路径的方式类似,将 DN 视为绝对路径,将 RDN 视为相对路径。 示例 LDAP 条目如下所示。本示例搜索指定用户帐户(\ ``uid``\ )、组织单位(\ ``ou``\ )和组织(\ ``o``\ )的条目: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session % ldapsearch -xb "uid=trhodes,ou=users,o=example.com" # extended LDIF # # LDAPv3 # base 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\ 。 32.5.2. 配置 LDAP 服务器 ------------------------ FreeBSD 不提供内置的 LDAP 服务器。通过软件包或 port `net/openldap `__ 来安装服务器软件以开始配置: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # pkg install openldap-server `软件包 `__\ 中启用了大量默认选项。可通过运行 ``pkg info openldap-server`` 来查看它们。如果它们还不够(例如,如果需要支持 SQL),请考虑使用适当的\ `框架 `__\ 重新编译 port。 安装中将创建目录 **/var/db/openldap-data** 来保存数据。必须创建用于存储证书的目录: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # mkdir /usr/local/etc/openldap/private 下一阶段是配置证书颁发机构。以下命令必须从 **/usr/local/etc/openldap/private** 执行。这一点很重要,因为文件权限需要受到限制,并且用户不应有权访问这些文件。有关证书及其参数的更多详细信息,请参阅 `OpenSSL `__\ 。要创建证书颁发机构,请从以下命令开始,然后按照提示操作: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # openssl req -days 365 -nodes -new -x509 -keyout ca.key -out ../ca.crt 提示的条目可能是通用的,\ *但* ``Common Name`` 条目必须与系统主机名 *不同*\ 。如果这是自签名证书,请在主机名前面加上“证书颁发机构”前缀 ``CA``\ 。 下一个任务是创建证书签名请求和私钥。输入以下命令并按照提示操作: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # openssl req -days 365 -nodes -new -keyout server.key -out server.csr 在证书生成过程中,请确保正确设置 ``Common Name`` 属性。证书签名请求必须使用证书颁发机构进行签名,才能用作有效证书: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # openssl x509 -req -days 365 -in server.csr -out ../server.crt -CA ../ca.crt -CAkey ca.key -CAcreateserial 证书生成过程的最后一部分是生成客户端证书并对其进行签名: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # 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 将用于实现安全通道。第一部分是全局配置: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # # 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``\ 。 第二部分是关于后端模块的,可以按如下方式配置: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # # 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`` 架构:它们是必不可少的。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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 接下来,前端配置部分: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # 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 服务器配置的唯一方法是作为全局超级用户。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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** 之前,以后任何人都无法修改 *全局配置* 部分。 最后一部分是关于数据库后端的: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session ####################################################################### # 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** 放在空目录中。建议将其创建为: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # mkdir /usr/local/etc/openldap/slapd.d/ 导入配置数据库: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # /usr/local/sbin/slapadd -n0 -F /usr/local/etc/openldap/slapd.d/ -l /usr/local/etc/openldap/slapd.ldif 启动 **slapd** 守护程序: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # /usr/local/libexec/slapd -F /usr/local/etc/openldap/slapd.d/ ``-d`` 选项可用于调试,如 slapd(8)中所述。要验证服务器是否正在运行且工作正常,请执行以下操作: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # 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: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # pkg install openssl 从存储 **ca.crt** 的目录(在此示例中为 **/usr/local/etc/openldap**\ ),运行: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # c_rehash . CA 和服务器证书现在都可以在各自的角色中正确识别。若要验证这一点,请从 **server.crt** 目录运行以下命令: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # openssl verify -verbose -CApath . server.crt 如果 **slapd** 正在运行,请重新启动它。如 **/usr/local/etc/rc.d/slapd** 中所述,要在引导时正确运行 **slapd**\ ,必须在 **/etc/rc.conf** 中添加以下行: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session 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.log**\ 、\ **dmesg -a** 和 **/var/log/messages**\ 。 下面的例子将组 ``team`` 和用户 ``john`` 添加到 ``domain.example`` LDAP 数据库中,该数据库仍然是空的。首先,创建文件 **domain.ldif**\ 。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # 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`` 管理员来修改数据库。 .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # ldapadd -W -D "cn=mdbadmin,dc=domain,dc=example" -f domain.ldif 对 *全局配置* 部分的修改只能由全局超级用户执行。例如,假定最初指定了选项 ``olcTLSCipherSuite: HIGH:MEDIUM:SSLv3``\ ,现在必须将其删除。首先,创建一个包含以下内容的文件: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # cat global_mod dn: cn=config changetype: modify delete: olcTLSCipherSuite 然后,应用修改: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # ldapmodify -f global_mod -x -D "cn=config" -W 当被要求时,提供在 *配置后端* 部分选择的密码。用户名不是必需的:在这里,\ ``cn=config`` 代表要修改的数据库部分的 DN。另外,使用 ``ldapmodify`` 来删除数据库中的某一行,\ ``ldapdelete`` 来删除整个条目。 如果出现问题,或者全局超级用户无法访问配置后端,则可以删除并重新写入整个配置: .. raw:: latex \diilbookstyleinputcell .. code:: shell-session # rm -rf /usr/local/etc/openldap/slapd.d/ 然后可以编辑并再次导入 **slapd.ldif**\ 。请仅在没有其他可用解决方案时才执行此过程。 这只是服务器的配置。同一台计算机还可以托管 LDAP 客户端,并具有自己单独的配置。