☰
rkt 元数据服务实战:用 mds-example 在 Pod 间实现文件签名与验签
2026/9/25 5:41:20 网站建设 项目流程
  • 容器运行时
  • 云原生
  • 网络

【免费下载链接】rkt

[Project ended] rkt is a pod-native container engine for Linux. It is composable, secure, and built on standards.

项目地址:https://gitcode.com/gh_mirrors/rk/rkt
点击查看免费下载

本指南以 rkt 仓库中的 metadata-service 示例程序 为骨架,系统讲解 rkt 元数据服务(Metadata Service)的身份断言能力。你将学会编译 mds-example、在两个 busybox Pod 之间传输并校验带签名的消息文件,同时通过源码(metadata_service.go、registration.go)深入理解签名背后的 HMAC 机制与AC_METADATA_URL的注入链路。

一、背景:rkt 元数据服务能做什么

rkt 的元数据服务(由rkt metadata-service命令实现)旨在帮助运行中的应用感知自身执行环境并断言其 Pod 身份,具体能力包括:

  • 暴露 Pod 与镜像的 manifest 内容(通过 handlePodManifest、handleImageManifest);
  • 提供便捷的注解(annotations)查询接口(handlePodAnnotations 等);
  • 为 Pod 提供密码学可验证的身份,即基于 HMAC 的签名与验签端点。

服务对外暴露两类监听(见 runMetadataService):

通道地址用途
Unix socket/run/rkt/metadata-svc.sockPod 注册/注销事件(仅本机 stage0 访问)
TCP端口18112Pod 内应用访问公开 API(manifest、注解、签名验签)

其中端口与 socket 路径定义在 common/common.go(MetadataServicePort = 18112、MetadataServiceRegSock = "/run/rkt/metadata-svc.sock"),TCP 端口可用--listen-port调整。

本指南示例程序(mds-example)正是围绕元数据服务的Identity Endpoint构建的:它调用元数据服务完成对文件的签名(sign)与验签(verify),遵循 App Container 规范(appc spec)中 ACE 的 Identity 端点设计。签名文件格式就是元数据服务返回的签名内容的 base64 编码。

二、示例程序源码拆解:sign 与 verify 两个子命令

示例程序位于 Documentation/examples/metadata-service/main.go,是一个单文件 Go 程序,通过两个子命令工作:

  • sign --file=FILE --signature=SIGNATURE_FILE:读取待签名文件内容,POST 到元数据服务的签名端点,将响应体(base64 签名)写入指定文件;
  • verify --file=FILE --uuid=POD_UUID --signature=SIGNATURE_FILE:将文件内容、发送方 Pod UUID 与签名一并 POST 到验签端点,根据 HTTP 状态码判定签名是否有效。

2.1 命令行参数定义

init()中使用标准库flag定义参数(main.go):

  • sign:--file(待签名文件)、--signature(签名输出文件);
  • verify:--file(待验证文件)、--uuid(发送方Pod 的 UUID)、--signature(签名文件)。

usage()(main.go)输出完整的命令帮助;parseArgs在参数缺失或命令非法时报错退出。

2.2 签名流程:sign()

签名核心逻辑在 sign():

  1. 从AC_METADATA_URL环境变量读取元数据服务地址mdsURL;
  2. 拼接签名端点路径acMetadata/v1/pod/hmac/sign(使用path.Join生成,与元数据服务端路由runPublicServer中注册的/pod/hmac/sign对应);
  3. ioutil.ReadFile读入待签名文件,通过http.PostForm将内容作为content表单字段提交;
  4. 响应状态为 200 时,将响应体(base64 编码的 HMAC 签名)以0600权限写入签名文件。

值得注意的是,verify()(main.go)在拼接端点时使用path.Join("/", "acMetadata", "v1", "pod", "hmac", "verify"),注意首部没有 token 段——因为AC_METADATA_URL本身已包含 token 前缀(见第五节)。

2.3 验签流程:verify()

  1. 读取待验证文件内容与签名文件;
  2. POST 三个表单字段:content、uuid(发送方 Pod UUID)、signature;
  3. 按状态码判定:200 OK返回有效(true);403 Forbidden返回无效(false);其他状态视为未知错误。

