Harbor LDAP 模式用户登录与登出验证指南:从测试用例到源码实现

Harbor LDAP 模式用户登录与登出验证指南:从测试用例到源码实现

【免费下载链接】harbor An open source trusted cloud native registry project that stores, signs, and scans content. 【免费下载链接】harbor 项目地址: https://gitcode.com/GitHub_Trending/ha/harbor

本篇指南以 Harbor 仓库测试用例 1-05-LDAP-user-log-in-log-out.md 为主体,完整讲解在 LDAP 认证模式(auth_mode = ldap_auth)下验证普通用户登录、登出、错误凭据处理以及管理员管理的全过程。文中同时结合 Harbor 源码中 LDAP 认证链路的实现细节(搜索用户、绑定校验、首次登录落库、密码随机化等),帮助读者理解"为什么测试用例要这样设计",并可直接将用例步骤复用到自己的 Harbor 环境做验收验证。

一、测试目标与适用场景

该用例的核心目标非常明确:验证当用户由外部 LDAP 服务器统一管理时,一个非管理员用户(non-admin user)能够正常完成 UI 与 Docker CLI 的登录、登出,且其身份管理完全受制于 LDAP 数据源。具体而言,它验证:

  • LDAP 用户首次登录 Harbor 后能否被正确识别与落库;
  • LDAP 用户在 UI 上无法修改自身账户设置、无法修改密码(密码由 LDAP/AD 管理);
  • 登录失败时的错误提示不会泄露到底是用户名还是密码错误;
  • 管理员可以在 Harbor 中看到 LDAP 用户,并为其授予/移除系统管理员角色;
  • 当用户被从 LDAP 中禁用或删除后,将彻底失去登录能力。

该用例适用于 Harbor 使用 LDAP 作为唯一认证源(LDAP mode) 的部署场景,这也是企业内部将 Harbor 与公司统一身份目录(OpenLDAP / AD / FreeIPA 等)对接时的最常见形态。

二、环境前提(Environment)

用例明确要求如下环境,缺一不可:

  1. 一个正在运行且可访问的 Harbor 实例;
  2. Harbor 配置为 LDAP 认证模式,即 auth_mode 设置为 ldap_auth。在 Harbor 源码中,该取值定义于 src/common/const.go#L24:
    LDAPAuth = "ldap_auth"
    

    同时,用户数据存储于 LDAP 服务器(本地数据库仅保存认证所需的"影子"记录,详见下文首次登录一节);

  3. 一台安装了 Docker CLI(Docker 客户端)的 Linux 主机,用于验证 docker login;
  4. 一台已配置好、并包含若干可用于测试用户的 LDAP 服务器。

从源码角度,LDAP 会话与认证相关的所有功能集中在三个层级:

三、LDAP 模式核心配置参数

要复现本用例,需要先保证 Harbor 的 LDAP 配置正确。配置项集中定义于 src/lib/config/models/model.go#L77-L99,分为"基本 LDAP 配置"与"LDAP 组配置"两部分。

3.1 基本 LDAP 配置(LdapConf)

