feat(template-chart): networkPolicy.ingress.ambientMesh, plus its checked CI exception

Gitea #143. A pod cannot be reached inside Istio's ambient mesh unless its
NetworkPolicy lets ztunnel's HBONE tunnel in - ambient explicitly does not
bypass policy enforcement - and 15008 is deliberately no containerPort of the
application, so assertion 2 of ci/run-networkpolicy-test.sh rejected it.

The switch is a named intention rather than a free port list, and it renders
two things that cannot be had separately: 15008/TCP inbound, and - where
probeSources is non-empty - 169.254.7.127/32 as an additional peer, because
inside the mesh the kubelet's probes are redirected away from the worker
node's address. Whoever opens 15008 by hand has not added the probe address,
and the first narrowed probeSources then drops every probe: the pod never
becomes Ready and the symptom reads as a broken application. That is
assertion 4 of the same script, one value further along.

Assertion 2 keeps its edge. It gets ONE named exception for ONE port number,
gated on one value, and it is not granted in the main loop at all - which
renders every chart with the value at its default, so 15008 stays an ordinary
failure there. The free-port variant was design A of #143 and would have
opened the assertion for all 30 charts.

Assertion 7 holds the exception to five things on two renders per chart:
the port and the ipBlock are absent while the switch is off, 15008 is present
while it is on, 15008 is still the ONLY ingress port that is not a
containerPort, and wherever the rule carrying it restricts sources it also
carries the ipBlock. The last one is reachable because these renders apply
ci/networkpolicy/<chart>.yaml, where probeSources is non-empty.

Built into the existing script, as for #136: the exception weakens assertion 2
and belongs beside it - a separate script could go green while the exception
it is paired with is wrong, with nothing connecting the two. The job already
runs over every chart on every pull request.

Found by running it: the policy's name comes from Chart.yaml's name, not from
the directory - template-chart is called "xxx".

Not proven, and said so in values.yaml, the template header and the script's
header: that 15008 alone suffices comes from Istio's documentation, and the
probe-source half reaches no release, because probeSources is empty in every
chart here. No namespace of this cluster carries istio.io/dataplane-mode, so
there is no path on which either could be measured. What is proven is the
render and the assertions.
This commit is contained in:
jklein committed 2026-10-19 17:56:19 +00:00
1 parent 725d046dd9
commit badfaa32cb
4 files changed
+548

No files matched your search

+56
View File
@@ -272,6 +272,62 @@ networkPolicy:
podLabels:
app.kubernetes.io/name: rke2-traefik
# The policy's INBOUND side. Mind the three names that overlap as substrings,
# because a search for "ingress" matches all of them and they are not the
# same thing:
#
# ingress (top level) the Ingress OBJECT this chart
# renders - host, TLS, className
# networkPolicy.ingress this block: what the policy lets IN
# networkPolicy.ingressController the SOURCE SELECTOR for the controller's
# pods. A SIBLING of this block and NO PART
# OF IT, despite the name.
ingress:
# Opens the pod for Istio's ambient mesh. OFF by default, and additive:
# with it off this chart renders byte for byte as it did before the value
# existed.
#
# WHAT IT RENDERS, and the two halves are the point:
#
# 1. 15008/TCP inbound - ztunnel's HBONE tunnel. Every inbound mesh
# connection arrives on it, and Istio's documentation is explicit that
# ambient "does not and will never bypass Kubernetes NetworkPolicy
# enforcement". Without this port a pod whose namespace has joined the
# mesh is unreachable.
# 2. 169.254.7.127/32 as an additional probe source - but ONLY where
# probeSources is non-empty, because only then does the rule have a
# "from" list at all. Inside the mesh the kubelet's probes are
# redirected and no longer come from the worker node's address.
#
# WHY ONE SWITCH AND NOT TWO. Point 2 is the one nobody thinks of. An
# operator who adds "15008" to a port list by hand writes a policy that
# passes review, and the first time probeSources is narrowed every probe is
# dropped: the pod never becomes Ready and the symptom reads as a broken
# application rather than as a policy. A switch whose halves can only be
# had together cannot be set halfway - which is why this is a named
# intention and not a free port list.
#
# TWO THINGS IT DOES NOT PROVE, and they belong next to the value rather
# than in a commit message:
#
# - That 15008 alone is enough. That comes from Istio's documentation.
# Measured on this cluster is only which listeners ztunnel opens
# ([::]:15008 inbound, plus its own admin and stats ports). Provable at
# the first namespace that joins the mesh; none carries
# istio.io/dataplane-mode yet.
# - That the probe-source half works. probeSources is empty in every
# chart here, so no release reaches that branch. CI proves its render,
# not its effect.
#
# If the policy restricts sources, note that 15008 is restricted with them:
# the port joins the rule that carries the probe port, so it is reachable
# from that rule's peers only. Whether ztunnel needs a peer of its own is
# open - it runs with its own pod IPs in istio-system (measured: no
# hostNetwork on the DaemonSet, Istio 1.30.6), so if one turns out to be
# needed it is expressible as a namespaceSelector plus podSelector and does
# not need an unknowable node-IP CIDR.
ambientMesh: false
egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
# has no switch - a knob whose only other setting breaks every name lookup