ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Harbor 镜像删除权限验证:Admin 用户删除他人项目镜像(DB 认证模式)实战与实现原理

Harbor 镜像删除权限验证:Admin 用户删除他人项目镜像(DB 认证模式)实战与实现原理 Harbor 镜像删除权限验证Admin 用户删除他人项目镜像DB 认证模式实战与实现原理【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor导读本文基于 Harbor 仓库中的系统测试用例 tests/testcases/Group2-image-management/2-23-admin-delete-images.md完整还原在本地数据库认证模式auth_mode: db_auth下管理员admin删除普通用户所属项目镜像的验证流程。文章既给出可直接落地的 UI 操作步骤与 Docker CLI 验证命令也深入 Harbor 源码剖析删除动作背后的 RBAC 权限判定、仓储repository删除、制品artifact级联删除等核心链路帮助你理解管理员为何能删、删了什么、删完客户端为何拉取失败这三个关键问题。一、测试场景背景1.1 为什么需要验证管理员删除他人镜像Harbor 是开源的企业级云原生制品仓库支持多租户项目隔离。在真实生产环境中镜像的所有权往往与项目成员角色绑定但管理员账号承担平台级运维职责需要能够管理、清理所有项目下的资源包括删除普通用户上传的镜像。该用例的核心断言有两个管理员可以删除普通用户项目中的镜像镜像被删除后Docker 客户端拉取该镜像会失败。这组断言验证的不仅是删除功能本身更是 Harbor管理员的系统级权限是否能够覆盖项目级资源这一 RBAC 关键行为。1.2 测试环境前提原文明确了以下环境约束缺一不可前提说明Harbor 实例可用测试需要一个正在运行且可访问的 Harbor 实例认证模式为本地数据库配置项auth_mode必须设置为db_auth用户数据存储在本地数据库中装有 Docker CLI 的 Linux 主机需要一台安装了 Docker 客户端的主机用于 push/pull 操作至少一个非管理员用户用于创建项目并上传镜像验证跨用户删除场景auth_mode在安装配置模板 make/harbor.yml.tmpl 中定义用户数据由 Harbor core 服务写入本地 PostgreSQL 数据库相关 DAO 实现见 src/common/dao/base.go。当认证模式为db_auth时用户的创建、登录校验、权限授予全部由 Harbor 自身完成管理员账号与普通用户账号共存于同一套本地用户体系这使得管理员删除普通用户资源的权限测试成为可能。二、完整测试步骤可直接复现原文提示下述步骤中用户 A 为非管理员用户用户 A和项目 X在真实执行时应替换为更有意义的长名称例如alice用户与demo-images项目。2.1 准备阶段普通用户创建项目并上传镜像以用户 A非管理员身份登录 Harbor UI。此时该用户没有任何项目级资源先验证其具备创建项目的权限。创建项目 X。项目创建者自动成为该项目的项目管理员Project Admin拥有项目内的完整管理权限详见下文 RBAC 分析。在 Docker 客户端上以用户 A 身份登录 Harbor并向项目 X 推送第一个镜像docker login harbor-host docker tag local-image harbor-host/projectX/myimage:v1 docker push harbor-host/projectX/myimage:v1推送第二个不同名称的镜像到项目 Xdocker tag local-image harbor-host/projectX/newimage:v1 docker push harbor-host/projectX/newimage:v1验证镜像可正常拉取docker pull harbor-host/projectX/myimage:v1 docker pull harbor-host/projectX/newimage:v1该步骤用于建立删除前镜像可用的基线从而在删除后形成鲜明的对比断言。2.2 执行阶段管理员删除镜像在 UI 中退出用户 A 的会话。此步骤确保后续操作完全以管理员身份执行排除会话复用造成的权限混淆。以 admin 用户身份登录 Harbor。进入项目 X逐个删除两个镜像projectX/myimage:v1与projectX/newimage:v1。在 UI 中位置为项目 → 镜像仓库 → 选中镜像 → 删除。2.3 验证阶段删除后客户端拉取必须失败回到 Docker 客户端以用户 A 身份登录再次拉取已删除的两个镜像docker login harbor-host docker pull harbor-host/projectX/myimage:v1 docker pull harbor-host/projectX/newimage:v12.4 预期结果步骤预期结果步骤 8管理员可以成功删除项目 X 中的两个镜像步骤 9Docker 客户端拉取已删除镜像时应报错拉取失败的直接原因是镜像的 manifest 已从 Harbor 存储中删除Registry 返回manifest unknown之类的错误Docker CLI 因此无法解析镜像内容。三、源码深潜管理员为什么能删除3.1 删除接口的权限门禁镜像删除在 API 层对应DELETE /projects/{project_name}/repositories/{repository_name}实现在 src/server/v2.0/handler/repository.go 的DeleteRepositoryfunc (r *repositoryAPI) DeleteRepository(ctx context.Context, params operation.DeleteRepositoryParams) middleware.Responder { if err : r.RequireProjectAccess(ctx, params.ProjectName, rbac.ActionDelete, rbac.ResourceRepository); err ! nil { return r.SendError(ctx, err) } repository, err : r.repoCtl.GetByName(ctx, fmt.Sprintf(%s/%s, params.ProjectName, params.RepositoryName)) ... if err : r.repoCtl.Delete(ctx, repository.RepositoryID); err ! nil { return r.SendError(ctx, err) } // fire event notification.AddEvent(ctx, metadata.DeleteRepositoryEventMetadata{...}) return operation.NewDeleteRepositoryOK() }关键点在RequireProjectAccess删除操作要求调用者对rbac.ResourceRepository资源拥有rbac.ActionDelete动作。权限枚举定义见 src/common/rbac/const.goActionDelete Action(delete)与 src/common/rbac/const.goResourceRepository。系统管理员的特殊地位Harbor 的权限上下文中系统管理员secCtx.IsSysAdmin()在权限评估时被直接放行无需逐项匹配项目级角色策略。因此 admin 用户对任意项目内的repository:delete动作天然具备权限——这正是用例中管理员能删除普通用户项目镜像的权限层依据。3.2 项目管理员的权限清单对照组用例同时要求用户 A 在创建项目后获得项目管理员角色。查看 src/common/rbac/const.go 中项目管理员Project Admin的策略声明可以看到它被授予了{Resource: ResourceRepository, Action: ActionRead}, {Resource: ResourceRepository, Action: ActionUpdate}, {Resource: ResourceRepository, Action: ActionDelete}, {Resource: ResourceRepository, Action: ActionList}, {Resource: ResourceRepository, Action: ActionPull}, {Resource: ResourceRepository, Action: ActionPush},也就是说普通用户在自己创建的项目内同样拥有镜像删除权限而管理员则在此基础上拥有跨项目、跨用户的全局删除能力。两者叠加构成了本例完整的权限对照用户 A 能删自己的镜像但删不了别人的admin 能删所有人的镜像。四、源码深潜删除镜像时后端到底做了什么4.1 仓储控制器先清制品再删记录API 层在权限校验通过后调用repository.Ctl.Deletesrc/controller/repository/controller.gofunc (c *controller) Delete(ctx context.Context, id int64) error { candidates, err : c.artCtl.List(ctx, q.Query{ Keywords: map[string]any{RepositoryID: id}, }, nil) ... for len(candidates) 0 { artifacts : candidates candidates nil for _, artifact : range artifacts { parents, err : c.artMgr.ListReferences(...) if err ! nil { return err } if len(parents) 0 { // 制品仍被其他制品引用放入下一轮再试 candidates append(candidates, artifact) continue } if err c.artCtl.Delete(ctx, artifact.ID); err ! nil { return err } } } return c.repoMgr.Delete(ctx, id) }该实现有两个值得注意的工程细节先删制品、后删仓储记录一个 repository 下可能挂多个 artifact不同 tag 指向的 manifest 等。必须等所有制品删除成功后才删除repository表记录避免残留孤儿数据。引用感知的循环删除若某个制品仍被其他制品引用例如被其他 manifest 引用当前轮次会跳过它并放入candidates重新排队下一轮再尝试——这种让位重试机制保证了被依赖的制品不会被误删这一点与下面的深度删除逻辑配合。4.2 制品控制器事务内的深度级联删除真正的物理删除发生在制品控制器artifact.Ctl.Deletesrc/controller/artifact/controller.go在一个 ORM 事务内调用deleteDeeplyfunc (c *controller) Delete(ctx context.Context, id int64) error { accs, err : c.accessoryMgr.List(ctx, q.New(q.KeyWords{ArtifactID: id})) ... return orm.WithTransaction(func(ctx context.Context) error { return c.deleteDeeply(ctx, id, true, len(accs) 0) })(orm.SetTransactionOpNameToContext(ctx, tx-delete-artifact-delete)) }deleteDeeplysrc/controller/artifact/controller.go 起处理了完整的依赖链若制品已被其他制品引用parents非空根制品删除会返回ViolateForeignKeyConstraintCode错误并中止非根制品则跳过避免级联破坏。依次删除制品上的accessory如 cosign 签名、SBOM 等附加物仅硬引用类型被删除与child artifact 引用再递归处理子制品。整个过程包在事务中任何一步失败都会整体回滚保证删除的原子性。正是这条仓储 → 制品 → 深度级联的链路使得 UI 上的一次删除操作能够干净地移除镜像在 Harbor 元数据库中的全部记录。4.3 删除后的联动审计与通知事件回到 API 层删除成功后还会通过notification.AddEvent发布DeleteRepositoryEventMetadata事件见 src/server/v2.0/handler/repository.go。这意味着配置了Webhook的项目会收到仓库删除通知外部系统如 CI/CD 流水线、工单系统可以据此联动审计日志audit log会记录该删除动作便于事后追溯谁在何时删除了哪个镜像。4.4 单测对删除链路的锁定Harbor 为仓储删除逻辑提供了单元测试 src/controller/repository/controller_test.gofunc (c *controllerTestSuite) TestDelete() { art : artifact.Artifact{} art.ID 1 mock.OnAnything(c.argMgr, ListReferences).Return(nil, nil) mock.OnAnything(c.artCtl, List).Return([]*artifact.Artifact{art}, nil) mock.OnAnything(c.artCtl, Delete).Return(nil) c.repoMgr.On(Delete, mock.Anything, mock.Anything).Return(nil) err : c.ctl.Delete(nil, 1) c.Require().Nil(err) }该测试覆盖了先遍历删除制品、再删除仓储记录的核心路径与2-23用例在功能层相互印证前者保证后端删除逻辑的正确性后者保证端到端UI Docker CLI行为符合预期。五、删除后拉取失败的机制解读用例的最终断言是删除后docker pull必须失败。其背后机制为Harbor 使用 OCI Distribution 规范与 Docker Registry 交互镜像通过 manifest digest 寻址。删除操作同时清理了 Harbor 元数据库中的 repository/artifact/tag 记录并触发对 Registry 存储层 blob/manifest 的清理由 GC 流程协调。客户端再次docker pull时Registry 无法找到对应 manifest返回manifest unknown一类错误Docker CLI 随即终止并报错。从用户视角看这正是删除立即生效的体现。需要注意的是存储层物理空间的回收通常由后续的垃圾回收GC任务完成元数据删除与磁盘空间释放之间存在时间差这不影响本文用例的判定用例验证的是客户端可访问性而非磁盘空间。六、实操建议与注意事项基于源码链路与用例流程在实际运维中可注意以下几点权限最小化普通项目管理员Project Admin已具备本项目的镜像删除权限见 src/common/rbac/const.go日常清理建议由项目管理员完成admin 账号的全局删除能力应保留给跨项目治理、下线清理等场景。删除前确认引用关系若镜像被其他制品引用如被其他 manifest 引用删除会被后端引用检查拦截或推迟见 src/controller/repository/controller.go这是保护机制而非故障。关注 Webhook 与审计生产环境建议为关键项目配置删除事件通知并开启审计日志使每一次管理员删除操作都可追溯。配合 GC 规划空间回收删除操作只保证镜像不可访问若需要立即回收磁盘空间应关注 Harbor 的 GC 任务执行情况相关任务框架见 src/jobservice/job/impl。多模式认证差异本用例针对db_auth本地认证模式验证。若使用 LDAP/OIDC 等外部认证管理员判定逻辑由 src/common/security 下的安全上下文实现建议在对应认证模式下单独回归测试。总结2-23 Admin User Delete Images (DB Mode)这一用例看似只是一个 UI 手工测试实则覆盖了 Harbor 权限模型与删除链路的多个核心设计系统管理员跨项目删除权限、项目管理员角色策略、仓储-制品两级删除与深度级联清理、删除事件的发布。通过 src/server/v2.0/handler/repository.go、src/controller/repository/controller.go 与 src/controller/artifact/controller.go 的源码印证你可以清晰地回答管理员为什么能删、删了什么、删完客户端为何拉取失败并在此基础上规划自己的镜像清理与权限治理策略。【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表