Cosign 镜像签名实战:防范容器供应链攻击

容器供应链攻击正成为云原生时代最隐蔽的安全风险:攻击者只需篡改镜像仓库或投毒基础镜像,就能让恶意代码随部署流入生产。本文用 Cosign(Sigstore 生态核心工具)给容器镜像签名,从本地构建、CI 自动签名到集群准入校验,全程保证镜像来源可信、未被篡改,构筑一道抵御供应链攻击的防线。

一、为什么需要镜像签名

传统部署流程默认“拉到什么就跑什么”。但镜像在构建、推送、拉取链路上有太多被动手脚的机会:基础镜像被投毒、私有仓库遭中间人替换、同一 tag 背后镜像内容悄悄变更。一旦恶意层进入生产,凭据泄露、后门植入往往难以追溯。

一条典型的投毒链路是:攻击者往热门基础镜像塞入后门 → 你的 CI 基于它构建出业务镜像 → 镜像被推送进共享仓库 → 集群拉取运行。由于全程没有来源校验,恶意层会一路畅通。镜像签名正是要在“构建完成”与“运行之前”这两个关口插入可信验证,让任何非可信来源的镜像在调度前就被拦下。

镜像签名要解决的正是“信任”问题:发布方用私钥对镜像 digest 签名,消费方在运行前用公钥验证——只有签名通过且来自可信身份,才允许部署。这把安全左移到交付环节,而不是等运行时告警。

二、Cosign 是什么

Cosign 是 Sigstore 项目的签名引擎,负责对 OCI 制品(镜像、blob、SBOM)进行签名与验证。它有两种模式:

  • Keyed 模式:用你自管的非对称密钥对(推荐小团队起步)。
  • Keyless 模式:无需长期私钥,借助 Fulcio(短期证书 CA)、Rekor(透明日志)与你的 OIDC 身份(GitHub/Google)完成短时签名,私钥用完即销毁。

Sigstore 不是单点工具,而是一套零信任签名基础设施:cosign 负责签名动作,Fulcio 是无状态短期 CA(用 OIDC 身份签发代码签名证书),Rekor 是防篡改的透明日志(记录每次签名证据,任何人可审计),三者共同消弭了“私钥长期持有”这一最大风险点。

三、安装与本地快速上手

先装好 Cosign CLI(macOS 用 Homebrew,Linux 直接拉二进制):

# macOS
brew install cosign

# Linux
curl -sSfL https://raw.githubusercontent.com/sigstore/cosign/main/release/release.sh \
  | sh -s -- v2.4.0
cosign version

Keyed 模式三步走:生成密钥对 → 对镜像签名 → 验证。注意签名对象是镜像 digest 而非可变的 tag。

# 1. 生成密钥对(会提示设置私钥口令)
cosign generate-key-pair

# 2. 对镜像签名(强烈建议锁定 digest)
cosign sign --key cosign.key \
  registry.example.com/app@sha256:abc123...

# 3. 验证(消费方只需 cosign.pub)
cosign verify --key cosign.pub \
  registry.example.com/app@sha256:abc123...

四、无密钥签名:Keyless 模式

不想长期保管私钥?Keyless 模式用你的 GitHub/Google 身份换取 Fulcio 签发的短期证书,并将签名写入 Rekor 公开透明日志,任何人都能审计。首次使用会弹出 OIDC 登录。

# Keyless 签名(自动走 OIDC 登录)
cosign sign registry.example.com/app@sha256:abc123...

# 验证时收紧身份,防止伪造签发者
cosign verify \
  --certificate-identity-regexp "https://github.com/your-org/.*" \
  --certificate-oidc-issuer "https://oauth2.sigstore.dev/auth" \
  registry.example.com/app@sha256:abc123...

除了镜像本身,cosign 还能对 SBOM、provenance(构建来源)等制品签名。例如用 cosign attest 把 SLSA 来源证明附加到镜像,让消费方不仅能验证“是谁签的”,还能验证“是怎么构建的”,进一步压缩伪造空间。

五、在 CI 中自动签名

