alice101-dev/kyverno-policy-library

GitHub: alice101-dev/kyverno-policy-library

一套经过单元测试和 e2e 测试的 Kubernetes 准入策略库,通过 Kyverno 在准入阶段强制执行 Pod 安全、网络隔离、供应链完整性和运维规范。

Stars: 1 | Forks: 0

# Kyverno 策略库 — Kubernetes 的策略即代码 [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/alice101-dev/kyverno-policy-library/actions/workflows/ci.yml) 针对 Kubernetes 的**策略即代码**:集群护栏像软件一样进行定义、版本控制和 单元测试,并通过 [Kyverno](https://kyverno.io) 作为**准入时期的铁律**强制执行。一个忘记设置资源限制、 以 root 身份运行或带有 `:latest` 标签的部署,不会只收到代码评审意见——它 会**被集群直接拒绝**。每个策略都使用了良好和不良的测试配置进行单元测试, 因此该库是一道真正的关卡,而不仅仅是一堆 YAML。 如果没有策略即代码,上述每一项控制都只会作为一种约定存在——一个 wiki 页面、 一份评审清单、一条 Slack 提醒。**集群本身会接受 RBAC 允许的任何内容**: 一个距离通过某个内核 CVE 攻陷其所在节点仅一步之遥的 root 容器, 一个来自无人审查过的 registry 的未签名镜像,一个在多次滚动发布之间悄悄改变含义的 `:latest` 标签,一个悄悄向互联网暴露内部 API 的 Service,或者一个篡改工作负载且在 git 中不留痕迹的 `kubectl exec`。这些都不是什么罕见的配置错误——这是**原生集群的默认行为**, 而代码评审中漏看的一行代码就足以将其发布出去。策略即代码 将约定转化为经过版本控制、测试和强制执行的规则:这就是 *“我们要求团队不要以 root 身份运行”*与*“API server 拒绝 root”*之间的区别。 ## 策略 | 策略 | 分类 | 拒绝内容 | | --- | --- | --- | | [`require-non-root`](policies/require-non-root/) | Pod 安全 | 可以 root 身份运行的容器(未设置或覆盖了 `runAsNonRoot`,包括 init 容器) | | [`require-ro-rootfs`](policies/require-ro-rootfs/) | Pod 安全 | 可写的容器根文件系统(包括 init 容器) | | [`require-runtimedefault-profiles`](policies/require-runtimedefault-profiles/) | Pod 安全 | 没有 RuntimeDefault seccomp **和** AppArmor 的 Pod,或覆盖了它们的容器 | | [`disallow-privilege-escalation`](policies/disallow-privilege-escalation/) | Pod 安全 | 未设置或设为 true 的 `allowPrivilegeEscalation` 容器(包括 init 容器) | | [`require-drop-all-capabilities`](policies/require-drop-all-capabilities/) | Pod 安全 | 没有丢弃所有 Linux capabilities 的容器(包括 init 容器) | | [`disallow-automount-sa-token`](policies/disallow-automount-sa-token/) | Pod 安全 | 挂载了不需要的 Kubernetes API token 的 Pod | | [`block-pod-exec`](policies/block-pod-exec/) | Pod 安全 | 系统命名空间之外的 `kubectl exec`/`attach` 会话 — 交互式访问绕过了清单审查 | | [`block-kubectl-cp`](policies/block-kubectl-cp/) | Pod 安全 | `kubectl cp`(一个运行 `tar` 的 exec 操作)— 文件窃取/篡改通道 | | [`restrict-external-ips`](policies/restrict-external-ips/) | 网络安全 | 设置了 `externalIPs` 的 Service(CVE-2020-8554 MITM 攻击向量) | | [`restrict-nodeport`](policies/restrict-nodeport/) | 网络安全 | NodePort 类型的 Service(主机端口会绕过 NetworkPolicy) | | [`no-localhost-service`](policies/no-localhost-service/) | 网络安全 | 指向 `localhost` 的 ExternalName Service(Ingress-controller 漏洞利用) | | [`no-loadbalancer-service`](policies/no-loadbalancer-service/) | 网络安全 | LoadBalancer 类型的 Service(默认面向互联网,且会产生费用) | | [`verify-image-signatures`](policies/verify-image-signatures/) | 供应链 | 没有来自发布密钥的有效 cosign 签名的镜像 | | [`restrict-image-registries`](policies/restrict-image-registries/) | 供应链 | 来自受信任允许列表之外 registry 的镜像(包括 init 容器) | | [`require-image-digests`](policies/require-image-digests/) | 供应链 | 未通过 `@sha256:` 摘要锁定的镜像 — 标签是可变的(包括 init 容器) | | [`disallow-latest-tag`](policies/disallow-latest-tag/) | 供应链 | `:latest`、未打标签或仅带端口的镜像 — 通过摘要锁定则没问题(包括 init 容器) | | [`imagepullpolicy-always`](policies/imagepullpolicy-always/) | 供应链 | 没有设置 `imagePullPolicy: Always` 的可变标签镜像 | | [`block-images-with-volumes`](policies/block-images-with-volumes/) | 供应链 | 使用 VOLUME 语句构建的镜像(会悄悄绕过只读根文件系统) | 该库还附带了一些上面未列出的操作类(非安全类)护栏: [`require-requests-limits`](policies/require-requests-limits/)、 [`require-pod-probes`](policies/require-pod-probes/)、 [`require-pod-anti-affinity`](policies/require-pod-anti-affinity/)、 [`disallow-default-namespace`](policies/disallow-default-namespace/)、 [`disallow-deprecated-apis`](policies/disallow-deprecated-apis/)、 [`prevent-bare-pods`](policies/prevent-bare-pods/)、 [`restrict-jobs`](policies/restrict-jobs/) 和 [`require-container-port-names`](policies/require-container-port-names/)。 每个文件夹都包含该策略以及一个带有测试配置的 `.test/` 目录,还有一个 `kyverno test` 规范,用于断言每个资源的通过/失败状态。 有些行为无法离线模拟——CONNECT 子资源(`kubectl exec`/`cp`)、webhook 注册、API-server 默认值设置。这些策略由实时的 [`e2e/`](e2e/) 测试套件覆盖:[`e2e/run.sh`](e2e/run.sh) 启动一个 kind 集群,安装 Kyverno,并针对该集群运行每个 [`e2e/tests/*.sh`](e2e/tests/)——目前包括 exec 策略和 `disallow-deprecated-apis`。为另一个策略添加实时测试覆盖,只需在 `e2e/tests/` 下添加一个引入了 [`e2e/lib.sh`](e2e/lib.sh) 的新文件;无需 更改工作流。 ### 验证风格:CEL 每个规则都是用 [CEL](https://kyverno.io/docs/policy-types/cluster-policy/validate/#common-expression-language-cel) 编写的—— Kubernetes 原生的验证语言(Kyverno 1.11+),与支持内置 `ValidatingAdmissionPolicy` 的表达式相同。与这些策略最初使用的声明式 `pattern` 风格相比,CEL 带来了以下优势: - 遍历*所有*容器,包括 `initContainers`(patterns 会悄悄跳过 它们); - 捕获覆盖了合规的 Pod 级别设置的容器 (`runAsNonRoot`、seccomp/AppArmor); - 为未设置的字段提供与 kubelet 在运行时实际行为相匹配的 显式默认值(`.?field.orValue(...)`); - 真正的字符串逻辑——例如,registry 端口(`registry:5000/app`)不会 被误认为是镜像标签。 临时容器被刻意排除在容器循环之外,因此 `kubectl debug` 依然可以正常工作。 这些规则有两种形式,都是 CEL。大多数是带有 `validate.cel` 的 `ClusterPolicy`;最新的添加项(`no-loadbalancer-service`、 `require-container-port-names`、`block-images-with-volumes`)使用了 下一代的 `ValidatingPolicy` 类型(Kyverno 1.14+)——表达式相同, 但采用 K8s `ValidatingAdmissionPolicy` 风格的规范。`block-images-with-volumes` 还 使用了 `image.GetMetadata()` CEL 库来从 registry 检查镜像配置;其测试使用 CLI `Context` 文件模拟了这些查找,因此 CI 可以保持 离线状态。 一个刻意的例外:[`verify-image-signatures`](policies/verify-image-signatures/) 使用了经典的 `verifyImages` 规则类型而不是 CEL——这是 Kyverno 自己的 CLI 测试套件所采用的形式,而且 CEL 的 `ImageValidatingPolicy` cosign 证明者无法在 `kyverno test` 下验证。其测试针对 `ghcr.io/kyverno/test-verify-image` 验证了真实的 cosign 签名,因此这是 唯一需要网络访问的测试(在 GitHub Actions 中没问题)。 ## 为什么会有这个项目 清单的安全性仅取决于阅读它的评审人员。该库使这些 规则变得**不可选**:在每次应用时对每个团队强制执行,无论是否有人 审查 YAML。 它与 [supply-chain-secure-build](https://github.com/alice101-dev/supply-chain-secure-build) 配合使用: 那个仓库*生产*可信的镜像(通过摘要锁定、已签名);而这个仓库使 集群*要求*使用它们,在准入阶段拒绝 `:latest` 和未打标签的镜像。 ## 试一试 ``` # Unit-test every policy against its fixtures (what CI runs) kyverno test ./policies/ # Dry-run a policy against your own manifest kyverno apply policies/require-non-root/policy.yaml --resource my-deployment.yaml # Live e2e for policies offline tests can't cover — needs Docker; creates # (and deletes) a throwaway local kind cluster ./e2e/run.sh # all live tests ./e2e/run.sh deprecated-apis # just one ``` ## 安全推出 策略发布时带有 `validationFailureAction: Enforce`(阻断)。当将它们引入到 生产集群时,请先切换到 `Audit` 以查看在不中断部署的情况下*将会*拒绝哪些内容, 修复违规项,然后再切换回 `Enforce`: ``` kubectl apply -f policies/require-non-root/policy.yaml kubectl get policyreport -A # what currently violates the policy ``` ### 系统命名空间已被排除(兼容 GKE) 这些策略仅管理**你的**工作负载。每条规则都排除了 集群自身所依赖的命名空间: | 排除的命名空间 | 原因 | | --- | --- | | `kube-system`, `kube-public`, `kube-node-lease` | 核心组件(kube-dns、konnectivity、metrics-server 等)*在设计上*就是以 root 身份和可写文件系统运行的。 | | `kyverno` | 阻断 Kyverno 自身 Pod 的策略会导致整个集群的准入机制死锁。 | | `gke-*`, `gmp-*`, `config-management-*`, `cnrm-system` | GKE 托管的附加组件(Managed Prometheus、Config Sync、Config Connector)。 | 如果没有这些排除项,节点升级或自动修复(这会重新创建 系统 Pod)可能会被 webhook 阻断,并导致 DNS、日志记录 和指标采集全面瘫痪。 有两个保证作为后盾: - **经过测试**:每个策略的测试套件都在 `kube-system` 中包含了一个不合规的 Pod,并断言它被*跳过*,而不是被拒绝——exec 策略则是以实时方式断言这一点:它们的 e2e 测试套件会 exec 进入一个 `kube-system` Pod,并 期望该操作被允许。移除排除项会导致 CI 失败。 - **可移植**:排除规则存在于策略本身中,因此无论 Kyverno 是如何安装的,它们都能起作用(`resourceFilters` 和 webhook `namespaceSelector` 因安装方式而异)。 `ClusterPolicy` 规则将该列表作为一个 `exclude` 块携带;`ValidatingPolicy` 规则将相同的列表表示为一个 `matchConditions` 条目(该类型没有 `exclude`)。 ## CI 每次推送和拉取请求都会通过 [GitHub Actions](.github/workflows/ci.yml) 运行: - **`kyverno test`** — 每个策略都会针对其良好/不良测试配置进行测试。 - **Gitleaks** — 完整的仓库历史秘密扫描。 - **实时 e2e** ([e2e.yml](.github/workflows/e2e.yml)) — 发生任何 `policies/` 或 `e2e/` 更改时,CI 会运行 [`e2e/run.sh`](e2e/run.sh):启动一个 kind 集群,安装 Kyverno, 针对实时准入机制断言每个 `e2e/tests/*.sh`(exec 策略 和 `disallow-deprecated-apis`,这些是离线测试无法覆盖的)。 ## 参考 - [Kyverno 策略库](https://kyverno.io/policies/) — 官方的即用型策略集合。 - [Kyverno Playground](https://playground.kyverno.io/#/) — 在浏览器中针对资源测试策略,无需集群。 - [kind](https://github.com/kubernetes-sigs/kind) — 在 Docker 中运行临时的本地 Kubernetes 集群,被实时 e2e 测试套件所使用。
标签:DevSecOps, PyVis, 上游代理, 子域名突变, 策略即代码, 聊天机器人安全