哪个年轻人不想拥有属于自己的 LDAP 呢?虽然我之前借助 Active Directory 的 LDAP 服务完成过多个平台上的集成认证,但是始终没有体验过 Linux 原生的 OpenLDAP。反正刚入学,不愁毕业问题。最近接着新部署某个集群的机会,体验了一把完整的配置流程。
下列所有的配置基于 Debian 12 “bookworm”。
服务器端
安装与初始配置
首先安装 OpenLDAP server 并设置为默认开机启动:
apt install slapd
systemctl start --now slapd
在安装时,会通过 dpkg-configure
要求给出 admin 账号的初始密码。默认情况下,base DN 从系统的 DNS domain name 获取。很多时候这不是我们想要的,或者会获取失败(此时就会变成悲惨的 dc=nodomain
)。因此需要运行 dpkg-reconfigure slapd
,重新设置 DN,其他选项保持默认即可。如果有防火墙,需要保证入站的 389 端口是启用的。下文中,总是假设 DN 是 dc=foo,dc=com
。因此 admin 用户的 DN 即为 cn=admin,dc=foo,dc=com
,这也是修改 LDAP 配置需要使用的账号。
默认的配置文件保存在 /etc/ldap
下,数据库保存在 /var/lib/ldap
下,ZFS 爱好者可以为它们创建单独的 dataset。安装和初始配置完成后,就获得了一个空空如也的 OpenLDAP server,大部分配置需要通过写 LDAP 数据库的方式进行。
OpenLDAP 的 ldap-utils
包中提供了 ldapadd
, ldapmodify
, ldapdelete
等命令,对于管理 OpenLDAP 服务器非常有用。此外,ldapvi
包提供的 ldapvi
工具能够交互式地编辑 LDAP 数据库,slapd
自带的 slapcat
命令能够打印出当前的完整数据库。这些都是用于调试和管理 OpenLDAP server 的有力工具。
创建第一个用户
OpenLDAP 中有两种管理用户从属关系的模型,分别是 LDAP 常用的 groupOfNames
和 Linux 默认使用的 posixGroup
。这两种 class 是互相冲突的,无法用于同一层级。考虑到本文仅设计 Linux 用户认证,因此将仅使用 posixGroup
的模型。如果需要接入其他系统的认证,则需要一些技巧来混合使用这两种 class
,并且可能需要启用 memberOf
的 LDAP overlay。网上有相当多关于这部分内容的教程,本文不再涉及。
首先创建 00_basic_ou.ldif
(LDAP Data Interchange Format),这个文件中创建基本的两个 OU 用于放置用户和组:
dn: ou=User,dc=foo,dc=com
objectClass: organizationalUnit
ou: User
dn: ou=Group,dc=foo,dc=com
objectClass: organizationalUnit
ou: Group
使用 ldapadd -H ldapi:/// -D "cn=admin,dc=foo,dc=com" -W -f filename.ldif
命令即可在服务器上应用这个 LDIF 文件。-W
表示在命令行中输入密码(-w
则是直接跟在后面),-f
表示从文件中读取(不给出则直接从 stdin 读取)。
此后创建第一个用户和组:
dn: uid=harry,ou=User,dc=foo,dc=com
objectClass: posixAccount
objectClass: shadowAccount
objectClass: inetOrgPerson
cn: Shengqi
sn: Chen
uid: harry
uidNumber: 20001
gidNumber: 20000
homeDirectory: /home/foo/harry
loginShell: /bin/bash
gecos: Shengqi Chen
dn: cn=users,ou=Group,dc=foo,dc=com
objectClass: posixGroup
cn: users
gidNumber: 20000
description: all LDAP users
memberUid: harry
默认的用户都应该属于 inetOrgPerson
类型,而为了使得 Linux 可以用于认证,还需要属于 posixAccount
和 shadowAccount
类型。这里的 cn
与 sn
是 inetOrgPerson
的必选字段,但不会被 Linux 读取;gecos
是账户全名,会出现在 getent passwd
等命令的输出中。其他几个字段的含义是不言自明的。注意 posixGroup
需要至少一个 memberUid
,否则会创建失败。
创建用户后,可以通过 ldappasswd
命令设置密码:
ldappasswd -W -S -D "cn=admin,dc=foo,dc=com" -x "uid=harry,ou=User,dc=foo,dc=com"
-S
表示提示输入新密码,-s
接收参数作为密码,-T
从指定文件读取密码,如果这三个选项都不给出,则工具会直接生成一个新的密码。
添加第二个用户、修改用户
添加第二个用户与第一个的区别是,未必需要创建一个新组。此时,需要单独使用 ldapadd
增加用户(假设名为 laekov
),使用 ldapmodify
工具来将用户添加到组(也就是在组内添加一个新的 memberUid
属性):
dn: cn=users,ou=Group,dc=foo,dc=com
changetype: modify
add: memberUid
memberUid: laekov
如果需要修改用户属性,也是类似的,但是需要使用 replace
方法:
dn: cn=users,ou=Group,dc=foo,dc=com
changetype: modify
replace: gecos
gecos: Jiaao He
当然,可以直接使用上文提到的 ldapvi
工具来交互式地进行编辑:运行 ldapvi --discover -D cn=admin,dc=hpc-lab,dc=pacman-thu,dc=org -W
,在弹出的编辑器中修改完后,按 y 即可。需要注意的是,这个工具不能添加新项目,只能修改、删除已有的项目。
管理 hosts
LDAP 也可以用来管理 hosts 条目,而不需要额外搭建 DNS 服务器。相应的条目格式如下:
dn: cn=machine1,ou=Hosts,dc=foo,dc=com
objectClass: top
objectClass: ipHost
objectClass: device
ipHostNumber: 192.168.1.1
cn: machine1
同样使用 ldapadd
添加到数据库后,相应的客户端即可直接使用这些条目。
增强安全性
修改密码 Hash
OpenLDAP 默认使用 SSHA (SHA-1 with salt) 作为密码 hash,在目前已经不算很强。用 ldapmodify
应用以下修改可以使用更安全的 hash 算法:
dn: cn=config
changetype:modify
--
add: olcPasswordHash
olcPasswordHash: {CRYPT}
-
add: olcPasswordCryptSaltFormat
olcPasswordCryptSaltFormat: $6$rounds=50000$%.16s
其中 {CRYPT}
代表使用系统的 libcrypt
库,olcPasswordCryptSaltFormat
指定了算法和盐的格式,需要与 crypt(3)
可以接受的一致。这里是 50000 轮加盐的 SHA-512。
增加密码策略
OpenLDAP 的 password policy overlay 可以用来设置密码策略,如密码长度、复杂度、过期时间等。下面这一部分参考了 OpenLDAP Password Policy Overlay 的内容。
首先安装 policy overlay:
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: ppolicy.la
然后创建一个 OU 用来放置默认策略(需要用管理员账号进行 bind):
dn: ou=policies,dc=foo,dc=com
objectClass: organizationalUnit
ou: policies
dn: cn=default,ou=policies,dc=foo,dc=com
objectClass: pwdPolicy
objectClass: organizationalRole
cn: default
pwdAttribute: userPassword
pwdMinLength: 12
pwdCheckQuality: 2
pwdMaxFailure: 5
pwdLockout: TRUE
pwdLockoutDuration: 600
pwdMustChange: TRUE
其中 pwdCheckQuality
表示强制检查,pwdMustChange
表示在管理员重置密码后,用户必须再次重置密码。其他选项的含义是很显然的。
最后,加载此策略作为默认密码策略:
dn: olcOverlay=ppolicy,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcPPolicyConfig
olcOverlay: ppolicy
olcPPolicyDefault: cn=default,ou=policies,dc=foo,dc=com
需要注意的是,密码强度策略对于管理员重置密码并不生效。
客户端
此处的客户端专指使用 LDAP 进行认证,可以与安装 OpenLDAP server 的服务器是同一台。
认证主要用到两个包:libnss-ldapd
和 libpam-ldapd
,前者主要用来同步用户、组数据库,后者用来进行认证。两者都有对应的陈旧版本,名为 libnss-ldap
和 libpam-ldap
(都少一个 d
),注意不要搞错。更高级的解决方案如 sssd
也可以使用 LDAP 进行中心化认证,这里不涉及此部分内容。
直接使用 apt
安装这两个包,在 configure 阶段会要求输入一些信息,包括 LDAP 服务器地址(ldap://your_server/
)、base DN(与配置的一致)、bind account(admin 用户的 CN)与密码。此外,还包括要从 LDAP 获取的数据源(如下图),这里对应选择 paswd, group, shadow, hosts
即可。如果之后要更改,可以直接修改 /etc/nsswitch.conf
或者运行 dpkg-reconfigure libnss-ldapd
。安装完成后,PAM 默认被修改为使用 LDAP,可以通过 pam-auth-update
命令来切换。
这两个包同时在系统中带入了名为 nscd
和 nslcd
的两个服务。前者名为 Name Service Cache Daemon,是各种 name service 通用的缓存管理进程,如在没有网络的情况提供旧的结果;后者名为 Daemon for NSS and PAM lookups using LDAP,是为新安装的两个包提供 LDAP 查询服务的进程,如果不运行则会导致 LDAP 查询失败。这两个服务是默认自启的,通常也不应该修改。
在完成这些配置后,LDAP 用户就可以直接通过 ssh 或者本地登录了,也可以使用服务器中配置的 hosts 项目。通过修改 PAM 配置和 nslcd 的配置,可以进行更精细的权限控制,如只允许/不允许属于某些组的 LDAP 用户登录,重新映射 UID 等。值得一提的是,LDAP 中的用户可以直接在客户端上通过 passwd
命令修改自己的 LDAP 密码。
常见问题
- LDAP 服务器中的更新未能及时在客户端体现(如修改了组关系、重置密码后无效):很可能是
nscd
的陈旧缓存导致,使用nscd -i group
等命令即可清除缓存。也可以配置更短的缓存时间(修改/etc/nscd.conf
即可)。 - 如此配置的 LDAP 服务器,使用其他服务器时无法找到组从属关系:因为用户中并没有
memberOf
的字段,可以手工添加这一字段解决(内容为组的 DN),也可以使用memberOf
overlay 来自动化地解决。