配置键字段含义说明
ldap_urlLDAP 服务器地址支持 ldap:// 与 ldaps://;源码 formatURL 会在未指定端口时自动补全 389(ldap)或 636(ldaps),见 src/pkg/ldap/ldap.go#L77-L115
ldap_search_dn搜索 DN用于以只读身份绑定 LDAP 执行目录搜索(SearchUser 前先 Bind)
ldap_search_password搜索 DN 的密码与 ldap_search_dn 配套
ldap_base_dn用户搜索基 DN例如 ou=people,dc=example,dc=com
ldap_filter用户搜索过滤器在源码中会与 (uid=<用户名>) 条件通过 AND 组合,见 createUserSearchFilter(src/pkg/ldap/ldap.go#L291-L309)
ldap_uid用户标识属性用例中特别强调"id 可以是 LDAP 用户 DN 中配置的 uid 属性(或 ldap_uid 中配置的属性)";源码 SearchUser 会依据该属性从返回条目中提取用户名,见 src/pkg/ldap/ldap.go#L163-L181
ldap_scope搜索范围对应 go-ldap 的 Scope 枚举(Base/OneLevel/Subtree)
ldap_connection_timeout连接超时(秒)源码 Open 中会写入 goldap.DefaultTimeout
ldap_verify_cert是否校验证书仅对 ldaps 生效;为 false 时 InsecureSkipVerify 为真,见 src/pkg/ldap/ldap.go#L217-L228

3.2 LDAP 组配置(GroupConf)

组配置在登录过程中用于将 LDAP 组成员关系附着到用户身上,并识别"组管理员":

配置键字段含义
ldap_group_base_dn组搜索基 DN
ldap_group_filter组搜索过滤器
ldap_group_name_attribute组名属性(默认为 cn)
ldap_group_search_scope组搜索范围
ldap_group_admin_dn组管理员 DN(匹配到该 DN 的用户获得 AdminRoleInAuth)
ldap_group_membership_attribute成员关系属性(如 memberof)
ldap_group_attach_parallel是否并行附着组(源码中最多 5 个 worker,见 src/core/auth/ldap/ldap.go#L142-L195)

注意:当 ldap_group_filter 为空时,登录流程会跳过组附着(attachLDAPGroup 直接返回),但组管理员 DN 的判断仍会执行,见 src/core/auth/ldap/ldap.go#L103-L123。

四、首次登录(用例 Step 1):LDAP 认证的完整调用链

用例 Step 1 要求:一个从未登录过 Harbor 的用户,第一次用其 LDAP id(uid 属性或 ldap_uid 配置的属性)登录 UI。这是整条认证链路最核心的一步,其源码流程如下。

4.1 认证主流程

入口是 src/core/auth/ldap/ldap.go#L55-L101 的 Authenticate 方法,逻辑分为四段:

  1. 空用户名校验:p 为空时直接返回 auth.NewErrAuth("Empty user id")。
  2. 建立 LDAP 会话:通过 ldapCtl.Ctl.Session(ctx) 读取系统 LDAP 配置并创建 Session,随后 Open() 拨号建立连接(ldap/ldaps 协议处理见 src/pkg/ldap/ldap.go#L195-L231)。
  3. 搜索用户:调用 ldapSession.SearchUser(p),实际执行带分页的目录搜索(pageSize = 1000,见 src/pkg/ldap/ldap.go#L33)。搜索结果必须恰好一条:
    • 0 条 → auth.NewErrAuth("Not found an entry");
    • 多于 1 条 → auth.NewErrAuth("Multiple entries found")。
  4. 绑定校验密码:用搜索到的用户 DN 加用户输入密码执行 ldapSession.Bind(dn, m.Password)(见 src/pkg/ldap/ldap.go#L189-L192)。绑定失败即认证失败——LDAP 服务器才是密码校验的真正执行者,Harbor 本地并不存储 LDAP 用户密码。

搜索返回的用户模型定义在 src/pkg/ldap/model/ldap.go#L18-L24,包含用户名、邮箱、真实姓名、DN 及组成员 DN 列表。

4.2 首次登录的用户落库(OnBoardUser)

认证通过后,PostAuthenticate(src/core/auth/ldap/ldap.go#L329-L368)检查用户是否已存在于 Harbor 本地 user 表:

  • 已存在:同步本地记录的 UserID,并校验 LDAP 返回的邮箱是否合法、是否需要回写更新;
  • 不存在:调用 OnBoardUser(src/core/auth/ldap/ldap.go#L227-L241)插入一条"影子记录"。

OnBoardUser 有两个值得注意的实现细节:

pwd, err := utils.GenerateRandomStringOrError()
u.Password = pwd         // Password is not kept in local db
u.Comment  = "from LDAP." // Source is from LDAP
  • 密码不落库:为本地记录生成一个随机密码占位,真实密码始终由 LDAP 管理;
  • 来源标记:Comment 标记为 "from LDAP.",便于管理员在用户列表中识别 LDAP 导入用户。

这正解释了用例 Step 3 的断言:LDAP 用户无法在 Harbor 内修改密码,因为密码根本不存在于 Harbor,必须到 LDAP/AD 中修改。

4.3 组附着与系统管理员标记

attachLDAPGroup(src/core/auth/ldap/ldap.go#L103-L140)在登录时完成两件事:

  • 若用户的某个组 DN 与配置的 ldap_group_admin_dn 匹配,则将 u.AdminRoleInAuth 置为 true;
  • 若配置了组搜索过滤器,则验证组 DN 存在性并调用 ugCtl.Ctl.Populate 将组 ID 关联到用户(支持串行/并行两种模式)。

此外 syncUserInfoFromDB(src/core/auth/ldap/ldap.go#L213-L223)会从本地库回填 SysAdminFlag,保证通过 UI 授予过系统管理员角色的 LDAP 用户在会话中依然生效。

五、登出与再次登录(用例 Step 2–4):非管理员视角验证

用例要求:

  • Step 2:用户从 UI 登出;
  • Step 3:用户再次登录 UI,此时不应看到自己的账户设置,也不能修改密码(需到 LDAP/AD 修改);
  • Step 4:再次登出。

5.1 登出行为

登出后应回到登录页(用例 Expected Outcome 明确要求"login page will be displayed again")。这是标准的服务端会话销毁 + 前端跳转流程,可在任何认证模式下复验。

5.2 为何 LDAP 用户看不到"账户设置/改密码"

根源即 4.2 节所述:LDAP 用户密码由外部目录管理,Harbor 仅保存占位随机密码与 "from LDAP." 标记。因此 UI 依据认证来源隐藏账户设置与密码修改入口,避免用户产生"在 Harbor 改了密码"的误解——真正生效的密码变更必须在 LDAP/AD 侧完成。

验证要点(来自用例 Expected Outcome):

  • 首次登录与再次登录后,dashboard 与导航栏都应呈现非管理员视图(看不到 admin options);
  • 账户设置与修改密码能力对 LDAP 用户不可用;
  • 每次登出后重新显示登录页。

六、错误密码登录(用例 Step 5):错误信息不泄露细节

用例要求使用"错误的用户名 + 错误的密码"组合登录,并检查错误信息。Expected Outcome 给出明确约束:

The error message in Step 5 should not show which input value is incorrect. It should only display the username(email) and password combination is incorrect.

即:错误提示只能笼统地说"用户名/邮箱与密码组合不正确",绝不能区分"用户不存在"或"密码错误"。这是安全最佳实践,防止攻击者通过错误信息差异枚举有效用户名。

从实现看,这一约束也贯穿认证源码的多个分支:"Empty user id"、"Not found an entry"、"Multiple entries found"、绑定失败的错误均通过 auth.NewErrAuth 包装为统一的认证失败语义,由上层控制器收敛为不泄露细节的提示文案(参见 src/core/auth/ldap/ldap.go#L55-L101 各失败分支)。

七、Docker CLI 登录验证(用例 Step 6–7):docker login

用例要求在有 Docker CLI 的主机上执行:

docker login <harbor_host>
  • Step 6:使用正确的用户名/密码,必须登录成功;
  • Step 7:使用错误的密码,必须登录失败。

这一步验证的是 Harbor 的 Registry v2 token 服务与 LDAP 认证的打通:docker login 将用户名/密码提交给 Harbor 的认证接口,走的就是 4.1 节描述的 LDAP 认证链路;认证成功后 Harbor 签发 Docker 所需的 token。因此,凡 UI 能登录的 LDAP 用户,docker login 也应能登录;反之亦然,两个入口共享同一套 Authenticate 实现。

八、管理员视角(用例 Step 8):LDAP 用户管理

用例要求系统管理员登录 UI,进入 "admin Options"(管理员选项),并确认:

  1. 上述 LDAP 用户出现在用户列表中;
  2. 管理员可以给该用户授予或移除系统管理员角色。

这验证了 "LDAP user should have no difference from a user in local database"(LDAP 用户与本地数据库用户无差别)这一预期。支撑机制:

  • 首次登录落库的"影子记录"让 LDAP 用户拥有了与本地用户一致的 Harbor 内身份(UserID),从而可以参与项目成员、角色、审计等所有实体关联(见 src/core/auth/ldap/ldap.go#L52-L54 注释:"if the check is successful a dummy record will be inserted into DB, such that this user can be associated to other entities in the system");
  • 管理员授予的 SysAdminFlag 会在用户下次登录时通过 syncUserInfoFromDB 回填到会话(src/core/auth/ldap/ldap.go#L213-L223)。

九、禁用/移除 LDAP 用户(用例 Step 9–10):即时失去登录能力

用例最后要求:

  • Step 9:在 LDAP 服务器中禁用或删除该用户;
  • Step 10:该用户不再能通过 UI 或 Docker 客户端登录。

由于 Harbor 每次登录都实时与 LDAP 交互(搜索用户 → 绑定校验),而非使用缓存的凭据,因此在 LDAP 侧禁用/删除用户后,后续登录会因:

  • 搜索不到用户("Not found an entry"),或
  • 绑定失败(LDAP 返回凭据无效)

而立即失败。无需在 Harbor 侧做任何额外操作,这正是外部认证源管理模式的核心特性:账户生命周期完全由 LDAP 目录掌控,Harbor 只是忠实执行校验结果。管理员若希望从 Harbor 用户列表中彻底清理该影子记录,可另行通过用户管理界面删除(Harbor 支持基于 GDPR 的用户删除配置,见 src/lib/config/models/model.go#L101-L104 的 GDPRSetting)。

十、预期结果速查表(Expected Outcome 汇总)

步骤预期结果
Step 1 & 3可用 LDAP id 登录 UI;dashboard 与导航栏为非管理员视图(无 admin options)
Step 3无法更新账户设置、无法修改密码
Step 2 & 4登出后重新显示登录页
Step 5错误提示仅显示"用户名/邮箱与密码组合不正确",不泄露具体是哪个输入有误
Step 6Docker 客户端可正常 docker login
Step 7Docker 客户端以错误密码登录失败
Step 8LDAP 用户与本地数据库用户无差别;管理员可授予/移除其系统管理员角色
Step 10LDAP 用户登录失败(UI 与 Docker 客户端均不可用)

十一、相关源码与测试索引

如需深入阅读实现与测试,可按以下路径继续:

  • 认证主流程:src/core/auth/ldap/ldap.go(Authenticate / OnBoardUser / PostAuthenticate / attachLDAPGroup)
  • LDAP 会话与搜索:src/pkg/ldap/ldap.go(Session.Open / SearchUser / Bind / formatURL / createUserSearchFilter)
  • LDAP 管理器与导入:src/pkg/ldap/manager.go
  • LDAP 控制器:src/controller/ldap/controller.go
  • LDAP 配置模型:src/lib/config/models/model.go(LdapConf / GroupConf)
  • 认证模式常量:src/common/const.go(LDAPAuth = "ldap_auth")
  • 原始测试用例:tests/testcases/Group1-user-management/1-05-LDAP-user-log-in-log-out.md
  • LDAP 单元测试:src/core/auth/ldap/ldap_test.go、src/pkg/ldap/ldap_test.go、src/pkg/ldap/filter_test.go

按照上述步骤完成全部验证后,即证明当前 Harbor 实例的 LDAP 认证模式在 UI 与 Docker CLI 双入口上均工作正常,用户生命周期与密码策略完全由外部 LDAP 目录托管,符合企业内部统一身份认证的预期。

【免费下载链接】harbor An open source trusted cloud native registry project that stores, signs, and scans content. 【免费下载链接】harbor 项目地址: https://gitcode.com/GitHub_Trending/ha/harbor

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值