Harbor 内容信任Content Trust实战指南如何在启用签名策略的 Docker 私有仓库中拉取未签名镜像【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harborHarbor 提供基于 Notary 的内容信任机制允许项目级强制要求镜像必须经过签名才能被拉取。本文以 Harbor 仓库的 9-04 测试用例tests/testcases/Group9-Content-trust/9-04-DB-user-pull-unsigned-images.md为线索完整讲解在本地数据库认证模式DB mode下普通用户尝试拉取未签名镜像的全过程从环境准备、Docker 客户端DOCKER_CONTENT_TRUST变量控制到 Harbor 服务端底层如何通过中间件拦截未签名镜像的拉取请求并结合源码与测试用例给出可验证的结论。1. 测试用例背景9-04 在做什么在 Harbor 的测试用例体系中tests/testcases/Group9-Content-trust/目录下按认证模式 × 用户角色 × 操作类型组织内容信任相关测试其中9-04-DB-user-pull-unsigned-images.md聚焦于一个典型的安全验证场景认证模式Harbor 使用内置本地数据库DB mode认证用户数据存储在 Harbor 自身的数据库中用户角色普通用户非系统管理员操作类型拉取pull一个未签名unsigned的镜像核心断言当 Docker 客户端启用了内容信任DOCKER_CONTENT_TRUST1时用户不能拉取未签名的镜像。与其配套的用例形成一个完整的验证矩阵同目录下其他用例用例场景预期结果9-01-DB-user-push-signed-images.md用户推送已签名镜像推送成功UI 显示绿色对勾9-02-DB-user-push-unsigned-images.md用户推送未签名镜像推送成功UI 签名列显示红色叉号9-03-DB-user-pull-signed-images.md用户拉取已签名镜像拉取成功9-04-DB-user-pull-unsigned-images.md用户拉取未签名镜像拉取被拒绝9-30-Project-level-content-trust.md项目级内容信任策略开启策略后仅已签名镜像可拉取9-04 是其中反向验证的一环确认策略确实生效未签名镜像在启用内容信任的客户端上无法被拉取。2. 环境准备与前置条件根据测试用例的 Environment 章节复现该场景需要满足以下条件一个运行中的 Harbor 实例make/目录下的 install.sh 与 harbor.yml.tmpl 提供了标准安装配置Harbor 需配置为使用本地数据库认证默认配置即 DB mode用户数据存放在本地数据库中。一台安装 Docker CLI 的 Linux 主机用作 Docker 客户端用于执行docker login、docker push、docker pull。网络可达客户端能够访问 Harbor 的 HTTPS 入口默认 443 端口以及 Notary 签名服务默认 4443 端口。关于证书若 Harbor 使用自签名证书未通过正式 CA 签发9-01 与 9-03 用例的 NOTE 明确指出需要将 CA 根证书复制到/etc/docker/certs.d/harbor_ip/Docker 守护进程信任目录和$HOME/.docker/tls/harbor_ip:4443/Docker 客户端访问 Notary 服务所需的 TLS 目录否则 Docker 与 Notary 均会因证书校验失败而无法工作。相关证书生成与信任机制可参考 docs/signature-verification.md。3. 核心机制DOCKER_CONTENT_TRUST 环境变量内容信任的开关完全由 Docker 客户端的两个环境变量控制这也是 9-04 测试步骤 2、4 的操作对象环境变量作用9-04 中的取值DOCKER_CONTENT_TRUST是否启用内容信任签名/校验步骤 2 中unset关闭步骤 4 中设为1开启DOCKER_CONTENT_TRUST_SERVER指定 Notary 签名服务地址指向 Harbor 的harbor_ip:44439-01/9-03 中显式设置关键理解DOCKER_CONTENT_TRUST是客户端行为开关。当其为1时docker pull会要求镜像必须带由 Notary 签发的信任数据Trust Data才能拉取当其为空/未设置时Docker 客户端不执行任何签名校验可以正常拉取任何镜像。9-04 的精妙之处在于验证客户端与服务器端的双重校验客户端校验步骤 4 将DOCKER_CONTENT_TRUST重置为1后docker pull在本地便会因缺少签名信任数据而拒绝拉取这是 Docker 客户端自身的安全行为服务端校验即便客户端绕过校验例如直接使用docker pull之外的 HTTP 请求拉取 manifestHarbor 服务端也会通过内容信任中间件进行二次拦截详见第 5 节。因此该用例的预期结果是用户不能拉取未签名镜像这条链路在客户端与服务端两个层面同时生效。4. 分步执行复现拉取未签名镜像被拒绝将 9-04 的测试步骤翻译为可直接执行的完整流程harbor_ip替换为你的 Harbor 地址或 FQDN# 步骤 1在 Harbor Web UI 登录创建一个项目记为 project-a # 步骤 2在 Docker 客户端关闭内容信任登录 Harbor unset DOCKER_CONTENT_TRUST docker login harbor_ip # 输入用户名/密码DB mode 下的本地用户 # 步骤 3向 project-a 推送一个未签名镜像 docker pull busybox docker tag busybox harbor_ip/project-a/busybox:unsigned docker push harbor_ip/project-a/busybox:unsigned # 步骤 4重新开启内容信任 export DOCKER_CONTENT_TRUST1 export DOCKER_CONTENT_TRUST_SERVERhttps://harbor_ip:4443 # 步骤 5尝试拉取刚才推送的未签名镜像 docker pull harbor_ip/project-a/busybox:unsigned # 预期拉取失败Docker 提示镜像缺少信任数据预期结果Expected Outcomedocker pull失败用户无法拉取未签名镜像。若在 UI 上查看 project-a 的镜像列表可以看到该镜像在签名Signed列下显示红色叉号——这是 9-02 用例验证的 UI 表现即 Harbor 通过镜像的签名附属物signature accessory状态区分签名/未签名镜像。5. 服务端原理Harbor 的内容信任中间件9-04 验证的用户不能拉取未签名镜像并非仅仅依赖 Docker 客户端Harbor 服务端在分发distribution层也实现了强制校验。核心实现在 src/server/middleware/contenttrust/contentrust.go注意文件名拼写即 contenttrust 中间件// ContentTrust handle docker pull content trust check func ContentTrust() func(http.Handler) http.Handler { return middleware.BeforeRequest(func(r *http.Request) error { ctx : r.Context() af : lib.GetArtifactInfo(ctx) // ... pro, err : project.Ctl.GetByName(ctx, af.ProjectName) // ... // If signature policy enabled, it has to at least have one signature. if pro.ContentTrustCosignEnabled() { if err : signatureChecking(ctx, r, af, pro.ProjectID, model.TypeCosignSignature); err ! nil { // ... } } if pro.ContentTrustEnabled() { if err : signatureChecking(ctx, r, af, pro.ProjectID, model.TypeNotationSignature); err ! nil { // ... } } return nil }) }该中间件的工作流程可以概括为解析请求上下文从请求中取出ArtifactInfo包含项目名、仓库、引用/标签、摘要若缺失则返回NotFound错误查询项目策略通过project.Ctl.GetByName获取项目并检查项目的元数据策略执行签名校验若项目启用了内容信任Notation 签名或 Cosign 签名策略则调用signatureChecking遍历镜像的附属物Accessories判断是否存在对应类型的签名。若没有任何签名返回PROJECTPOLICYVIOLATION项目策略违规错误——对应 9-04 中未签名镜像拉取被拒绝的服务端行为。6. 项目级内容信任策略服务端拦截的开关服务端拦截是否生效取决于项目的元数据配置。在 src/pkg/project/models/pro_meta.go 中定义了相关元数据键ProMetaEnableContentTrust enable_content_trust ProMetaEnableContentTrustCosign enable_content_trust_cosign而 src/pkg/project/models/project.go 中的ContentTrustEnabled()与ContentTrustCosignEnabled()则从项目元数据中读取这些键并将字符串值如true解析为布尔值func (p *Project) ContentTrustEnabled() bool { enabled, exist : p.GetMetadata(ProMetaEnableContentTrust) if !exist { return false } return isTrue(enabled) }也就是说Harbor 服务端对拉取请求的签名强制检查只有在项目开启了内容信任策略时才触发。这一开关对应测试用例 9-30-Project-level-content-trust.md 的验证场景先在未开启策略时推送一个未签名镜像再开启DOCKER_CONTENT_TRUST1推送一个已签名镜像在项目配置页启用项目级内容信任此时未签名镜像无法拉取而已签名镜像可以正常拉取。9-04 与 9-30 的区别在于9-04 通过客户端DOCKER_CONTENT_TRUST1触发 Docker 客户端自身的校验9-30 则验证服务端项目策略的强制拦截。两者结合构成客户端 服务端的双层防护。7. 例外场景谁可以绕过服务端校验服务端的签名校验并非对所有请求一刀切。src/server/middleware/util/util.go 中的SkipPolicyChecking定义了可以跳过策略检查的合法场景// 1, scanner pull access can bypass. // 2, cosign/notation pull can bypass, it needs to pull the manifest before pushing the signature. // 3, pull cosign/notation signature can bypass. if ok secCtx.Name() v2token { if secCtx.Can(ctx, rbac.ActionScannerPull, ...) || (secCtx.Can(ctx, rbac.ActionPush, ...) strings.Contains(r.UserAgent(), cosign)) || (secCtx.Can(ctx, rbac.ActionPush, ...) strings.Contains(r.UserAgent(), notation)) { return true, nil } }这些例外包括Scanner 拉取漏洞扫描器如 Trivy拉取镜像清单做扫描时不应被签名策略拦截Cosign/Notation 签名工具回拉签名工具在推送签名前需要先拉取目标镜像的 manifest属于签名流程的必要环节拉取签名附属物本身当请求拉取的就是签名signature accessory对象时直接放行。在 contentrust_test.go 的测试中可以找到这些行为的直接验证TestScannerPulling扫描器拉取放行返回http.StatusOK、TestCosignPullingcosign 拉取放行、TestAuthenticatedUserPulling普通认证用户拉取无签名镜像返回http.StatusPreconditionFailed即 412。而TestContentTrustDisabled则验证了项目未开启策略时放行http.StatusOK。8. 排查思路拉取失败时的定位方向当复现 9-04 时出现与预期不符的现象例如未签名镜像居然拉取成功可以从以下方向排查确认项目策略已开启检查项目的内容信任元数据enable_content_trust是否为true。只有项目级策略开启服务端中间件才会执行signatureChecking参见 src/pkg/project/models/project.go 的ContentTrustEnabled确认请求链路经过中间件内容信任中间件依赖前置的 artifact 信息中间件注入ArtifactInfo若请求未经过该链路中间件会直接返回NotFound而非策略拒绝确认客户端环境变量DOCKER_CONTENT_TRUST是否确实为1DOCKER_CONTENT_TRUST_SERVER是否指向正确的:4443地址确认是否为合法例外若请求来自扫描器或 cosign/notation 客户端SkipPolicyChecking会放行这是设计行为而非漏洞查看日志中间件对跳过检查的请求会记录skip the checking of pulling artifact的 Debug 日志可用于确认是否命中例外分支。9. 小结9-04 用例通过一个简单的关闭信任推送 → 开启信任拉取的操作序列验证了 Harbor 内容信任体系中的关键安全边界启用内容信任后未签名镜像无法被拉取。其背后的实现由 Docker 客户端的DOCKER_CONTENT_TRUST校验与 Harbor 服务端的 contenttrust 中间件src/server/middleware/contenttrust/contentrust.go共同保障后者以项目元数据enable_content_trust/enable_content_trust_cosign为开关以镜像签名附属物为判定依据并针对扫描器与签名工具保留了必要的例外路径。理解这条链路有助于在生产环境中正确配置镜像签名策略确保供应链中运行的镜像都经过可信签名验证。【免费下载链接】harborAn open source trusted cloud native registry project that stores, signs, and scans content.项目地址: https://gitcode.com/GitHub_Trending/ha/harbor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考