
Kubescape Rego 与 CEL 对等性验证regocelparity 夹具目录的设计、实现与刷新指南【免费下载链接】kubescapeKubescape is an open-source Kubernetes security platform for your IDE, CI/CD pipelines, and clusters. It includes risk analysis, security, compliance, and misconfiguration scanning, saving Kubernetes users and administrators precious time, effort, and resources.项目地址: https://gitcode.com/GitHub_Trending/ku/kubescape导读Kubescape 的安全评估引擎正处于从 OPA/Rego 规则库向 CELCommon Expression Language策略迁移的阶段同一个安全控制control同时拥有 regolibrary 提供的 Rego 规则实现和 vendored CEL bundle 提供的 ValidatingAdmissionPolicy 实现。core/pkg/opaprocessor/testdata/regocelparity/目录专门存放喂给Rego 对 CEL 对等性测试装置parity harness的夹具数据配合 processorhandler_regocelparity_test.go 在完全相同的 Kubernetes 对象上分别运行两套引擎逐资源比对裁决verdict从而在迁移过程中持续暴露两套实现之间的语义漂移。阅读本文后你将理解这套夹具的目录布局、哪些文件被刻意丢弃、哪些文件被做了机械性修改及原因、已知分歧divergence是如何被登记和固化的以及如何按官方流程刷新夹具并重新运行对等性测试。背景Kubescape 为什么需要 Rego 与 CEL 两套引擎的对等性验证Kubescape 是一个开源的 Kubernetes 安全平台支持 IDE、CI/CD 流水线和集群三类场景的风险分析、安全、合规与错误配置扫描。其策略评估经历了从 OPA/Rego 到 CEL 的渐进式迁移一部分控制control被转换为 ValidatingAdmissionPolicyVAP随 CEL bundlecel-admission-library一起被 vendored 进仓库见 cel/bundle.go 与 cel/loader.go。迁移带来的核心风险是两套实现说不同的话Rego 规则仍在 regolibrary 中维护CEL 策略在 cel-admission-library 中维护而仓库里此前没有任何机制校验它们对同一对象给出相同结论。regocelparity测试正是为此而生——对每个同时以 Rego 规则集和 CEL 策略发布的控制把同一批对象分别喂给两个引擎逐一比较每个资源的两边裁决。测试文件开头对此有清晰注释A control that has been converted now has two implementations: the Rego rules regolibrary still ships, and the ValidatingAdmissionPolicy the CEL bundle ships. Nothing in the tree checked that they said the same thing. This runs both over the same objects and compares them.而夹具的来源选择也刻意避开了自己写夹具自证清白的陷阱rules/是 kubescape/regolibrary 在标签v2.0.33上对应目录的拷贝仅保留那些同时以 CEL 策略发布的控制背后的规则。夹具直接采用 regolibrary 自己的规则测试用例——这些是规则作者认为有代表性的案例比在 CEL 策略旁边手写的夹具更难被质疑harder to argue with than fixtures written alongside the CEL policy。夹具目录布局镜像 regolibrary让刷新接近纯拷贝core/pkg/opaprocessor/testdata/regocelparity/顶层由两部分组成default-config-inputs.jsonregolibrary 原样拷贝、未做任何修改。它是扫描在用户未做任何覆盖override时交给 Rego 侧的postureControlInputs。rules/按规则名镜像 regolibrary 的目录结构。每个规则目录rules/rule-name/的布局如下rules/rule-name/rule.metadata.json 规则元数据不含 Rego 源码 rules/rule-name/raw.rego 成为 PolicyRule.Rule规则主体 rules/rule-name/filter.rego 成为 PolicyRule.ResourceEnumerator仅部分规则有 rules/rule-name/test/case/input/* 清单文件每个文件一个对象 rules/rule-name/test/case/input.yaml regolibrary 以单文件书写用例时的同一内容这一布局在夹具目录 README 中有完整定义其目的是mirroring regolibrary so a refresh is close to a plain copy——刷新时几乎可以逐目录原样复制。以真实规则为例rule-credentials-in-env-var 的 rule.metadata.json 展示了元数据如何声明match匹配哪些 apiGroups/apiVersions/resources与configInputs该规则消费哪些postureControlInputs配置项{ name: rule-credentials-in-env-var, ruleLanguage: Rego, match: [ { apiGroups: [], apiVersions: [v1], resources: [Pod] }, { apiGroups: [apps], apiVersions: [v1], resources: [Deployment, ReplicaSet, DaemonSet, StatefulSet] }, { apiGroups: [batch], apiVersions: [*], resources: [Job, CronJob] } ], configInputs: [ settings.postureControlInputs.sensitiveValues, settings.postureControlInputs.sensitiveKeyNames, settings.postureControlInputs.sensitiveValuesAllowed, settings.postureControlInputs.sensitiveKeyNamesAllowed ], ruleQuery: armo_builtins }raw.rego则是规则主体的实际策略源码。以 duplicate-env-var/raw.rego 为例它通过deny contains msga if { ... }结构对 Pod 的 containers / initContainers / ephemeralContainers 以及各类工作负载Deployment、ReplicaSet、DaemonSet、StatefulSet、Job、ReplicationController、CronJob逐层检查是否存在同名环境变量并借助make_alert生成包含alertMessage、failedPaths、alertScore、alertObject的告警结构。注意规则声明package armo_builtins且ruleQuery为armo_builtins这正是 Kubescape 规则库的统一约定。filter.rego是规则级资源枚举器ResourceEnumerator仅部分规则携带。例如 automount-default-service-account/filter.rego 只筛选出metadata.name default的 ServiceAccount 进行告警从而缩小上报范围。在测试装置中loadParityRules 会读取rule.metadata.json将raw.rego赋给PolicyRule.Rule将存在的filter.rego赋给PolicyRule.ResourceEnumerator与 regolibrary 自身导出规则的方式完全一致。default-config-inputs.jsonRego 侧的默认 postureControlInputsdefault-config-inputs.json 以settings.postureControlInputs为根包含大量控制可消费的配置项例如insecureCapabilities如SETPCAP、NET_ADMIN、SYS_ADMIN、ALL、BPF等被认为不安全的 Linux capabilitiessensitiveKeyNames如aws_secret_access_key、password、token、jwt、bearer、credential等敏感键名sensitiveValues如BEGIN \\w PRIVATE KEY、AKIA[0-9A-Z]{16}、ghp_[0-9A-Za-z]{36}、AIza[0-9A-Za-z\\-_]{35}、xox[baprs]-[0-9A-Za-z]{10,48}等敏感值正则sensitiveKeyNamesAllowed/sensitiveValuesAllowed用于降低误报的允许名单默认为空以及imageRepositoryAllowList、floatingImageTags、trustedCosignPublicKeys、wlKnownNames、recommendedLabels、k8sRecommendedLabels等。测试装置通过 regoLibrarySettings 读取该文件并解出postureControlInputs。这里有一个刻意为之的设计CEL 侧不从这个文件取配置——CEL 策略消费的是 bundle 自带的参数ControlConfiguration。这与真实扫描的行为完全一致Rego 规则读 regolibrary 分发的postureControlInputsCEL 策略读 sync-vap 分发的配置。把两侧配置成相同文件会是一个更干净的实验却是一个更没用的实验因为配置库之间的漂移改变扫描结果的程度与表达式本身的差异不相上下。因此夹具保留了这个不对称让任何配置漂移都以分歧disagreement的形式浮出水面而不是被抹平掩盖。对等性测试装置如何工作同一个对象两个引擎一次裁决比较整个装置的实现集中在 processorhandler_regocelparity_test.go核心思路可以浓缩为三句话两侧都走生产代码里的processRule路径唯一的差异是规则语言Rego vs CEL因此枚举enumeration、命名空间过滤、通过推断pass-inference等逻辑完全一致分歧只能来自评估本身只比较裁决verdict不比较修复路径remediation与风险评分分母——这两个领域两侧刻意描述得不同。测试入口 TestRegoCELParityTestRegoCELParity 遍历regoCELParityControls表对每个控制用 loadParityRules 加载其 Rego 规则集用 parityCases 收集其全部夹具用例一个用例 一个test/case/input/目录或单个input.yaml/input.json对每个用例分别调用regoVerdicts与celVerdicts得到两侧的逐资源裁决用 compareVerdicts 逐资源比较产出形如resourceID: regoverdict celverdict的分歧行。Rego 侧regoVerdicts把控制的所有规则逐条跑在同一个newParityScanner上再按生产代码processControl的方式折叠多规则控制的裁决——任一规则判失败则资源整体判失败。CEL 侧celVerdicts则构造一个合成的cel-controlID规则把 Rego 规则的Match与DynamicMatch全部搬运过去让两个引擎面对相同的候选对象随后 CEL 策略自己的matchConstraints排除掉的对象会以CEL 从未给出裁决not evaluated的形式显现——这本身就是一种值得看见的分歧而非需要隐藏的细节。processRule在生产代码中的实现见 processorhandler.go#L1094-L1112无显式 scope 时正是对等性装置走的路径遍历所有 evaluation scope逐 scope 调用processRuleOnScope并把结果按资源 ID 合并。注释也明确指出 processRule no longer mutates the state of the current OPAProcessor instance, and returns a map instead, to be merged by the caller——这也是两侧各建独立 scannernewParityScanner的原因processRuleOnScope会把聚合器输出写回AllResources一侧的运行不能让另一侧看到自己新增的内容。裁决合并的优先级多规则、多 scope 的结果通过 mergeVerdicts 折叠优先级与正式报告一致失败failed 未知unknown 通过passed由apis.Compare实现。任何一侧对某资源缺失裁决时verdictOr 会将其视为not evaluated保证比较是全量而非部分。防止空对空的守卫测试装置内置两道防线防止夹具退化TestRegoCELParityTestdataIsReferencedL292-L318保证rules/下每个规则目录都被regoCELParityControls引用且每个被引用的规则都真实存在raw.rego。孤儿目录意味着刷新时拷贝了装置永远不会运行的东西看起来像覆盖实则没有。必须至少评估一个资源对无分歧控制require.NotZero(t, evaluated, ...)保证 CEL 策略在 vendored 夹具中真的匹配并评估过资源——CEL 一个都没匹配于是天然与 Rego 一致不算覆盖。此外newParityScanner 会用require.NotContains拒绝同一用例内共享资源 ID 的两个夹具AllResources按 ID 键控同 ID 会静默坍缩成一个导致用例安静地只测了一半。资源组键group/version/kind由 resourceGroupKey 从对象自身的apiVersion/kind推导末段使用 kind 而非复数资源名从而无需维护覆盖二十多种 kind 的复数映射表。什么被丢弃为什么夹具目录刻意不拷贝 regolibrary 规则测试中的两类文件test/case/expected.json装置比较的是两个引擎彼此之间的结果而不是与某个存储的期望输出比对因此 regolibrary 自己的期望结果在这里不是参考基准。test/case/data.json这是 regolibrary 用例级别的postureControlInputs覆盖。装置对每个用例统一读取default-config-inputs.json因为那才是扫描真正读取的配置regolibrary 针对收窄后的输入写的用例在这里可能失败尽管 regolibrary 自己的测试预期它通过。关键在于两个引擎看到的是扫描中它们各自会看到的配置——这正是装置要回答的问题。什么被修改为什么三处机械编辑夹具并非原样拷入而是做了三处机械性修改。README 特别提醒这三处修改都值得反馈回 regolibrary以免后续刷新把它们又改回去。移除被提升到已提供版本served version的 API 版本。7 个 CronJob 夹具原本使用batch/v1beta11 个 CSIStorageCapacity 夹具使用storage.k8s.io/v1beta1。VAP 的matchConstraints会指名具体版本而 regolibrary 规则匹配用的是apiVersions: [*]——于是在旧版本下夹具只到达 Rego 侧而到不了 CEL 侧用例实际什么都没比较。升级到batch/v1与storage.k8s.io/v1后两侧都参与比较并且结论一致。重命名了一个 Service。在ensure-https-loadbalancers-encrypted-with-tls-aws/test/failed_multiple_loadbalancers用例中两个夹具原本都叫Service/api且没有命名空间因而共享同一个资源 ID第二个覆盖了第一个名为多个负载均衡器的用例实际只测了一个。第二个 Service 现改名为api-tls见 loadbalancer_success.yaml其metadata.name: api-tls并携带service.beta.kubernetes.io/aws-load-balancer-ssl-cert注解。由于装置会拒绝共享 ID 的夹具这个问题不可能再悄悄复发。把一个 Tab 换成空格。在role-in-default-namespace/test/role中装置使用的sigs.k8s.io/yaml基于go.yaml.in/yaml/v2接受注释前用 Tab 做分隔夹具因此能解析、用例照常运行但更严格的 YAML 读取器会拒绝它使该夹具成为任何其他消费方的隐患因此现在改为空格。分歧管理gap 与 expect让故意不一致也有据可查regoCELParityControls表中的每个条目都是一个 regoCELControl 结构体关键字段controlID控制 IDrules该控制运行的规则目录名多规则控制的裁决是任一规则失败即失败的 OR 折叠divergence非空时说明该控制的两套实现为什么故意不一致。这样的控制仍会被两侧评估且被要求确实在某处不一致——一条过期的分歧条目比从未写过的更糟expect该控制产生的精确分歧集合每资源一行case resourceID: regoverdict celverdict。钉死集合而非只断言有分歧意味着上游修好的缺口和新出现的无关分歧都会在此失败而不是互相掩盖gap标记该分歧是缺陷而非决策——CEL 侧本应与 Rego 一致但目前没有。修复应发生在 cel-admission-library 而绝不在此处条目会一直保留直到 pin 升级带回修正后的策略、测试因相反原因开始失败。gap 清零是目标。regoCELParityControls覆盖了同时以 Rego 规则集和 CEL 策略发布的全部控制规则列表取自各控制 regolibrary JSON 的rulesNamesREADME 注明ControlID_RuleName.csv只覆盖规则库的一部分缺失了其中大多数。从源码可见覆盖范围包括 C-0012、C-0013、C-0193/0194/0195、C-0197、C-0202/0203/0204、C-0207、C-0212、C-0225、C-0231、C-0234、C-0262、C-0263、C-0275/0276、C-0292、C-0295、C-0296 等其中 C-0081CVE-2022-24348虽已转换但缺席——regolibrary 没有为其提供规则测试两侧都没有可喂的对象覆盖它需要手写夹具属于另一类论证regolibrary 自己的用例一致应单独提交。几个值得展开的已知分歧C-0012凭据泄漏gapRego 做了三件 CEL 策略没做的事豁免文件路径值is_not_file_path、通过(?i)对sensitiveValues做大小写不敏感匹配以及 bundle 的ControlConfiguration缺少 regolibrary 分发的四个敏感值正则AKIA、AIza、ghp_、xox。预期分歧精确到四条例如rule-credentials-in-env-var/env-value-akia-key /v1//Pod/akia-key-pod: regofailed celpassed。C-0013非 root 容器gapCEL 策略额外要求allowPrivilegeEscalationfalse而non-root-containers不检查它一个非 root 但未声明提权策略的容器通过 Rego、失败于策略。PSA 系列C-0193/0194/0195/0197/0202/0203/0204共用常量 psaNamespaceSubject 记录的原因——Rego 规则读取强制命名空间的pod-security.kubernetes.io/enforce标签并判 Namespace 失败CEL 策略检查的是 CIS 条目真正关注的 workload。评估主体不同两侧报告的就不是同一批资源regolibrary 的 Namespace 夹具根本到不了 CEL 策略。这是一个决定而非未完成的转换而且是跨七个控制的一个分歧不是七个分歧。预期结果形如.../Namespace/my-namespace: regofailed celnot evaluatedbaseline 系列见 psaBaselineExpect。C-0212default 命名空间资源Rego 豁免 apiserver 在 default 命名空间创建的 kubernetes Service 与 EndpointsCIS 5.7.4 排除项对应 regolibrary#644CEL 策略没有该豁免于是它会对每个集群都存在的两个资源判失败。该控制扇形展开到17 条规则每个不得存在于 default 命名空间的 kind 一条裁决取所有规则的 OR。刷新流程从 regolibrary 拉取并清理README 给出了标准刷新流程假设已经有一个 regolibrary 的本地克隆git -C regolibrary archive tag | tar -x -C /tmp/regolib cp /tmp/regolib/default-config-inputs.json . # for each rule directory named in regoCELParityControls: cp -r /tmp/regolib/rules/rule-name rules/ find rules -name expected.json -delete find rules -name data.json -delete随后还需要更新本文档README顶部的 tag更新测试中的regoCELParityLibraryVersion常量当前为v2.0.33见 processorhandler_regocelparity_test.go#L64使其与夹具来源保持一致——这个常量紧挨装置存放即使刷新时忘记更新 README测试读取的位置仍会有记录运行装置测试。刷新后某个控制的裁决不再一致正是这套装置存在的意义要么 Rego 变动了、CEL 策略必须跟进要么该分歧是刻意的、应当登记到divergence列表并给出理由。如何在本地运行这些测试在仓库根目录执行go test ./core/pkg/opaprocessor/ -run TestRegoCELParity -v若要连同夹具树与控制表同步的守卫一起验证go test ./core/pkg/opaprocessor/ -run TestRegoCELParityTestdataIsReferenced -v需要注意的边界夹具读取器sigs.k8s.io/yaml只解码单个 YAML 文档readManifestL570-L585会显式拒绝多文档清单\n---当前 vendored 夹具都是单文档刷新若带入多文档文件需要先拆分。用例按规则目录逐个收集parityCases只遍历test/下的目录跳过非目录条目单文件用例支持input.yaml与input.json两种形式readCaseObjects。CEL 侧报错几乎总是 bundle 的问题策略没带该控制或loadVAP拒绝离线引擎无法兑现的策略如依赖 namespaceSelector 收窄matchConstraints的策略见 cel/loader.go 中 refusing it to preserve scan/admission parity 的注释。小结一套自证其罪的迁移护栏regocelparity的价值不在于证明 Rego 与 CEL 一致而在于持续、精确地暴露不一致夹具取自规则作者自己的测试用例配置按真实扫描的分布喂给两侧分歧被登记为必须逐条匹配的精确集合缺陷性分歧gap被要求最终清零。这套机制让 Rego→CEL 迁移不再是相信转换完成而是每次刷新都重新证明。对希望为 Kubescape 贡献策略迁移工作的开发者而言regoCELParityControls表、divergence/gap字段与 README 中的刷新脚本构成了完整的入手路径。【免费下载链接】kubescapeKubescape is an open-source Kubernetes security platform for your IDE, CI/CD pipelines, and clusters. It includes risk analysis, security, compliance, and misconfiguration scanning, saving Kubernetes users and administrators precious time, effort, and resources.项目地址: https://gitcode.com/GitHub_Trending/ku/kubescape创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考