邓宇星,Rancher 中国软件架构师,7 年云原生领域经验,参与 Rancher 1.x 到 Rancher 2.x 版本迭代变化,目前负责 Rancher for openEuler(RFO)项目开发。
最近 Rancher v2.7.4 发布了,作为一个安全更新版本,也让大家对软件供应链安全多了一份思考。本文将围绕其中比较流行的容器镜像安全管理进行探讨和实践。

—— 运行环境介绍 ——
| 软件/操作系统 | 版本 |
|---|---|
| Rancher | v2.7.4 |
| Host OS: openEuler | 22.03-LTS-SP1 |
| K8s: RFO | v1.26.5+rfor1 |
| cosign | v2.0.2 |
| Harbor | v2.8.2 |
| Client OS: MacOS | 13.4 |
| Kubewarden | v1.6.0 |
本次实践将使用最新版 Rancher v2.7.4 作为 K8s 集群管理工具。在此之上可安装 Kubewarden UI 作为可视策略管理工具。整体运行架构以及流程如下:

本次使用 Harbor + cosign + Kubewarden 的组合,对容器镜像进行存储/签名/验证操作,cosign 与 harbor v2.5.0+版本有较好的兼容性,能直接在 harbor UI 上看到 cosign 证书配置。经测试 Harbor v2.1.1 版本也可使用 cosign 进行签名,只是 Harbor UI 上显示不太直观。

—— 环境准备 ——
Harbor 安装请参考官网文档1,请尽量使用 https 方式让 Harbor 对外提供服务,并使用可信的证书减少配置步骤。在本次实践当中,我们使用一个免费可信证书来规避此问题。
cosign 安装参考官方文档2,本次使用的是
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
的方式安装。RFO 参考上文的 quick 地址,本次准备环境使用 1 master + 1 worker 的集群,机器规格都是 4c8g,确保有足够的资源运行所需组件。
Rancher 使用 Docker run 方式启动,并将 RFO 集群使用 import 方式纳管到 Rancher 中。
Kubewarden 是一个开源项目,它是针对 Kubernetes 平台的策略引擎。它的目标是通过策略即代码的方式,为 Kubernetes 集群提供强大的安全性和策略控制功能。Kubewarden 可以帮助管理员和开发人员定义和实施各种策略,以确保应用程序和基础设施的安全性和合规性。
首先,我们需要在 Rancher 开启 extension 以及安装 Kubewarden 插件,从左侧菜单进入 Configuration
-> Extensions
:
extension开启 Extensions 后,在 Available
选项卡中,找到 Kubewarden
进行安装:

PS:安装时,如果是国内的环境,容易出现安装插件失败的情况,可以使用以下操作进行修复
进入 local 集群的 workload 页面,编辑 cattle-ui-plugin-system
命名空间下 ui-plugin-operator
工作负载配置
在 Pod
选项卡中,修改 Networking
配置,添加 Host Aliases
,配置:185.199.110.133 raw.githubusercontent.com
并保存,这样能减少国内 Github 相关域名被 DNS 污染的问题。
安装成功后,刷新页面,并进入下游 RFO 集群管理页面,左侧菜单出现 Kubewarden
管理页面。

并根据 Kubewarden 安装指引安装 Kubewarden,这个过程需要安装 cert-manager
,将 Kubewarden
的 charts repo 添加到 Rancher 中,并在集群中安装 Kubewarden
APP。安装成功后,集群中 Kubewarden
页面如下:

根据提示安装 default-policy-server,这个默认 policy-server 将会使用 validatingwebhookconfigurations
以及 mutatingwebhookconfigurations
进行策略应用。


—— 镜像构建推送并验证 ——
整个镜像推送验证的环节包括:
使用 cosign 创建镜像签名密钥
在
Kubewarden
界面中,创建镜像签名验证策略构建镜像并推送到镜像仓库中
对镜像使用 cosign 签名并推送
在集群中部署镜像
参考文档3,这次我们使用 k8s secret 作为 cosign 的 kms provider,使用以下命令创建密钥:
# 创建 cosign namespace
kubectl create ns cosign
# 使用cosign 创建 key,并存储在 k8s secret中,通过 COSIGN_PASSWORD 指定证书密码,此处为了简化操作,使用了空密码,请在应用时使用密码加强管理
COSIGN_PASSWORD= cosign generate-key-pair k8s://cosign/imagekey
# 通过kubectl 查看secret是否创建成功
kubectl get secret -n cosign imagekey
# NAME TYPE DATA AGE
# imagekey Opaque 3 2m46s
正常情况下,公钥会同时保存在运行目录下的 cosign.pub
文件中。
进入集群下的 Kubewarden
菜单下 ClusterAdmissionPolicy
页面,点击 Create
进入创建页面,查找 Verify Image Signatures
:

PS: 在第一次进入时,可能无法看到 artifacthub 中 kubewarden 策略,可以根据页面提示,添加 artifacthub.io 到访问白名单即可查询到配置。
选择 Verify Image Signatures
策略之后,在 General 中填写 Name image-validate
,然后切换到 Settings
配置页面中,修改 Signature Type
为 PublicKey
:

