尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

containerd 镜像验证机制实战指南:bindir ImageVerifier 插件配置、二进制 API 与拉取判定契约

containerd 镜像验证机制实战指南:bindir ImageVerifier 插件配置、二进制 API 与拉取判定契约 containerd 镜像验证机制实战指南bindir ImageVerifier 插件配置、二进制 API 与拉取判定契约【免费下载链接】containerdAn open and reliable container runtime项目地址: https://gitcode.com/GitHub_Trending/co/containerd镜像验证Image Verification是 containerd 在拉取镜像前执行的一道可插拔安全门禁通过外部 verifier 二进制对镜像引用reference与 OCI 内容描述符descriptor进行审查只有所有 verifier 均返回允许判定的镜像才会被实际拉取。本篇指南以官方文档 docs/image-verification.md 为主体结合仓库源码pkg/imageverifier、plugins/imageverifier 与 core/transfer/local/pull.go完整讲解默认bindir插件的配置方式、verifier 二进制的 API 契约、判定合并规则及其底层实现原理。读完本文你将能够编写自己的镜像验证器、正确配置 containerd 并在源码层面理解验证调用链。一、什么是 bindir ImageVerifier 插件containerd 通过插件机制注册镜像验证服务。默认实现名为bindir其职责是扫描指定目录bin_dir下的所有可执行文件将它们作为镜像验证器依次调用根据每个验证器的退出码汇总出最终判定。该插件注册在 plugins/imageverifier/plugin.go 中registry.Register(plugin.Registration{ Type: plugins.ImageVerifierPlugin, ID: bindir, Config: defaultConfig(), InitFn: func(ic *plugin.InitContext) (any, error) { cfg : ic.Config.(*bindir.Config) return bindir.NewImageVerifier(cfg), nil }, })从源码结构看镜像验证被设计为接口抽象核心接口定义于 pkg/imageverifier/image_verifier.gotype ImageVerifier interface { VerifyImage(ctx context.Context, name string, desc ocispec.Descriptor) (*Judgement, error) } type Judgement struct { OK bool Reason string }即给定镜像名称与 OCI 描述符返回是否允许拉取的判定及原因。bindir只是该接口的默认实现意味着后续可替换为其他插件形态的验证器。二、启用镜像验证配置文件示例与默认值在 containerd 配置文件中加入如下 stanza 即可启用镜像验证[plugins] [plugins.io.containerd.image-verifier.v1.bindir] bin_dir /opt/containerd/image-verifier/bin max_verifiers 10 per_verifier_timeout 10s三个配置项与 pkg/imageverifier/bindir/bindir.go 中的Config结构体一一对应配置项TOML 字段说明默认值bin_dirbin_dirverifier 可执行文件所在目录/opt/containerd/image-verifier/bin见 plugins/imageverifier/path_unix.goWindows 下另有默认路径max_verifiersmax_verifiers实际调用的 verifier 数量上限 0表示不设限10per_verifier_timeoutper_verifier_timeout单个 verifier 的超时时间10s默认值同样来自 plugins/imageverifier/plugin.go 的defaultConfig()。需要特别指出bin_dir下所有文件只要该目录存在都会被当作 verifier 可执行程序。因此务必保证目录中只放入符合下述 API 的验证器二进制避免将无关文件如 README、临时文件放进该目录。三、Image Verifier 二进制 API3.1 命令行参数每个被调用的 verifier 二进制都会收到三个 CLI 参数-name可能被拉取的镜像的给定引用reference例如registry.example.com/image:abc-digest镜像解析后得到的摘要digest例如sha256:98ea6e4f...-stdin-media-type通过标准输入传入的 JSON 数据的媒体类型。这三个参数在 bindir.go 的runVerifier中组装args : []string{ -name, imageName, -digest, desc.Digest.String(), -stdin-media-type, ocispec.MediaTypeDescriptor, }3.2 标准输入Standard Inputverifier 二进制通过标准输入接收一段 JSON 编码的载荷其媒体类型由-stdin-media-type参数指定可能在 containerd 未来版本中变化。当前版本以仓库源码为准载荷的媒体类型为application/vnd.oci.descriptor.v1json内容是被拉取镜像的 OCI Content Descriptor即ocispec.Descriptor包含 digest、size、mediaType、annotations 等字段。实现层面containerd 通过自建管道os.Pipe写入 stdin且以异步 goroutine 执行json.NewEncoder(stdinWrite).Encode(desc)见 bindir.go。这样做是因为 descriptor 一旦超过管道缓冲区Linux 上通常为 64 KiB若子进程不读取 stdin同步写入会使父进程阻塞。同时管道读写均设置了与超时一致的 deadline避免 I/O 挂死。3.3 拉取判定Image Pull Judgementverifier 通过退出码 标准输出两部分表达判定结果标准输出打印一条判定原因reasoncontainerd 会将其纳入日志与最终判定信息退出码返回0表示允许拉取该镜像返回任何非0退出码表示拒绝拉取。仓库测试目录中的示例 verifier 直观展示了两种形态。允许型accept_reason_a.gopackage main import fmt func main() { fmt.Println(Reason A) }拒绝型reject_reason_d.gopackage main import ( fmt os ) func main() { fmt.Println(Reason D) os.Exit(1) }3.4 判定原因与输出截断containerd 会对 verifier 的输出做严格的资源控制具体体现在 bindir.go 的常量outputLimitBytes 1 15即32 KiB标准输出判定 reason限制为最多读取 32 KiB若检测到截断会在 reason 末尾追加(stdout truncated)标记标准错误按行读取并以 debug 级别写入 containerd 日志同样受 32 KiB 限制截断时会输出(previous logs may be truncated)提示。测试目录中的 large_stderr.go 专门构造了写入 50,000 字节 stderr 的场景用于验证该截断行为large_stdout.go、large_stderr_chunked.go、large_stdout_chunked.go等则覆盖了块式大输出的边界情况。所有相关行为由 bindir_test.go 中的TestBinDirVerifyImage及其子测试统一验证。四、Image Verifier Caller Contract调用方契约官方文档明确规定了 containerd 作为调用方应遵守的行为契约理解这 7 条是正确使用镜像验证的前提目录缺失或为空则不拦截如果bin_dir不存在或目录中没有文件镜像验证不会阻塞任何拉取操作。实现上bindir.go目录不存在时返回OK: true并附带image verifier directory ... does not exist的 reason目录为空时同样放行。AND 语义合并只有当所有被调用的 verifier 都返回ok判定退出码 0时镜像才被允许拉取任一 verifier 拒绝即整体拒绝。超时与执行失败任一 verifier 超过per_verifier_timeout或无法成功 exec验证以错误error终止返回nil判定最终表现为阻止拉取。max_verifiers 0对调用的 verifier 数量不设上限。max_verifiers 0对调用的数量设限。bin_dir中的条目按名称字典序lexicographic排序取前n max_verifiers个执行其余跳过并记录 warn 日志。这一排序行为来源于os.ReadDir本身按名称排序的特性。执行顺序无保证虽然排序决定了哪些 verifier 被调用但各 verifier 二进制的实际执行顺序不提供任何保证。资源归属verifier 二进制消耗的系统资源当前计入并受限于 containerd 自身的 cgroup但这一行为未来可能变化。在 bindir.go 的VerifyImage主循环中可以看到契约 2、4、5 的具体落地遍历排序后的条目一旦发现(i1) maxVerifiers maxVerifiers 0即跳过剩余条目任一 verifier 退出码非 0 时立即返回OK: falsereason 形如verifier bin rejected image (exit code n): reason全部通过则合并输出bin1 reason1, bin2 reason2, ...形式的 reason。五、验证在镜像拉取流程中的接入点镜像验证被编排在镜像拉取transfer/pull流程中、解析resolve完成之后、实际拉取内容之前。核心调用链位于 core/transfer/local/pull.go// Verify image before pulling. for vfName, vf : range ts.config.Verifiers { logger : log.G(ctx).WithFields(log.Fields{ name: name, digest: desc.Digest.String(), verifier: vfName, }) logger.Debug(Verifying image pull) jdg, err : vf.VerifyImage(ctx, name, desc) if err ! nil { logger.WithError(err).Error(No judgement received from verifier) return fmt.Errorf(blocking pull of %v with digest %v: image verifier %v returned error: %w, ...) } ... if !jdg.OK { logger.Warn(Image verifier blocked pull) return fmt.Errorf(image verifier %s blocked pull of %v with digest %v for reason: %v, ...) } logger.Debug(Image verifier allowed pull) }由此可见两条关键事实此时desc已经是解析后的最终描述符含确定的 digestverifier 拿到的是将要被拉取的确切镜像身份任何 verifier 返回 error 或OK: false拉取都会被直接阻断并返回错误错误信息中携带 verifier 名称、镜像引用、digest 与拒绝原因便于审计排查。六、从零编写一个 verifier 二进制结合第三节的 API 契约一个最小可用的 verifier 只需三步解析参数读取-name、-digest、-stdin-media-type三个参数可用标准库flag读取 stdin按application/vnd.oci.descriptor.v1json解析 OCI descriptor提取 digest、size 等字段做策略判断例如对私有仓库白名单、对 digest 做签名校验、对 annotations 做标签策略等输出判定允许时打印原因并os.Exit(0)拒绝时打印原因并os.Exit(1)或任意非 0 值。将其编译为可执行文件放入bin_dir重启 containerd 即生效。需要注意verifier 由 containerd 以子进程方式exec见 bindir.go 的exec.CommandContext因此它不依赖 containerd 内部库可用任意语言编写只要满足解析参数、读 stdin、按退出码表态的协议即可。七、测试与验证手段仓库提供了完整的自动化测试套件用于验证 bindir 插件的各项契约行为主测试位于 pkg/imageverifier/bindir/bindir_test.goTestBinDirVerifyImage。测试会动态编译 testdata/verifiers 下的多个验证器覆盖以下场景verifier_test_input_output_management由 模板 生成校验传入参数与 stdin 内容是否与契约一致accept_reason_a/b/c退出码 0 的多个 verifier验证 AND 合并下的放行行为reject_reason_d退出码 1 的 verifier验证拒绝行为large_stderr/large_stdout及 chunked 变体验证 32 KiB 截断与管道 deadline 处理slow_child_process验证per_verifier_timeout超时终止与资源清理。如果要在本仓库环境手动复现可在该目录运行go test ./pkg/imageverifier/...执行相关测试用例。结语containerd 的 bindir 镜像验证插件以目录扫描 子进程协议 AND 判定合并的极简设计为镜像拉取提供了一道可编程、语言无关的安全门禁。理解其配置语义bin_dir/max_verifiers/per_verifier_timeout、二进制 API-name/-digest/-stdin-media-type与 stdin descriptor以及 7 条调用方契约即可在实际生产环境中落地镜像签名校验、仓库白名单、供应链安全策略等场景。进一步深读 pkg/imageverifier/bindir/bindir.go 与 core/transfer/local/pull.go 的源码实现将帮助你准确预判边界行为截断、超时、排序、cgroup 资源约束写出稳健可靠的镜像验证器。【免费下载链接】containerdAn open and reliable container runtime项目地址: https://gitcode.com/GitHub_Trending/co/containerd创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表