feat(ci): assert every rendered NetworkPolicy against its own render

The part that carries the rollout of Gitea #5. 29 charts get a policy that is
on by default, and every way one of them can be wrong is SILENT: the API server
accepts a policy that filters nothing just as readily as one that filters
everything, and neither is a render error, so neither the build nor a rollout
sees it.

ci/run-networkpolicy-test.sh holds every rendered NetworkPolicy against the pod
specs, probes and Services of the SAME render:

  1. the podSelector really matches a pod of that render - otherwise the object
     sits in the namespace, is listed by kubectl, reads as a boundary and
     filters nothing, usually because a label was renamed in another file;
  2. every INGRESS port is a containerPort of the selected pods, which catches
     the NodePort mistake mechanically. Egress ports are deliberately excluded:
     an egress port belongs to the DESTINATION, and comparing it would fail
     every chart that reaches a database;
  3. no ingress rule allows every source by accident. "from: []" always fails -
     it allows everything while reading as a restriction. A MISSING "from" is
     the documented open form and is accepted only where the render justifies
     it: every port of the rule is addressed by a network probe, or published by
     a NodePort Service. That is stricter than the plan's wording, which banned
     both forms, and it had to be, because the precedents use the second one on
     purpose;
  4. the probe path. For every port a NETWORK probe addresses, the policy either
     leaves it unrestricted or the chart offers networkPolicy.probeSources AND
     says in NOTES.txt that a list missing the node's IP leaves the pod
     permanently un-Ready. exec probes are excluded by design - they run inside
     the container and put no packet on the network, which is exactly why
     valkey and minecraft-server can restrict their port by source with no
     probeSources at all.

Two guards beyond the four: a chart that renders NO policy fails, because that
is how this issue decayed in the first place - garage and keycloak were both
created after #5 and both arrived without one. And a "#" comment inside a
rendered policy fails, because it is part of the object and is shipped to the
cluster.

Helm is the parser through ci/yaml2json, as in the other render tests: the
runners have neither pyyaml nor yq, and a hand-written parser could read
nothing, find nothing and leave this green over a chart it never looked at.

Every failure path was run, not just the green ones, and one of them earned its
keep immediately: the probeSources check was a substring test, so a chart whose
key had been deleted while the comment above it still discussed the value
passed. It now looks for the key.

Part of #5
This commit is contained in:
jklein committed 2026-10-14 13:56:06 +00:00
1 parent 1001de620a
commit 36bcfd3b11
3 files changed
+918

No files matched your search

+46
View File
@@ -303,6 +303,52 @@ jobs:
- name: "Check every rendered scalar that must be a string"
run: ci/run-yaml-scalar-render-test.sh
networkpolicy:
name: "NetworkPolicy"
runs-on: ubuntu-latest
# The sixth job that ignores the changed-chart matrix and the fifth that runs
# over *every* chart (Gitea #5), for the same reason as the render tests
# above: the mistake arrives by copy into a chart whose own diff nobody reads
# for it - and since #5 this convention is in all 29 installable charts, so
# there is a lot of copy.
#
# What it asserts, on every NetworkPolicy a chart renders, against the pod
# specs, probes and Services of the SAME render: the podSelector really
# matches a pod; every ingress port is a containerPort of those pods; no
# ingress rule allows every source by accident; and every port a NETWORK
# probe addresses is either left unrestricted or comes with
# networkPolicy.probeSources and the consequence written into NOTES.txt.
# Plus two guards of its own: a chart that renders NO policy fails, and a
# rendered "#" comment fails.
#
# Every one of those failures is SILENT. The API server accepts a policy
# whose selector matches nothing - it then sits in the namespace, is listed
# by kubectl, reads as a boundary and filters exactly nothing - just as
# readily as it accepts one naming a NodePort number, which matches no
# traffic at all because the policy is evaluated at the pod's interface
# after kube-proxy has already translated the port. Neither is a render
# error and neither denies anything, so neither the build nor a rollout can
# see it.
#
# It does NOT replace the install-test, which is the real net under this
# convention: that one proves ENFORCEMENT on a live k3s with its policy
# controller running, and it is what makes "all charts at once, default on"
# defensible. This job is the cheap half, and it covers the two charts the
# install-test cannot reach at all - satisfactory and template-chart, both
# in ci/excluded-charts.txt.
#
# No cluster and no Trivy; a few seconds for the whole repository, depends
# on no other job. The YAML is parsed by Helm through ci/yaml2json, because
# the runners have neither pyyaml nor yq and a hand-written parser could read
# nothing and report nothing - see the script's header.
steps:
- name: "Checkout Repository"
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: "Install Helm"
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5
- name: "Check every rendered NetworkPolicy against the render it belongs to"
run: ci/run-networkpolicy-test.sh
trivy-config:
name: "Trivy config ${{ matrix.chart }}"
needs: detect-changed-charts