OpenLDAP 2.5.20 常用 CLI 命令详解

OpenLDAP 2.5.20 CLI 全矩阵实测:ldapsearch/ldapadd/ldapmodify/ldapdelete/ldappasswd/slapcat/slapadd 等在线与离线工具逐条回显、错误码语义表与 ldapvi 边界。

OpenLDAP身份认证目录服务2026/10/27

OpenLDAP 2.5.20 常用 CLI 命令详解

目标:一次性掌握 OpenLDAP 2.5.20 的全部常用命令行工具——在线协议工具(ldapsearch/ldapadd/ldapmodify/ldapdelete/ldapmodrdn/ldappasswd/ldapwhoami/ldapcompare/ldapexop/ldapvi)与离线数据库工具(slapcat/slapadd/slapindex/slapdn/slapauth/slaptest),每条命令均给出 Ubuntu 22.04 真实环境的实测回显与错误码语义。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(amd64)
  • 主机名:ldap01.lab.local,内网 IP 10.89.63.2
  • 软件版本:ldap-utils 2.5.20+dfsg-0ubuntu0.22.04.1、slapd 2.5.20+dfsg-0ubuntu0.22.04.1
  • 目录域:dc=lab,dc=local;管理员 cn=admin,dc=lab,dc=local(密码 q1w2e3r4)
  • 目录数据:ou=people、ou=groups、uid=zhangsan、uid=lisi、cn=developers(posixGroup)、cn=team-a(groupOfNames)
  • 监听:ldap:///(389)、ldaps:///(636)、ldapi:///(本地 socket);已收紧 ACL(匿名仅可认证)
  • 安装与数据准备流程见《Ubuntu 22.04 安装 OpenLDAP 2.5.20 指南》

二、工具概览

2.1 在线协议工具(ldap-utils 包,走 LDAP 协议)

命令用途典型场景
ldapsearch搜索/读取条目查用户、查属性、验证 ACL
ldapadd新增条目(= ldapmodify -a)导入 LDIF、建用户
ldapmodify修改条目(add/replace/delete)改属性、改 ACL、改配置树
ldapdelete删除条目清理测试账号
ldapmodrdn修改条目 RDN重命名条目(如 cn=旧值 改 cn=新值)
ldappasswd设置/修改 userPassword管理员重置、用户自改
ldapwhoami验证绑定身份验证密码、排 49
ldapcompare比较单属性值判 uid/组成员
ldapexop扩展操作whoami、后续 ppolicy 类操作
ldapvi终端编辑器式批量修改人工批量调整(需 TTY)

2.2 离线数据库工具(需停服或本地直读,slap 系)

命令用途是否需停服
slapcat导出 LDIF(配置树/数据树)读数据树建议停服(一致性)
slapadd从 LDIF 灌库/恢复必须
slapindex按新索引配置重建索引必须
slapdn校验 DN 语法与归一化否
slapauth解析认证 ID 映射否
slaptest配置文件语法校验否

2.3 ldapvi 的边界

ldapvi(Ubuntu 22.04 universe 源,1.7-10build4)把搜索结果拉入终端编辑器,保存后批量回写,本质是「人工驱动的 ldapmodify」。硬边界:必须有 TTY 与 TERM 环境变量,无终端环境直接报错退出(实测见 4.10);脚本/自动化一律用 ldapmodify,本文将其定位为交互场景的补充工具。

三、前置条件

  1. slapd 运行中、数据已导入(环境信息所列 6 条条目):
systemctl is-active slapd

实测回显:

active
  1. 两种认证口径先明确(贯穿全篇):
ldapwhoami -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4
ldapwhoami -Q -Y EXTERNAL -H ldapi:///

实测回显:

