
1. 为什么还要在 CentOS 7 上折腾 LDAP先把场景说清楚。你手里有一批 CentOS 7 的机器可能是几台也可能是几十台每台上面都跑着不同的服务每个服务都有自己的账号体系。今天加一个人你得在 A 机器上useradd在 B 机器的 Web 后台里建账号在 C 机器的数据库里再插一条用户记录。人一多离职一频繁这套流程迟早出乱子——要么漏删要么权限对不上。LDAP 要解决的就是这个问题把用户是谁、属于哪个组、能访问什么这件事从各台机器里抽出来集中放到一个目录服务里。所有机器都去问同一个地方这个人的账号密码对不对、他在不在某个组里本地不再各自维护一份。这就是所谓的集中式身份认证。CentOS 7 虽然已经进入维护末期但大量存量生产环境还在跑它尤其是内网、实验室、传统企业的机房。这些环境往往不方便整体升级但又确实需要一套统一的账号体系。所以CentOS 7 下部署 LDAP这个需求到今天依然真实存在。这篇文章面向的是有一定 Linux 基础、但没怎么碰过目录服务的运维或开发人员。我会从零把一套可用的 LDAP 服务搭起来包括服务端安装、目录结构设计、用户和组的创建、客户端接入、TLS 加密以及后面最容易出问题的排错环节。全程基于 OpenLDAP 这套开源实现命令和配置都可以直接抄。提示本文所有主机名、域名、IP 均为示例实际部署时请替换成你自己的规划。生产环境务必先在小范围验证再批量推开。2. 部署前的规划目录树怎么设计决定了后面顺不顺很多人装 LDAP 的第一步就是yum install装完发现不知道 base DN 该写什么用户该往哪个分支放于是随手建了个dctest,dccom结果后面接应用时各种别扭。目录树的设计是整套体系的地基值得先花二十分钟想清楚。2.1 Base DN 与命名后缀的确定Base DN 是整个目录的根所有条目都挂在它下面。最常见的做法是用你的域名倒过来写比如域名是example.com那 Base DN 就是dcexample,dccom。如果你的环境根本没有正式域名也别用dctest,dccom这种一看就是临时的名字建议用一个内部约定的名字比如dccorp,dcinternal至少语义上说得通。这里有个容易踩的坑Base DN 一旦定下来后面改起来非常麻烦因为所有条目的 DN 都依赖它。所以规划阶段就要定死别中途改主意。2.2 目录树的分支结构一个清晰的分支结构通常长这样dcexample,dccom ├── oupeople # 存放所有用户账号 ├── ougroups # 存放所有用户组 └── ouservice # 存放服务账号可选把用户和组分开放在不同的 OU组织单元下好处是后面做访问控制、写查询过滤条件时逻辑清晰。比如你要查所有用户直接搜oupeople就行不用在全目录里捞。如果组织规模较大还可以在people下面再按部门细分比如oudev,oupeople、ouops,oupeople。但我不建议一上来就分太细因为 LDAP 的查询是树形遍历层级越深维护成本越高。中小规模环境用户和组各一个 OU 完全够用。2.3 主机与端口规划LDAP 默认监听 389 端口明文或 StartTLS636 端口是 LDAPSSSL/TLS 加密。生产环境强烈建议启用加密因为明文传输的密码在内网里也是可以被抓包的。规划时还要考虑LDAP 服务端是单点还是做主从复制。如果只是小规模内部使用单点加定期备份可以接受如果客户端数量多、对可用性有要求就要考虑主从或多主复制。本文先聚焦单机部署把基础打牢复制的话题后面单独展开。规划项建议值说明Base DNdcexample,dccom用真实或内部约定域名用户 OUoupeople所有用户账号组 OUougroups所有用户组管理员 DNcnadmin,dcexample,dccom目录管理员监听端口389 / 636636 用于加密数据后端mdb现代 OpenLDAP 默认后端3. 服务端安装与初始化配置规划清楚了动手就有方向。这一节把服务端从裸机装到能跑起来每一步我都会说明为什么这么做。3.1 安装 OpenLDAP 相关软件包CentOS 7 的官方源里就有 OpenLDAP直接装即可yum install -y openldap openldap-servers openldap-clients装完之后几个关键路径要记住/etc/openldap/slapd.d/这是配置目录现代 OpenLDAP 用cnconfig的方式管理配置所有配置都是 LDIF 格式的文件不再直接改slapd.conf。/var/lib/ldap/数据存储目录用户数据都在这里。/etc/openldap/schema/schema 文件目录定义目录里能存哪些属性。这里要特别提醒CentOS 7 的 OpenLDAP 默认使用slapd.d动态配置网上很多老教程还在教你改/etc/openldap/slapd.conf那个文件在新版本里根本不存在照着做只会浪费时间。判断方法很简单看/etc/openldap/下有没有slapd.d目录有就以它为准。3.2 启动服务并设置开机自启systemctl start slapd systemctl enable slapd systemctl status slapd启动后确认 389 端口在监听ss -tlnp | grep 389如果服务起不来先看日志journalctl -u slapd -n 50常见原因是数据目录权限不对。/var/lib/ldap/的属主必须是ldap:ldap如果之前手动动过这个目录权限乱了就会启动失败。修复chown -R ldap:ldap /var/lib/ldap chmod 700 /var/lib/ldap3.3 导入基础 schemaOpenLDAP 默认只加载了核心 schema要支持用户和组还需要导入cosine、nis、inetorgperson这几个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注意这里的-Y EXTERNAL和ldapi:///这是通过本地 Unix socket 用外部认证方式连接不需要密码只有 root 能用。这是初始化阶段的标准做法。3.4 设置管理员密码并创建 Base DN先给cnadmin设置密码。先生成一个加密后的密码串slappasswd -s YourStrongPassword它会输出一段{SSHA}...开头的字符串复制下来。然后写一个 LDIF 文件chrootpw.ldifdn: olcDatabase{0}config,cnconfig changetype: modify add: olcRootPW olcRootPW: {SSHA}你生成的密码串执行ldapadd -Y EXTERNAL -H ldapi:/// -f chrootpw.ldif接着创建 Base DN 和管理员条目写basedomain.ldifdn: dcexample,dccom objectClass: top objectClass: dcObject objectClass: organization o: Example Organization dc: example dn: cnadmin,dcexample,dccom objectClass: simpleSecurityObject objectClass: organizationalRole cn: admin description: LDAP administrator userPassword: {SSHA}你生成的密码串导入ldapadd -x -D cnadmin,dcexample,dccom -W -f basedomain.ldif-x表示用简单认证-D指定绑定 DN-W提示输入密码。这一步成功后你的目录根就建好了。注意olcRootPW那一步改的是cnconfig这个管理后端的密码和cnadmin的业务管理员密码是两回事。前者用于管理配置后者用于管理业务数据。两个都设上别混。4. 用户与组的批量创建手工建和脚本建怎么选目录根建好了接下来往里放人。少量用户手工建没问题几十上百个就得靠脚本。这一节两种方式都讲并说明各自的适用场景。4.1 手工创建 OU、用户和组先建两个 OU写ou.ldifdn: oupeople,dcexample,dccom objectClass: organizationalUnit ou: people dn: ougroups,dcexample,dccom objectClass: organizationalUnit ou: groups导入ldapadd -x -D cnadmin,dcexample,dccom -W -f ou.ldif然后建一个用户写user1.ldifdn: uidzhangsan,oupeople,dcexample,dccom objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount uid: zhangsan cn: Zhang San sn: Zhang givenName: San displayName: Zhang San uidNumber: 10001 gidNumber: 10001 homeDirectory: /home/zhangsan loginShell: /bin/bash mail: zhangsanexample.com userPassword: {SSHA}用户密码串这里posixAccount这个 objectClass 很关键它提供了uidNumber、gidNumber、homeDirectory、loginShell这些 Linux 系统登录必需的属性。没有它客户端就没法把这个 LDAP 用户映射成本地用户。建组写group1.ldifdn: cndev,ougroups,dcexample,dccom objectClass: posixGroup cn: dev gidNumber: 10001 memberUid: zhangsanposixGroup对应 Linux 的组memberUid里放的是用户的uid不是完整 DN这点和groupOfNames那种用 DN 做成员的组不一样别搞混。4.2 用脚本批量导入用户手工建三个五个还行建五十个就是折磨。更实际的做法是准备一个 CSV然后用脚本生成 LDIF 再批量导入。假设有个users.csvuid,cn,sn,uidNumber,gidNumber,mail zhangsan,Zhang San,Zhang,10001,10001,zhangsanexample.com lisi,Li Si,Li,10002,10001,lisiexample.com wangwu,Wang Wu,Wang,10003,10001,wangwuexample.com写个 shell 脚本生成 LDIF#!/bin/bash while IFS, read -r uid cn sn uidnum gidnum mail; do [ $uid uid ] continue cat EOF dn: uid$uid,oupeople,dcexample,dccom objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount uid: $uid cn: $cn sn: $sn uidNumber: $uidnum gidNumber: $gidnum homeDirectory: /home/$uid loginShell: /bin/bash mail: $mail userPassword: {SSHA}统一初始密码串 EOF echo done users.csv users.ldif生成后一次性导入ldapadd -x -D cnadmin,dcexample,dccom -W -f users.ldif这里有个实操心得批量导入时uidNumber一定要规划好区间别和本地系统用户通常 1000 以下冲突。建议从 10000 开始按部门或用途分段比如 10000-19999 给普通员工20000-29999 给服务账号。这样后面排查问题时看一眼 uid 就知道是什么类型的账号。4.3 验证导入结果导入完别急着往下走先查一下确认数据进去了ldapsearch -x -D cnadmin,dcexample,dccom -W -b oupeople,dcexample,dccom (objectClassposixAccount) uid cn uidNumber如果能看到你导入的用户列表说明数据没问题。查不到就检查 DN 拼写、objectClass 是否齐全、导入时有没有报错。操作命令要点常见错误建 OUobjectClass: organizationalUnit忘了 ou 属性建用户必须含 posixAccount缺 uidNumber 导致无法登录建组posixGroup memberUid误用完整 DN 做成员批量导入脚本生成 LDIFuidNumber 与系统冲突5. 客户端接入让 Linux 机器认这套账号服务端数据齐了真正的价值体现在客户端能不能用这些账号登录。这一节把一台 CentOS 7 客户端接进来实现用 LDAP 账号 SSH 登录。5.1 安装客户端组件在客户端机器上yum install -y nss-pam-ldapd pam_ldap openldap-clients这里要解释一下这几个包的分工。nss-pam-ldapd提供nslcd服务负责把系统的用户查询NSS转发给 LDAPpam_ldap负责认证环节PAM。两者配合才能实现查得到用户 验证得了密码。5.2 配置 nslcd编辑/etc/nslcd.conf核心几行uri ldap://ldap.example.com/ base dcexample,dccom binddn cnadmin,dcexample,dccom bindpw 管理员密码uri指向服务端base是搜索起点binddn和bindpw是客户端用来查询目录的账号。生产环境不建议直接用cnadmin应该建一个只有读权限的专用账号最小权限原则。然后改/etc/nsswitch.conf让系统在查用户和组时也去问 LDAPpasswd: files sss ldap shadow: files sss ldap group: files sss ldapfiles在前表示先查本地/etc/passwd查不到再走 LDAP。这个顺序很重要保证本地系统账号比如 root不受影响。启动服务systemctl start nslcd systemctl enable nslcd5.3 验证客户端能否识别 LDAP 用户getent passwd zhangsan id zhangsan如果getent能返回用户信息id能列出他的组说明 NSS 这一层通了。这时候尝试 SSH 登录ssh zhangsan客户端IP能登进去整套链路就打通了。登不进去问题通常出在 PAM 配置或密码验证环节下一节专门讲排查。5.4 自动创建家目录的坑LDAP 用户第一次登录时本地是没有家目录的。默认情况下 SSH 登录会因为找不到家目录而报错或直接踢出。解决办法是启用pam_mkhomedirauthconfig --enablemkhomedir --update或者手动在/etc/pam.d/system-auth和/etc/pam.d/password-auth里加session required pam_mkhomedir.so skel/etc/skel umask0022这个坑我见过太多次很多人卡在密码明明对就是登不进去最后发现是家目录没建。记住这一条能省你半小时。6. 加密与安全加固别让密码裸奔前面跑通的是明文 LDAP389 端口上密码是明文的。内网也不是绝对安全抓包工具一开密码一览无余。这一节把 TLS 加上。6.1 生成自签证书内部环境用自签证书就够了。先生成 CA 和服务器证书openssl req -x509 -newkey rsa:2048 -keyout ca.key -out ca.crt -days 3650 -nodes -subj /CNExample-CA openssl req -newkey rsa:2048 -keyout server.key -out server.csr -nodes -subj /CNldap.example.com openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650把证书放到 OpenLDAP 能读到的位置注意权限mkdir -p /etc/openldap/certs cp server.crt server.key ca.crt /etc/openldap/certs/ chown -R ldap:ldap /etc/openldap/certs chmod 600 /etc/openldap/certs/server.key6.2 配置 slapd 启用 TLS写一个tls.ldifdn: cnconfig changetype: modify add: olcTLSCACertificateFile olcTLSCACertificateFile: /etc/openldap/certs/ca.crt - add: olcTLSCertificateFile olcTLSCertificateFile: /etc/openldap/certs/server.crt - add: olcTLSCertificateKeyFile olcTLSCertificateKeyFile: /etc/openldap/certs/server.key导入ldapmodify -Y EXTERNAL -H ldapi:/// -f tls.ldif重启服务确认 636 端口在监听systemctl restart slapd ss -tlnp | grep 6366.3 客户端改用加密连接客户端把nslcd.conf里的 uri 改成uri ldaps://ldap.example.com/ ssl on tls_reqcert allowtls_reqcert allow表示接受自签证书。如果是正式 CA 签发的证书可以设成demand更严格。改完重启nslcd再验证一次登录。注意自签证书的CN必须和客户端连接时用的主机名一致否则证书校验会失败。要么把主机名写进客户端 hosts要么在证书里加 SAN 扩展。这个细节不注意加密配好了反而登不进去。7. 排错实录那些让人抓狂的典型故障LDAP 的报错信息往往很含糊Invalid credentials背后可能是十种原因。这一节把最常见的几类故障和排查思路整理出来都是实际踩过的。7.1 客户端查不到用户现象getent passwd zhangsan返回空但服务端ldapsearch能查到。排查顺序先看nslcd服务状态systemctl status nslcd没起来先起来。看/etc/nsswitch.conf里passwd那行有没有ldap漏了就加上。看nslcd.conf里的base和binddn对不对用ldapsearch手动模拟一次查询。看防火墙客户端到服务端 389/636 通不通telnet ldap.example.com 389试一下。这四步走完九成问题能定位。7.2 密码正确却登录失败现象ldapwhoami验证密码通过但 SSH 登录被拒。这种多半是 PAM 环节的问题。检查/etc/pam.d/sshd和/etc/pam.d/system-auth里有没有正确引用pam_ldap。另外确认pam_mkhomedir有没有配家目录建不出来也会导致登录失败。还有一个隐蔽的坑shadowAccount的shadowLastChange属性。如果这个值设置不当PAM 可能认为密码已过期。检查方法ldapsearch -x -D cnadmin,dcexample,dccom -W -b uidzhangsan,oupeople,dcexample,dccom shadowLastChange如果返回 0 或者一个很旧的值把它更新成当前天数从 1970-01-01 算起。7.3 服务端启动失败现象systemctl start slapd失败日志报unable to open pid file或database environment open failed。八成是数据目录权限或残留锁文件的问题。处理systemctl stop slapd rm -f /var/lib/ldap/__db.* /var/lib/ldap/alock chown -R ldap:ldap /var/lib/ldap systemctl start slapd__db.*是 Berkeley DB 的残留文件异常关闭后可能损坏删掉让 slapd 重建。注意这只在确认数据已备份或可重建时操作别在数据重要时乱删。7.4 常见错误对照表报错信息可能原因处理方向Invalid credentials密码错 / binddn 错用 ldapwhoami 验证No such objectDN 拼写错逐级 ldapsearch 确认Cant contact LDAP server网络 / 服务未起查端口和防火墙Insufficient access权限不足检查 binddn 权限Object class violation属性与 objectClass 不匹配核对 schema 要求8. 数据备份与日常维护的几个习惯LDAP 一旦成为认证中心它挂了所有人都登不上备份和巡检就是保命的事。8.1 用 slapcat 做逻辑备份最可靠的备份方式是slapcat它把整个目录导出成 LDIFslapcat -v -l /backup/ldap-$(date %Y%m%d).ldif这个命令要在服务停止或只读状态下跑最稳妥热备时可能有一致性问题。建议写进 crontab每天凌晨跑一次保留最近 30 天。恢复时用slapaddsystemctl stop slapd slapadd -v -l /backup/ldap-20240101.ldif chown -R ldap:ldap /var/lib/ldap systemctl start slapd8.2 定期巡检要看什么我一般会盯这几个指标目录条目总数有没有异常增长或减少ldapsearch加-s base看namingContexts。服务进程和端口状态systemctl status slapd和ss -tlnp。日志里有没有反复出现的认证失败可能是有人在暴力尝试。证书到期时间自签证书也要记着续别等过期了才发现。8.3 一个容易被忽略的维护点cnconfig这个配置后端本身也是数据改了配置之后最好也备份一份slapcat -n 0 -l /backup/ldap-config-$(date %Y%m%d).ldif-n 0表示导出第 0 个数据库也就是配置库。哪天配置改崩了这份备份能救急。这个习惯很多人没有等出事才后悔。9. 关于这套方案的一些个人体会搭 LDAP 这件事技术本身不算特别复杂难的是把它用对、用稳。我自己在几个环境里折腾下来最大的感受是规划阶段的每一分钟都能在后期省下十分钟的排错时间。Base DN、OU 结构、uid 区间这些定好了后面接应用、加用户都是顺水推舟定得随意后面处处别扭。另一个体会是别把 LDAP 当成万能钥匙。它擅长的是集中存账号、统一认证但细粒度的权限控制、复杂的授权逻辑它并不擅长。真要做精细的访问控制通常还得配合应用层自己的权限系统。把 LDAP 用在它该用的地方它就很稳。最后说个实操小技巧调试阶段多用ldapsearch和ldapwhoami手动验证别一上来就改客户端配置。服务端数据对不对、密码能不能过这两条命令一测便知。把服务端确认无误了再去调客户端问题范围能缩小一大半。这套思路我在排查任何认证类问题时都用屡试不爽。