news 2026/9/15 17:14:35

skopeo standalone-sign 实战指南:不上传镜像,在本地为镜像清单生成 OpenPGP 签名

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
skopeo standalone-sign 实战指南:不上传镜像,在本地为镜像清单生成 OpenPGP 签名

skopeo standalone-sign 实战指南:不上传镜像,在本地为镜像清单生成 OpenPGP 签名

【免费下载链接】skopeoWork with remote images registries - retrieving information, images, signing content项目地址: https://gitcode.com/GitHub_Trending/sk/skopeo

导读

skopeo standalone-sign是 skopeo 提供的一个纯本地调试签名工具:它只要求你提供一个镜像 manifest 文件、一个 docker 引用和一个 GPG 密钥指纹,就能在不上传、不拉取镜像的情况下生成一份符合 containers-signature 规范的签名文件,随后可用skopeo standalone-verify在本地完成校验。阅读本文后,你将掌握该命令的完整语法、每个参数与选项的精确语义、底层签名调用链(源码级),并理解它与skopeo copy --sign-by的定位差异,能在调试签名流程、离线生成签名等特殊场景下正确使用它。

命令定位:本地调试工具,而非日常操作流程

根据 docs/skopeo-standalone-sign.1.md 的定义,skopeo standalone-sign是一个Debugging tool,它“Sign an image locally without uploading”(在本地为镜像签名而不上传)。

官方文档明确提醒:

This is primarily a debugging tool, useful for special cases, and usually should not be a part of your normal operational workflow; useskopeo copy --sign-byinstead to publish and sign an image in one step.

即:日常发布镜像时,你不应该先手动签名再上传,而应使用 skopeo-copy(1) 的--sign-by选项在复制镜像的同时一步完成签名。standalone-sign只服务于两类特殊场景:

  • 调试签名机制:验证 GPG 密钥、指纹、manifest 与签名之间的绑定关系是否符合预期;
  • 离线/非交互签名:镜像清单已落在本地文件系统,希望在完全不接触网络的情况下生成签名。

在 cmd/skopeo/main.go 的命令注册表中,standaloneSignCmd()standaloneVerifyCmd()一同被挂载到根命令下,二者配套使用,构成完整的“本地签名—本地验证”闭环。

命令语法与三个位置参数

命令的完整语法为:

skopeo standalone-sign [options] manifest docker-reference key-fingerprint --output|-o signature

注意:--output/-o是必填项(下文会从源码验证这一点)。三个位置参数的含义如下:

位置参数含义说明
manifest镜像 manifest 文件的路径一个包含镜像清单的本地文件,例如skopeo copydir:目录后生成的manifest.json
docker-reference标识镜像的 docker 引用用于把签名“绑定”到该镜像身份(如registry.example.com/example/busybox),签名验证时会严格比对
key-fingerprint用于签名的密钥身份即本地 GPG 私钥的完整指纹(40 位十六进制),而非短 Key ID

位置参数的含义

官方文档对三个参数的定义如下:

  • manifest:Path to a file containing the image manifest —— 包含镜像清单的文件路径;
  • docker-reference:A docker reference to identify the image with —— 用于标识镜像的 docker 引用;
  • key-fingerprint:Key identity to use for signing —— 用于签名的密钥身份。

manifest文件与docker-reference是签名内容的一部分:签名不仅覆盖 manifest 字节本身,还同时绑定目标镜像引用,这正是 containers-signature 规范的核心——一份签名必须同时绑定“清单摘要”和“镜像身份”,防止签名被复制到其他镜像上冒用。

选项详解

参见 skopeo(1),全局选项(如--policy--insecure-policy--registries.d)放在子命令名之前;本命令自身的选项如下。

--help/-h

打印命令的使用说明(usage statement)。

--output/-ooutput file(必填)

将生成的签名写入_output file。虽然文档将其列为选项,但从源码看它是强制参数——缺少它命令直接报错退出(见下文源码剖析)。示例中签名文件的后缀常取.signature以示区分。

