内容节选自《containerd 原理剖析与实战》,新书内购中,点击阅读原文,限时 69.9 元购买。
1. containerd 中的镜像解密
OCI 镜像规范中,一个镜像是由多层镜像层构成的,镜像层可以通过加密机制来加密机密数据或代码,以防止未经授权的访问。如下图所示。
图 镜像加密原理
OCI 镜像加密原理主要是在原来的 OCI 镜像规范基础上,添加了一种新的 mediaType,表示数据文件被加密;同时在 annotation中添加具体加密相关信息。镜像层没加密前的原始数据如下。
json
"layers":[
{
"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip",
"digest":"sha256:7c9d20b9b6cda1c58bc4f9d6c401386786f584437abbe87e58910f8a9a15386b",
"size":760770
}
]
加密之后的数据如下。
json
"layers":[
{
"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip+encrypted",
"digest":"sha256:c72c69b36a886c268e0d7382a7c6d885271b6f0030ff022fda2b6346b2b274ba",
"size":760770,
"annotations": {
"org.opencontainers.image.enc.keys.jwe":"eyJwcm90ZWN0Z...",
"org.opencontainers.image.enc.pubopts":"eyJjaXBoZXIiOi..."
}
}
]
在启动容器时, containerd 通过解密信息来解密这些加密镜像。这些解密信息包括密钥、选项和加密元数据。这些信息配置在 CRI Plugin 的 image_decryption 配置项中。此外,还需要设置正确的密钥模型并确保已正确配置 stream processors
和 containerd imgcrypt
解码器。
下面介绍如何在 containerd 的 CRI Plugin 中配置镜像解密。在介绍 CRI Plugin 中的镜像解密前,先介绍 k8s 中的镜像加解密 和 containerd 中的 stream_processor。
1.k8s 生态的镜像加解密
首先介绍镜像加密模式,Kubernetes 社区共支持两种镜像加密模式:
-
Node Key Model,将密钥放在 Kubernetes 工作节点上,以节点为粒度实现解密,参考图4.21 所示。
-
Multi-tenancy Key Model,多租户模型,以集群粒度实现解密(当前社区还未实现)。
图 镜像解密模式 Node Key Model
containerd 中当前支持的 是 Node Key Model ,如上图所示,这种模式下 containerd 会在可信的 Node 上进行拉取镜像并利用私钥进行解密镜像。具体配置如下。首先是 containerd 中配置 CRI Pluging 的 image_decryption
选项,
ini
version = 2
[plugins."io.containerd.grpc.v1.cri".image_decryption]
key_model = "node"
在 containerd 及以后的版本中, key_model = "node"
是默认的配置,如果是 1.4 以及以前的配置,则需要手动配置上述信息并重启 containerd。除此之外,还需要配置 stream_processors
配置项。
2. containerd 中的 stream_processors
stream_processors 是 containerd 中的一种基于内容流的二进制 API。
传入的内容流通过 STDIN 传递给对应的二进制文件,二进制处理后输出 STDOUT 到 stream_processors,如下图所示。
_图 stream\processors 处理流程
streaming_processor 是对二进制的调用,如上图所示,相当于针对每层镜像都进行了 unpiz 操作,等价于:
arduino
<tar image layer>=`unpiz -d -c <tar.gzip image layer>`
其中:
-
<tar.gzip image layer>
为输入的 targzip 格式的镜像层。 -
<tar image layer>
则为执行unpiz -d -c
之后的stdout 输出,即解压的结果。 该示例的 stream_processor 配置如下。
ini
version = 2
[stream_processors]
[stream_processors."io.containerd.processor.v1.pigz"]
accepts = ["application/vnd.docker.image.rootfs.diff.tar.gzip"]
returns = "application/vnd.oci.image.layer.v1.tar"
path = "unpigz"
args = ["-d", "-c"]
stream_processor
中支持的配置有:
-
ID : 即 示例中的 "
io.containerd.processor.v1.pigz
",通过stream_processors.<Process ID>
来指定某个 processor 的配置。 -
accepts: 该 processor 能处理的格式。
-
returns: 该 processor 处理之后的格式。
-
path: 该 processor 对应的可执行二进制文件的路径。
-
args :该 processor 处理时所需的参数。path + args 组成该 processor 的处理步骤,例如上述示例则是
unpigz -d -c
。
此外,processor 还支持 env 配置,格式为 ["key1=value1","key2=value2"]
。
3. 配置镜像解密
containerd 中的镜像解密则是利用了 stream_processor
机制,containerd/imgcrypt (https://github.com/containerd/imgcrypt)
中的二进制 ctd-decoder
对每层镜像进行解密。具体配置如下。
ini
version = 2
[plugins."io.containerd.grpc.v1.cri".image_decryption]
key_model = "node"
[stream_processors]
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]
accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]
returns = "application/vnd.oci.image.layer.v1.tar+gzip"
path = "ctd-decoder"
args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
env= ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
[stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
returns = "application/vnd.oci.image.layer.v1.tar"
path = "ctd-decoder"
args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
env= ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
上述配置中,利用二进制 ctd-decoder 通过参数 --decryption-keys-path
指定镜像解密私钥,分别对 tar 格式和 tar.gzip 格式进行解密。
以上内容节选自新书 《containerd 原理剖析与实战》
最后,附上本书的购买链接,新书刚刚上架原价 109,限时优惠内购 69.9 元 ,感兴趣的朋友可以尽快入手。内购链接点击左下角**"阅读原文**"
本文使用 文章同步助手 同步