镜像签名与SBOM通过Sigstore与Cosign实现容器镜像的来源验证与供应链安全。本文介绍镜像签名流程、SBOM生成方法与策略准入验证。适用于在云原生CI/CD中建立软件供应链安全保障机制。通过系统性学习,读者将掌握云原生架构设计、资源管理与运维实践方法,能够将其应用于云平台建设与优化。本文适合云架构师、DevOps工程师与技术管理者参考,助力构建高效稳定的云基础设施。

学习提示

需要学习镜像签名与SBOM(Sigstore/Cosign),架构设计需要现代软件架构方法论。本篇涵盖微服务、Service Mesh、Serverless、可观测性,帮助你设计现代软件架构。

目录
  1. 可视化演示
  2. AI 应用场景
  3. Python 演示
  4. 常见误区
  5. 面试题

可视化演示

状态—

镜像签名与SBOM通过Sigstore与Cosign实现容器镜像的来源验证与供应链安全。本文介绍镜像签名流程、SBOM生成方法与策略准入验证。适用于在云原生CI/CD中建立软件供应链安全保障机制。通过系统性学习,读者将掌...

供应链安全威胁

你收到一双限量球鞋,打开后却发现是莆田货。从品牌工厂到你手上,包裹要经过干线运输、中转仓、快递网点,任何一个环节都能偷梁换柱。现实世界的对策很简单:封条加验货。但在软件世界,这个"包裹"的可怕之处在于——里面装的是几万个零件拼出的操作系统:基础镜像、C库、Python包、NVIDIA驱动,任何一个零件被换成恶意版本,整个应用都会跟着中毒。

供应链攻击早已不是科幻。2024年著名的 XZ Utils 后门事件,攻击者潜伏三年,在压缩库的源码里植入后门,险些波及全球所有 Linux 服务器。容器镜像作为软件交付的主要载体,自然成为重点目标:往公共镜像仓库上传带后门的镜像,诱导工程师 pull 下来跑训练;或者攻破某个小型开源项目,把挖矿程序混进依赖包,再被 CI 流水线自动吸收。这就是容器供应链安全必须回答的三个核心问题:

1. 来源可信:这个镜像到底是谁构建的?是官方组织还是冒牌货?

2. 内容完整:镜像在传输和存储过程中有没有被篡改?

3. 依赖透明:镜像里每一个组件是什么版本、来自哪里、有没有已知漏洞?

这"安全三问"正对应本文的三板斧:镜像签名、SBOM 生成、策略准入验证。三者缺一不可——只做访问控制而不做完整性校验,就像相信门锁而不管窗户有没有关。所以,验包裹这件事不能靠"看运气",要在快递链路里设立三道关卡:封条(签名)、清单(SBOM)、门卫(准入)。好消息是,这三道关卡都有现成的开源兵器库——接下来登场的,就是 Sigstore 与 Cosign。

镜像签名原理

要识破掉包,快递封口处一定要贴一张防伪贴纸。但贴纸本身防不住高手——真正起作用的是贴纸上那枚无法仿冒的印章。数字世界里,"无法仿冒的印章"叫私钥签名。签名者用自己的私钥给镜像内容盖"电子章",验证者用对应的公钥验章。公钥可以公开张贴,谁都能验;私钥只有签名者自己持有,别人就算拿到镜像也伪造不出同样的章。这个"公私钥匙"配对使用的机制,就是非对称加密。

设镜像内容为 $M$,签名过程分三步:先对镜像做 SHA-256 摘要,得到一个固定长度的"指纹";再用私钥对指纹加密;把加密结果 $S$ 作为签名附加到镜像上。整个过程可写成:

通俗说明:先给包裹称重取一个"体重指纹"(哈希),再用私人印章按在指纹上方(私钥加密),封条就完成了。任何人想伪造,不仅要复刻你的章,还得真正掌握你的私钥。

