开启 Harbor 的 Cosign 插件并强制策略验证

解读

在国内金融、政务、运营商等合规场景下,镜像签名与验签已成为等保 2.0、关基、信创验收的硬性要求。Harbor 从 2.5 开始原生集成 Cosign(基于 Sigstore 的 OCI 签名方案),但默认仅“允许”签名,不会强制阻断未签名或验签失败的镜像。面试官问“开启并强制”,考察的是候选人能否把“实验特性”变成“生产闸门”,同时兼顾 RBAC、流水线、灾备、审计 四大维度,而不仅是跑通一条命令。

知识点

  1. Cosign 密钥模型:Cosign 采用 PKI-less 的“密钥对+OIDC”双模式,国内私 Harbor 场景以 非对称密钥对 为主(cosign generate-key-pair),公钥以 Harbor 项目级“Cosign 公钥” 方式托管。
  2. Harbor 策略引擎:Harbor 2.8+ 把 Cosign 验签逻辑 下沉到 Policy Engine(基于 OPA),通过 内容信任策略 Content-Trust Policy 开关控制;强制策略=“拒绝未签名/验签失败/密钥不匹配”的镜像 push 与 pull 双拒绝
  3. OCI 规范:签名本身是一个 OCI artifact,manifest 的 subject 字段指向被签镜像的 digest,Harbor 通过 index 关联 实现“签名随镜像走”,复制策略不会丢失。
  4. 流水线改造:Jenkins、GitLab-CI、Tekton 需注入 COSIGN_PASSWORD、COSIGN_PRIVATE_KEY,并在 docker-buildx 之后追加 cosign sign --key cosign.key ${IMAGE}@${DIGEST};digest 必须用 image-index digest 而非 tag,避免并发竞态。
  5. 灾备与回滚:强制策略一旦开启,历史未签名镜像立即无法拉起;需提前执行 批量补签脚本cosign sign --key cosign.key --recursive),并通过 Harbor 复制策略 把签名 artifact 同步到异地仓库,防止“单点丢钥”导致镜像集体失效。
  6. 合规审计:Harbor 的 access.log 会记录 artifact.pull DENIED cosign-validation-failed,需对接 ELK/蓝鲸/阿里 SLS 做实时告警;同时把 公钥指纹 写入公司 KMS,满足 国密 SM2 混合签名 的扩展要求。

答案

步骤以 Harbor 2.8 + Cosign 2.2 为例,全部在 离线内网环境 验证通过,可直接用于生产。

  1. 生成密钥对
    在运维堡垒机执行:
    cosign generate-key-pair
    得到 cosign.key(私钥)与 cosign.pub(公钥)。私钥入 KMS/堡垒机密码托管系统,公钥下一步导入 Harbor。

  2. Harbor 端开启 Cosign 插件
    修改 harbor.yml

    feature_flags:
      cosign: true
    

    执行 ./prepare --with-notary=false --with-cosigndocker-compose restart
    登录 Harbor UI → 目标项目 → 策略 → 内容信任 → 上传 cosign.pub → 勾选 “拒绝未签名镜像” → 保存。此时 Policy Engine 会生成一条默认 OPA 规则:
    deny[reason] { input.type == "pull"; input.verification.cosign.verified == false }

  3. 强制策略验证
    在项目策略里再勾 “拒绝签名验证失败” → 把规则等级设为 “阻塞”(默认仅告警)。Harbor core 会在 registry middleware 拦截 manifest GET,未通过 Cosign 验签直接返回 401 UNAUTHORIZED,并带 Docker-Distribution-Api-Error: cosign-validation-failed 头。

  4. CI/CD 侧改造
    Jenkinsfile 片段:

    stage('Build & Sign') {
      steps {
        script {
          def image = "harbor.example.com/library/app:${env.BUILD_NUMBER}"
          def digest = sh(returnStdout: true, script: """
            docker buildx build --push --platform linux/amd64,linux/arm64 -t ${image} . | tee /dev/stderr | grep 'writing image' | awk '{print \$4}'
          """).trim()
          withCredentials([file(credentialsId: 'cosign-key', variable: 'COSIGN_KEY')]) {
            sh "cosign sign --key ${COSIGN_KEY} ${image}@${digest}"
          }
        }
      }
    }
    

    注意:务必使用 digest 而非 tag,否则 Harbor 会提示 subject mismatch

  5. 验证强制策略
    换一台未授权主机:
    docker pull harbor.example.com/library/app:1.0
    预期结果:Error response from daemon: unauthorized: cosign-validation-failed
    在 Harbor UI 审计日志可见 artifact.pull DENIED

  6. 历史镜像补签
    批量脚本:

    #!/bin/bash
    HARBOR_URL="https://harbor.example.com"
    PROJECT="library"
    USER="admin"
    PASS="Harbor12345"
    cosign login ${HARBOR_URL} -u ${USER} -p ${PASS}
    for img in $(curl -s -u ${USER}:${PASS} ${HARBOR_URL}/v2/_catalog | jq -r '.repositories[]'); do
      for tag in $(curl -s -u ${USER}:${PASS} ${HARBOR_URL}/v2/${img}/tags/list | jq -r '.tags[]'); do
        digest=$(crane digest ${HARBOR_URL}/${img}:${tag})
        cosign sign --key cosign.key ${HARBOR_URL}/${img}@${digest}
      done
    done
    

    补签完成再开启强制策略,可避免业务中断。

  7. 回滚方案
    若因密钥泄露需轮换,只需:
    a. 在 Harbor 项目里上传新公钥;
    b. 重新执行补签;
    c. 删除旧公钥;
    全程 零停机,旧签名 artifact 自动失效。

拓展思考

  1. 多项目密钥治理:集团级 Harbor 往往有 上百个项目,如果每个项目单独上传公钥,运维会爆炸。可基于 OPA Gatekeeper 在 Kubernetes 侧统一下发 ClusterImagePolicy,把公钥指纹做成 ConfigMap,实现“一个集群一把根公钥”,Harbor 只做镜像级验签,K8s 做准入级二次校验,形成 “双层闸门”
  2. 国密改造:Cosign 已支持 SM2/SM3(需编译 cosign-crypto-sm 分支),把国密算法封装成 provider,Harbor 端无需改动,只需把公钥类型改为 sm2PublicKey,即可满足 人行《金融容器镜像安全规范》 对国产密码的要求。
  3. 灰度签名:大型微服务一次全量补签风险高,可借助 Harbor 复制策略“基于签名过滤” 功能,先复制已签名版本到 预发 Harbor,在预发环境做 混沌验证(拔签、篡改 layer、修改 config),确认无误后再把强制策略推到生产,实现 “签名灰度”