diff --git a/ci/l0.sh b/ci/l0.sh index b6b4dec..9708d2d 100755 --- a/ci/l0.sh +++ b/ci/l0.sh @@ -52,12 +52,25 @@ elif [ ! -d "$KYVERNO_POLICY_DIR" ] || ! compgen -G "${KYVERNO_POLICY_DIR}/**/ky skip "no kyverno-test.yaml under '${KYVERNO_POLICY_DIR}' (T-0.5 / platform/kyverno not delivered yet) — UNWIRED, not green" summary "- kyverno-l0: **SKIPPED** (no policy tests yet — T-0.5)" else - if kyverno test "$KYVERNO_POLICY_DIR"; then + # The ClusterPolicies carry a Flux postBuild token in validationFailureAction + # (${KYVERNO_VALIDATION_ACTION}, substituted in-cluster from + # clusters/base/cluster-vars.yaml). The offline `kyverno test` CLI does NOT run + # Flux substitution, so the literal token is an invalid enum value and those + # policies fail to load (no result rows -> "Not found"). Render a throwaway copy + # with the base/prod value (Enforce — the strict posture the gate must prove) + # before testing. This does not change what the policies enforce in-cluster. + : "${KYVERNO_VALIDATION_ACTION:=Enforce}" + l0_render="$(mktemp -d)" + cp -a "${KYVERNO_POLICY_DIR}/." "$l0_render/" + find "$l0_render" -type f -name '*.yaml' -print0 \ + | xargs -0 sed -i "s/\${KYVERNO_VALIDATION_ACTION}/${KYVERNO_VALIDATION_ACTION}/g" + if kyverno test "$l0_render"; then ok "Kyverno L0 policy tests passed" summary "- kyverno-l0: **passed**" else err "Kyverno L0 policy tests failed"; summary "- kyverno-l0: **FAILED**"; rc=1 fi + rm -rf "$l0_render" fi case "$rc" in diff --git a/ci/versions.env b/ci/versions.env index 4807ddd..e235451 100644 --- a/ci/versions.env +++ b/ci/versions.env @@ -10,7 +10,7 @@ # L0 policy gate (Kyverno CLI) + ServiceDefinition validation (kyverno-json) KYVERNO_VERSION="1.13.4" -KYVERNO_JSON_VERSION="0.0.5" +KYVERNO_JSON_VERSION="0.0.3" # L1/L2/L3 declarative runner (Chainsaw) — invoked via conformance/ entrypoint, # pinned here so a local run matches CI even before the in-cluster Job exists. diff --git a/platform/kyverno/scripts/run-l0.sh b/platform/kyverno/scripts/run-l0.sh index b8e788f..40174b1 100755 --- a/platform/kyverno/scripts/run-l0.sh +++ b/platform/kyverno/scripts/run-l0.sh @@ -18,7 +18,18 @@ cd "$HERE/.." echo "== L0 gate: Kyverno CLI $KYVERNO_VERSION / kyverno-json $KYVERNO_JSON_VERSION ==" echo "-- [1/2] ClusterPolicy tests (kyverno test) --" -kyverno test ./tests +# The ClusterPolicies carry a Flux postBuild token in validationFailureAction +# (${KYVERNO_VALIDATION_ACTION}, substituted in-cluster from clusters/base/cluster-vars.yaml). +# The offline `kyverno test` CLI does not run Flux substitution, so render a throwaway copy +# with the base/prod value (Enforce) before testing — otherwise those policies fail to load +# and their result rows report "Not found". This does not change in-cluster enforcement. +: "${KYVERNO_VALIDATION_ACTION:=Enforce}" +L0_RENDER="$(mktemp -d)" +trap 'rm -rf "$L0_RENDER"' EXIT +cp -a . "$L0_RENDER/" +find "$L0_RENDER" -type f -name '*.yaml' -print0 \ + | xargs -0 sed -i "s/\${KYVERNO_VALIDATION_ACTION}/${KYVERNO_VALIDATION_ACTION}/g" +kyverno test "$L0_RENDER/tests" echo "-- [2/2] ServiceDefinition validation (kyverno-json scan) --" kyverno-json scan \ diff --git a/platform/kyverno/tests/kyverno-test.yaml b/platform/kyverno/tests/kyverno-test.yaml index dfbbc4d..68426fa 100644 --- a/platform/kyverno/tests/kyverno-test.yaml +++ b/platform/kyverno/tests/kyverno-test.yaml @@ -6,6 +6,8 @@ apiVersion: cli.kyverno.io/v1alpha1 kind: Test metadata: name: platform-kyverno-l0 +# Namespace labels for namespaceSelector resolution offline (see values.yaml). +variables: values.yaml policies: - ../policies/require-default-deny-baseline.yaml - ../policies/validate-default-deny-shape.yaml diff --git a/platform/kyverno/tests/values.yaml b/platform/kyverno/tests/values.yaml new file mode 100644 index 0000000..c613ff4 --- /dev/null +++ b/platform/kyverno/tests/values.yaml @@ -0,0 +1,18 @@ +# Values for `kyverno test` -- supplies the Namespace LABELS the offline CLI needs to +# resolve `namespaceSelector` matches (R-SEC-02/R-NET-05/R-IDP-02 policies scope by the +# app-namespace label). Without this the engine cannot see a Namespace's labels for a +# Pod under test, so every namespaceSelector rule is silently EXCLUDED and the pass/fail +# assertions below become vacuous. These labels mirror resources/namespaces.yaml so the +# app-namespace pods are genuinely evaluated and the platform-namespace pod is genuinely +# out of scope (skip). +apiVersion: cli.kyverno.io/v1alpha1 +kind: Values +metadata: + name: values +namespaceSelector: + - name: app-ns + labels: + homelab.keithjackson.dev/app-namespace: "true" + - name: platform-ns + labels: + kubernetes.io/metadata.name: platform-ns