Cosign签名是对OCI manifest的密码学绑定,非简单标签;Kubernetes 1.30+强制验证,签名失败或遗漏将直接拒绝Pod调度。CI/CD须在镜像稳定落库后立即签名,推荐用digest而非tag操作,私钥严禁暴露于CI环境,公钥须以PEM格式可信分发至集群,且验证需模拟kubelet行为并检查digest、signature URI及webhook超时。

Cosign 签名不是“给镜像加个标签”,而是对 OCI manifest 做密码学绑定——签名失败或遗漏,Kubernetes 1.30+ 集群会直接拒绝调度 Pod。
CI/CD 流水线中必须在 push 后立即签名,不能依赖本地镜像存在
很多人误以为 cosign sign 是对本地 Docker 镜像操作,实际它只读取远程仓库中该 tag 对应的 manifest digest,然后生成签名并推送到同一仓库的 .sig 路径。若 CI 流水线中先 docker push 再执行 cosign sign,但中间有人覆盖了同名 tag(比如重跑构建),就会导致签名对象和最终拉取的镜像 digest 不一致。
- 必须确保签名前镜像已稳定落库:用 digest 替代 tag 操作更安全,例如
cosign sign --key cosign.key ghcr.io/org/app@sha256:abc123... - 私钥绝不能出现在 CI runner 环境中:使用
--key-env从 KMS 或 HashiCorp Vault 注入加密密钥,或通过--key-file挂载只读 Secret 卷 - 签名命令需显式指定 registry 认证:确保
cosign能访问目标仓库,否则报错failed to get signature payload: GET ... unauthorized
公钥必须以可信方式分发到集群,不能靠手动 copy
Kubernetes 准入控制器(如 Kyverno、Connaisseur 或自建 webhook)校验时,用的是公钥内容本身,不是文件路径。把 cosign.pub 直接塞进 ConfigMap 或 Secret 是常见做法,但要注意:
- Secret 中的公钥必须是 PEM 格式纯文本,不含多余空行或注释;可用
cat cosign.pub | tr -d '\n'检查是否单行 - 若使用多个签名者(如 dev/test/prod 分离密钥),需在验证逻辑中支持多公钥匹配,Cosign 的
--key参数不支持数组,得靠 wrapper 脚本或 admission controller 实现 - 公钥轮转时,旧公钥不能立刻删除:TUF 风格的信任链要求新旧公钥共存至少一个元数据过期窗口(通常 24h),否则会出现
no valid signatures found且无法回退
签名后必须验证,且验证命令要模拟 kubelet 行为
本地 cosign verify --key cosign.pub 成功 ≠ 集群能过验。kubelet 实际调用的是 ImagePolicyWebhook,后者可能额外检查 issuer、subject、证书链或时间戳策略。
- 验证时加上
--certificate-oidc-issuer和--certificate-identity(如果用了 Sigstore Fulcio) - 用
cosign verify --key cosign.pub --insecure-ignore-tlog关闭透明日志校验,避免因 tlog 不可达导致误判 - 关键一步:在节点上手动运行
cosign verify,确认 ~/.docker/trust/ 下无残留旧密钥干扰(尤其当混合使用 Notary v1/v2 时)
Kubernetes 准入阶段失败日志里最常漏看的三个字段
当 Pod 卡在 ImagePullBackOff 且日志出现 failed to verify signature,别急着重签,先查这三项:
-
image digest:用docker pull确认实际拉取的是哪个 digest,签名是否针对它&& docker inspect --format='{{.RepoDigests}}' -
signature URI:Cosign 默认存于,若私有 registry 关闭了 blob 重定向,这个路径会 404/ : .sig -
webhook timeout:admission controller 若超时(默认 30s),kubelet 会静默失败,需查 apiserver 日志中admission-control字段的denied原因码
签名本身只是起点,真正卡住的永远是信任链的传递细节——比如公钥格式错一位、时间偏移两分钟、或 registry 的 OCI artifact 支持不完整。这些地方没有报错提示,只有日志里一行被截断的 URI。