主函数 main() 中,AC_METADATA_URL缺失时会输出诊断提示:

$AC_METADATA_URL env variable not found, are you in a rkt container and is the rkt metadata service running on the host?

验签成功打印signature OK,失败打印signature INVALID并以退出码 1 结束。

三、编译示例程序

在 Documentation/examples/metadata-service 目录下执行:

$ CGO_ENABLED=0 go build -o mds-example

设置CGO_ENABLED=0可生成纯静态二进制,便于在容器内挂载运行。

四、端到端演练:在两个 Pod 之间签名并传输文件

以下步骤取自 README 原文并补充注释。前提:已编译 mds-example、宿主上元数据服务正在运行。

4.1 启动两个 busybox Pod

启动 "POD a"(交互式、注册元数据服务,并把 mds-example 以 host volume 挂载进/bin/mds-example):

$ sudo rkt run --mds-register --interactive --volume=mds-example,kind=host,source=$PWD/mds-example kinvolk.io/aci/busybox:1.24 --mount volume=mds-example,target=/bin/mds-example [POD a] / #

再启动 "POD b"(相同命令):

$ sudo rkt run --mds-register --interactive --volume=mds-example,kind=host,source=$PWD/mds-example kinvolk.io/aci/busybox:1.24 --mount volume=mds-example,target=/bin/mds-example [POD b] / #

关键点是--mds-register:该标志在 rkt/run.go 中定义为"register pod with metadata service. needs network connectivity to the host (--net=(default|default-restricted|host)",且与--net=none不兼容(rkt/run.go)。

4.2 在 POD a 中创建并签名消息

[POD a] / # echo "Very trustworthy message" > msg.txt [POD a] / # mds-example sign --file=msg.txt --signature=msg.sig [POD a] / # ls -l msg* -rw------- 1 root root 125 Dec 18 17:34 msg.sig -rw-r--r-- 1 root root 25 Dec 18 17:34 msg.txt

可以看到签名文件以0600权限写出,大小 125 字节,即 96 字节 SHA-512 HMAC 的 base64 编码加上换行。

4.3 通过 nc 将消息与签名传输到 POD b

先获取 POD b 的 IP:

[POD b] / # ip a (...) inet 172.16.28.84/24 scope global eth0 (...)

在 POD b 上先监听接收消息,再到 POD a 发送:

# 在 POD b [POD b] / # nc -l -p 9090 > msg.txt # 切换到 POD a 发送 [POD a] / # nc 172.16.28.84 9090 < msg.txt

再用同样方式传输签名文件:

# 在 POD b [POD b] / # nc -l -p 9090 > msg.sig # 切换到 POD a 发送 [POD a] / # nc 172.16.28.84 9090 < msg.sig

4.4 获取 POD a 的 UUID

元数据服务把 Pod UUID 通过公开接口暴露,Pod 内可用AC_METADATA_URL直接查询:

[POD a] / # echo $(wget -q -O - $AC_METADATA_URL/acMetadata/v1/pod/uuid) d1703a6a-568a-48ee-b84b-4ccd803743dd

这里AC_METADATA_URL由 rkt 在 Pod 启动时注入(见第五节)。

4.5 在 POD b 中验证消息

[POD b] / # cat msg.txt Very trustworthy message [POD b] / # mds-example verify --file msg.txt --uuid=d1703a6a-568a-48ee-b84b-4ccd803743dd --signature=msg.sig signature OK

验证时提交的--uuid必须使用发送方(签名方)Pod的 UUID,因为签名正是用该 UUID 参与计算的(见下文 HMAC 原理)。如果文件被篡改或 UUID 不匹配,程序会输出signature INVALID并退出码为 1。

五、底层原理:AC_METADATA_URL 注入与 HMAC 签名

5.1 注册与 token 机制

Pod 注册发生在 stage0 的 run.go:当--mds-register开启时,调用registerPod()(registration.go),流程为:

  1. 生成 16 字节随机 token(generateMDSToken);
  2. 通过 Unix socket/run/rkt/metadata-svc.sock以PUT /pods/{uuid}?token=...注册 Pod manifest;
  3. 再为每个 app 以PUT /pods/{uuid}/{app}注册 Image manifest;
  4. 将--mds-token=$TOKEN传给 stage1,最终 stage1 init 依据网络情况构造元数据服务公开 URL。

