一、项目目标

  • OpenLDAP 统一存放账号。

  • iMC 通过 LDAP 读取账号做 MS-CHAP / 802.1X 认证

  • 提供 自助改密 页面(用户自己改密码)。

  • 提供 图形化管理后台(比 phpLDAPadmin 好用、仍在维护)。

关键约束:iMC 做 MS-CHAP 认证需要 sambaNTPassword 这个字段(NT 哈希 / 或本方案中的明文密码)。普通的 userPassword(SSHA) 不行。


二、最终架构(docker compose 三服务)

服务

镜像

端口

作用

openldap

自建 openldap-imc:local(基于 osixia/openldap:1.5.0,Debian buster,OpenLDAP 2.4.57)

389 / 636

LDAP 目录服务,存账号与密码

ldap-ssp

ltbproject/self-service-password:latest

80

自助改密前端

ldap-lam

ldapaccountmanager/lam:stable

8081

图形化管理后台

数据目录(宿主 bind 挂载,固定为 /srv/openldap):

  • /srv/openldap/data/var/lib/ldap

  • /srv/openldap/config/etc/ldap/slapd.d

compose 网络:默认 openldap_default,服务间用服务名 openldap 互相访问(SSP/LAM 都填 ldap://openldap:389)。


三、关键配置参数(以下均为示例值,部署时请替换为你的真实环境)

域名

test.com.cn

Base DN

dc=test,dc=com,dc=cn

管理员 DN

cn=admin,dc=test,dc=com,dc=cn

管理员密码

Admin@1234,a(含逗号,已写入 .env

数据目录

/srv/openldap

组织单元

ou=students(学生组)、ou=teachers(教师组)

SSP 访问地址

http://<服务器IP>/(端口 80)

LAM 访问地址

http://<服务器IP>:8081/

账号字段约定(用户定):

  • uid = 账户名(登录名)

  • cn = 名字

  • sn = 必填,填成账户名(inetOrgPerson 强制要求 sn)


四、目录结构与文件清单

/srv/openldap/                      # 宿主部署目录(bind 挂载源)
├── docker-compose.yml
├── .env
├── openldap/
│   ├── Dockerfile                  # 基于 osixia,补装 slapd-contrib
│   └── bootstrap/                  # samba.schema + seed(bootstrap 阶段加载)
├── ssp-conf/
│   ├── config.inc.local.php        # SSP PHP 配置(覆盖默认)
│   └── posthook-update-smbnt.php   # 改密后同步明文到 sambaNTPassword
├── scripts/
│   ├── add_user.sh                 # 建号(自动建 OU + 写明文密码)
│   ├── setup-smbk5pwd.sh           # 手动加载 smbk5pwd overlay(已加载,可不动)
│   └── (备份)add_user.sh.* / setup-smbk5pwd.sh.back
├── config/                         # slapd.d 配置库(持久化)
└── data/                           # LDAP 数据(持久化)

本文档以 test.com.cn / dc=test,dc=com,dc=cn 作为示例域名;实际部署时请将全文统一替换为你的真实域名与 Base DN。


五、核心技术方案(最重要的认知)

5.1 iMC 对通用 OpenLDAP 的密码处理方式

这是整个方案的关键转折点。

  • H3C iMC 官方的 MS-CHAPv2 案例全部基于 Windows AD:iMC 用"虚拟计算机账号"加入 AD,直接从 AD 取 NT 哈希

  • 但接的是 通用 OpenLDAP(不是 AD)时,iMC 没有"取 NT 哈希"的机制,只能退而求其次:从你指定的"用户密码属性"读取明文密码,再自己算 NT 哈希

结论

  • sambaNTPasswordNT 哈希(32 位 hex)→ iMC 把 hex 当明文再 MD4 一遍 → 错误 → 报 E63032 密码错误

  • sambaNTPassword明文密码 → iMC 自己 MD4 → 正确 → 认证通过。

所以本方案让 sambaNTPassword 存明文(不是 NT 哈希)。

5.2 因此方案做了这些调整

  1. add_user.sh:直接把明文密码写入 sambaNTPassword(不再用 smbk5pwd / exop / NT 哈希双保险)。

  2. SSP:关闭 $samba_mode(它会写 NT 哈希),改用 $posthook,在改密成功后把新明文同步到 sambaNTPassword

  3. smbk5pwd overlay:对 iMC 场景不再必要(已加载也无害,仅 exop 触发)。

5.3 两个密码字段的区别(务必理解)

字段

内容

谁改

用途

userPassword

SSHA 哈希

SSP 改密时改

用户登录 SSP / LDAP 绑定

sambaNTPassword

明文

add_user.sh 建号写 / posthook 同步

iMC 读这个做认证

posthook 的作用:用户在 SSP 网页改密后,自动把新明文同步写进 sambaNTPassword,保证两个字段一致,iMC 才能用新密码认证。

5.4 安全注意

  • sambaNTPassword 的 schema 长度限制是 32 字符,密码超过 32 位会写不进去(密码策略别设太长)。

  • 该字段以明文存储密码;当前 ACL 已禁止匿名读取,但管理员和用户本人仍可读到。

  • 如果以后 iMC 版本支持"NT 哈希模式"或切到 AD,建议改回存 NT 哈希。


六、踩坑记录(时间线,含已推翻的错误假设)

#

现象

根因

修复

1

docker compose buildapt-get update 报错 404

osixia/openldap:1.5.0 底层是 Debian buster(已 EOL),官方源失效

Dockerfile 把 apt 源改指 archive.debian.org + -o Acquire::Check-Valid-Until=false

2

dockerfile parse error: unknown instruction: 基于

复制文件时行首 # 被吞,注释变指令

Dockerfile 改为纯指令无注释版本

3

SSP 镜像 tiredofit/... 拉取 403

该镜像已从 Docker Hub 下架

改用官方 ltbproject/self-service-password(用 PHP 配置文件,不读环境变量)

4

openldap 容器 Restarting (0) 死循环

Dockerfile 末尾 USER openldap 把 osixia 启动脚本卡死(osixia 必须以 root 启动、自己降权)

删掉 Dockerfile 最后的 USER openldapdocker compose build 重建

5

LAM 连不上 LDAP(Can't contact LDAP server

LAM 向导默认填 ldap://ldap:389,但服务名是 openldap

LAM 服务器改填 ldap://openldap:389

6

add_user.sh 建号报 No such object (32)

父节点 ou=people 不存在;且旧脚本把 uid 写死成 testuser

重写脚本:自动建 OU、用 $1 作 uid

7

add_user.sh 报"已存在"但数据库里没有

查重 grep "uid=xxx" 命中了 ldapsearch 的注释头 # filter:

grep 加行首锚点 ^dn: uid=

8

SSP 改密后 sambaNTPassword 不更新

$posthook 没有 php 前缀(LTB SSP 把它当 shell 命令 exec,不是 PHP 加载)

$posthook = "php /var/www/conf/posthook-update-smbnt.php"

9

iMC 认证报 E63032 密码错误

把 NT 哈希放进 sambaNTPassword,iMC 当明文再算一次

sambaNTPassword 改存明文

注:早期关于"重启循环是 bind 挂载权限 / 坏配置"的判断,最终被第 4 条证伪——真正元凶是 USER openldap


七、当前状态(截至 2026-07-31 18:06,posthook 闭环后订正)

  • ✅ 三个容器均 Upopenldapldap-sspldap-lam

  • smbk5pwd overlay 已加载并持久化(olcOverlay={2}smbk5pwd

  • ou=studentsou=teachers 两个 OU 已建

  • ✅ 测试账号已建:

    • uid=zhangsan,ou=students,...(cn=张三,初始 zhangsan@123

    • 示例账号:uid=zhangsan2,ou=students,...(cn=张三2,密码 Test@1234;两字段一致,可用于 iMC 测试)

  • 已闭环:服务器上 $posthookphp 前缀已加上(第 49 行 = "php /var/www/conf/posthook-update-smbnt.php"),SSP 自助改密可正常同步明文到 sambaNTPassword,整条链路完整跑通。


八、待办事项

1. ✅【已完成】补 SSP posthook 的 php 前缀

服务器第 49 行已确认为 "php /var/www/conf/posthook-update-smbnt.php",SSP 自助改密同步已生效,该项关闭。

2. 【建议】iMC 实测

在 iMC 侧:

  • 接入类型:LDAP

  • 服务器:ldap://<服务器IP>:389

  • Base DN(只同步学生组):ou=students,dc=test,dc=com,dc=cn

  • 用户密码属性:sambaNTPassword(iMC 当明文读,自己算 NT 哈希)

  • 认证协议:MS-CHAPv2

  • 测试账号:zhangsan2 / Test@1234

3. 【可选】建号工具补充

  • 管理员重置密码脚本(同时重置 userPassword + sambaNTPassword):待补充 reset_user.sh

  • 教师组账号:./add_user.sh <uid> <cn> [密码] teachers


九、常用命令速查

# 建学生组账号(默认组 students)
./scripts/add_user.sh <uid> <cn> [密码]

# 建教师组账号
./scripts/add_user.sh <uid> <cn> [密码] teachers

# 查账号 + NT 哈希
docker exec openldap ldapsearch -x -D "cn=admin,dc=test,dc=com,dc=cn" -w 'Admin@1234,a' \
  -b "ou=students,dc=test,dc=com,dc=cn" "(uid=<uid>)" dn sambaNTPassword sambaSID

# 手动触发 posthook(绕过 SSP,验证脚本本身)
docker exec ldap-ssp php /var/www/conf/posthook-update-smbnt.php <uid> <新明文密码>

# 管理员重置某账号密码(同时两字段)
docker exec openldap ldappasswd -x -D "cn=admin,dc=test,dc=com,dc=cn" -w 'Admin@1234,a' \
  -s '<新密码>' "uid=<uid>,ou=students,dc=test,dc=com,dc=cn"
docker exec -i openldap ldapmodify -x -D "cn=admin,dc=test,dc=com,dc=cn" -w 'Admin@1234,a' <<LDIF
dn: uid=<uid>,ou=students,dc=test,dc=com,dc=cn
changetype: modify
replace: sambaNTPassword
sambaNTPassword: <新密码>
LDIF

# 看容器状态
docker ps

# 看 openldap 日志
docker logs --tail 40 openldap

十、一句话总结

OpenLDAP 存账号、sambaNTPassword明文密码(iMC 读它自己算 NT 哈希做 MS-CHAPv2)、SSP 自助改密通过 posthook 把明文同步进 sambaNTPassword、LAM 做图形管理。整条链路(建号 / 改密 / iMC 认证)已全部跑通并完成闭环,可直接用于生产或分发给同事部署。