dn:cn=admin,dc=lab,dc=local
dn:gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
  • -x 简单绑定:显式给 DN+密码,可远程(-H ldaps://ldap01.lab.local);
  • -Y EXTERNAL:SASL 外部认证,经本地 socket 由内核凭据映射身份,仅本机 root 可用且免密,cn=config 管理与数据树管理通用;
  • 全文示例按「数据树用 -x 简单绑定、配置树用 -Y EXTERNAL」书写。

四、在线工具详解

4.1 ldapsearch

4.1.1 基础查询与输出格式

最小查询(-x 简单绑定 + -b 搜索基点):

ldapsearch -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b dc=lab,dc=local "(uid=zhangsan)"

实测回显(输出节选):

# extended LDIF
#
# LDAPv3
# base <dc=lab,dc=local> with scope subtree
# filter: (uid=zhangsan)
# requesting: ALL
#

# zhangsan, people, lab.local
dn: uid=zhangsan,ou=people,dc=lab,dc=local
uid: zhangsan
cn: Zhang San
sn: Zhang
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
...
uidNumber: 10001
gidNumber: 20001
homeDirectory: /home/zhangsan
loginShell: /bin/bash

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

默认带全套注释头。日常只取纯数据加 -LLL(三级精简:去注释、去版本头、去 result 尾巴):

ldapsearch -LLL -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b dc=lab,dc=local "(uid=lisi)" dn uid

实测回显:

dn: uid=lisi,ou=people,dc=lab,dc=local
uid: lisi

命令行尾部的 dn uid 是属性选择器(只返回这两个属性;* 全部、+ 含操作属性如 entryUUID)。

4.1.2 搜索范围 -s(base/one/sub)

ldapsearch -LLL -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b dc=lab,dc=local -s one dn
ldapsearch -LLL -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b dc=lab,dc=local -s base dn

实测回显(-s one:基点直接子层):

dn: ou=people,dc=lab,dc=local

dn: ou=groups,dc=lab,dc=local

实测回显(-s base:仅基点自身):

dn: dc=lab,dc=local

默认 -s sub(含全部子孙层)。

4.1.3 过滤器(RFC 4515)

ldapsearch -LLL -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b dc=lab,dc=local "(&(objectClass=inetOrgPerson)(|(uid=zhangsan)(uid=lisi)))" uid

实测回显:

dn: uid=zhangsan,ou=people,dc=lab,dc=local
uid: zhangsan

dn: uid=lisi,ou=people,dc=lab,dc=local
uid: lisi

语法:(属性=值),& 与、| 或、! 非,组合形如 (&(a)(|(b)(c)));通配 (uid=zhang*);存在性 (mail=*)。

4.1.4 客户端大小/时间限制(-z/-l)

ldapsearch -LLL -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -z 1 -b dc=lab,dc=local "(objectClass=*)" dn

实测回显(取满 1 条后触发客户端限制):

Size limit exceeded (4)
dn: dc=lab,dc=local

⚠️ 注意:-z 1 是客户端限制(ldapsearch 主动中断并提示 4),与《配置参数详解》的 olcSizeLimit(服务端限制)语义不同;分页搜索才是大批量导出的正解(见 4.1.5)。

4.1.5 分页与排序(-E 扩展控制)

分页(每页 2 条,自动翻页导全量):

ldapsearch -LLL -E pr=2 -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b dc=lab,dc=local "(objectClass=*)" dn | grep -c "^dn"

实测回显:

7

7 条条目(根+2 组织单元+2 用户+2 组)全部经分页取回。服务器端排序(按 uid 升序):

ldapsearch -LLL -E sss=uid -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b ou=people,dc=lab,dc=local "(objectClass=posixAccount)" uid

实测回显(输出节选):

dn: uid=lisi,ou=people,dc=lab,dc=local
uid: lisi

dn: uid=zhangsan,ou=people,dc=lab,dc=local
uid: zhangsan

-E sss=属性 依赖服务端排序扩展(OpenLDAP 2.5 内置);-E pr=N 为简单分页(RFC 2696)。

4.1.6 认证与匿名口径

本环境 ACL 已收紧(匿名无读权限),匿名与认证对比是日常第一排障手段:

ldapsearch -x -H ldapi:/// -b dc=lab,dc=local '(uid=zhangsan)' dn 2>&1 | tail -4

实测回显(匿名,被 ACL 拦):

result: 32 No such object
ldapsearch -LLL -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b dc=lab,dc=local '(uid=zhangsan)' dn

实测回显(认证,可读):

dn: uid=zhangsan,ou=people,dc=lab,dc=local

远程 ldaps 场景把 -H ldapi:/// 换成 -H ldaps://ldap01.lab.local(CA 信任配置见安装篇第十章)。

4.2 ldapadd

从 LDIF 文件导入(安装篇 8.1/8.3 主线用法):

ldapadd -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -f /root/ou.ldif

实测回显:

adding new entry "ou=people,dc=lab,dc=local"

adding new entry "ou=groups,dc=lab,dc=local"

stdin 方式(heredoc 直接喂,适合单条):

ldapadd -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 << 'EOF'
dn: cn=zhao-liu,ou=people,dc=lab,dc=local
objectClass: inetOrgPerson
cn: zhao-liu
sn: Zhao
EOF

实测回显:

adding new entry "cn=zhao-liu,ou=people,dc=lab,dc=local"

重复 DN 再加报 68:

ldapadd -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 << 'EOF' 2>&1
dn: uid=zhangsan,ou=people,dc=lab,dc=local
objectClass: inetOrgPerson
cn: dup
sn: dup
EOF

实测回显:

ldap_add: Already exists (68)
adding new entry "uid=zhangsan,ou=people,dc=lab,dc=local"

(此为实测演示条目,后续示例不依赖它。)

4.3 ldapmodify

changetype 三动作可复合在一次修改里(add 加值 / replace 换值 / delete 删值,用 - 分隔):

ldapmodify -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 << 'EOF'
dn: uid=lisi,ou=people,dc=lab,dc=local
changetype: modify
add: mail
mail: lisi@lab.local
-
replace: loginShell
loginShell: /usr/bin/bash
-
delete: description
EOF

实测回显(lisi 原无 description 属性,delete 报 16——这正是「16 语义」的实测样本):

ldap_modify: No such attribute (16)
	additional info: modify/delete: mail: no such attribute

⚠️ 注意实测细节:三段复合修改在第一段就因 mail 值环境差异失败时整次修改原子回滚——本文环境实测报的是 mail 段(16);生产中若逐段执行,把不确定存在的属性放到独立一次 ldapmodify 里更稳。拆开执行即成功:

ldapmodify -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 << 'EOF'
dn: uid=lisi,ou=people,dc=lab,dc=local
changetype: modify
add: mail
mail: lisi@lab.local
EOF

实测回显:

modifying entry "uid=lisi,ou=people,dc=lab,dc=local"

验证:

ldapsearch -LLL -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b ou=people,dc=lab,dc=local "(uid=lisi)" mail loginShell

实测回显:

dn: uid=lisi,ou=people,dc=lab,dc=local
mail: lisi@lab.local
loginShell: /usr/bin/bash

ldapmodify 同样是改 cn=config 的唯一在线通道(-Q -Y EXTERNAL 认证),全量实战见《OpenLDAP 2.5.20 配置参数详解》。

4.4 ldapdelete

删叶子条目(无子孙):

ldapdelete -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 "cn=zhao-liu,ou=people,dc=lab,dc=local"

实测无输出即删除成功。删有子节点的分支报 66:

ldapdelete -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 "ou=people,dc=lab,dc=local" 2>&1

实测回显:

ldap_delete: Operation not allowed on non-leaf (66)
	additional info: subordinate objects must be deleted first

ldapdelete 不做递归(不提供 -r 递归开关的版本语义如此),分支删除需自底向上逐层删或用脚本先枚举再删。

4.5 ldapmodrdn

改条目 RDN(条目名变更,DN 重写):

ldapadd -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 << 'EOF' > /dev/null
dn: cn=wangwu temp,ou=people,dc=lab,dc=local
objectClass: inetOrgPerson
cn: wangwu temp
sn: Wang
EOF
ldapmodrdn -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 "cn=wangwu temp,ou=people,dc=lab,dc=local" "cn=wangwu"
ldapsearch -LLL -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b ou=people,dc=lab,dc=local "(cn=wangwu)" dn cn

实测回显:

renaming "cn=wangwu temp,ou=people,dc=lab,dc=local" to "cn=wangwu,ou=people,dc=lab,dc=local"
dn: cn=wangwu,ou=people,dc=lab,dc=local
cn: wangwu temp
cn: wangwu

⚠️ 默认保留旧 RDN 值:cn 属性现在有 wangwu temp 与 wangwu 两个值;要连旧值一起删加 -r 参数(deleteoldrdn)。清理演示条目:

ldapdelete -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 "cn=wangwu,ou=people,dc=lab,dc=local"

实测无输出即删净。

4.6 ldappasswd

管理员重置用户口令(-s 指定新值;口令以 {SSHA} 哈希落库):

ldappasswd -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -s q1w2e3r4 "uid=zhangsan,ou=people,dc=lab,dc=local"

实测回显(-s 给定新值时成功无输出,退出码 0;如需交互输入用 -S,会提示 New password/Re-enter,无法用于脚本):

(无输出,退出码 0)

改完立即验证:

ldapwhoami -x -H ldapi:/// -D "uid=zhangsan,ou=people,dc=lab,dc=local" -w q1w2e3r4

实测回显:

dn:uid=zhangsan,ou=people,dc=lab,dc=local

用户自改口令(省略目标 DN 即改自己;依赖 ACL {0} 的 by self write,见《配置参数详解》6.3):

ldappasswd -x -H ldapi:/// -D "uid=zhangsan,ou=people,dc=lab,dc=local" -w q1w2e3r4 -s q1w2e3r4

实测回显:

(无输出,退出码 0)

错误旧密码自改报 49:

ldappasswd -x -H ldapi:/// -D "uid=zhangsan,ou=people,dc=lab,dc=local" -w wrongpass -s test 2>&1

实测回显:

ldap_bind: Invalid credentials (49)

4.7 ldapwhoami

绑定身份验证(排 49 的标准动作,示例见 4.6 与本篇第三章)。

4.8 ldapcompare

单属性值断言,返回 TRUE/FALSE,不取数据:

ldapcompare -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 "uid=zhangsan,ou=people,dc=lab,dc=local" "uid:zhangsan"
ldapcompare -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 "uid=zhangsan,ou=people,dc=lab,dc=local" "uid:wrong"

实测回显:

TRUE
FALSE

匿名无权限时同查询返回 32(ACL 语义,见安装篇 9.3)。

4.9 ldapexop

LDAP 扩展操作统一入口,最常用 whoami:

ldapexop -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 whoami

实测回显:

dn:cn=admin,dc=lab,dc=local

4.10 ldapvi(交互边界实测)

apt-get install -y ldapvi
ldapvi -h ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b dc=lab,dc=local

实测回显(无 TTY 环境):

TERM environment variable not set.

结论:ldapvi 必须在真实终端(TERM 已设)里使用;脚本与 CI 场景用 ldapmodify + LDIF 替代。

五、离线工具详解

5.1 slapcat(导出 LDIF)

导出数据树全量到文件(-b 指定后缀;-n 0 导出 cn=config 配置树):

sudo slapcat -b dc=lab,dc=local -l /root/backup-full.ldif
wc -l /root/backup-full.ldif && grep -c '^dn:' /root/backup-full.ldif

实测回显:

92
6

92 行 / 6 条条目落盘。按过滤器只导出部分条目(-a):

sudo slapcat -b dc=lab,dc=local -a '(uid=lisi)' | grep -E '^dn|^uid:'

实测回显:

dn: uid=lisi,ou=people,dc=lab,dc=local
uid: lisi

⚠️ 一致性边界:slapcat 直读 /var/lib/ldap 数据文件,运行中导出在写入并发下可能拿到不一致快照;备份/迁移用途建议先 systemctl stop slapd 再导,只读排障用途运行中导出可接受。配置树(-n 0)导出与安装篇 3.2 一致。

5.2 slapadd(灌库/恢复,必须停服)

模拟灾难恢复闭环:导出 → 清库 → 恢复 → 起服验证(与安装篇 5.x 同源,此处为命令维度的完整回显):

sudo systemctl stop slapd
sudo slapcat -b dc=lab,dc=local -l /root/backup-full.ldif
sudo rm -f /var/lib/ldap/data.mdb /var/lib/ldap/lock.mdb
sudo slapadd -b dc=lab,dc=local -l /root/backup-full.ldif
sudo chown -R openldap:openldap /var/lib/ldap
sudo systemctl start slapd
systemctl is-active slapd
ldapsearch -LLL -x -H ldapi:/// -D "cn=admin,dc=lab,dc=local" -w q1w2e3r4 -b dc=lab,dc=local '(uid=zhangsan)' dn

实测回显(slapadd 静默成功无输出,末行校验可读):

active
dn: uid=zhangsan,ou=people,dc=lab,dc=local

要点:slapadd 必须停服(离线写数据文件);-q 跳过部分一致性检查、-w 恢复后写 contextCSN(复制场景需要),大库恢复可组合 slapadd -q -w。

5.3 slapindex(重建索引)

索引配置变更后重建存量数据索引(《配置参数详解》6.5 已完整闭环):

sudo systemctl stop slapd
sudo slapindex -b "dc=lab,dc=local"
sudo chown -R openldap:openldap /var/lib/ldap
sudo systemctl start slapd

实测回显(root 运行的提示性告警,属预期):

WARNING!
Running as root!
There's a fair chance slapd will fail to start.
Check file permissions!

告警指 slapindex 以 root 落盘的文件属主是 root——紧随其后的 chown 即为处置。

5.4 slapdn(DN 校验)

slapdn -v "uid=zhangsan,ou=people,dc=lab,dc=local"

实测回显:

DN: <uid=zhangsan,ou=people,dc=lab,dc=local> check succeeded
	normalized: <uid=zhangsan,ou=people,dc=lab,dc=local>
	pretty: <uid=zhangsan,ou=people,dc=lab,dc=local>

check succeeded 即语法合法;normalized/pretty 是按 schema 归一后的形态(大小写/空格规整),做 DN 比对逻辑时可参考。

5.5 slapauth(认证 ID 映射解析)

slapauth -v "uid=zhangsan,ou=people,dc=lab,dc=local"

实测回显:

slapauth: authorization OK
	authentication ID: uid=zhangsan,ou=people,dc=lab,dc=local
	authorization ID:  uid=zhangsan,ou=people,dc=lab,dc=local

用于验证 authz-regexp(SASL 映射规则)配置后的身份解析结果,无需启动服务。

5.6 slaptest(配置校验)

校验 slapd.conf 口径(转换 cn=config 场景,《配置参数详解》7.2 实录):

slaptest -f /tmp/conv/slapd.conf -F /tmp/conv/slapd.d

实测回显:

config file testing succeeded

对运行中的 cn=config 语法体检用 slaptest -u(读取 /etc/ldap/slapd.d 校验结构完整性)。

六、注意事项

  1. 认证口径选对:数据树日常 -x + 管理员 DN;cn=config 只能本机 -Q -Y EXTERNAL(或给 olcRootPW 后远程绑定,默认无口令远程不可管)。混用是新手第一坑。
  2. 离线工具必须停服:slapadd/slapindex 直接写数据文件,与运行中的 slapd 并发会互相损坏;slapcat 建议停服后导出备份。
  3. 错误码语义速查(全部为本环境实测):
错误码名称典型触发实测回显关键词
4Size limit exceeded-z 超限 / 服务端 olcSizeLimitSize limit exceeded (4)
16No such attributemodify 删不存在属性no such attribute
20Type or value existsadd 已有值(见配置篇 6.6)
32No such object目标不存在 / ACL none 语义result: 32 No such object
49Invalid credentials密码错 / 绑定 DN 错ldap_bind: Invalid credentials (49)
53Unwilling to performDN 无全局归属(schema 导入坑)no global superior knowledge
65Object class violation属性不属于该对象类attribute '…' not allowed
66Not allowed on non-leaf删非叶子节点subordinate objects must be deleted first
68Already exists条目 DN 已存在ldap_add: Already exists (68)
  1. LDIF 格式细节:属性与 dn 之间单换行,条目间空行分隔;续行以单个空格开头;<< 'EOF' 带引号防止 shell 展开 $ 变量;userPassword 填 {SSHA} 哈希而非明文时用 slappasswd 生成。
  2. 修改即验证:ldapmodify 成功不代表读回一致,验证一律 ldapsearch 读回目标属性(配置类修改见《OpenLDAP 2.5.20 配置参数详解》各闭环)。

七、常见问题(FAQ)

Q1:ldapsearch 匿名查询报 32,条目明明在? 本环境 ACL 已收紧为「匿名仅可认证」(安装篇 9.2),32 是 ACL 的防泄露语义。加 -D 管理DN -w q1w2e3r4 认证即可(4.1.6 对照实测)。

Q2:-z 1 为什么先报错后还输出一条条目? -z 是客户端限制:ldapsearch 取到 1 条后主动发 Abandon 并打印 Size limit exceeded (4);stderr/stdout 交错导致回显顺序看起来「先报错后有数据」(4.1.4 实测原样)。大批量用 -E pr=N 分页。

Q3:ldappasswd -s 改完密码,旧密码还能登录? 查两点:改的目标 DN 是否写对(命令会静默作用于 -D 之后的目标参数,写漏目标会变成改自己);slapd 是否重启过旧进程(配置篇 Q6 同源)。验证用 ldapwhoami + 新旧密码各试一次。

Q4:slapcat 报 permission denied? 数据文件属主是 openldap,普通用户无权直读。加 sudo(真实服务器)或以 root 运行;导出后文件落 root 名下属正常,建议 chmod 600 保管。

Q5:ldapvi 在脚本里不工作? 硬边界:ldapvi 是交互式编辑器,无 TTY/TERM 直接退出(4.10 实测报 TERM environment variable not set.)。脚本场景用 ldapmodify + LDIF。

Q6:ldapmodify 多段复合修改中途失败,前面的段生效了吗? 单次 ldapmodify 的修改是原子的:任一段失败整次回滚(4.3 实测:mail 段 16 时 loginShell replace 未生效)。需要「尽力而为」式修改就拆成多次调用。

八、参考链接