网络初始化阶段(stage1/init/init.go)根据 Pod 的网络模式决定公开 URL 的 IP:有 CNI 网络时使用可转发的主机 IP,否则使用localhost。URL 的拼接格式定义在 common/common.go:

func MetadataServicePublicURL(ip net.IP, token string) string { return fmt.Sprintf("http://%v:%v/%v", ip, MetadataServicePort, token) }

即http://<ip>:18112/<token>。随后 stage1 在构建应用环境时把该 URL 注入为环境变量(stage1/init/common/app.go):

if p.MetadataServiceURL != "" { pa.env.Set("AC_METADATA_URL", p.MetadataServiceURL) }

这正是示例程序从AC_METADATA_URL取地址、且 URL 自带 token 前缀的原因——token 是 Pod 在元数据服务中的身份凭证,公开 API 的路由统一以/{token}/acMetadata/v1为前缀(runPublicServer)。

5.2 签名与验签的 HMAC 实现

元数据服务在启动时通过initCrypto()从系统熵源生成 512 位 HMAC 密钥(metadata_service.go),密钥仅存于进程内存。

签名端点 handlePodSign:

  1. 用 token 查得 Pod UUID;
  2. 校验content表单字段非空;
  3. 计算HMAC-SHA-512( UUID 字节 || content );
  4. 以 base64 编码返回,Content-Type 为text/plain; charset=us-ascii。

验签端点 handlePodVerify:

  1. 解析uuid、content、signature(base64 解码)三个字段;
  2. 用提交的 UUID 与 content 重算 HMAC;
  3. 通过hmac.Equal常量时间比较,匹配返回200,不匹配返回403 Forbidden。

这里的核心安全语义:签名是把内容绑定到特定 Pod 身份的密钥化哈希,因此验签时必须知道签名方的 UUID;同时 HMAC 密钥不会离开元数据服务,应用之间无法伪造彼此签名。token 即"谁有权限让元数据服务代表 Pod 签名/验签"的认证凭据,因此元数据服务进程与 Pod 之间应视为互信边界。

5.3 Pod 注销

Pod 停止时,stage0 的unregisterPod()(registration.go)会依据mds-registered标记文件向/pods/{uuid}发送DELETE请求,从 podStore(内存中的双索引表,metadata_service.go)移除对应记录。

六、局限与注意事项

README 明确声明这是一个示例应用:

  • 它把整个文件内容读入内存并作为表单字段 POST,不适用于大文件;
  • 签名仅提供完整性校验与 Pod 身份绑定,不提供加密与机密性;
  • 未做并发、流式、超时等生产级处理,不适合生产环境直接使用。

生产场景应参考 Documentation/subcommands/metadata-service.md 的完整接口说明(含--listen-port选项与 systemd socket activation 注意事项),并结合业务自行实现流式签名方案。

七、相关文档与源码速查

  • 元数据服务完整说明:Documentation/subcommands/metadata-service.md
  • 示例程序源码:Documentation/examples/metadata-service/main.go
  • 元数据服务实现:rkt/metadata_service.go
  • Pod 注册/注销与 token 生成:stage0/registration.go
  • --mds-register标志定义:rkt/run.go
  • 元数据服务端口与 socket 常量:common/common.go
  • AC_METADATA_URL注入:stage1/init/common/app.go
  • 元数据服务相关测试:rkt/metadata_service_test.go、rkt_metadata_service_test.go
  • 容器运行时
  • 云原生
  • 网络

【免费下载链接】rkt

[Project ended] rkt is a pod-native container engine for Linux. It is composable, secure, and built on standards.

项目地址:https://gitcode.com/gh_mirrors/rk/rkt
点击查看免费下载
上一篇:RabbitMQ备份恢复:数据迁移和灾难恢复方案
下一篇:如何通过自定义LabelImg导出模板解决特定项目标注格式需求

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询