S=EncKpriv(Hash(M))\displaystyle S = \mathrm{Enc}_{K_{\mathrm{priv}}}(\mathrm{Hash}(M))
  1. 镜像内容 $M$$M$ 表示镜像的完整二进制内容,包括每一层文件系统。
    M\displaystyle M
  2. 摘要算法 $\mathrm{Hash}(M)$对镜像内容做 SHA-256 哈希,得到固定长度的"指纹",内容改一个字节,指纹就面目全非。
    Hash(M)\displaystyle \mathrm{Hash}(M)
  3. 私钥加密 $\mathrm{Enc}_{K_{\mathrm{priv}}}$用签名者私钥对指纹加密,这一步是旁人无法伪造的"盖章"动作。
    EncKpriv(Hash(M))\displaystyle \mathrm{Enc}_{K_{\mathrm{priv}}}(\mathrm{Hash}(M))
  4. 签名结果 $S$最终得到签名值 $S$,随镜像一起发布,作为可验证的封条。 私钥不能躺在明文文件里睡觉。企业通常把私钥托管到云端 KMS 或硬件安全模块,签名动作发生在加密机内部,私钥永不离开设备;权限上做最小化,只有 CI 机器人持有签名权限,验证用的公钥则公开分发,集群和审计系统随时取用。有了这枚电子章,你的镜像就像贴上了防伪封条——任何人在任何环节动过手脚,指纹都会变,旧章对新内容立刻失效。
    S=EncKpriv(Hash(M))\displaystyle S = \mathrm{Enc}_{K_{\mathrm{priv}}}(\mathrm{Hash}(M))

Sigstore生态

自己保管印章总让人提心吊胆:丢了怎么办?被偷了怎么办?现实中的快递公司早有更好的方案——不需要你随身带印章,凭身份证在快递柜"刷脸"就能取件。Sigstore 的"无密钥签名"就是这个思路。Sigstore 是 Linux 基金会下的开源工具链,由三大组件配合:Fulcio 证书中心、Rekor 透明日志、Cosign 签名工具。

用户用 GitHub、Google、邮箱等 OAuth 身份登录后,Fulcio 会签发一张有效期极短的证书(通常 10 分钟),证书里绑定用户身份与一次性公钥。Cosign 用这把临时私钥完成签名,同时把签名记录广播到 Rekor——一个任何人都可查询的公开账本。这套设计妙在两处:

  • 私钥短命:临时私钥只在证书有效期内存在,泄露了也无所谓,10 分钟后自动作废;
  • 行为留痕:所有签名行为都记录在 Rekor 里,攻击者想"签个恶意镜像再销声匿迹"几乎不可能,因为账本上的身份、时间、证书一旦对不上,立刻会被审计发现。

业内把这种特性称为透明日志审计——它不阻止坏人进门,但保证每一次进门都留下不可删除的录像。从此,你的团队不用再战战兢兢地保管私钥文件,就像人脸识别取件一样,刷个脸就能完成签名,账本上还清清楚楚地记着"谁、什么时候、签了哪个包裹"。

Cosign工具实战

设备再好,快递员手里还得有一把扫码枪。Sigstore 生态里的"扫码枪"就是 Cosign——一个命令行工具,负责给镜像盖章(sign)和验章(verify)。用 Cosign 签名的典型流程是:本地生成密钥对(或接入 Sigstore 无密钥模式)→ 为镜像签名 → 签名作为独立 OCI 制品推送到镜像仓库,原镜像内容丝毫不动。

关键点在于:签名和镜像一起存在仓库里,但签名不是"改写了镜像"。镜像出厂时的 Digest 不变,签名只是附加的"封条记录"。验证时,Cosign 拉取当前镜像重新计算摘要,并与签名里解出的原始摘要比对:

通俗说明:重新给包裹称重(重算哈希),再用手里的铜印(公钥)对照封条上的数字解密(签名解密)。重量对不上,说明中途被掉包。

