镜像固定与来源管控
目标
建立一套标准配置:将进入集群的镜像固定到 Digest,将来源限制为允许列表,并在 ServiceAccount 层级处理私有 Registry 认证。
为什么重要
标签是可变指针。同一个 v1.2 标签可以推送不同镜像,因此昨天验证的镜像可能不同于今天运行的镜像。Digest 是内容哈希,从根本上消除了这一问题。发生事故时,能否立即回答“当前运行的究竟是什么”,会直接决定响应速度。
接下来是来源控制。允许的 Registry 列表会直接成为策略引擎的参数,imagePullSecrets 则是连接该 Registry 的凭据。与其在每个 Pod 中填写,不如附加到 ServiceAccount,让新工作负载自动继承,从而减少遗漏。
此环境无法访问互联网,因此不能真正拉取镜像,也无法安装 Kyverno 等策略引擎 CRD。所以策略清单以文件形式编写和评分,其他部分则按真实对象评分。
步骤
- 创建命名空间
cks-supply,并在其中创建 Podpinned。镜像不使用标签,而固定到 Digest(registry.cks.local/shop/api@sha256:后跟 64 位十六进制数)。imagePullPolicy为IfNotPresent。 - 在
/root/cks-supply-chain/allowed-registries.txt中逐行写入允许的 Registry prefix,严格按以下顺序写 4 行:registry.cks.local/、harbor.cks.local/library/、registry.k8s.io/、ghcr.io/cks-lab/ - 在
cks-supply中创建类型为kubernetes.io/dockerconfigjson的 Secretharbor-pull。服务器为harbor.cks.local,用户名为robot$cks,密码为cks-lab-token。 - 在
cks-supply中创建 ServiceAccountdeployer,并在其imagePullSecrets中加入harbor-pull。 - 在
/root/cks-supply-chain/verify-images.yaml中编写签名验证策略清单。apiVersion: kyverno.io/v1、kind: ClusterPolicy,名称为verify-image-signature;spec.validationFailureAction为Enforce;唯一规则的spec.rules[0].name为check-signature,并通过verifyImages[0].imageReferences[0]将registry.cks.local/*设为目标。 - 在
cks-supply中创建 Podlegacy-web(镜像nginx:latest),复现未固定工作负载。然后在/root/cks-supply-chain/find-unpinned.sh中编写检测脚本,并将执行结果保存到/root/cks-supply-chain/unpinned.txt。结果应列出cks-supply命名空间中容器镜像未固定到@sha256:的 Pod,格式为cks-supply/<파드이름>,按字典序排序,每行一个。 - 在
cks-supply中创建 Deploymentcheckout。replicas 为 2,选择器和 Pod 标签为app=checkout,serviceAccountName为deployer,容器名称为app;镜像以registry.cks.local/开头并固定到@sha256:,imagePullPolicy为IfNotPresent。
参考
- 如需 64 位 Digest,不必使用
head -c 32 /dev/urandom | sha256sum;也可像echo cks | sha256sum一样使用任意字符串的哈希。只要格式正确即可。 kubectl create secret docker-registry harbor-pull --docker-server=... --docker-username=... --docker-password=... -n cks-supplykubectl patch serviceaccount deployer -n cks-supply -p '{"imagePullSecrets":[{"name":"harbor-pull"}]}'- 常见错误 1:镜像同时使用标签和 Digest(
nginx:1.27@sha256:...)。本实验只保留 Digest。 - 常见错误 2:允许列表 prefix 末尾漏写斜杠,会让
registry.cks.local.evil.com/之类的名称通过。
使用 Digest 而非标签固定镜像
创建命名空间 cks-supply,并在其中创建 Pod pinned。镜像不使用标签,而固定到 Digest(registry.cks.local/shop/api@sha256: 后跟 64 位十六进制数)。imagePullPolicy 为 IfNotPresent。
镜像引用格式为 <레지스트리>/<이름>@sha256:<64자리 16진수>。不要同时使用标签和 Digest,只保留 Digest。
允许的 Registry 列表
在 /root/cks-supply-chain/allowed-registries.txt 中逐行写入允许的 Registry prefix,严格按以下顺序写 4 行:
registry.cks.local/、harbor.cks.local/library/、registry.k8s.io/、ghcr.io/cks-lab/
该列表会原样传给策略引擎。使用 prefix 格式并保留末尾斜杠,避免相似名称的其他 Registry 通过。
Registry 认证 Secret
在 cks-supply 中创建类型为 kubernetes.io/dockerconfigjson 的 Secret harbor-pull。
服务器为 harbor.cks.local,用户名为 robot$cks,密码为 cks-lab-token。
一行 kubectl create secret docker-registry 即可。类型名称是 kubernetes.io/dockerconfigjson,数据键为 .dockerconfigjson。
为 ServiceAccount 添加 pull Secret
在 cks-supply 中创建 ServiceAccount deployer,并在其 imagePullSecrets 中加入
harbor-pull。
加入 ServiceAccount 的 imagePullSecrets 后,所有使用该 SA 的 Pod 都会自动认证,比逐个 Pod 配置更可靠。
签名与 SBOM 验证策略清单
在 /root/cks-supply-chain/verify-images.yaml 中编写签名验证策略清单。
apiVersion: kyverno.io/v1、kind: ClusterPolicy,名称为 verify-image-signature;
spec.validationFailureAction 为 Enforce;唯一规则(spec.rules[0].name 为
check-signature)通过 verifyImages[0].imageReferences[0] 将 registry.cks.local/*
设为目标。
此集群没有策略引擎 CRD,因此只编写文件。通过 imageReferences 选择目标 Registry,并在 attestors 中放置公钥。
检测未固定 Digest 的 Pod
在 cks-supply 中创建 Pod legacy-web(镜像 nginx:latest),复现未固定工作负载。
然后在 /root/cks-supply-chain/find-unpinned.sh 中编写检测脚本,并将执行结果保存到
/root/cks-supply-chain/unpinned.txt。结果应列出 cks-supply 命名空间中镜像未固定到
@sha256: 的容器所在 Pod,格式为 cks-supply/<파드이름>,按字典序排序,每行一个。
镜像字符串不含 @sha256: 即为未固定。可以使用 jq 的 contains 或 test 过滤,同时不要遗漏 init 容器。
综合:具备固定、认证与来源控制的部署
在 cks-supply 中创建 Deployment checkout。replicas 为 2,选择器和 Pod 标签为
app=checkout,serviceAccountName 为 deployer,容器名称为 app,镜像以
registry.cks.local/ 开头并固定到 @sha256:,imagePullPolicy 为
IfNotPresent。
原样使用前面创建的 ServiceAccount 和允许列表中的第一个 prefix。将镜像固定到 Digest,并同时指定 imagePullPolicy。