
1. 为什么还要在 CentOS 7 上折腾 LDAP先把话说在前头CentOS 7 已经进入维护尾声很多人觉得这时候再谈它的 LDAP 部署有点“考古”。但现实情况是大量企业内部的老业务系统、测试环境、离线机房里的机器依然跑在 CentOS 7 上。你不可能因为官方停止更新就把整套认证体系推倒重来成本扛不住。所以把 LDAP 在 CentOS 7 上部署稳、调通、能落地依然是一项很实在的技能。LDAP全称轻量级目录访问协议你可以把它理解成一个“专门用来存账号和组织结构的电话簿”。它不负责计算也不负责存储业务数据它只干一件事把用户、用户组、部门层级这些信息用一种树状结构管起来然后让其他系统来查。邮件系统查它、代码仓库查它、内部管理后台查它、甚至操作系统的登录认证也可以查它。一旦你把 LDAP 跑起来后面新增一个内部系统就不用再单独建一套账号体系了直接对接 LDAP 就行。这篇文章面向的是谁主要是三类人第一类是有一定 Linux 基础但没怎么接触过目录服务的运维人员第二类是需要给内部系统做统一认证但不想上太重方案的后端开发第三类是在离线或半离线环境里做交付必须自己动手搭一套认证服务的实施人员。我会从零开始把 CentOS 7 下 LDAP 的部署过程拆开讲清楚包括为什么选这个组件、每一步在干什么、参数怎么定、踩过哪些坑、怎么排查问题。你照着做大概率能一次跑通。需要提前说明的是LDAP 的部署方案有很多种我这里选择的是一套在 CentOS 7 上最稳妥、依赖最少、最容易复现的组合。这不是唯一解但它是经过大量实际环境验证过的解。下面所有操作都基于常见实践补充了细节你可以在自己的测试环境里先走一遍确认没问题再上生产。2. 部署前的整体设计与组件选型2.1 为什么选 389-ds 而不是 OpenLDAP在 CentOS 7 上部署 LDAP你面前基本有两条路一条是用 OpenLDAP另一条是用 389-ds。OpenLDAP 名气大、资料多但它的配置方式偏底层很多操作要手写 LDIF 文件对新手不太友好。389-ds 是 Red Hat 体系里长期维护的目录服务组件在 CentOS 7 的官方源里就能直接装自带图形化管理界面和命令行工具配置起来更“开箱即用”。我选 389-ds 的核心理由有三个。第一它和 CentOS 7 的兼容性最好安装包直接来自系统源不需要额外编译省去了很多依赖问题。第二它提供了dsconf、dsctl这类现代命令行工具也保留了ldapmodify这种通用方式你既可以用命令快速操作也可以写 LDIF 做批量处理。第三它的日志和状态查询比较直观出问题的时候更容易定位。当然OpenLDAP 也不是不能用。如果你的团队已经有 OpenLDAP 的运维经验或者你需要对接的某些系统明确要求 OpenLDAP 的特定 schema那继续用 OpenLDAP 也合理。但对于大多数“从零搭一套统一认证”的场景389-ds 在 CentOS 7 上的综合体验更好。2.2 目录树结构怎么规划才不给自己挖坑LDAP 的核心是目录树树的结构一旦定下来后面改起来非常麻烦。所以部署之前必须先想清楚你的组织结构和命名规则。我见过太多人上来就随手写一个dctest,dccom结果后面要接真实业务的时候发现根本对不上只能重建。一个比较稳妥的做法是用你公司的域名倒过来作为根节点。比如内部域名是example.internal那根节点就是dcexample,dcinternal。如果公司有多个子品牌或者多个业务线可以在根节点下面再分oubrandA、oubrandB。用户统一放在oupeople下面用户组放在ougroups下面服务账号放在ouservices下面。这样结构清晰后面做权限控制的时候也好写过滤规则。这里有个细节要注意LDAP 的条目命名尽量用英文和数字不要用中文。虽然协议本身支持 UTF-8但很多对接系统在处理中文 DN 的时候会出各种编码问题排查起来非常痛苦。用户名可以用拼音或者工号组名用英文部门名如果一定要用中文也尽量放在属性里而不是放在 DN 的ou或cn上。2.3 网络与端口规划LDAP 默认监听 389 端口加密的 LDAPS 监听 636 端口。在 CentOS 7 上如果你只是内部测试可以先只开 389等调通了再配 TLS。但如果是生产环境我强烈建议直接上 636因为 LDAP 的简单绑定是明文传输密码的哪怕在内网也不安全。端口规划上389 和 636 不要和系统上其他服务冲突。CentOS 7 默认不会占用这两个端口但如果你之前装过其他目录服务要先确认端口没有被占用。可以用ss -tlnp | grep -E 389|636来检查。另外如果服务器开了 firewalld记得把这两个端口放行否则本机测试没问题远程连不上你会以为是配置错了其实是防火墙拦了。3. 核心细节解析与实操要点3.1 安装 389-ds 与基础环境准备在 CentOS 7 上安装 389-ds 非常简单因为它就在基础源里。执行下面这条命令yum install -y 389-ds-base 389-ds-base-libs装完之后你会得到几个关键工具dscreate用来创建实例dsctl用来控制实例启停dsconf用来修改配置ldapsearch和ldapmodify用来查询和修改数据。这里要注意CentOS 7 的 389-ds 版本可能不是最新的但足够稳定不要盲目去追新版本因为新版本可能依赖更新的系统库在 CentOS 7 上反而容易出问题。安装完成后先别急着创建实例。先检查一下主机名和 hosts 文件。389-ds 在创建实例的时候会用到主机名如果主机名解析有问题后面启动会报错。用hostname -f确认能返回完整域名如果返回的是localhost或者报错就去/etc/hosts里加一条记录把主机名和 IP 对应上。这个坑我踩过当时实例创建成功但启动失败日志里只写“无法解析主机名”查了半天才发现是 hosts 没配。3.2 创建目录实例的关键参数创建实例用dscreate命令它支持交互式和配置文件两种方式。交互式适合第一次操作配置文件适合批量部署。我先说交互式你执行dscreate interactive它会依次问你实例名、端口、root DN 密码、后缀等信息。实例名随便起比如ldap1。端口默认 389如果你要同时跑多个实例就改成 3890 之类的。root DN 是管理员账号默认是cnDirectory Manager这个不要改改了后面很多工具会不认。密码要设复杂一点至少 8 位包含大小写和数字。后缀suffix就是你的根节点比如dcexample,dcinternal。这里填什么后面目录树就从什么开始。填完之后它还会问你要不要创建默认的oupeople和ougroups建议选是省得后面手动建。如果你要用配置文件方式可以先生成一个模板dscreate create-template /tmp/ldap-instance.inf然后编辑这个文件把里面的instance_name、root_password、suffix改成你的值再执行dscreate from-file /tmp/ldap-instance.inf配置文件方式的好处是可重复、可版本控制适合多台机器部署。但要注意配置文件里的密码是明文的部署完记得把文件删掉或者加密保存。3.3 目录树初始化与基础条目创建实例创建好之后目录树里只有根节点和默认的oupeople、ougroups。你需要往里加用户和组。加用户有两种方式一种是用ldapadd命令加 LDIF 文件另一种是用dsconf的user子命令。我习惯用 LDIF因为可读性好也方便批量导入。先创建一个基础结构的 LDIF 文件比如base.ldifdn: ouservices,dcexample,dcinternal objectClass: top objectClass: organizationalUnit ou: services dn: oudepartments,dcexample,dcinternal objectClass: top objectClass: organizationalUnit ou: departments然后用ldapadd导入ldapadd -x -D cnDirectory Manager -W -f base.ldif这里-x表示用简单认证-D指定绑定账号-W表示交互式输入密码-f指定文件。导入成功后用ldapsearch验证一下ldapsearch -x -b dcexample,dcinternal -D cnDirectory Manager -W (objectClassorganizationalUnit)如果能看到你刚加的ou说明基础结构没问题。加用户的时候要注意objectClass的选择。一个标准的用户条目通常包含top、person、organizationalPerson、inetOrgPerson这几个类。inetOrgPerson提供了uid、mail、userPassword这些属性是最常用的。下面是一个用户条目的例子dn: uidzhangsan,oupeople,dcexample,dcinternal objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson uid: zhangsan cn: Zhang San sn: Zhang givenName: San mail: zhangsanexample.internal userPassword: {SSHA}xxxxxxxx密码不要直接写明文要用slappasswd生成哈希slappasswd -s 你的密码它会输出类似{SSHA}...的字符串把这个字符串填到userPassword里。这样即使 LDIF 文件泄露密码也不会直接暴露。3.4 用户组管理与成员引用用户组在 LDAP 里通常用groupOfNames或groupOfUniqueNames来表示。groupOfNames用member属性引用用户 DNgroupOfUniqueNames用uniqueMember。两者区别不大我一般用groupOfNames因为兼容性更好。创建一个组的 LDIFdn: cndevops,ougroups,dcexample,dcinternal objectClass: top objectClass: groupOfNames cn: devops member: uidzhangsan,oupeople,dcexample,dcinternal member: uidlisi,oupeople,dcexample,dcinternal这里有个坑要注意groupOfNames的member属性是必须的也就是说你不能创建一个空组。如果你先建组再往里加人第一次创建的时候必须至少写一个成员否则会报错。解决办法是先用一个占位成员后面再替换或者直接用groupOfUniqueNames它对空组更宽容一些。另外成员引用的是完整 DN不是uid。很多人写member: zhangsan这是错的LDAP 不认。必须是uidzhangsan,oupeople,dcexample,dcinternal这种完整路径。这个细节在批量导入的时候特别容易出错建议用脚本生成 LDIF不要手写。4. 实操过程与核心环节实现4.1 从零到可用的完整部署流程我把整个部署过程压缩成一条清晰的路径你按顺序执行就行。第一步装包yum install -y 389-ds-base 389-ds-base-libs第二步配 hosts确保hostname -f能返回完整域名。第三步创建实例dscreate interactive按提示填实例名、端口、root 密码、后缀。第四步启动实例dsctl ldap1 start第五步验证实例状态dsctl ldap1 status如果显示running说明服务起来了。第六步导入基础结构 LDIF。第七步创建用户和组。第八步用ldapsearch验证数据。第九步配置防火墙放行 389 和 636。第十步如果要用 TLS生成证书并配置。这套流程走下来顺利的话半小时内能搞定。但实际环境中最容易卡住的地方是实例创建后的启动失败以及 LDIF 导入时的 schema 报错。下面我会专门讲这两个问题的排查。4.2 TLS 加密配置的实操细节生产环境一定要配 TLS。389-ds 支持用自签证书或者 CA 签发的证书。测试环境可以用自签生产环境建议用内部 CA 签发。配置 TLS 的步骤稍微多一点但每一步都有明确目的。首先生成证书和私钥。如果你有内部 CA直接用它签。如果没有用openssl自签openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout /etc/dirsrv/slapd-ldap1/ldap.key \ -out /etc/dirsrv/slapd-ldap1/ldap.crt \ -subj /CNldap.example.internal然后把证书和私钥的权限设好确保只有 dirsrv 用户能读chown dirsrv:dirsrv /etc/dirsrv/slapd-ldap1/ldap.key /etc/dirsrv/slapd-ldap1/ldap.crt chmod 600 /etc/dirsrv/slapd-ldap1/ldap.key接着把证书导入到 389-ds 的 NSS 数据库certutil -d /etc/dirsrv/slapd-ldap1 -A -n Server-Cert -t u,u,u -i /etc/dirsrv/slapd-ldap1/ldap.crt最后用dsconf启用 TLSdsconf ldap1 security enable --cert-name Server-Cert重启实例dsctl ldap1 restart然后用ldapsearch走 636 端口测试ldapsearch -x -H ldaps://ldap.example.internal:636 -b dcexample,dcinternal -D cnDirectory Manager -W如果连接成功说明 TLS 配好了。这里有个常见问题证书的 CN 必须和客户端连接时用的主机名一致否则会报证书不匹配。如果你用 IP 连接证书里就要包含 IP 的 SAN否则也会报错。这个坑在测试环境经常遇到因为大家习惯用 IP 连。4.3 对接系统时的绑定与查询示例LDAP 部署好之后最终是要给其他系统用的。对接方式通常有两种一种是应用直接用管理员账号绑定然后查用户另一种是应用先用一个只读账号绑定再根据用户输入的账号密码做一次绑定验证。第一种简单但不安全因为管理员账号权限太大。第二种更规范但需要你创建一个只读账号。创建只读账号的 LDIFdn: uidreadonly,ouservices,dcexample,dcinternal objectClass: top objectClass: person objectClass: organizationalPerson objectClass: inetOrgPerson uid: readonly cn: Read Only sn: ReadOnly userPassword: {SSHA}xxxxxxxx然后给它配置 ACI限制它只能读不能写。389-ds 的 ACI 配置在dsconf里dsconf ldap1 aci create --name readonly-read \ --target ldap:///dcexample,dcinternal \ --scope sub \ --permission read,search,compare \ --bind-rule userdnldap:///uidreadonly,ouservices,dcexample,dcinternal配置好之后用只读账号测试查询ldapsearch -x -H ldap://ldap.example.internal:389 \ -D uidreadonly,ouservices,dcexample,dcinternal -W \ -b oupeople,dcexample,dcinternal (uidzhangsan)如果能查到用户信息说明权限配置正确。这里要注意ACI 的规则是从上往下匹配的如果你之前有更宽泛的规则可能会覆盖你的限制。所以配置完一定要用只读账号实际测一下确认它不能做写操作。5. 常见问题与排查技巧实录5.1 实例启动失败怎么查实例启动失败是最常见的问题表现是dsctl ldap1 start执行后没有报错但dsctl ldap1 status显示stopped。这时候不要瞎猜直接看日志。389-ds 的日志在/var/log/dirsrv/slapd-ldap1/下面主要看errors文件。我遇到过几种典型的启动失败原因。第一种是主机名解析问题日志里会写Unable to resolve hostname。解决办法是配好/etc/hosts。第二种是端口被占用日志里会写Address already in use。用ss -tlnp找到占用端口的进程要么停掉它要么改 LDAP 的端口。第三种是权限问题比如证书文件权限不对日志里会写Permission denied。把文件权限改成dirsrv用户可读就行。还有一种比较隐蔽的情况实例创建时指定的后缀和已有的数据冲突。比如你之前创建过一个实例删掉后没清理干净再创建同名实例时就会失败。这时候需要手动清理/etc/dirsrv/slapd-ldap1和/var/lib/dirsrv/slapd-ldap1目录再重新创建。5.2 LDIF 导入报错速查LDIF 导入报错通常是因为 schema 不匹配或者 DN 重复。下面这张表整理了几种常见错误和解决办法错误信息原因解决办法Object class violation缺少必需的 objectClass 或属性检查条目是否包含top和对应的结构类补全必需属性Already existsDN 重复用ldapdelete删掉旧条目或者改用ldapmodify更新No such attribute属性名拼写错误或 schema 不支持确认属性名大小写正确检查 schema 是否加载Invalid syntax属性值格式不对比如userPassword没加密或者mail格式不对Insufficient access绑定账号权限不够换管理员账号操作或者检查 ACI 配置这里重点说Object class violation因为它最常见。很多人写用户条目的时候只写inetOrgPerson忘了写top和person。LDAP 的 objectClass 是有继承关系的inetOrgPerson继承自organizationalPersonorganizationalPerson继承自personperson继承自top。虽然有些服务器会自动补全但 389-ds 比较严格最好显式写全。5.3 客户端连接超时的排查思路客户端连不上 LDAP表现是连接超时或者拒绝连接。排查顺序应该是先本机、再网络、最后配置。本机测试用ldapsearch -H ldap://localhost:389如果本机都连不上说明服务没起来或者端口没监听。用ss -tlnp | grep 389确认端口状态。本机通了但远程连不上先检查防火墙。CentOS 7 默认用 firewalld执行firewall-cmd --list-ports看 389 和 636 有没有放行。如果没有执行firewall-cmd --permanent --add-port389/tcp firewall-cmd --permanent --add-port636/tcp firewall-cmd --reload防火墙没问题的话检查 LDAP 的监听地址。默认 389-ds 可能只监听localhost不监听外部 IP。用dsconf ldap1 config get nsslapd-listenhost查看如果是localhost改成0.0.0.0或者具体的外部 IPdsconf ldap1 config set nsslapd-listenhost 0.0.0.0改完重启实例。这个配置项在实例创建时也可以指定但很多人会忽略导致远程连不上。5.4 性能与日常维护的实操心得LDAP 本身很轻量日常维护主要是备份和监控。备份用dsctl ldap1 db2ldif导出数据dsctl ldap1 db2ldif --ldif-file /backup/ldap-backup.ldif恢复用dsctl ldap1 ldif2dbdsctl ldap1 ldif2db --ldif-file /backup/ldap-backup.ldif注意ldif2db会覆盖现有数据执行前一定要确认。我一般会在恢复前先把当前数据导出一份以防万一。监控方面主要看连接数和查询响应时间。用dsconf ldap1 monitor可以查看实时状态。如果连接数持续很高可能是客户端没有正确关闭连接需要检查客户端的连接池配置。另外LDAP 的索引对查询性能影响很大默认只对cn、sn、uid建了索引如果你经常按mail查询就要手动加索引dsconf ldap1 backend index add --index mail --attr mail --type eq加完索引后要重建索引才生效dsctl ldap1 db2index这个操作在数据量大的时候会比较慢建议在业务低峰期做。6. 几个容易被忽略的细节6.1 密码策略与账号锁定默认情况下LDAP 不会锁定多次登录失败的账号这在安全上是个隐患。389-ds 支持密码策略可以配置失败次数和锁定时间。用dsconf配置dsconf ldap1 pwpolicy set --pwdsafestate on --pwdmaxfailure 5 --pwdlockoutduration 300这条命令的意思是开启密码安全状态连续失败 5 次锁定账号锁定时间 300 秒。配置完之后用户连续输错密码就会被临时锁定能有效防止暴力破解。但要注意锁定是针对账号的如果攻击者用不同账号尝试这个策略就防不住还需要配合网络层的限流。6.2 日志轮转与磁盘空间389-ds 的日志默认会一直增长如果不做轮转时间长了会把磁盘占满。日志轮转可以用系统的logrotate也可以配置 389-ds 自己的轮转策略。我一般用logrotate在/etc/logrotate.d/下建一个文件/var/log/dirsrv/slapd-ldap1/*.log { daily rotate 7 compress missingok notifempty create 0600 dirsrv dirsrv }这样每天轮转一次保留 7 天自动压缩。配置完用logrotate -d /etc/logrotate.d/dirsrv测试一下确认没有语法错误。这个细节很多人会忽略直到磁盘报警才想起来那时候可能已经影响服务了。6.3 数据导出与迁移的注意事项如果你要把 LDAP 数据迁移到另一台机器不能直接拷贝数据库文件因为 389-ds 的数据库格式和实例配置绑定。正确做法是用db2ldif导出然后在目标机器上用ldif2db导入。导出的时候要注意默认导出的是用户数据不包含配置和 schema。如果目标机器的 schema 和源机器不一致导入会失败。所以迁移前要先确认两边的 schema 一致不一致的话先把 schema 文件拷过去。另外导出的时候可以用-r参数排除某些后缀或者用-s只导出指定后缀。如果数据量很大导出文件会很大建议先压缩再传输。导入的时候如果数据量超过百万条可能会比较慢可以适当调整nsslapd-cachememsize和nsslapd-dbcachesize参数给数据库更多内存缓存。7. 我个人在实际操作中的体会这套东西我在不同环境里部署过很多次最大的感受是LDAP 本身不复杂复杂的是“想清楚你要用它来干什么”。如果你只是想让几个内部系统共用一套账号那结构越简单越好不要一上来就搞多级 OU、复杂 ACI后面维护起来会非常痛苦。我见过一个环境目录树设计了七八层结果每次加用户都要确认半天该放哪个 OU最后大家干脆都往oupeople里塞前面的设计全白费了。另一个体会是文档和实际环境永远有差距。网上很多教程写的是 OpenLDAP你照着做在 389-ds 上就是跑不通。所以遇到问题不要死磕教程直接看日志日志里写什么就查什么比到处搜答案快得多。还有就是任何配置改动之前先备份LDAP 的数据虽然不大但重建起来很麻烦尤其是用户密码丢了就得全部重置。最后分享一个小技巧如果你不确定某个dsconf命令的参数怎么写直接加--help比如dsconf ldap1 backend index add --help它会列出所有可用参数和示例。这个比查文档快而且不会因为版本差异导致命令不兼容。CentOS 7 上的 389-ds 版本虽然老但常用功能都有--help基本能解决大部分操作问题。