在 Public Key
下点击 Add
之后,出现 image
输入框,此处需要输入镜像匹配名称,本次填写为 yuxing-test.cnrancher.com/test/*
,其中 yuxing-test.cnrancher.com
是本次测试使用的 Harbor 域名,test
为镜像所在项目。
在 Public Keys
下添加我们的证书公钥,内容可以在cosign 创建签名密钥 中找到。输入完成后,点击 Finish 创建完成。
创建完成后,回到 ClusterAdmissionPolicies
列表页面,我们可以看到刚创建的 policy 状态:

等待 Policy 状态为 Active
后生效,过程中 default-policy-server
会重启进行重新配置。若规则配置有问题,可以通过 kubectl get po -n cattle-kubewarden-system
找到 policy-server 并查看日志确认问题。
本次简化构建镜像过程,使用 alpine:3.18.0
作为测试目标。
# pull alpine image
docker pull alpine:3.18.0
# retag it to our registry
docker tag $DIGEST yuxing-test.cnrancher.com/test/alpine:3.18.0
# push to registry
docker push yuxing-test.cnrancher.com/test/alpine:3.18.0
使用 cosign 对镜像进行签名:
# get the digest of alpine image, it should be only to diegst for this image and the one we need should be index to 1
IMAGE_DIGEST=`docker inspect --format='{{index .RepoDigests 1}}' yuxing-test.cnrancher.com/test/alpine:3.18.0`
# sign image with key stored in k8s, --tlog-upload=false is aim to not upload transparency log to sigstore server.
cosign sign --key k8s://cosign/imagekey --tlog-upload=false ${IMAGE_DIGEST}
# Pushing signature to: yuxing-test.cnrancher.com/test/alpine
签名完成后,可以登录到 Harbor,查看该镜像的签名情况:

同时我们推送 alpine:3.17.0
镜像到镜像仓库中但不进行 cosign 签名。
docker pull alpine:3.17.0
docker tag alpine:3.17.0 yuxing-test.cnrancher.com/test/alpine:3.17.0
docker push yuxing-test.cnrancher.com/test/alpine:3.17.0
我们在 Rancher UI 上,进入集群管理页面,使用 yuxing-test.cnrancher.com/test/alpine:3.18.0
镜像创建 deployment:

点击创建后,成功部署,并注意到镜像名称被修改为只用 digest 的格式。
然后我们使用没有签名的 alpine:3.17.0
镜像创建 deployment,在创建后,会提示由于无签名而无法创建 pod:

如果我们对 Verify Image Signatures
规则进行调整/升级,并应用到 Deployment/StatefulSet/DaemonSet 等类型上,则会在创建该类型资源时提示错误。
至此,我们完成了整个镜像的构建、签名和部署。

—— 总结与思考 ——
镜像签名的场景,可以应用在容器供应链安全的保障上,增强镜像来源的可信程度,在以下场景能发挥巨大的作用:
使用镜像签名对生产环境进行保护,只部署已签名的镜像,减少镜像从其他环境误操作导致进入生产环境的问题。
对引入的开源镜像进行可信扫描并签名,进行镜像准入核查。
使用签名结合 SBOM(Software Bill of Materials)文档,增强对镜像中的软件进行溯源和审计的能力。
在引入签名的同时,也会带来额外的管理成本:
使用
self-generated
证书固然很方便,但不利于分发和管理,对于大型的开发团队来说并不是一个好的选择。引入 CA(certificate authority) 管理机构,或使用公开可信的 CA 机构签发的证书进行签名。
使用公网中可信 CA 的证书进行签名,需要 CI 环境有互联网访问的能力,并不利于私有环境的场景。cosign 本身也提供基于 sigstore 的 CA 机构
Fulcio
签发证书对镜像进行签名的操作。虽然公有云中有提供私有 CA 证书管理服务,但大部分场景下企业需要有自己的证书管理工具与流程,这些流程本身就不利于证书的利用。
对签名密钥的生命周期管理一直都是证书管理领域的大问题,这个问题在容器镜像签名中被放大。对何时需要对证书进行轮换,以及证书轮换时的影响与调整方案,都需要更深入的研究。
[1]https://goharbor.io/docs/2.8.0/install-config/
[2]https://docs.sigstore.dev/cosign/installation
[3]https://docs.sigstore.dev/cosign/signing_with_self-managed_keys/


Containerd 的 Bug 导致容器被重建!如何避免?

SUSE 成立 RFO SIG,建设面向 openEuler 的容器基础设施平台


About Rancher
Rancher 是一个开源的企业级 Kubernetes 管理平台,实现了 Kubernetes 集群在混合云+本地数据中心的集中部署与管理。Rancher 一向因操作体验的直观、极简备受用户青睐,被 Forrester 评为“ 2020 年多云容器开发平台领导厂商”以及“ 2018 年全球容器管理平台领导厂商”,被 Gartner 评为“2017年全球最酷的云基础设施供应商”。
目前 Rancher 在全球拥有超过三亿的核心镜像下载量,并拥有包括中国联通、中国平安、中国人寿、上汽集团、三星、施耐德电气、西门子、育碧游戏、LINE、WWK 保险集团、澳电讯公司、德国铁路、厦门航空、新东方等全球著名企业在内的共 40000 家企业客户。
2020 年 12 月,SUSE 完成了对 RancherLabs 的收购,Rancher 成为了 SUSE "创新无处不在 (Innovate Everywhere) "企业愿景的关键组成部分。SUSE 和 Rancher 共同为客户提供了无与伦比的自由和所向披靡的创新能力,通过混合云IT基础架构、云原生转型和IT运维解决方案,简化、现代化并加速企业数字化转型,推动创新无处不在。
当前,SUSE 及 Rancher 在中国大陆的业务由数硕软件(北京)有限公司承载。SUSE 在国内拥有优秀的研发团队、技术支持团队和销售团队,将结合Rancher 领先的云原生技术,为中国的企业客户提供更加及时和可信赖的技术支撑及服务保障。