--passphrase-file=path

签名时使用的口令文件。官方文档给出明确警告:

The passphare to use when signing with the key ID from--sign-by. Only the first line will be read. A passphrase stored in a file is of questionable security if other users can read this file. Do not use this option if at all avoidable.

要点:

  • 只读取文件的第一行作为口令,文件其余内容被忽略;
  • 将口令明文存放在文件中,如果其他用户可读,则安全性堪忧;
  • 除非万不得已,不要使用该选项

对于未加密的测试密钥(如cmd/skopeo/fixtures中配套的测试密钥),可以完全不提供口令;对于有口令保护的私钥,交互式输入或 gpg-agent 是更安全的选择。仓库测试夹具中甚至准备了空口令文件cmd/skopeo/fixtures/empty.passphrase,用于验证“空口令”场景下签名流程依然可用。

实战示例:从 manifest 到签名文件的完整流程

官方文档给出的示例为:

$ skopeo standalone-sign busybox-manifest.json registry.example.com/example/busybox 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8 --output busybox.signature $

下面按完整工作流展开讲解每一步。

第一步:获取镜像 manifest 文件

standalone-sign不直接与远程仓库交互,因此你需要先用其他手段把 manifest 落到本地。最方便的方式是使用skopeo copy配合dir:transport:

$ mkdir -p /tmp/busybox-dir $ skopeo copy docker://busybox:latest dir:/tmp/busybox-dir $ ls /tmp/busybox-dir/ 2b8fd9751c4c0f5dd266fcae00707e67a2545ef34f9a29354585f93dac906749.tar manifest.json

此时/tmp/busybox-dir/manifest.json就是签名命令所需的manifest文件。仓库内 cmd/skopeo/fixtures/image.manifest.json 提供了一个可用于复现的 Docker schema 2 清单样例(含config与三个layers,每个 blob 都有mediaTypesizedigest)。

第二步:准备 GPG 私钥并确认指纹

签名依赖本机 GPG 密钥环(默认GNUPGHOME环境变量指向的目录)中的私钥。可用gpg --list-secret-keys --with-fingerprint查看完整指纹:

$ gpg --list-secret-keys --with-fingerprint sec rsa3072 2024-01-01 [SC] 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8 uid [ultimate] Example User <user@example.com>

注意必须使用40 位完整指纹。测试代码 cmd/skopeo/signing_test.go 中的常量也印证了这一点:

  • 测试用完整指纹:08CD26E446E2E95249B7A405E932F44B23E8DD43
  • 对应的短 Key ID:E932F44B23E8DD43

若在测试目录cmd/skopeo/fixtures中设置了GNUPGHOME,即可用上述指纹直接对样例 manifest 签名。

第三步:执行签名

$ skopeo standalone-sign \ /tmp/busybox-dir/manifest.json \ registry.example.com/example/busybox \ 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8 \ --output busybox.signature

命令成功时没有任何标准输出(静默成功),并在--output指定的路径生成签名文件。测试 cmd/skopeo/signing_test.go 对成功路径的断言是:out为空、错误为nil,且生成的签名可用signature.VerifyDockerManifestSignature重新验证,验证结果中的DockerReferenceDockerManifestDigest均与输入一致。

第四步:本地验证签名(配套命令)

生成的签名可用skopeo standalone-verify在本地校验:

$ skopeo standalone-verify \ /tmp/busybox-dir/manifest.json \ registry.example.com/example/busybox \ 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8 \ busybox.signature Signature verified using fingerprint 1D8230F6CDB6A06716E414C1DB72F2188BB46CC8, digest sha256:20bf21ed457b390829cdbeec8795a7bea1626991fda603e0d01b4e7f60427e55

