1. 为什么我要在这个时间点写 OpenLDAP 安装
前阵子帮朋友整理一套内部测试环境的账号体系,几十个服务各自维护一套密码,乱得让人头疼。统一认证是个绕不开的话题,我翻了半天方案,最后落到了 OpenLDAP 上。为什么选它?因为 CentOS 7 在存量服务器里的占比依然不低,系统自带源里的 OpenLDAP 恰恰是 2.4.44 这个稳定版本。没选择从源码编译新版本,就是图省心:yum 安装、yum 升级、依赖关系清晰,出问题也好回滚。
这篇东西不是概念科普,是一份可以直接照着敲的实战记录。你会看到从最小化 CentOS 7 环境开始,怎么装 openldap 三件套、怎么初始化配置、怎么规划目录树、怎么把用户和组导进去,以及我踩过的几个坑和对应的排查手法。适合刚接手内部认证系统、或者想在虚拟机里搭一套 LDAP 练手的同学。不管你有没有玩过目录服务,只要会 ssh 和 vi,就能跟着跑通。
2. 安装前需要想清楚的几个事
2.1 LDAP 到底帮我们解决什么问题
LDAP 全称是轻量级目录访问协议,名字听着唬人,本质就是一本“组织通讯录”。公司里几十个系统都要登录,如果每个系统都存一份账号密码,那员工改密码要改几十次,离职了一堆系统还在用旧账号。通过 LDAP,把人员、组织、设备这类“查询多、修改少”的数据统一放进去,各个系统只需要对接 LDAP 做认证和查询,账号改一次,所有系统同步生效。
OpenLDAP 是这套协议最经典的开源实现。CentOS 7 里自带的是 2.4.44,尽管新版本已经发展到了 2.5、2.6,但对企业内部存量环境来说,跟着系统版本走永远是最稳妥的。2.4.44 足够稳定,功能也全,支持动态配置、访问控制、TLS 加密、多种数据后端,日常的账号认证和目录管理完全够用。
2.2 为什么锁定 2.4.44 而不是追新
我在选型时不是没想过直接上最新版。可一旦考虑到生产环境的真实情况,就明白了一个道理:操作系统的内置版本和发行版深度绑定,没必要自己折腾高版本。
- 依赖干净:openldap 2.4.44 的依赖包在 CentOS 7 源里都有,不会出现为了装一个依赖去额外配置第三方源的情况。
- 行为可预期:网上大量资料、故障案例都基于这个版本,遇到问题搜得到答案。
- 升级路径平滑:系统自身升级时 openldap 也会跟着收到安全补丁,不需要手工维护。
- 稳定性优先:LDAP 这种基础服务,求稳比求新重要。只要不追求新特性,2.4.44 完全够用。
如果你确实需要 mdb 后端或者新版的一些访问控制语法,再考虑用二进制包或源码编译也不迟。但如果是第一次搭,别给自己增加额外变量。
2.3 最小化安装后的准备工作
我这次用的是 CentOS 7 最小化安装,装完第一件事是确认系统版本和基础环境。
cat /etc/redhat-release yum update -y更新系统这个步骤建议做,因为后续安装 openldap 时,依赖库版本越新越不容易冲突。接着看时间同步,LDAP 对时间比较敏感,尤其是后面做 TLS 证书校验,如果服务器时间偏差大,认证会莫名失败。确认一下 ntp 或 chrony 在跑:
timedatectl set-ntp true然后是 SELinux 和防火墙。很多人一来就把 SELinux 关掉,我不建议直接关,但确实有部分环境里 SELinux 策略会干扰 slapd 访问某些目录。排查时可以先临时放行:
setenforce 0这只是临时生效,重启后恢复。真正生产环境要在 /etc/selinux/config 里设置成 permissive 或 enforcing,并配套配置策略。我的建议是:先以 permissive 模式跑通流程,确认没问题后再逐步收紧。
防火墙这边,如果没改过,默认会是开启状态。先放行 LDAP 相关端口:
firewall-cmd --permanent --add-service=ldap firewall-cmd --permanent --add-service=ldaps firewall-cmd --reloadldap 对应 389,ldaps 对应 636,如果后面还有 ldapi socket 走本地,不需要额外开端口。
3. OpenLDAP 2.4.44 服务端安装与初始化
3.1 安装 openldap 相关软件包
CentOS 7 推荐用三件套:
yum install -y openldap openldap-servers openldap-clients装完之后确认一下版本:
slapd -VV正常会看到版本信息里面包含 2.4.44 字样。这三个包各自的职责要搞清楚,别装错了没概念:
- openldap:客户端运行库,很多程序联调 LDAP 时会依赖它。
- openldap-servers:服务端程序 slapd,以及一堆示例配置和 schema 文件。
- openldap-clients:ldapsearch、ldapadd、ldapmodify、ldapwhoami 这些命令行工具,调试时离不开。
3.2 初始化数据库目录和配置
安装完后不要急着启动服务,先处理两个目录。
第一个是数据目录 /var/lib/ldap。OpenLDAP 2.4.44 默认使用的后端是 hdb,数据文件、索引文件都存在这个目录里。初始情况下目录里只有一些模板,或者干脆是空的。我们需要准备一个 DB_CONFIG 文件,它定义了 BerkeleyDB 的缓存大小、日志参数等。系统提供了一个示例:
cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG chown -R ldap:ldap /var/lib/ldap如果不复制 DB_CONFIG 而直接启动,slapd 虽然也可能起得来,但会提示找不到配置文件,而且性能参数不理想。把属主改成 ldap 是必须的,因为 slapd 启动后会以 ldap 用户身份读写这些文件。权限不对的话,日志里会报各种奇怪的错误。
第二个是配置目录 /etc/openldap/slapd.d。这个目录存放动态配置,也就是 cn=config 的 LDIF 格式条目。安装完成后,目录里会有一份默认配置。可以看一眼:
ls -la /etc/openldap/slapd.d/里面会有一个 cn=config 子目录,OpenLDAP 2.4 以后的配置不再依赖传统的单文件 slapd.conf,而是支持在线修改,改动即时生效,不用重启服务。这个设计非常好,但同时也要求我们操作时要遵循 LDIF 的格式要求,不能用 vi 直接改这些文件。
3.3 启动 slapd 并确认状态
初始化数据目录后,立刻启动并设置开机自启:
systemctl start slapd systemctl enable slapd systemctl status slapd如果状态显示 active (running),说明服务已经起来了。再确认一下端口监听:
ss -lntp | grep 389正常能看到 slapd 监听在 0.0.0.0:389。如果这里没有任何输出,服务多半没起来,去 /var/log/messages 里看日志,或者用 journalctl -u slapd 查。
到这里,OpenLDAP 只是跑起来了,但还没有配置我们自己的域名和管理员。默认配置里有 sample 条目,但都是占位符,不能直接用于生产。继续往下。
4. 配置目录树和管理员账号
4.1 设计自己的目录树
LDAP 的数据结构是树形的,从根往下延伸。我先画一个最简单、最常见的模型:
dc=example,dc=com ├── ou=People # 用户账号 └── ou=Group # 用户组dc 是 domain component 的缩写,一般对应域名拆开的部分。如果你公司内部域是 corp.example.com,那根往往是 dc=corp,dc=example,dc=com。这里我统一用 dc=example,dc=com 举例,你实际部署时替换成自己的域名即可。
在根下面通常直接挂 ou,也就是组织单元,用来区分不同类型的条目。People 放人员,Group 放组,这是最通用的玩法。也可以按部门分 ou=IT、ou=Finance,但那样会导致后面配置权限时规则变复杂,建议前期先简单一点。
4.2 生成管理员密码散列
管理员账号一般叫 rootDN,例如 cn=Manager,dc=example,dc=com,它不是系统 root,而是 LDAP 目录的管理员。在 OpenLDAP 配置里,rootDN 和系统超级用户等价,拥有整个目录的最高权限。
rootDN 的密码不能直接以明文写在配置里,要用散列值。OpenLDAP 提供了专门命令生成:
slappasswd -s 'your-password'执行后会输出类似于{SSHA}xxxxxxxxxxxxxxxx的字符串。这个字符串就是我们要填到配置里的。注意 -s 后面直接跟密码,会出现在 shell 历史里,如果环境不允许,可以不带参数执行,会提示输入两次密码:
slappasswd我更推荐第二种方式,安全一点。生成的散列值保存好,下一步要用。
4.3 修改 olcSuffix、olcRootDN 和 olcRootPW
动态配置直接存在 cn=config 下面,我们可以通过一个 LDIF 文件来修改。先写一个修改文件:
dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcSuffix olcSuffix: dc=example,dc=com replace: olcRootDN olcRootDN: cn=Manager,dc=example,dc=com replace: olcRootPW olcRootPW: {SSHA}你的密码散列值这里要注意 dn 中的{2}hdb,这是 OpenLDAP 默认数据库的编号。如果不确定当前编号,可以用下面的命令查询:
ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config" "(objectClass=olcDatabaseConfig)" dn执行时会通过本地 IPC socket 以 EXTERNAL 方式认证,也就是 slapd 进程本身的权限,不需要额外密码。查询结果里会显示类似dn: olcDatabase={2}hdb,cn=config,确认编号后,把上面的 dn 改成实际的编号。
然后应用修改:
ldapmodify -Y EXTERNAL -H ldapi:/// -f modify_rootdn.ldif如果返回modifying entry "olcDatabase={2}hdb,cn=config"和success,说明修改成功。这里有个非常关键的点:修改配置用的是-Y EXTERNAL,不要去用-x -D绑定管理员账号,因为管理员账号在配置尚未生效前是不存在的,容易陷入鸡生蛋蛋生鸡的死循环。EXTERNAL 走的是本地 socket,认证级别基于 Linux 系统用户,当前 root 用户有权限操作,这就绕开了 bootstrap 问题。
4.4 加载基础 Schema
OpenLDAP 自带很多 schema 文件,比如 cosine、nis、inetorgperson。默认配置只加载了核心 schema,我们需要把常用的加载进去,否则后面添加人员条目时会报 objectClass 不存在。
加载方法也是用 EXTERNAL 认证:
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/cosine.ldif ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/nis.ldif ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/openldap/schema/inetorgperson.ldif这里要注意,schema 目录下还会有 .schema 文件,但动态配置只认 LDIF 版本,所以一定要用 .ldif 文件。加载成功的标志是每个命令都返回adding new entry。加载完后可以在 cn=schema 下看到对应的 cn 子目录。
5. 创建基础条目并导入用户和组
5.1 编写基础 LDIF
有了域名后缀和管理员之后,先创建根节点和两个 ou。新建一个 base.ldif:
dn: dc=example,dc=com objectClass: top objectClass: dcObject objectClass: organization o: Example Inc. dc: example dn: ou=People,dc=example,dc=com objectClass: organizationalUnit ou: People dn: ou=Group,dc=example,dc=com objectClass: organizationalUnit ou: Group这里有几个字段要解释:o 代表组织名称,dc 代表域组件,ou 是组织单元。objectClass 决定了这条条目必须携带哪些属性,比如 organization 类必须有 o,dcObject 必须有 dc。
5.2 导入基础条目
导入命令:
ldapadd -x -D "cn=Manager,dc=example,dc=com" -W -f base.ldif-x表示使用简单认证,-D指定绑定 DN,也就是我们之前设置的管理员;-W会提示输入密码,输入的是管理员密码明文(也就是我们在上一节生成散列值前那个原始密码)。注意这里和修改配置的 EXTERNAL 是两种不同机制,别搞混了。
导入成功后,可以立刻验证:
ldapsearch -x -b "dc=example,dc=com" -s base能看到刚才创建的根节点,说明导入成功。
5.3 创建测试用户和组
接下来创建一个用户,名字用 tester 好了。新建 user.ldif:
dn: uid=tester,ou=People,dc=example,dc=com objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount cn: Tester User sn: User uid: tester uidNumber: 10001 gidNumber: 10001 homeDirectory: /home/tester loginShell: /bin/bash mail: tester@example.com userPassword: {SSHA}你的密码散列值这里同时用到了 inetOrgPerson 和 posixAccount,前者提供邮箱、姓名等个人信息,后者提供 Unix 账号属性,比如 uidNumber、gidNumber、homeDirectory。如果把 LDAP 对接 Linux 登录,posixAccount 必须存在。userPassword 同样用 slappasswd 生成的散列值,也可以先留一个明文密码,但导入后建议改成散列。
再创建一个组:
dn: cn=developers,ou=Group,dc=example,dc=com objectClass: top objectClass: posixGroup cn: developers gidNumber: 10001 memberUid: testergidNumber 要和用户里的 gidNumber 保持一致,这样才算真正把用户放进组。memberUid 是可选的,但通常要写上。
导入这两个文件:
ldapadd -x -D "cn=Manager,dc=example,dc=com" -W -f user.ldif ldapadd -x -D "cn=Manager,dc=example,dc=com" -W -f group.ldif导入完成后,用查询验证一下:
ldapsearch -x -b "dc=example,dc=com" "(uid=tester)"应该能看到完整的用户条目。再试试用户认证:
ldapwhoami -x -D "uid=tester,ou=People,dc=example,dc=com" -w 密码能返回自己的 DN,说明 LDAP 认证已经通了。这一步如果报Invalid credentials,不要慌,大概率是密码不对,检查一下 userPassword 的属性值和输入的是否一致。
5.4 配置默认访问控制
OpenLDAP 2.4.44 的默认访问控制比较严格,允许匿名用户读取绝大多数数据,但修改操作只有 rootDN 可以做。对于内部使用,这通常够了。但如果想限定某条 OU 只让特定用户改,就需要自定义 olcAccess。这里给一个示例,在基础数据上增加规则:允许所有用户在 b 认证后读取 People,只有管理员可以修改。
dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcAccess olcAccess: to attrs=userPassword by self write by anonymous auth by dn="cn=Manager,dc=example,dc=com" write by * none olcAccess: to * by dn="cn=Manager,dc=example,dc=com" write by users read by * read应用:
ldapmodify -Y EXTERNAL -H ldapi:/// -f acl.ldifACL 规则是顺序匹配的,第一条匹配后不再继续,所以敏感属性要放在前面。这段规则的核心含义:userPassword 这个属性允许自己修改、允许匿名绑定认证、管理员可改、别人不能看;其他条目条目允许管理员写、所有用户读、匿名也能读。这比默认规则更便于客户端查询。
6. 常见问题排查与避坑实录
6.1 服务启动失败,端口没监听
这是新手遇到最多的坑。先看服务状态:
systemctl status slapd journalctl -u slapd -n 50常见原因有三个:
- /var/lib/ldap 目录权限不对,slapd 无法读写数据文件。确认
ls -ld /var/lib/ldap的属主是 ldap:ldap,若不是就 chown。 - /etc/openldap/slapd.d 配置目录被手工改动过,导致权限或内容格式错误。如果手动编辑过里面的 ldif,最容易出问题。解决办法是把原始配置目录备份后,用安装包生成的默认配置恢复:先卸载 openldap-servers 再重装,或者从 /usr/share/openldap-servers 下的模板恢复。
- SELinux 阻止了 slapd 访问数据目录或绑定端口。先临时
setenforce 0,如果服务能起来,就是 SELinux policy 问题,需要调整策略而不是长期关闭。
6.2 ldapadd 报 Insufficient access
修改 cn=config 时,如果你用-x -D "cn=Manager,..."去添加 schema 或修改配置,很大概率会得到Insufficient access。原因是 cn=config 的访问控制和管理员机制跟普通数据目录不是一回事,配置树只认EXTERNAL或专用的配置管理员 DN,而没有提取生成配置管理员的话,只能用本地 EXTERNAL。
正确姿势永远是:
ldapadd -Y EXTERNAL -H ldapi:/// -f some.ldif记住这个口诀:改配置用 EXTERNAL,改数据用 -D rootDN。
6.3 ldapadd 报 Invalid credentials
这个最常见于导入数据阶段。检查顺序:
- 确认 rootDN 书写完全一致,包括大小写和空格。cn=Manager,dc=example,dc=com 和 cn=manager,dc=example,dc=com 是两条完全不同的 DN。
- 确认在修改 rootPW 时是否成功,可以重新用 ldapsearch EXTERNAL 查询一下 olcRootPW 是否存在。
- 如果有多个 hdb 数据库编号,可能修改到了默认的 {2}hdb,但导入时连接的数据库并非同一个。查询一下配置确认。
6.4 防火墙或主机名导致客户端连不上
服务端本地查没问题,可别的机器连不上。检查服务端防火墙是否放行;检查客户端 DNS 解析;如果用主机名连接,确认 /etc/hosts 有对应记录。有时候两台机器不在同一网段,中间交换机拦截,也要查一下。
另外注意,如果没有配置 TLS,ldapsearch 默认用明文传输,只建议在内网使用。如果必须跨公网,一定要配置 LDAPS 或 STARTTLS。
6.5 备份与恢复的小提醒
OpenLDAP 2.4.44 自带的 slapcat 和 slapadd 是一对非常好用的数据备份恢复工具。备份整库:
slapcat -b "dc=example,dc=com" > ldap_backup.ldif如果误删了数据,把备份 LDIF 放到 /var/lib/ldap 后执行:
systemctl stop slapd slapadd -l ldap_backup.ldif chown -R ldap:ldap /var/lib/ldap systemctl start slapd这个操作一定要停服务后做,否则可能造成数据损坏。没停服务直接 slapadd,轻则数据混乱,重则备份也没用了。
7. 更进一步:TLS 加密和日常维护建议
虽然内网明文 LDAP 很常见,但不代表值得推荐。尤其现在大部分系统都要求传输加密。配置 TLS 最省事的方式是用自签名证书。
生成私钥和证书:
mkdir /etc/openldap/certs openssl req -new -x509 -days 3650 -nodes \ -out /etc/openldap/certs/server.crt \ -keyout /etc/openldap/certs/server.key chown -R ldap:ldap /etc/openldap/certs然后把证书信息写入 cn=config:
dn: cn=config changetype: modify replace: olcTLSCertificateFile olcTLSCertificateFile: /etc/openldap/certs/server.crt replace: olcTLSCertificateKeyFile olcTLSCertificateKeyFile: /etc/openldap/certs/server.key应用后重启 slapd。然后用 ldaps:// 协议访问:
ldapsearch -H ldaps://ldap.example.com -x -b "dc=example,dc=com"首次连接因为是自签名证书,可能需要加-Z或者配置 LDAPTLS_REQCERT=never 来跳过验证。生产环境应该使用内网 CA 签发的证书,并让所有客户端信任 CA。
日常维护方面,我个人的习惯是每个月做一次全量备份,每次修改 schema 或 ACL 之前,先用 cp -r 备份 /etc/openldap/slapd.d 和 /var/lib/ldap。OpenLDAP 的配置和数据都在文件系统上,备份它们比想象中简单,关键时刻能救命。
8. 写在最后的一点实战体会
OpenLDAP 2.4.44 虽然老,但胜在皮实。回过头来看整个安装过程,真正容易翻车的不是命令本身,而是对认证机制的理解。配置管理走 EXTERNAL,数据操作走管理员绑定,这两条线只要清晰,后面加用户、加组、改密码都很顺。
我个人踩过最多的坑是修改 rootPW 后立刻重启服务,其实不需要重启,动态配置本身就是即时生效的。还有一次是备份恢复时忘了把数据目录属主改回 ldap,导致 slapadd 后服务起不来,折腾了十分钟才发现是权限问题。
如果后面需要接入更多系统,建议先确定好 uid 的唯一性和 gidNumber 的规划,否则以后每个系统对接时都要返工。先跑通基础,再考虑复杂 ACL 和高可用,这条路是最稳的。希望这份记录能让你少走点弯路。