使用 OpenLDAP 在 Linux 上进行中心化用户管理

 

哪个年轻人不想拥有属于自己的 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 可以用于认证,还需要属于 posixAccountshadowAccount 类型。这里的 cnsninetOrgPerson 的必选字段,但不会被 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-ldapdlibpam-ldapd,前者主要用来同步用户、组数据库,后者用来进行认证。两者都有对应的陈旧版本,名为 libnss-ldaplibpam-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 命令来切换。

fang-wei-xing

这两个包同时在系统中带入了名为 nscdnslcd 的两个服务。前者名为 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 来自动化地解决。