手动签名容易遗漏,正确做法是在流水线里自动签名。下面是 GitHub Actions 片段,借助官方 cosign-action 在推送镜像后用 Keyless 模式签名并写入 Rekor。完整 CI/CD 编排思路可参考我们的《GitHub Actions 实战:自动化构建与部署流水线》(p=265)。

name: build-and-sign
on: [push]
jobs:
  release:
    runs-on: ubuntu-latest
    permissions:
      id-token: write   # 关键:授予 OIDC 令牌供 Keyless 签名
      contents: read
    steps:
      - uses: docker/build-push-action@v6
        with:
          push: true
          tags: registry.example.com/app:${"{ github.sha }"}
      - uses: sigstore/cosign-installer@v3
      - name: Sign image
        run: |
          cosign sign \
            registry.example.com/app@${"{ github.sha }"}

六、在集群侧强制校验

签名本身不等于安全,必须有人“卡口”。在 Kubernetes 侧,最省心的方案是用 Kyverno(策略即代码)配置镜像验签策略,未通过签名的镜像直接拒绝调度。下面是一条最小策略骨架:

# Kyverno ClusterPolicy(示意)
verifyImages:
  - image: "registry.example.com/app"
    key: |-
      -----BEGIN PUBLIC KEY-----
      MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD...
      -----END PUBLIC KEY-----
    # 或用 keyless 校验身份
    attestors:
      - entries:
          - keyless:
              url: https://fulcio.sigstore.dev
              identities:
                - issuer: https://oauth2.sigstore.dev/auth
                  subjectRegex: https://github.com/your-org/.*

七、与准入控制 / OPA 协同

若你的集群已用 OPA(Open Policy Agent)统一策略决策,也可以把镜像签名校验下沉到准入层,与鉴权、配额策略共用一套决策引擎,避免策略碎片化。无论用 Kyverno 还是 OPA,核心都是“默认拒绝 + 显式放行可信签名方”。

八、Keyed 与 Keyless 怎么选

维度Keyed(自管密钥)Keyless(Sigstore)
私钥管理需妥善保管 cosign.key无需长期私钥,用完即弃
身份来源你自己分发公钥OIDC(GitHub/Google 等)
可审计性依赖你自建流程Rekor 公开透明日志
适用规模小团队 / 离线环境中大型 / 多协作方

九、签名之外:SBOM 与漏洞扫描形成纵深

签名解决“来源可信”,但不解决“可信来源里有没有已知漏洞”。因此签名必须和 SBOM 生成、镜像漏洞扫描叠加,构成纵深防御。我们可以在 CI 里用 Trivy 先扫 CVE,再让 Cosign 签名,最后由准入策略同时校验“已签名 + 无高危漏洞”。三类手段各管一段:

手段解决的问题作用阶段
镜像签名(Cosign)来源可信、未被篡改构建后 / 运行前
SBOM 生成看清依赖清单与许可证构建时
漏洞扫描(Trivy)已知 CVE 风险构建后 / 准入前

十、常见误区与排查

  • 只在本地签名不验证:签名没有消费方校验形同虚设,务必在准入层强制。
  • 对 tag 签名:tag 可变,攻击者能替换内容保留签名,应锁定 @sha256:digest。
  • 公钥随意分发:公钥应通过可信渠道(配置管理 / Secret)注入,而非硬编码在公开仓库。
  • Keyless 忘开 OIDC 权限:CI 需授予 id-token: write,否则拿不到短期证书。
  • 密钥从不轮转:Keyed 模式下定期轮转 cosign 密钥,并保留旧公钥一段时间用于过渡验证,旧密钥泄露时可快速吊销。

十一、落地建议

从小处起步:先按《Docker 实战:容器化部署从零到生产》规范镜像构建,再用 Keyless + GitHub Actions 把签名嵌入流水线,最后用 Kyverno 在集群侧强制校验。三层叠加,容器供应链攻击面会被大幅压缩。签名不是银弹,但它是云原生安全里投入产出比极高的一道必选项。

上一篇 ClickHouse 实战:列式存储与海量数据分析
下一篇 Zed 编辑器实战:用 Rust 打造极速开发体验
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]