Hash(M′)=?DecKpub(S)\displaystyle \mathrm{Hash}(M') \stackrel{?}{=} \mathrm{Dec}_{K_{\mathrm{pub}}}(S)
  1. 当前镜像 $M'$$M'$ 是验证方刚刚拉取的镜像,可能被篡改过。
    M′\displaystyle M'
  2. 重新计算摘要 $\mathrm{Hash}(M')$对当前内容重新做哈希,得到当前体重指纹。
    Hash(M′)\displaystyle \mathrm{Hash}(M')
  3. 公钥解密 $\mathrm{Dec}_{K_{\mathrm{pub}}}(S)$用签名者公钥解开签名,得到原始体重指纹。
    DecKpub(S)\displaystyle \mathrm{Dec}_{K_{\mathrm{pub}}}(S)
  4. 比对两个指纹相等则验证通过,否则说明镜像被改动,立即拒绝。 Cosign 支持的密钥后端包括本地文件、KMS、GitHub Secrets 等,企业多采用 KMS 托管私钥,CI 只用权限最小的凭证。签名和 SBOM 作为 OCI Artifact 存储,让镜像仓库一个地方同时收着"包裹"和"封条档案"。扫码枪"嘀"一声,验证通过;"嘀嘀嘀"报警,拦截嫌疑包裹——这就是生产环境里每天都在发生的小确幸。
    Hash(M′)=?DecKpub(S)\displaystyle \mathrm{Hash}(M') \stackrel{?}{=} \mathrm{Dec}_{K_{\mathrm{pub}}}(S)
bash2 行
cosign sign --key cosign.key my-registry/app:tag     # 签名cosign verify --key cosign.pub my-registry/app:tag   # 验证

SBOM生成方法

买电器时,包装里一定有一张物品清单——遥控器×1、说明书×1、挂架×1。没有清单,你只能拆开箱子盲目翻;有清单,拿手机对一眼就能核对齐全。SBOM 就是软件镜像的"装箱清单"。SBOM 全称 Software Bill of Materials(软件物料清单),列出镜像中每一个组件:基础镜像的每一层、系统包、语言依赖、二进制文件,以及它们的名称、版本、供应商与许可证信息。

它对安全的价值在于:当某个开源库爆出 CVE 高危漏洞时,你能在几分钟内定位"哪些生产镜像受了影响",而不是把几百个镜像逐个拉下来盲查。主流标准格式有 SPDX(ISO/IEC 5962,字段丰富、强调许可证合规)和 CycloneDX(轻量、组件依赖关系建模更强,专门面向应用安全)。生成工具方面,开源社区常用 Syft、Trivy、Docker SBOM 插件等。

生成时机最好选在 CI 构建阶段——镜像刚构建完成就立即生成 SBOM,做到"封箱前就贴清单";然后随镜像一起推送归档,保证清单与镜像版本严格对应。对要求高的企业,还会把 SBOM 导入漏洞管理平台,持续监控新披露的漏洞,实现"清单自动更新,风险持续追踪"。有了这张清单,安全团队不再需要"拆箱盲赌"——每个生产镜像吃了哪些组件、用了什么版本,一目了然,清清楚楚。

策略准入验证

包裹到了小区门口,负责任的门卫不会直接放行,而是先看三样东西:封条完整吗?寄件人可追溯吗?箱内清单对得上吗?Kubernetes 集群门口,也有这么一个尽职的门卫——准入控制器。准入控制器在 Kubernetes API 收到创建 Pod 请求之后、真正生效之前执行校验,日常你写的 Deployment、Job、CronJob 最终都会经过这一关。

Kyverno 或 OPA/Gatekeeper 这类策略引擎,让你用几行配置表达"必须带有效签名""必须带 SBOM"等规则,不需要修改一行 Kubernetes 源码。一条典型的 Kyverno 准入策略,会在镜像拉取前调用 Cosign 校验签名,同时解析 SBOM 字段。若镜像未签名或清单缺失,Pod 创建请求直接被拒绝并返回错误信息。这套逻辑可以用一个布尔表达式概括:

通俗说明:把"封条有效"和"清单齐全"两个条件用逻辑与连接——两个都满足才放行,任何一个不满足都拒绝,没有讨价还价的余地。

Admit=SigOK∧SBOMOK\displaystyle \mathrm{Admit} = \mathrm{SigOK} \land \mathrm{SBOMOK}
  1. 签名验证通过 $\mathrm{SigOK}$准入控制器第一道检查,镜像签名是否有效。
    SigOK\displaystyle \mathrm{SigOK}
  2. SBOM 检查通过 $\mathrm{SBOMOK}$第二道检查,确认镜像带有可解析的物料清单。
    SBOMOK\displaystyle \mathrm{SBOMOK}
  3. 逻辑与 $\land$两个条件同时为真,准入结果 $\mathrm{Admit}$ 才为真。 安全变更最怕"一刀切"。建议先在审计模式运行:门卫只记录"这个包裹本来会被拒收",但不真的拦截;观察一两周,确认业务镜像大多带签名和清单后,再切到强制执行模式。这样既守住了底线,又不会在切换当天把整个集群的生产流量拦停在门外。门卫虽然看似死板,但他守护的是整个小区——一个几百节点的生产集群,任何未经核验的镜像都可能像一颗定时炸弹。
    Admit=SigOK∧SBOMOK\displaystyle \mathrm{Admit} = \mathrm{SigOK} \land \mathrm{SBOMOK}

CI/CD集成实践

依赖保安的自觉是不够的,要把"验快递"变成小区管理制度。在云原生世界里,这个制度就是 CI/CD 流水线——把签名、生成 SBOM、准入校验固化成流水线的标准环节,每一次发布都自动执行同样的安检流程。所谓"安全左移",就是把安全动作从部署时前移到构建时:镜像一构建完成,流水线立即执行签名和 SBOM 生成,相当于产品出厂前就贴好封条和清单;部署阶段,集群再从准入端做最后一道核验,形成"构建—签名—清单—准入"的完整闭环。

在 GitHub Actions 中,可以这样搭建流水线:

1. 用 actions/checkout 拉取代码,构建并推送镜像;

2. 用 sigstore/cosign-installer 安装 Cosign,基于 OIDC 身份直接签名——私钥根本不写进流水线;

3. 用 anchore/sbom-action 生成 SPDX 格式的 SBOM,随镜像上传;

4. 部署到集群后,由 Kyverno 准入策略复核签名与 SBOM,任一缺失即拒绝。

审计上,Rekor 里的签名记录 + 流水线日志 + SBOM 归档,构成完整的合规证据链,可以回答"谁在什么时间、用什么身份、签发了哪个镜像"。至此,从厂商(构建机)到门卫(集群准入),快递包裹全程带着封条、清单和录像。软件供应链安全不再是"防君子",而是"制度化地防小人"。

AI 应用场景

MLOps流水线中的模型镜像签名

阶段:模型部署与发布

解决的问题:AI 模型打包成镜像推送到生产环境,若镜像被篡改,模型可能被植入后门、悄悄窃取推理数据。传统私有仓库的访问控制只能挡外部黑客,挡不住内部凭证泄露或恶意上传。

具体案例:某算法团队训练完大模型后,在 CI 中用 Cosign 对模型镜像签名,生产集群的准入控制器只放行带有效签名的镜像。一次攻防演练中,攻击者拿到仓库写入权限并推送了未签名镜像,部署请求在准入层被当场拒绝,事故被自动拦截,演练报告被评为"教科书级防御"。

相关角色:MLOps 工程师、DevOps 工程师

SBOM驱动的AI供应链漏洞排查

阶段:合规与审计

解决的问题:AI 镜像往往包含 PyTorch、CUDA 运行时、各种 Python 依赖与基础镜像层,组件数量动辄上千,漏洞排查靠人工逐层翻看几乎不可能。

具体案例:某公司每月自动为镜像生成 SBOM,并与 CVE 数据库持续比对。一次某开源库爆出高危漏洞,安全团队靠 SBOM 索引在 5 分钟内定位了所有受影响的生产镜像,随后批量升级并重新签名发布——而采用传统手工排查的团队花了三周才完成同样的工作。

相关角色:安全工程师、云平台架构师

多团队共享集群的准入策略

阶段:平台治理与策略管控

解决的问题:多个业务团队共享同一个 Kubernetes 集群,不同团队对不同镜像的信任等级各不相同,需要差异化的准入规则。

具体案例:平台团队配置 Kyverno 策略:基础镜像必须由平台团队签名,业务镜像必须由对应业务团队签名;先以审计模式运行两周,确认各团队已接入签名流程后,再切换为强制执行。从此每个团队只信任自己签发过的镜像,跨团队投毒风险大幅下降。

相关角色:云平台架构师、SRE 工程师

Python 演示

python66 行
import hashlibimport hmacimport datetime  def calc_digest(content: str) -> str:    # 对镜像内容做 SHA-256 哈希,得到唯一指纹    return hashlib.sha256(content.encode()).hexdigest()  SECRET_KEY = "supply-chain-demo-key"  def sign(digest: str) -> str:    # 用密钥(模拟私钥)对摘要签名    return hmac.new(SECRET_KEY.encode(), digest.encode(), hashlib.sha256).hexdigest()  def generate_sbom(packages):    # 生成简易 SPDX 格式的 SBOM 清单    return {        "spdxVersion": "SPDX-2.3",        "name": "example-image-sbom",        "creationInfo": {"created": datetime.date.today().isoformat()},        "packages": [            {"name": p["name"], "versionInfo": p["version"]} for p in packages        ],    }  def verify(digest, sig, packages):    # 用同样的密钥重算签名,与传入签名比对    expected = hmac.new(        SECRET_KEY.encode(), digest.encode(), hashlib.sha256    ).hexdigest()    sig_ok = hmac.compare_digest(expected, sig)    sbom_ok = len(packages) > 0  # SBOM 非空即通过    return sig_ok and sbom_ok  def main():    image_content = (        "FROM python:3.12\n"        "COPY model.pt /app/model.pt\n"        "RUN pip install numpy==1.26.4 torch==2.2.0"    )    digest = calc_digest(image_content)    signature = sign(digest)     packages = [        {"name": "numpy", "version": "1.26.4"},        {"name": "torch", "version": "2.2.0"},    ]    sbom = generate_sbom(packages)     print("Image digest:", digest)    print("Signature:", signature[:16], "……")    print("SBOM packages:", len(sbom["packages"]))     admitted = verify(digest, signature, sbom["packages"])    print("Admission result:", "PASS" if admitted else "REJECT")    assert admitted, "valid image should be admitted"  if name == "main":    main()

常见误区

误区镜像放在公司内网私有仓库里,只有我们的人能访问,所以是安全的,没必要做签名。

正解私有仓库只能限制"谁能进来",不能证明"进来后内容没被改"。内部人员误操作、CI 账号被盗、镜像同步工具被劫持,都会让恶意镜像混入。签名解决的是"内容完整性+来源可追溯",即使仓库被攻破,签名也能立刻暴露篡改。就像快递放在自家门口,也不能证明包裹没被掉包,还是要验封条。

为何常见:把"访问控制"和"完整性验证"两个安全维度混为一谈,以为门锁够结实就不需要防盗警报。

误区签名就是用密钥把镜像内容加密,别人看不到里面的东西。

正解签名是对镜像摘要做加密运算,但镜像本体完全公开、可任意拉取。签名只额外提供两条信息:谁签的、有没有被改。好比封条上盖的章——章不能把包裹锁进保险箱,只能证明"这箱子出厂后没被打开过"。

为何常见:加密与签名同属密码学,初学者容易把它们当成同一件事,实际上一个隐藏内容、一个认证来源。

误区docker images 能看到镜像的层和大小,这不就是 SBOM 了吗?

正解docker images 只展示镜像层的大小与概要,而 SBOM 要列出镜像内每一个组件(包括每个开源包的名称、版本、许可证、来源与传递依赖),并且用 SPDX/CycloneDX 标准化格式表达。快递单只显示包裹重量,SBOM 则列出里面每个零件的品牌、型号与出厂编号。

为何常见:把"镜像层的存储视图"误当成"软件成分的分析视图",两者维度完全不同。

误区我都有 SBOM 了,依赖信息全都列出来了,还签什么名?

正解SBOM 回答"里面有什么",签名回答"谁打包的、有没有被改"。没有签名的 SBOM 本身也可能被篡改——攻击者完全可以删掉恶意依赖再伪造一份干净的清单。物品清单不能证明清单是原厂出具的,还需要封条上的印章来背书。

为何常见:不理解两个安全控制服务于不同目标,以为做到其中一个就完成了全部供应链防护。

误区Sigstore 无密钥签名是全自动的,接入之后所有镜像就自动安全了。

正解Sigstore 解决了"密钥管理难"的问题,但供应链安全是闭环:构建端要真的调用 Cosign 签名,仓库端要正确存储签名和 SBOM,集群端要配置准入策略强制校验。任何一环缺失,签名都形同虚设。装了摄像头不安排人看监控,防盗效果自然大打折扣。

为何常见:把"引入工具"误认为"完成治理",忽视了安全链路需要端到端的工程落地。

面试题

Q1. 镜像签名的核心目的是什么?

单选题 · easy

  • A.加密镜像内容防止他人查看
  • B.验证镜像来源并防内容篡改← 正确答案
  • C.加速镜像拉取和部署流程
  • D.压缩镜像体积降低存储成本

答案:B。镜像签名验证"谁签的"与"有没有被改",就像检查包裹上的防伪封条。A 混淆了加密与签名——签名不隐藏内容;C、D 与签名毫无关系,属于干扰项。

Q2. 与"加密"相比,"签名"的关键区别是什么?

单选题 · easy

  • A.签名使用私钥而加密使用公钥
  • B.签名不隐藏内容只做完整认证← 正确答案
  • C.签名比加密速度快很多
  • D.签名过程完全不需要密钥

答案:B。加密隐藏内容,签名不隐藏内容,只提供"谁签的、有没有被改"两样信息,好比封条上的印章不是保险箱密码。A 说法不全面,C 不是核心区别,D 错误——签名同样需要密钥。

Q3. Sigstore的"无密钥签名"主要解决什么问题?

单选题 · easy

  • A.镜像拉取速度慢
  • B.私钥保管难题← 正确答案
  • C.镜像体积过大
  • D.网络带宽不足

答案:B。传统签名要求妥善保管长期私钥,泄露就全盘崩溃。Sigstore 用 OAuth 身份+短期证书替代长期私钥,把保管难题交给证书中心和透明日志,相当于用"刷脸"代替"带印章"。

Q4. Cosign验证镜像签名时,应使用哪个命令?

单选题 · mid

  • A.cosign sign
  • B.cosign verify← 正确答案
  • C.cosign inspect
  • D.docker verify

答案:B。cosign sign 用于签名,cosign verify 用于验证,cosign inspect 查看镜像信息,docker 没有内置 verify 概念。就像扫码枪要按对按钮:盖章按 sign,验章按 verify。

Q5. SBOM的全称是?

单选题 · mid

  • A.Software Block Object Model
  • B.Software Bill of Materials← 正确答案
  • C.Security Backup of Mirrors
  • D.System Build Operation Module

答案:B。SBOM 全称 Software Bill of Materials(软件物料清单),相当于镜像的装箱清单。其余三个是拼凑的干扰项,含义不通。

Q6. Kubernetes中,拦截未签名镜像的部署机制是什么?

单选题 · mid

  • A.服务网格
  • B.准入控制器← 正确答案
  • C.自动扩缩容器
  • D.配置映射组件

答案:B。准入控制器在对象持久化前执行校验,可以拒绝无签名或无 SBOM 的 Pod。服务网格管流量、HPA 管扩缩、ConfigMap 管配置,都不负责部署准入。

Q7. Rekor在Sigstore体系中的角色是?

单选题 · hard

  • A.签发自绑定用户身份的短期证书
  • B.公开透明账本记录签名审计← 正确答案
  • C.负责镜像内容的持久化存储
  • D.负责CI流水线的任务调度

答案:B。Rekor 是 Sigstore 的透明日志账本,记录所有签名行为供审计追溯;Fulcio 才是签发短期证书的中心,镜像存储由仓库负责,流水线调度是 CI 系统的事。

Q8. 以下哪些属于SBOM的标准格式?

多选题 · mid

  • A.SPDX 格式← 正确答案
  • B.CycloneDX 格式← 正确答案
  • C.Dockerfile 指令
  • D.Golang 代码文件

答案:AB。SBOM 的两大标准格式是 SPDX 和 CycloneDX。Dockerfile 是构建指令,Golang 是编程语言,都与"软件物料清单格式"无关。

Q9. 关于镜像签名与准入验证,下列说法正确的有?

多选题 · hard

  • A.镜像签名会改变镜像的Digest← 正确答案
  • B.准入控制可拒绝无签名镜像← 正确答案
  • C.签名可发现镜像是否被篡改
  • D.SBOM可完全替代签名验证
  • E.签发自绑定用户身份的短期证书
  • F.负责镜像签名与验证操作
  • G.负责镜像内容的持久化存储
  • H.自动生成镜像的SBOM清单

答案:AB。Fulcio 根据 OAuth 身份签发短期证书,Cosign 负责签名与验证操作。Fulcio 不存储镜像,Cosign 不生成 SBOM(SBOM 由 Syft 等工具生成),C、D 均张冠李戴。