输出会报告实际使用的验证指纹以及签名绑定的 manifest 摘要(DockerManifestDigest)。standalone-verify还支持--public-key-file指定公钥文件,以及用逗号分隔的指纹列表或any(表示信任公钥文件中的任意密钥)作为key-fingerprints,详见 docs/skopeo-standalone-verify.1.md。

源码级剖析:签名是如何完成的

命令的完整实现位于 cmd/skopeo/signing.go,核心流程清晰可读。

参数定义与命令注册

type standaloneSignOptions struct { output string // Output file path passphraseFile string // Path pointing to a passphrase file when signing }

standaloneSignCmd()(cmd/skopeo/signing.go)中注册两个 flag:

  • --output/-o,短选项别名o,帮助文本为 “output the signature toSIGNATURE”;
  • --passphrase-file,帮助文本为 “file that contains a passphrase for the --sign-by key”。

执行逻辑与调用链

run()(cmd/skopeo/signing.go)按以下顺序工作:

  1. 参数校验len(args) != 3 || opts.output == ""时直接返回Usage: skopeo standalone-sign manifest docker-reference key-fingerprint -o signature。由此可见:位置参数恰好 3 个、且-o必须显式给出,否则命令拒绝执行(测试 cmd/skopeo/signing_test.go 用 6 组非法参数组合逐一验证了这一行为)。
  2. 读取 manifestos.ReadFile(manifestPath),文件不存在时返回Error reading <path>
  3. 初始化 GPG 签名机制signature.NewGPGSigningMechanism()(来自go.podman.io/image/v5/signature包,对应 go.mod 中声明的依赖),失败时返回Error initializing GPG。这正是文档 NOTES 中所说“本命令面向 OpenPGP 本地签名”的落地实现。
  4. 读取口令cli.ReadPassphraseFile(opts.passphraseFile),内部实现“只取第一行”的语义。
  5. 生成签名:调用signature.SignDockerManifestWithOptions(manifest, dockerReference, mech, fingerprint, &signature.SignOptions{Passphrase: passphrase})—— 这一步同时把 manifest 字节、docker-reference 与密钥指纹绑定进签名内容。
  6. 写文件os.WriteFile(opts.output, signature, 0o644),权限为0644,写入失败时返回Error writing signature to <path>

测试 cmd/skopeo/signing_test.go 还验证了其余失败路径:不存在的 manifest、空 docker 引用(报 “empty signature content”)、未知密钥指纹、以及写入目标不可写(/dev/full)均会以非零状态退出。

指纹与摘要的可验证事实

仓库测试夹具中的样例清单(image.manifest.json)与签名(image.signature)配合固定密钥指纹,构成了一个可以反复复现的验证基准:

  • manifest 摘要:sha256:20bf21ed457b390829cdbeec8795a7bea1626991fda603e0d01b4e7f60427e55
  • 签名私钥指纹:08CD26E446E2E95249B7A405E932F44B23E8DD43
  • 集成测试 integration/signing_test.go 演示了完整的冒烟流程:动态生成 GPG 密钥 →standalone-sign生成签名 →standalone-verify验证,输出匹配正则^Signature verified using fingerprint ..., digest ...$

为什么日常推荐skopeo copy --sign-by而非 standalone-sign

原文档在 DESCRIPTION 中明确建议日常场景使用skopeo copy --sign-by。这是因为:

  1. 一步到位:复制镜像的同时完成签名与上传,签名与镜像总是保持一致,不存在“签了旧 manifest”的窗口期;
  2. 身份自动绑定--sign-by对应当前复制目标的destination-image生成 “simple signing” 签名(见 docs/skopeo-copy.1.md),无需手工维护 manifest 文件与引用的一致性;
  3. 错误面更小standalone-sign要求你先正确导出 manifest、再精确指定指纹与引用,任何一步不一致都会导致签名与实际发布内容脱节。

底层实现上,copy的签名选项在 cmd/skopeo/utils.go 中定义:--sign-by(GPG 指纹)、--sign-by-sq-fingerprint(Sequoia-PGP 指纹)、--sign-by-sigstore(sigstore 参数文件)、--sign-by-sigstore-private-key(sigstore 私钥)。这些选项与--sign-passphrase-file配合使用,但同一时刻只能指定一个签名方式,且口令文件不存在会直接报错(测试见 cmd/skopeo/utils_test.go)。

结论:面向生产的发布签名一律走skopeo copy --sign-by;只有当你在排查签名格式、研究签名绑定关系,或需要完全离线地构造一份签名时,才使用standalone-sign

使用边界与注意事项

根据 docs/skopeo-standalone-sign.1.md 的 NOTES 章节,还有两点必须明确:

  1. 仅适用于本地签名格式(如 OpenPGP):命令遵循 containers-signature(5) 规范(由go.podman.io/image/v5/signature库实现),其他签名格式可能在未来加入。当前版本面向 GPG/OpenPGP 本地签名。
  2. 与 Docker Content Trust(DCT)无关:本命令不会与 Docker Content Trust(DCT)生成的工件产生任何交互。DCT 是 Docker 生态中另一套基于 notary 的签名体系,二者互不兼容,不要混用。

另外注意口令安全性:--passphrase-file只读取文件第一行,且口令文件若可被他人读取则形同虚设,官方建议“能不用就不用”。签名文件权限固定为0644,如对敏感环境有更严格的要求,可在生成后自行收紧。

相关命令与扩展阅读

  • skopeo(1):全局选项(--policy--insecure-policy--registries.d--require-signed等)与全部子命令总览;
  • skopeo-copy(1):日常签名推荐路径skopeo copy --sign-by的完整选项说明;
  • skopeo-standalone-verify.1.md:配套的本地签名验证命令;
  • 实现与测试:cmd/skopeo/signing.go、cmd/skopeo/signing_test.go、integration/signing_test.go;
  • 可复现的测试夹具:cmd/skopeo/fixtures/image.manifest.json、image.signaturecorrupt.signaturepubring.gpgsecring.gpgempty.passphrase(均在cmd/skopeo/fixtures/目录下)。

关于签名格式的完整规范(containers-signature 的格式、字段与校验规则),请查阅容器镜像签名规范文档(containers-signature(5)),它由本命令所依赖的go.podman.io/image/v5/signature库实现并遵循。

【免费下载链接】skopeoWork with remote images registries - retrieving information, images, signing content项目地址: https://gitcode.com/GitHub_Trending/sk/skopeo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 17:14:13

gRPC开发流程实战:从基础概念到完整服务搭建

1. 为什么我建议你一定要搞懂gRPC开发流程如果你这几年一直在写后端服务&#xff0c;肯定能感受到微服务架构已经把单体应用拆得越来越细&#xff0c;服务之间的通信方式也从简单的HTTP JSON调用&#xff0c;慢慢转向了高性能、强契约的RPC框架。gRPC就是其中绕不开的一个。我最…

作者头像 李华
网站建设 2026/9/15 17:13:36

DeepSeek V4专家模式:MoE架构与AI编程实践

1. DeepSeek V4专家模式的技术解析最近DeepSeek V4推出的"专家模式"在技术圈引发了热烈讨论。作为一个长期跟踪AI技术发展的从业者&#xff0c;我认为这个功能背后蕴含着MoE&#xff08;Mixture of Experts&#xff09;架构的深度应用。与传统的dense模型不同&#x…

作者头像 李华
网站建设 2026/9/15 17:12:31

APK静态分析实战:移动取证中的关键技术与流程

做移动端取证和恶意样本分析这些年&#xff0c;我拆过的APK少说也有几百个。每次拿到新样本&#xff0c;不管是业务部门送来的可疑应用&#xff0c;还是涉案手机里提取出来的安装包&#xff0c;我第一反应永远是&#xff1a;先别急上模拟器&#xff0c;先把静态分析做完。APK静…

作者头像 李华