容器 33:签名通过了,运行权限还要验证什么
通过10 的 digest可以检验拿到的 blob 是否和 manifest 里写的一样;但“内容没有变化”并未回答谁构建的、输入从何而来、程序是否有漏洞、运行时拥有何种权限。签名与构建来源同样不能替代08 的运行约束。
一次发布往往把四类判断缩成“镜像可信”:客户端按 digest 拉取,注册表带着一份签名、SBOM 和构建声明,部署模板又宣称自己以非 root 运行。真正有用的是分层问“哪个验证器接受了哪个断言,签名主体绑定哪一个 digest,运行容器的权限由谁执行”。有签名不代表签署者被允许交付生产,有来源也不表示二进制没有漏洞;权限正确更不能反推构建过程可信。本篇用同一 probe-app 制品和两份临时测试公钥构造明确拒绝路径,而不上传任何私钥。
四个问题独立判定
digest 用于核对字节;签名需要验证身份、信任策略和被签对象的 digest;SBOM 描述制品包含的软件成分,只有核对生成器、覆盖范围与版本才可用于下一步扫描;SLSA Provenance v1.0 为构建过程的输入、构建器和产物提供可核查的谓词语义,不能凭存在 attestation 就推断构建平台可信或扫描零漏洞。即便某个制品在签名策略中被允许,运行容器的 UID/capabilities、seccomp 和卷权限仍需另行检查。
摘要确认了什么,也遗漏了什么
OCI image index、manifest 和 layer 各有不同对象与 digest。把 image 引用固定到某一 manifest digest,可防止拉取时只靠可变 tag 选中另一份 manifest;在多平台索引下,还要区分引用的是 index 的 digest 还是平台专用 manifest。检查端必须核对最终运行容器的 imageID/所选平台,不是因为 Dockerfile 内容相同就自动得到相同的最终字节。签名的 subject 也要对齐预期对象:签了源码压缩包的摘要,不能当作部署用镜像 digest 的签名;签了 image index 的对象也不能在另一套不兼容策略中直接当单平台 manifest 签名。
SHA-256 是内容标识与完整性工具,不提供作者身份。攻击者若能替换镜像和同位置存放的“预期 SHA”,简单比较两个文件自然也会通过;预期值必须来自独立可信渠道,或由满足组织信任策略的签名认证。反过来,某次签名校验通过只能说明验证器在给定信任锚和时间/透明度条件下接受了该对象;不代表镜像内的命令安全,也不保证运行所依赖的共享节点、内核与挂载没有问题。把这些区别保留在同一份实验记录,避免后续事故复盘只看到一张绿色校验截图。
签名通过需要谁的信任边界
在 cosign v2.6.0 的 VerifyCommand 阅读定点中,--key 指定验证所用公钥,命令可据此检查签名是否匹配目标镜像。实验生成两套独立的本地测试密钥:使用 A 的私钥只签这次镜像,用 A 的公钥验证是正例;用 B 的公钥验证同一 digest 是拒绝身份的负例。两次验证目标完全相同,因此只有被允许的公钥变化。公钥的 SHA-256 指纹和私钥文件路径是实验记录,私钥及口令仅保留在私有目录并在结束后回收;不应把真实生产密钥、KMS 凭据或者签名包推到本篇同名素材目录。
本实验固定 --tlog-upload=false 与 --insecure-ignore-tlog 只用于独占离线练习;关闭透明日志验证并不是生产验证建议。生产环境还应设定可信身份或公钥来源、证书发行者、签名有效性与透明度策略,再由真实准入控制器对 Pod 引用的 digest 强制执行。cosign verify 在操作终端返回 0 不能阻止任何人绕开终端直接部署一个没签名的 Pod;必须区分“操作员手工校验成功”和“集群以拒绝策略强制准入”。若 VM 没安装此类控制器,本篇只可记录 CLI 级验证,Kubernetes 准入仍 NOT_RUN,不可将一次成功 kubectl apply 冒充策略拒绝。
密钥轮换又引入时间因素:如果同一团队把 A 公钥换成 B 公钥,新制品由 B 签名,不代表旧 A 签的制品立即不可用,反之也不是自动接受。允许哪些旧签名、轮换窗口多久、撤销列表如何传播,属于信任策略的一部分。实验特意让 B 与 A 的角色分明:B 没签本次镜像,因此 B 的验证失败是预期拒绝,不能拿这个输出证明“组织完成密钥轮换”。记录验证发生时实际启用的公钥指纹和镜像摘要,日后才可追溯为什么同一制品在两个时间点判定不同。
对签名验证工具的退出码也要防止混淆:若本地根本无法连上 registry,cosign verify 非零只能证明验证没有成功完成,不能归因为公钥 B 拒绝了 A。先在独占 VM 确认对同一 digest 使用 A 公钥成功,再用 B 公钥在相同 URL、相同网络和相近时间窗口内失败,保存两个进程退出码及原始 stderr。若 B 因证书信任、透明日志或仓库限制失败,继续缩小变量而不是填成“身份拒绝通过”。这是从字节哈希检查走向身份策略验证时最容易省略的一步。
换一个不同 digest 做负例时,首先得有真实存在、且可拉取的第二个制品。如果向同一 registry 拼一个并不存在的摘要,验证失败很可能只是网络或找不到 manifest,不能证明原签名拒绝另一个实际制品。要验证“重新构建后签名不再匹配”,必须单独构建/推送变更的 probe-app 镜像,保存两份 image digest、应用源码差异、真正被验证的 subject;两者也可能在可复现构建中生成相同 digest,这时“变了源码”与“变了制品”不是一回事。没有第二个可用 digest 时该负例保持 NOT_RUN,可先用 B 公钥完成确定的测试身份拒绝。
SBOM、Provenance 和漏洞结论各自独立
SBOM 是一张成分清单,不是安全检查报告。首先问生成器识别了哪些层和依赖:仅扫描源码目录可能遗漏基础镜像包;仅扫描镜像文件又可能漏掉运行时动态加载组件。记录 SBOM 的生成工具版本、格式(例如 SPDX)、subject digest 与生成时间,然后单独运行指定漏洞库版本的扫描并保存原始结果。漏洞扫描没有发现命中,可能是覆盖不足或数据库尚无条目,不能从“SBOM 已生成”推出“零漏洞”。在工具缺席时,可提供待执行的材料入口,但不创建假的 SPDX 输出或列出未经验证的包版本。
SLSA Provenance v1.0 关注哪项构建产出了哪份 subject、运行的 builder 以及声明的 buildDefinition/runDetails。要验证这种声明,至少核对其 subject digest 与本次拉取的镜像一致、签署或发布 attestation 的身份符合预期、来源仓库/修订/构建输入与策略吻合。自己在 JSON 中填上“可信 builder”文字而未验证签名,不具备来源证明价值;builder ID 即便被签,也还要审视构建平台的权限边界和材料完整性。更不能把“有 provenance”解释为“代码是无恶意的”——内容审查、依赖风险和执行权限仍然是后续独立判断。
签名与 provenance 也可能跨仓库转存:镜像经复制或重建后引用地址改变,若字节保持相同、digest 不变,原声明怎样关联新仓库取决于制品/attestation 存储方式与验证器查找策略;若字节变化,原签名没有自动覆盖新 digest。迁移验收时应记录从部署引用出发如何定位到证据、证据签名绑定哪个 subject、验证器实际接受了谁的身份,而非只记录“原仓库里曾有签名”。此时也需防止当年的构建身份已失效或被撤销,但缓存的旧校验结果仍被当作当前策略放行依据。
信任制品不等于限制进程
即使 A 公钥确认了镜像身份,Pod 若以 root 用户、可提权或挂了宿主敏感路径运行,其影响面仍与源码签名无关。正例需要检查运行时真实 UID/GID、capabilities、seccomp、只读根文件系统、挂载权限和所选 ServiceAccount;仅 Pod spec 声明 runAsNonRoot 并不能代替在容器内读 UID,也不能保证每个 CSI 卷对该 UID 可写。反例即使使用同一已签 digest,如果放宽了 allowPrivilegeEscalation 或暴露 hostPath,签名仍可通过,而运行风险显著变化。测试不靠关闭共享集群安全机制,只对自有 Pod 的配置做差异分析,在条件具备时用专用沙箱检查允许/拒绝的实际系统调用结果。
这些链路必须有各自的失败策略:签名身份不符要拒绝或中止部署,provenance subject 不符要拒绝来源结论,SBOM 数据不全要报告覆盖缺口,运行时权限过大要拒绝特权配置;不应用一个笼统的 trusted=true 抹平不同动作。独占 VM 签名验证与权限取证保留测试密钥生成、双公钥校验与限制范围;四种结论的独立判定卡提醒不能用一个状态代替四类证据;专项附件校验证明卡片字节与本篇一致,共用探针包用于构建输入。字节校验本身既不是镜像签名,也不是实际准入结果。
例如策略卡上的“预期来源 builder”可以在审阅时填写,但只有运行一个能验证 attestation 的工具并保存失败时的拒绝,才算对那一列做过实际验证;只把字符串写进文件不算强制执行。运行权限列也是如此:通过镜像签名的 Pod 可能配置了 runAsNonRoot: true 却在启动时因镜像要求 root 而失败,这不是签名失效;应该回到 Pod 的安全上下文和容器启动事件。反过来某个无签名镜像即使进程 UID 很小且 seccomp 配置严格,也不满足供应链身份要求。两组信任关系交叉而不互相覆盖。
迁移判断法是沿“镜像实际 digest → 谁对它签名 → 可验证的构建来源 → 成分覆盖/扫描 → 正在运行的进程权限”逐层走,在哪层缺证据就停在哪层。当前环境没有固定可用的 cosign/构建镜像、测试仓库或准入控制器,签名正反例、SBOM、SLSA 和运行权限对照均 NOT_RUN;不能从文档阅读、密钥模板或 Hexo 构建宣布链路验收通过。
审阅发布记录时不只问“有没有签”,还应分别找出哪份证据支撑哪项允许动作:仓库允许下载、验证器允许这个签署者、准入控制器允许部署、节点实际按最小权限执行,是四个可能在不同时间失败的步骤。任何一步缺失都应显示为未知或拒绝,不应被上一步的成功自动填绿。
正例在专用 VM 对自己构建的 probe-app 镜像做 A 测试公钥验证;反例对同一 digest 用 B 公钥拒绝,区分身份不符与镜像拉取失败。SBOM/来源及运行时权限分别补采,准入插件未安装则保持 NOT_RUN。测试私钥不进入文章包;本机没有固定镜像、签名工具或受支持的准入环境,以上实验全未运行。
| 现象 | 核对 | 不能推出 |
|---|---|---|
| digest 一致 | blob 与 manifest 字节 | 构建来源可信 |
| 签名通过 | 身份、策略、签名目标 | 没有漏洞/权限风险 |
| 有 SBOM | 软件组成与扫描输入 | 运行时行为安全 |
练习
- 先预测只替换签名身份、不改镜像 digest 后策略会如何处理;专用 VM 用测试身份记录签名校验与准入结果。
- 预测同一源码重新构建而 digest 改变是否仍应被旧签名接受,比较 provenance 所指产物与实际拉取 digest。
系列总目录:从进程隔离到运行时与编排。




