Harbor 镜像标签管理实战:全局标签与项目标签的添加、移除与角色权限验证
【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor
本文以 Harbor 仓库测试用例 11-03-add-labels-to-images-and-remove-labels-from-images.md 为核心骨架,系统讲解 Harbor 中如何为镜像(Artifact)添加/移除全局标签(Global Label)与项目标签(Project Label),并结合源码揭示标签的存储模型、API 实现与基于 RBAC 的角色权限校验,帮助你掌握标签功能的完整使用与验证方法。
一、用例背景:为什么要验证"给镜像打标签/摘标签"
Harbor 的标签(Label)功能允许用户用自定义的元数据对制品(Artifact,如镜像、Chart、CNAB 等)进行标记,从而实现分类、检索与团队协作。标签的管理动作包括创建标签、编辑标签、删除标签,以及本文主题——给镜像(含 Tag)添加标签、从镜像上移除标签。
用例 11-03 的核心验证目标是:
角色不低于项目开发人员(Project Developer)的用户,可以成功地为镜像添加/移除全局级标签和项目级标签;而项目访客(Project Guest)则不能执行添加标签操作。
这一用例同时覆盖了 Harbor 标签体系中两个维度的关键点:
- 标签的作用域:全局标签(
g,所有项目可见)与项目标签(p,仅限所属项目使用); - 操作的权限模型:在镜像上添加/移除标签所需的 RBAC 权限要求。
二、Harbor 标签体系核心概念
在进入操作步骤前,先理解 Harbor 标签数据模型。从源码 src/pkg/label/model/model.go 可以看到Label结构体包含以下核心字段:
| 字段 | 说明 | 约束 |
|---|---|---|
ID | 标签唯一 ID | 数据库自增主键 |
Name | 标签名称 | 必填,最大长度 128 |
Description | 标签描述 | 可空 |
Color | 标签颜色 | 界面展示用 |
Level | 标签层级,s(系统级)/u(用户级) | 由后端强制设置 |
Scope | 作用域,g(全局)/p(项目级) | 二者必居其一 |
ProjectID | 所属项目 ID | 仅当Scope=p时必填且需大于 0 |
Deleted | 软删除标记 | 删除标签时标记而非物理删除 |
源码中的常量定义见 src/common/const.go:
LabelLevelSystem = "s" // 系统级标签 LabelLevelUser = "u" // 用户级标签 LabelScopeGlobal = "g" // 全局标签 LabelScopeProject = "p" // 项目标签模型层通过Valid()方法做基础校验:名称不能为空且不超过 128 字符;Scope只能是g或p;当Scope为p时ProjectID必须大于 0(src/pkg/label/model/model.go)。
标签与镜像的关联关系记录在label_reference表(对应Reference模型),包含LabelID与ArtifactID两个外键(src/pkg/label/model/model.go),即一条记录代表"某个标签被添加到了某个制品上"。
三、测试环境准备
按照原用例要求,执行本验证前需要:
- 一个正在运行且可访问的 Harbor 实例(安装方式参见 make/install.sh 与 make/harbor.yml.tmpl);
- 至少一个全局级标签(Global Label)——由系统管理员在"系统管理 → 标签"中创建;
- 至少一个项目级标签(Project Label)——由系统管理员或项目管理员在项目内的"标签"页签中创建;
- 至少一个用于测试的镜像(含至少一个 Tag),并准备四个账号:项目开发人员、项目管理员、系统管理员、项目访客,均需加入同一目标项目。
四、分步操作:为镜像添加/移除标签
以下操作完整继承原用例的 8 个步骤,并补充界面操作细节与可验证要点。
Step 1:项目开发人员登录 UI
使用角色为Project Developer的账号登录 Harbor Web 界面,进入包含测试镜像的项目。
Step 2:为镜像添加全局标签
- 在项目下进入"镜像仓库",点击目标镜像的某个 Tag(或镜像本身);
- 打开"标签(Labels)"页签,点击"添加标签";
- 在标签选择列表中选中全局标签,确认添加。
预期:操作成功,全局标签出现在该镜像的标签列表中。
Step 3:为镜像添加项目标签
同样方式,在"添加标签"弹窗中选中项目标签(属于当前项目的标签),确认添加。
预期:操作成功,项目标签出现在该镜像的标签列表中。
Step 4:移除镜像上的全局标签
在镜像的"标签"列表中,对已添加的全局标签点击删除/移除按钮并确认。
预期:全局标签从该镜像上被移除。
Step 5:移除镜像上的项目标签
对已添加的项目标签执行同样的移除操作。
预期:项目标签从该镜像上被移除。
Step 6:项目管理员重复 Step 2–5
切换到Project Admin账号,重复添加/移除全局标签与项目标签的完整流程。
预期:项目管理员可以执行与项目开发人员完全相同的操作,全部成功。
Step 7:系统管理员重复 Step 2–5
切换到System Admin账号,重复相同流程。
预期:系统管理员同样可以成功完成全部操作。
Step 8:项目访客尝试添加标签
切换到Project Guest账号,进入镜像详情页尝试添加标签。
预期:操作被拒绝——访客无法为镜像添加标签(UI 中标签添加入口不可用,或通过 API 调用时返回 403 Forbidden)。
五、权限模型:为什么 Developer 可以而 Guest 不行
用例背后的权限判定来自 Harbor 的 RBAC 策略,相关定义集中在 src/common/rbac/project/rbac_role.go。
在镜像(Artifact)上添加/移除标签,对应的是artifact-label子资源上的create与delete动作。从源码可见:
- 项目开发人员(Project Developer)的策略中包含:
{Resource: rbac.ResourceArtifactLabel, Action: rbac.ActionCreate}, {Resource: rbac.ResourceArtifactLabel, Action: rbac.ActionDelete},因此拥有添加与移除镜像标签的权限;
- 项目管理员(Project Admin)继承开发者全部权限,同样具备这两项动作;
- 系统管理员(System Admin)拥有系统级最高权限,天然可以执行全部标签操作;
- 项目访客(Project Guest)的策略中仅包含对
label资源的read/list动作(src/common/rbac/project/rbac_role.go),不包含artifact-label的create/delete,因此无法为镜像添加或移除标签。
此外,src/common/rbac/project/rbac_util.go 中定义了公共项目(Public Project)的访客级策略,也只授予label资源的只读能力,进一步印证"访客不可写标签"的结论。
结论:原用例"角色 >= Project Developer 才能添加/移除镜像标签"的判定与 RBAC 源码策略完全一致,可直接依据 src/common/rbac/project/rbac_role.go 验证。
六、底层实现:标签添加/移除的 API 调用链
了解界面操作背后对应的服务端实现,有助于排查权限与数据问题。
1. 镜像标签操作的 Handler
标签与制品关联的 API 实现在 src/server/v2.0/handler/artifact.go:
AddLabel(添加标签到制品):
- 通过
RequireProjectAccess(ctx, projectID, rbac.ActionCreate, rbac.ResourceArtifactLabel)校验调用者在当前项目上是否拥有artifact-label的创建权限; - 调用
RequireLabelInProject(ctx, projectID, params.Label.ID)校验标签归属——项目级标签只能用于其所属项目; - 通过
artCtl.AddLabel(ctx, art.ID, params.Label.ID)写入关联。
- 通过
RemoveLabel(从制品移除标签):
- 通过
RequireProjectAccess(ctx, params.ProjectName, rbac.ActionDelete, rbac.ResourceArtifactLabel)校验删除权限; - 调用
artCtl.RemoveLabel(ctx, art.ID, params.LabelID)删除关联记录。
- 通过
其中RequireLabelInProject的实现(src/server/v2.0/handler/artifact.go)逻辑为:取出标签后,若标签Scope为项目级且ProjectID与当前项目不符,则返回 404——这就是"项目标签不能跨项目使用"的强制约束。
2. 标签管理 API 与权限分支
标签自身的 CRUD 实现在 src/server/v2.0/handler/label.go:
CreateLabel创建标签时,后端强制将Level设置为用户级(u),且若Scope为全局则把ProjectID置 0(src/server/v2.0/handler/label.go);requireAccess方法按作用域分流权限(src/server/v2.0/handler/label.go):- 全局标签→ 要求系统级权限
RequireSystemAccess(只有系统管理员能管理全局标签); - 项目标签→ 要求项目级权限
RequireProjectAccess(项目管理员及以上可管理);
- 全局标签→ 要求系统级权限
DeleteLabel删除标签前会先调用RemoveFromAllArtifacts清理所有镜像上的关联,再执行删除(src/server/v2.0/handler/label.go)。
3. 数据层 Manager
标签与关联关系的数据操作统一由 src/pkg/label/manager.go 提供,核心方法包括:
| 方法 | 作用 |
|---|---|
Create/Get/Update/Delete | 标签本身 CRUD |
ListByArtifact(artifactID) | 查询某制品已添加的全部标签 |
AddTo(labelID, artifactID) | 创建label_reference关联记录 |
RemoveFrom(labelID, artifactID) | 删除指定关联,不存在则返回 NotFound |
RemoveAllFrom(artifactID) | 清理某制品的全部标签 |
RemoveFromAllArtifacts(labelID) | 将某标签从所有制品上移除 |
其中AddTo的实现(src/pkg/label/manager.go)直接向label_reference表插入(LabelID, ArtifactID)记录,而RemoveFrom(src/pkg/label/manager.go)按两个 ID 组合条件删除记录,删除数为 0 时抛出 NotFound 错误。
七、用 API 与测试库复现验证
除 UI 外,标签操作也可通过 REST API 或仓库中的 Python 测试库复现。相关接口定义在 api/v2.0/swagger.yaml(搜索artifact下的addLabel/removeLabel,以及/labels路径)。
仓库中现成的自动化测试 tests/apitests/python/test_add_sys_label_to_tag.py 完整演示了"系统管理员创建全局标签 → 项目管理员将其添加到镜像 Tag"的链路:
- 创建用户并新建私有项目;
- 将用户以project-admin角色加入项目;
- 推送测试镜像
test_sys_label:latest; - 以管理员身份通过
create_label创建全局标签(默认scope="g",见 tests/apitests/python/library/label.py); - 以项目管理员身份调用
add_label_to_reference将全局标签添加到镜像 Tag 上。
对应的 Python 测试库方法位于 tests/apitests/python/library/label.py(标签创建/删除)与library/artifact.py(add_label_to_reference)。若要编写等价脚本,关键请求如下:
# 添加标签到镜像(v2.0 API) POST /api/v2.0/projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/labels Body: { "id": <label_id> } # 从镜像移除标签 DELETE /api/v2.0/projects/{project_name}/repositories/{repository_name}/artifacts/{reference}/labels/{label_id}注意:以 Project Guest 身份调用上述添加标签接口时,会因 RBAC 校验失败返回403,与用例 Step 8 的预期结果一致。
八、预期结果汇总与常见问题
用例预期结果一览
| 步骤 | 操作者 | 操作 | 预期结果 |
|---|---|---|---|
| Step 2 | Project Developer | 添加全局标签 | 成功 |
| Step 3 | Project Developer | 添加项目标签 | 成功 |
| Step 4 | Project Developer | 移除全局标签 | 成功 |
| Step 5 | Project Developer | 移除项目标签 | 成功 |
| Step 6 | Project Admin | 重复 Step 2–5 | 全部成功 |
| Step 7 | System Admin | 重复 Step 2–5 | 全部成功 |
| Step 8 | Project Guest | 添加标签 | 失败(无权限) |
常见问题排查
- 无法选择某个全局标签:全局标签只能由系统管理员在系统级"标签"中创建,普通开发者只能使用;若标签列表为空,请先以管理员身份创建全局标签。
- 项目标签在其他项目不可见/不可用:项目标签绑定
ProjectID,RequireLabelInProject会拒绝跨项目使用,属预期行为。 - 删除标签后镜像上标签消失:
DeleteLabel会先执行RemoveFromAllArtifacts清理全部关联,删除标签前请确认该标签不再被需要。 - 访客能看到标签但无法操作:Guest 仅有
label资源的只读/列表权限,无artifact-label的写权限,界面与 API 均会拒绝。
九、总结
通过用例 11-03 的完整演练,可以确认 Harbor 在镜像标签管理上的两个设计要点:
- 功能上:全局标签与项目标签均支持对镜像的添加与移除,操作面向镜像的 Tag(Artifact)维度;
- 权限上:镜像标签的写入权限授予项目开发人员及以上角色(Developer、Project Admin、System Admin),访客只读不可写,权限策略由 src/common/rbac/project/rbac_role.go 统一定义,并在 src/server/v2.0/handler/artifact.go 的
AddLabel/RemoveLabel中强制执行。
掌握这套操作与权限模型,即可在生产环境中安全、规范地利用标签对镜像资产进行分类管理。
【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考