Harbor 数据库认证(DB Mode)下非管理员用户查看与过滤操作日志的完整验证指南
【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor
本文基于 Harbor 仓库中的测试用例 4-01-DB-user-view-logs.md 展开,系统性地讲解在auth_mode 为
db_auth(本地数据库认证)的 Harbor 环境中,如何验证非管理员用户能否正确查看项目操作日志(审计日志),并逐一验证 push / pull / delete 等操作是否被完整记录、以及日志过滤功能是否按预期工作。读完本文,你将掌握一套可直接复跑的端到端验证流程,并理解 Harbor 审计日志从"记录 → 存储 → 权限过滤 → 查询"的完整链路及其底层源码实现。
测试目标与适用场景
该测试用例属于 Harbor 仓库tests/testcases/Group4-logging(日志功能测试组)的第一个用例,核心目标只有一个:
验证当用户由 Harbor 本地数据库(DB 模式)统一管理时,非管理员用户能够查看其有权限项目的操作日志,且日志可以被正确记录、浏览与过滤。
这个场景在真实生产中非常常见:一个团队把 Harbor 的认证模式配置为本地数据库(auth_mode: db_auth),各项目成员(非系统管理员)需要自行查看自己参与的项目的操作历史,用于排查"谁在什么时候 push / pull / delete 了什么镜像"。系统管理员则可以看到全部审计日志。
说明:Harbor 的认证模式还支持 LDAP / OIDC / UAA 等外部认证。本文用例仅覆盖DB 模式(用户数据存储在 Harbor 本地数据库),这是验证本地用户权限与日志隔离性的基础场景。
前置条件(Environment)
在执行测试前,需要确认以下环境全部就绪:
| 前置条件 | 说明 |
|---|---|
| Harbor 实例 | 一个正在运行、可访问的 Harbor 实例 |
| 认证模式 | Harbor 配置为本地数据库认证,即auth_mode设为db_auth,用户数据存放在本地数据库 |
| 客户端主机 | 一台安装了 Docker CLI(Docker 客户端)的 Linux 主机 |
| 用户账号 | 至少一个非管理员用户账号(由本地数据库管理) |
| 项目与镜像 | 项目中存在可 push / pull / delete 的镜像,且该非管理员用户对该项目拥有相应权限(如开发人员或项目管理员) |
其中认证模式在 Harbor 安装配置文件(make/harbor.yml.tmpl可查看配置模板)中以auth_mode参数体现,修改后需重新运行make/prepare并重启服务才会生效。
核心验证步骤(Test Steps)
整个验证过程分为六步:登录 → 制造操作 → UI 登录 → 删除镜像 → 查看日志 → 过滤日志。
Step 1:以非管理员用户身份登录 Docker 客户端
在安装了 Docker CLI 的主机上,使用非管理员用户账号登录 Harbor:
docker login <harbor_host><harbor_host>替换为实际的 Harbor 地址(如192.168.1.10或myharbor.example.com);- 输入的是非管理员用户的用户名与密码;
- 该用户必须是某个项目的成员,否则后续将没有可查看的日志项目。
Step 2:执行 push 与 pull 操作制造日志
登录成功后,执行一批docker push和docker pull命令,向 Harbor 推送镜像并从 Harbor 拉取镜像。例如:
# 为镜像打上 Harbor 仓库标签 docker tag nginx:latest <harbor_host>/<project>/nginx:latest # 推送镜像到 Harbor docker push <harbor_host>/<project>/nginx:latest # 从 Harbor 拉取镜像 docker pull <harbor_host>/<project>/nginx:latest这些操作是后续验证"日志是否被记录"的核心素材。建议 push 和 pull 各执行若干次,并故意穿插执行,方便后面验证"只过滤 push""只过滤 pull""同时过滤 pull 和 push"等组合条件。
Step 3:以非管理员用户登录 Web UI
打开 Harbor Web 界面,使用与 Step 1 相同的非管理员账号登录。此步骤验证的是:非管理员用户能够正常进入 Harbor 门户并看到自己有权限的项目。
Step 4:删除项目中的部分镜像
在 UI 中进入相应项目,删除刚才推送的若干镜像(或镜像的某个 tag)。删除操作同样会进入日志,用于验证delete类型的日志记录与过滤。
Step 5:查看项目日志
在项目页面中打开日志(Logs / 审计日志)视图,此时应能看到此前发生的 push、pull、delete 操作记录。重点核对每条日志的时间与操作内容是否与实际操作一致。
Step 6:逐项验证日志过滤条件
测试用例要求使用以下搜索条件逐一过滤日志,并确认过滤结果正确:
| 过滤组合 | 预期语义 |
|---|---|
| push only | 只显示 push 操作 |
| pull only | 只显示 pull 操作 |
| pull and push | 同时显示 pull 和 push 两类操作 |
| delete only | 只显示 delete 操作 |
| all | 显示全部操作,不限制操作类型 |
| push and delete | 同时显示 push 和 delete 两类操作 |
| 不同日期范围(date ranges) | 按不同的起止时间区间过滤 |
| 日期范围 + push | 时间区间与操作类型组合过滤 |
每次切换过滤条件后,都应检查返回列表是否只包含符合条件的日志,尤其是组合条件(日期范围 + push),用于验证多个条件同时生效时的过滤逻辑。
预期结果(Expected Outcome)
测试通过的标准如下,缺一不可:
- Step 2 与 Step 4 的所有操作都应被记录:push、pull、delete 全部出现在日志中,不存在遗漏;
- Step 5 可以正常查看日志:日志列表可见,且每条日志的时间与操作内容正确;
- Step 6 的过滤功能生效:所有列出的过滤条件都能返回与条件匹配的正确结果。
只有同时满足以上三点,才认为该用例通过,即"DB 模式下非管理员用户查看日志"功能正常。
从源码看审计日志的完整链路
仅仅跑通 UI 还不够,理解底层实现能帮助你在测试不通过时快速定位问题。下面结合本仓库源码说明 Harbor 审计日志从产生到展示的完整链路。
日志数据模型:audit_log 表
审计日志在数据库中对应audit_log表,其 ORM 模型定义在 src/pkg/audit/model/model.go:
type AuditLog struct { ID int64 `orm:"pk;auto;column(id)" json:"id"` ProjectID int64 `orm:"column(project_id)" json:"project_id"` Operation string `orm:"column(operation)" json:"operation"` ResourceType string `orm:"column(resource_type)" json:"resource_type"` Resource string `orm:"column(resource)" json:"resource"` Username string `orm:"column(username)" json:"username"` OpTime time.Time `orm:"column(op_time)" json:"op_time" sort:"default:desc"` } func (a *AuditLog) TableName() string { return "audit_log" }从字段可以看出每条日志的核心信息:属于哪个项目(project_id)、什么操作(operation,如 push / pull / delete)、操作对象(resource与resource_type)、谁做的(username)、什么时间(op_time)。其中op_time默认按降序排序,所以日志列表默认展示最新记录在前——这也是 UI 中"时间倒序"的底层来源。
日志的写入:请求中间件 + 事件通知
操作日志并非由业务代码手动一条条写入,而是通过 HTTP 请求中间件统一捕获。中间件实现位于 src/server/middleware/log/log.go,其核心流程为:
- 从请求中提取
X-Request-ID、trace ID 等信息附加到日志上下文; - 构造
commonevent.Metadata,记录用户名(默认unknown,若已认证则取安全上下文中的用户名)、请求方法(RequestMethod)、请求 URL(RequestURL); - 通过
PreCheckMetadata()判断该请求是否属于需要记录审计的操作; - 对需要审计的请求,读取请求体(受
common.MaxAuditLogPayloadSize限制,超出则直接返回413 Request Entity Too Large),记录响应码,并通过notification.AddEvent将事件投递到通知系统,最终落库。
同时,审计日志的管理接口定义在 src/pkg/audit/manager.go,提供Count、List、Get、Create、Delete、Purge(按保留小时数清理旧日志)等方法。这也是"Step 4 删除镜像后 delete 操作也会被记录"的实现基础——只要请求走了标准中间件链路,就会统一生成审计事件。
日志的查询与权限隔离:非管理员只能看到自己有权访问的项目
这是本用例最关键的验证点:非管理员用户查看日志时,系统如何保证他看不到其他项目的日志?
答案在 API Handler 中:src/server/v2.0/handler/auditlog.go 的checkPermissionAndBuildQuery函数:
- 先校验用户已认证(未认证直接返回 401 Unauthorized);
- 尝试以系统级权限查询(
RequireSystemAccess(ctx, rbac.ActionList, rbac.ResourceAuditLog)); - 若用户不是系统管理员,则改为列出该用户参与的所有项目(通过
projectCtl.List查询成员关系,同时考虑用户所属用户组GroupIDs); - 对每个项目进一步检查用户是否具备项目级日志查看权限(
HasProjectPermission(ctx, project.ProjectID, rbac.ActionList, rbac.ResourceLog)); - 最终把有权限的项目 ID 集合构造成一个 OrList,作为
ProjectID关键词追加到查询条件中——没有权限的项目一条日志都不会返回(如果没有任何有权限的项目,会塞入-1保证查询结果为空)。
对应的 RBAC 资源定义见 src/common/rbac/const.go:项目级资源ResourceLog = Resource("log")(项目日志),系统级资源ResourceAuditLog = Resource("audit-log")(全局审计日志)。而项目角色对日志的权限映射在 src/common/rbac/project/rbac_role.go({Resource: rbac.ResourceLog, Action: rbac.ActionList}),即只有具备对应项目角色的用户才拥有log资源的list动作。
换句话说:本用例的通过条件,本质上是验证"项目成员 → 项目角色 → log 资源 list 权限 → 查询条件注入"这条权限链是否完整生效。
对应的 REST API
日志查询对应的 API 定义在 api/v2.0/swagger.yaml,主要包括:
GET /audit-logs:获取用户作为项目管理员成员的项目的近期操作日志,系统管理员可获取全部审计日志(旧版接口,已标记 deprecated);GET /auditlog-exts:获取用户作为项目管理员成员的项目的近期操作日志,或系统管理员获取全部审计日志(扩展字段,包含操作描述与结果);GET /projects/{project_name}/logs:获取指定项目的近期日志(旧版项目级接口,已标记 deprecated);GET /auditlog-exts/events:获取审计日志的全部事件类型。
这些接口均支持q(查询条件)、sort、page、pageSize参数,返回X-Total-Count总条数头与Link分页链接——UI 上的日志过滤与分页正是通过q查询参数(如按操作类型、按时间范围组合过滤)实现的,这与 Step 6 中验证的过滤条件一一对应。
过滤条件的实际应用建议
在执行 Step 6 时,以下几点能帮助你更准确地完成验证:
- 操作类型维度:先单独验证单一类型(push only / pull only / delete only),确认各类型都被正确标记与过滤;再做组合(pull and push / push and delete),确认多选逻辑为"并集"关系;
- 时间维度:先选择一个覆盖全部操作的大时间范围(all),再缩小到一个只覆盖部分操作的窄范围,验证边界时间点上的日志是否被正确包含或排除;
- 组合维度:最后验证"日期范围 + push"这类跨维度组合,确认不同类型条件之间是"交集"(AND)关系;
- 空结果检查:故意选择一段没有任何操作的时间范围,确认返回空列表且不报错,验证过滤逻辑对空结果的健壮性。
常见问题排查(基于源码线索)
如果测试失败,可以从以下方向入手定位:
- 日志完全没有记录:检查请求是否经过
src/server/middleware/log/log.go的中间件链路,确认请求体大小未超过common.MaxAuditLogPayloadSize(超限会直接返回 413); - 非管理员看不到日志:重点检查 src/server/v2.0/handler/auditlog.go 的权限判定逻辑,确认用户的项目角色在 src/common/rbac/project/rbac_role.go 中确实映射了
log资源的list权限; - 时间/操作内容不正确:对照
audit_log表中的op_time、operation、resource字段与 UI 展示是否一致,可借助数据库客户端直接查询audit_log表核对原始数据; - 过滤结果不对:确认
q查询参数的格式与 src/pkg/audit/manager.go 中Count/List的实现一致,过滤条件最终会转换为 SQL 查询下推到数据库执行。
总结
本用例通过一次完整的端到端验证,覆盖了 Harbor 在DB 认证模式下非管理员用户查看日志的全部关键路径:Docker 客户端的 push / pull、UI 中的镜像删除、日志的查看与八种过滤组合。结合源码可以看到,Harbor 通过请求中间件统一捕获操作事件、以audit_log表持久化,并在 API 层通过"系统级权限 + 项目级log资源list权限"双层校验实现严格的日志隔离——这也是企业生产环境审计合规(谁在何时对哪个镜像做了什么)的基石。
如需进一步扩展验证,可参考仓库中的其他日志相关测试用例(tests/testcases/Group4-logging/目录),并结合 src/pkg/audit 与 src/server/v2.0/handler/auditlog.go 深入理解实现细节。
【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考