容器镜像在分发过程中面临被篡改、投毒的风险,Cosign提供了一套开源的签名与验证机制,让开发者和平台方能够确认镜像来源与内容完整性。它通过与OCI仓库的标准交互,将签名作为独立的制品附加在镜像索引上,而不改变镜像本身的层数据。理解这套机制,是构建可信软件供应链的基础环节。

Cosign的核心工作原理与密钥模型
Cosign本质上是一个符合Sigstore规范的命令行工具,它利用非对称加密算法对镜像的摘要(digest)进行签名,而不是对庞大的镜像层逐字节加密。每一个容器镜像在注册表中都有唯一的sha256摘要,Cosign使用私钥对该摘要生成数字签名,并将签名推送到同仓库的附属标签(如sha256-xxx.sig)。验证时,只需用公钥核对摘要与签名是否匹配,即可判断镜像自签名后是否被改动。
在密钥管理上,Cosign支持多种模式。最基础的是生成本地ECDSA或RSA密钥对,以PEM文件保存在磁盘;进阶用法可对接云厂商的KMS(密钥管理服务),例如AWS KMS、GCP KMS,私钥永不导出;还可使用硬件令牌或Sigstore的Fulcio证书颁发加Rekor透明日志实现短期证书签名。对于大多数内部平台,本地密钥对加定期轮换已经能满足基本可信需求,而跨组织分发则更适合引入KMS或透明日志。
需要特别区分的是,Cosign签名保护的是镜像的“内容身份”,并不对镜像做加密处理。也就是说,别人依然可以拉取你的镜像层,但无法在不知晓私钥的情况下生成有效签名。当验证策略设为必须存在有效签名时,任何中间人替换层内容都会导致摘要变化,从而验证失败。这种轻量设计让Cosign能无缝接入现有CI流水线和Kubernetes准入控制。
从零开始生成密钥并签名镜像
使用Cosign的第一步是安装二进制并生成密钥对。在Linux或macOS上,可通过官方脚本或包管理器获取cosign命令,随后执行生成命令,工具会提示设置密码以保护私钥文件。公钥可自由分发给验证方,私钥必须严格保密并备份。以下示例展示密钥生成与对本地构建镜像的签名过程:
# 生成ECDSA密钥对,私钥加密保存为cosign.key,公钥为cosign.pub cosign generate-key-pair # 登录镜像仓库(以ipipp.com镜像服务为例) docker login registry.ipipp.com # 对镜像打标签并推送 docker tag myapp:latest registry.ipipp.com/team/myapp:v1 docker push registry.ipipp.com/team/myapp:v1 # 使用私钥签名,会推送到同仓库的签名附件 cosign sign --key cosign.key registry.ipipp.com/team/myapp:v1
签名完成后,在注册表中会出现一个名为sha256-<digest>.sig的附属制品。此时若其他人拉取该镜像,可用对应公钥执行验证。如果镜像被恶意修改并重推,摘要改变,旧签名即刻失效。在CI环境中,通常把私钥作为Secret注入,签名步骤放在构建与推送之后,保证出厂即带签名。
对于使用KMS的团队,签名命令可改为cosign sign --key awskms:///arn:aws:kms:...形式,这样私钥驻留云端,本地仅持调用权限,降低泄露风险。此外,Cosign也支持在签名时附加证书链或透明日志条目,便于后续审计。无论哪种方式,验证端只需拥有对应公钥或证书根,就能独立确认,无需实时访问签名者系统。
在Kubernetes中落地自动验证策略
仅有签名动作还不够,必须在消费侧强制验证才能真正起到防护作用。在Kubernetes集群中,常用Connaisseur或Kyverno等策略引擎来拦截未签名镜像。以Kyverno为例,通过编写ClusterPolicy,要求所有来自特定仓库的镜像必须经过Cosign验证且密钥匹配,否则APIServer拒绝创建Pod。这样开发人员在本地即便误用未签名镜像,也无法进入集群。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: check-cosign-signature
spec:
validationFailureAction: enforce
rules:
- name: verify-image
match:
resources:
kinds:
- Pod
verifyImages:
- image: "registry.ipipp.com/team/*"
key: |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEexamplepubkeydata
-----END PUBLIC KEY-----
上述配置中,verifyImages字段指定了镜像匹配规则和内联公钥。当Pod引用不符合签名的镜像时,准入控制器返回拒绝信息,部署直接失败。相比人工审查,这种自动化门禁大幅降低运维负担。在离线环境中,可将公钥预置到策略里;在多租户平台,则可为不同团队配置不同公钥,实现分权管理。
除了集群内验证,CI/CD的出口处也应加入验证步骤,防止将错误镜像流入下个阶段。例如在GitLab Runner中,部署前先运行cosign verify --key cosign.pub 镜像地址,命令非零退出即阻断流水线。结合镜像扫描工具,可形成“漏洞扫描+签名验证”双保险。长远来看,把Cosign纳入企业镜像仓库的强制策略,是供应链安全成熟度的关键标志。
常见问题与签名轮换实践
实际使用中,团队常遇到私钥泄露或人员离职带来的密钥轮换难题。Cosign本身不提供中心化吊销列表,因此推荐做法是:定期生成新密钥对,用新私钥重新签名所有活跃镜像,并将新公钥同步到验证策略中,旧公钥保留一段时间以兼容存量签名。过渡期结束后移除旧公钥,完成软轮换。若怀疑私钥泄露,则立即用新密钥重签并缩短过渡期。
另一个误区是认为签名一次永久有效。实际上,当基础镜像升级或补丁重打后,摘要变化,原签名失效,必须重新签名。因此在Dockerfile变更触发构建时,流水线要自动包含签名环节,而不是手动补充。对于多架构镜像(manifest list),Cosign会分别对各个架构子镜像及整体索引签名,验证时也会逐级检查,确保全平台一致。
最后要注意注册表兼容性。主流如Harbor、Docker Hub、ECR均支持Cosign的附属制品存储,但部分老旧私有仓库可能不支持OCIArtifact类型,导致签名无法推送。此时可改用--attachment-tag-prefix参数调整签名标签格式,或升级仓库版本。理清这些边界情况,才能让Cosign在复杂环境中稳定运行。