Harbor 管理员账号登录与登出验证:从 UI 到 Docker CLI 的完整测试指南
【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor
导读
本文基于 Harbor 官方测试用例1-08 Admin User Log In and Log Out(DB 模式与 LDAP 模式),系统讲解如何验证管理员账号在 Harbor 中的完整登录/登出链路,包括 Web UI 登录、UI 登出、错误密码提示、以及 Docker 客户端docker login的身份验证。读完本文,你将掌握该用例的执行步骤、预期结果及其背后的源码级认证原理(认证器注册机制、错误码锁定策略、会话销毁与 Token 签发),可直接复现为 Harbor 部署后的基础验收用例。
1. 用例定位:认证是 Harbor 的第一道安全防线
Harbor 是一个开源的云原生镜像仓库,所有镜像的推送、拉取、复制等操作都建立在用户身份认证之上。用例1-08位于 tests/testcases/Group1-user-management/1-08-admin-log-in-log-out.md,属于用户管理测试组(Group1)的基础用例,核心目的是:
- 验证管理员(admin)账号在 Web UI 上可以正常登录与登出;
- 验证错误密码登录时,系统不会泄露"到底是用户名错还是密码错"的细节,只提示用户名与密码组合不正确;
- 验证 Docker CLI 通过
docker login <harbor_host>也能完成与 UI 相同的认证。
该用例同时覆盖了 Harbor 两种最常见的认证模式:数据库认证(DB Mode)与LDAP 认证(LDAP Mode)。
2. 环境准备
在执行本用例之前,需要满足以下条件:
| 条件 | 说明 |
|---|---|
| Harbor 实例 | 一个正在运行且可访问的 Harbor 实例 |
| 认证模式 | Harbor 配置为对本地数据库(DB)或 LDAP 服务器进行认证 |
| 客户端主机 | 一台安装了 Docker CLI(Docker client)的 Linux 主机,用于执行docker login |
| 测试账号 | 具备系统管理员权限的账号(默认部署时为admin) |
关于认证模式的配置,Harbor 通过安装配置生成,认证模式定义于系统配置项auth_mode。从源码结构看,认证模式是驱动 Harbor 选择认证器(authenticator)的关键参数,见 src/core/auth/authenticator.go 中的Login函数:
func Login(ctx context.Context, m models.AuthModel) (*models.User, error) { authMode, err := config.AuthMode(ctx) ... if authMode == "" || IsSuperUser(ctx, m.Principal) { authMode = common.DBAuth } authenticator, ok := registry[authMode] ... }注意:即使是 LDAP 模式,Harbor 也约定超级用户(admin,数据库内 ID 为 1)始终走本地数据库认证,因此管理员账号在任何认证模式下都能登录。
3. 测试步骤详解
3.1 步骤 1 & 2:UI 登录与登出
- 管理员用户通过用户名登录 Harbor Web UI;
- 管理员用户从 Web UI 登出。
预期结果:步骤 1、2 中管理员用户均能成功登录/登出。
Harbor UI 的登录与登出请求由CommonController处理,路由注册于 src/server/route.go:
web.Router("/c/login", &controllers.CommonController{}, "post:Login") web.Router("/c/log_out", &controllers.CommonController{}, "get:LogOut")POST /c/login:接收表单字段principal(用户名)与password,调用auth.Login完成认证,成功后通过PopulateUserSession将用户写入会话;GET /c/log_out:从安全上下文中取出当前用户名,若为 OIDC 用户则重定向到 OIDC Provider 的登出地址,否则调用DestroySession销毁会话。
对应实现见 src/core/controllers/base.go 中的Login与LogOut方法。
3.2 步骤 3:错误密码登录的错误信息验证
使用管理员的错误密码登录 Web UI,检查页面显示的报错信息。
预期结果:错误提示不应指明是哪个输入值(用户名或密码)不正确,而应只显示"用户名和密码的组合不正确"这类模糊提示。
这一安全设计在源码中有明确支撑:当认证失败时,auth.Login会对凭据错误统一处理,而 UI 侧的Login方法只返回 HTTP 401(CustomAbort(http.StatusUnauthorized, "")),不向前端回传具体失败原因,见 src/core/controllers/base.go。
更深一层,authenticator.go中通过ErrAuth错误类型区分"用户凭据错误"与"服务端错误",只有ErrAuth才被视为坏凭据:
type ErrAuth struct { details string } // Authenticate 返回 ErrAuth 时视为凭据错误,其余错误视为服务端错误3.3 步骤 4:Docker CLI 成功登录
在一台安装了 Docker CLI 的主机上执行:
docker login <harbor_host>输入管理员用户名与密码,验证管理员用户可以通过 Docker 客户端登录。
预期结果:Docker 客户端登录成功。
docker login背后的流程是:Docker 客户端向 Harbor 的 Token 服务发起请求,Harbor 完成用户名/密码校验后签发一个由私钥签名的 Bearer Token(签名算法为 RS256),Docker 客户端凭此 Token 访问镜像仓库 API。Token 服务路由为/service/token(见 src/server/route.go),签发逻辑见 src/core/service/token/authutils.go。
3.4 步骤 5:Docker CLI 错误密码登录
再次使用docker login <harbor_host>,但这次输入错误的密码。
预期结果:Docker 客户端登录失败(认证被拒绝)。
4. 预期结果汇总
| 步骤 | 操作 | 预期结果 |
|---|---|---|
| 1 | admin 在 UI 登录 | 登录成功 |
| 2 | admin 在 UI 登出 | 登出成功 |
| 3 | UI 使用错误密码登录 | 登录失败,仅提示"用户名和密码组合不正确",不泄露具体输入错误 |
| 4 | Docker CLI 使用正确凭据docker login | 登录成功 |
| 5 | Docker CLI 使用错误密码docker login | 登录失败 |
5. 源码级原理剖析
5.1 认证器注册机制:DB 与 LDAP 的统一抽象
Harbor 将认证逻辑抽象为AuthenticateHelper接口(见 src/core/auth/authenticator.go),各认证模式在包初始化时通过auth.Register注册到全局registrymap:
var registry = make(map[string]AuthenticateHelper) func Register(name string, h AuthenticateHelper) { ... }- DB 模式:注册名为
common.DBAuth,实现见 src/core/auth/db/db.go。核心逻辑是调用userMgr.MatchLocalPassword(principal, password)直接与本地数据库中的用户凭据比对,匹配失败返回auth.NewErrAuth("Invalid credentials"); - LDAP 模式:注册名为
common.LDAPAuth,实现见 src/core/auth/ldap/ldap.go。认证分两步:先用搜索过滤器定位用户条目,再用该条目的 DN 与用户提交的密码执行 LDAP Bind。Bind 成功即认证通过,随后 Harbor 会将用户信息同步(OnBoard)到本地数据库,以便与其他实体(项目、角色)关联。
此外还有 OIDC、UAA、HTTP Auth Proxy 等认证器(见 src/core/auth 目录),它们共享同一接口,这也是"DB 或 LDAP 模式"能共用同一套测试用例的根本原因。
5.2 登录失败锁定:暴力破解防护
为防止针对管理员账号的暴力破解,auth.Login内置了失败锁定机制:
// 1.5 seconds const frozenTime time.Duration = 1500 * time.Millisecond var lock = NewUserLock(frozenTime) if lock.IsLocked(m.Principal) { log.Debugf("%s is locked due to login failure, login failed", m.Principal) return nil, nil } user, err := authenticator.Authenticate(ctx, m) if err != nil { if _, ok = err.(ErrAuth); ok { log.Warningf("Login failed, locking %s, and sleep for %v", m.Principal, frozenTime) lock.Lock(m.Principal) time.Sleep(frozenTime) } return nil, err }即:一旦认证返回ErrAuth,该用户名会被锁定 1.5 秒并强制睡眠,期间再次登录直接失败。这也是为什么步骤 3 和步骤 5 中连续使用错误密码会被拒绝的底层保障之一。
5.3 会话与安全上下文
UI 登录成功后,用户信息被写入会话;后续请求由安全上下文(Security Context)承载权限判定。本地认证用户的上下文实现见 src/common/security/local/context.go:
IsAuthenticated():用户是否已认证;IsSysAdmin():是否为系统管理员(SysAdminFlag或 LDAP 管理员组角色AdminRoleInAuth为真);Can():通过 RBAC 评估器判定用户对某资源的操作权限,系统管理员直接获得全部权限(admin.New(...))。
管理员账号之所以能管理全部项目与系统设置,正是因为本地安全上下文为其挂载了系统管理员评估器。
5.4 Docker Token 认证链路
docker login与 UI 登录共享同一套凭据校验,但后续授权机制不同:Docker 客户端携带 Token 访问 registry。Token 服务根据请求中的scope(如repository:library/hello-world:pull,push)解析资源与动作,并签发带权限声明的 JWT,相关解析逻辑见 src/core/service/token/authutils.go 的GetResourceActions。
6. 执行要点与问题排查
- 端口与地址:
docker login使用的<harbor_host>需与 Harbor 的外部访问地址一致,若配置了 HTTPS 证书,客户端主机需信任 Harbor 的 CA 证书; - 错误信息检查:步骤 3 的关键是观察报错文案——如果页面提示"用户名不存在"或"密码错误"等能区分输入值的细节,说明认证信息泄露处理不符合预期;
- LDAP 模式注意:LDAP 用户登录成功后会 OnBoard 到本地数据库,但密码不落库,后续仍由 LDAP 服务器校验;管理员
admin不受 LDAP 影响,始终走 DB 认证(见 src/core/auth/authenticator.go 的IsSuperUser逻辑); - 相关自动化佐证:Harbor 的 API 自动化测试中也大量使用
admin/Harbor12345作为默认管理员凭据执行登录与用户管理操作,可参考 tests/apitests/python/test_user_crud.py 与 tests/apitests/python/test_user_group.py。
7. 小结
通过本用例,可以一次性覆盖 Harbor 认证体系的三个关键面:UI 会话登录/登出、错误凭据的信息最小化提示、Docker 客户端 Token 认证。结合 src/core/auth 目录下的认证器实现与 src/core/controllers/base.go 的控制器逻辑,读者既能复现完整测试流程,也能理解 Harbor 认证体系"统一接口 + 多模式注册 + 失败锁定 + 最小信息泄露"的设计思想,为后续进行 LDAP、OIDC 等更复杂认证模式的验证打下基础。
【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考