feat(template-chart)!: ambientMesh becomes an object, and the CI exception gets a pinned expectation
Gitea #143, maintainer's instruction. Both numbers this switch renders are ISTIO CONSTANTS rather than standards, and a value with a documented default is the better home for a vendor constant than a template literal: an operator can correct it when Istio changes the number, without waiting for a chart release. networkPolicy: ingress: ambientMesh: enabled: false port: 15008 redirectedProbeSources: - 169.254.7.127/32 One object under one switch, so the coupling argument survives the change instead of being undermined by it - two separate values could drift apart, which is the thing this design exists to prevent. redirectedProbeSources is a LIST because a dual-stack cluster needs BOTH of Istio's addresses at once, not one of two; that is a nameable case rather than speculation, so it does not contradict reason 4 of the issue. Istio's IPv6 counterpart is documented as the dual-stack addition, with the reason it is absent today unchanged: SingleStack IPv4, and an ipBlock of the wrong family is accepted and matches nothing. Named redirectedProbeSources, not probeSources, because the confusion risk with networkPolicy.probeSources one level up is larger than the ingress/ ingressController one: that value holds YOUR worker node CIDRs, is empty by default and means OPEN when empty; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. The distinction is written where both appear. PROVENANCE, which the previous round explained the coupling of but never attributed. 15008 is Istio's HBONE port - not IANA-registered for this, not prescribed by any protocol, unknown to Kubernetes. 169.254.7.127 is link-local (169.254.0.0/16, RFC 3927, never routed - the range one picks vendor magic out of); Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, which is what vendor constants look like; and Kubernetes knows the address in neither the NetworkPolicy API nor the kubelet - only Istio's data plane rewrites the probe source onto it. Both read for ztunnel 1.30.6. If either changes, the failure is SILENT: a pod unreachable in the mesh, or probes dropped where probeSources is non-empty, which is nowhere today. NULL-SAFE, per #99 and because #136 shipped this exact defect one value away (its NOTES.txt branch dereferenced .networkPolicy.egress directly): every read goes through a fallback reproducing the chart's own declared defaults, so a deleted object or one carrying only "enabled: true" renders the defaults rather than failing. Clearing the probe-source list restores the constant instead of removing the ipBlock, deliberately - the halves are not separable, and enabled: false is how you open nothing. THE CONSEQUENCE, and it is the point of the CI half. With the port configurable, the exception can no longer be checked against the rendered value: "whatever the value says" is design A's hole wearing design C's name, and a mistyped port would pass by sitting in the configurable field. So the expectation is PINNED IN THE TEST - AMBIENT_PORT and AMBIENT_CIDR - and assertion 7 now checks two things instead of one: that the render at chart defaults produces exactly that port and that ipBlock, and that every chart's values.yaml DECLARES the same defaults, read back through ci/yaml2json rather than by regex. A chart that drifts is red. Changing an Istio constant means touching 30 values files AND the pinned lines, and the pinned lines are what force someone to look. That block is therefore also the single place saying what we hold Istio's numbers to be, so it carries the provenance too. The renders set only enabled=true and never a port override, on purpose: the pin is a statement about the defaults we ship, not about what a release may choose - an operator overriding a value is outside this test's reach, as for every value here. The declared-defaults check is conditional on the chart offering probeSources, like the inseparability check and assertion 4: the five charts whose probe rule is never source-restricted render no ipBlock, so for them the key must be ABSENT - a value reaching no rule reads as a control that does nothing - and that absence is asserted rather than tolerated. Their schemas drop it too. No version change: nothing of this is published, the switch stays additive with its default off, and the render at defaults is byte-identical - re-diffed per chart against origin/main, not assumed.
This commit is contained in:
4 files changed
+358
-98
No files matched your search
@@ -113,7 +113,23 @@ spec this policy selects.
|
||||
round of this work already reported ingressController as an inbound
|
||||
escape hatch on the strength of the substring alone. */}}
|
||||
{{- $npIngress := $np.ingress | default dict }}
|
||||
{{- $ambientMesh := $npIngress.ambientMesh }}
|
||||
{{- /* NULL-SAFE ON PURPOSE, and not as decoration (#99). ambientMesh is an
|
||||
OBJECT, so every read below goes through a fallback that reproduces THIS
|
||||
chart's declared defaults - because an operator who deletes the object,
|
||||
or who writes only "enabled: true" and leaves the rest out, must land on
|
||||
the defaults and not in a nil dereference. #136 shipped exactly that
|
||||
defect one value away from here: the NOTES.txt branch dereferenced
|
||||
.networkPolicy.egress directly, and a release that had dropped the key
|
||||
failed to render.
|
||||
|
||||
Note which fallback the probe-source list gets: "default" treats an EMPTY
|
||||
list as unset, so clearing it restores the constant rather than removing
|
||||
the ipBlock. That is deliberate - the two halves of this switch are not
|
||||
separable, and the way to not open the port at all is enabled: false. */}}
|
||||
{{- $ambient := $npIngress.ambientMesh | default dict }}
|
||||
{{- $ambientEnabled := $ambient.enabled }}
|
||||
{{- $ambientPort := $ambient.port | default 15008 }}
|
||||
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
|
||||
{{- $routed := or .Values.ingress.enabled .Values.httpRoute.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
@@ -147,20 +163,36 @@ spec:
|
||||
- protocol: {{ .protocol | default "TCP" }}
|
||||
port: {{ .containerPort }}
|
||||
{{- end }}
|
||||
{{- if $ambientMesh }}
|
||||
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh. 15008/TCP is
|
||||
ztunnel's HBONE tunnel, the port every inbound mesh connection
|
||||
arrives on once the namespace carries istio.io/dataplane-mode.
|
||||
Istio's own documentation is explicit that ambient "does not and
|
||||
will never bypass Kubernetes NetworkPolicy enforcement", so
|
||||
without this port a pod in the mesh is simply unreachable.
|
||||
{{- if $ambientEnabled }}
|
||||
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh. The default is
|
||||
15008/TCP: ztunnel's HBONE tunnel, the port every inbound mesh
|
||||
connection arrives on once the namespace carries
|
||||
istio.io/dataplane-mode. Istio's own documentation is explicit
|
||||
that ambient "does not and will never bypass Kubernetes
|
||||
NetworkPolicy enforcement", so without this port a pod in the
|
||||
mesh is simply unreachable.
|
||||
|
||||
15008 IS AN ISTIO CONSTANT, NOT A STANDARD. It is not registered
|
||||
with IANA for this, Kubernetes knows nothing about it, and no
|
||||
protocol prescribes it - Istio picked it for HBONE and Istio can
|
||||
change it. That is precisely why it sits in a VALUE with a
|
||||
documented default rather than as a literal here: an operator can
|
||||
correct it without waiting for a chart release.
|
||||
|
||||
It is the one port in this rule that is NOT a containerPort of
|
||||
the selected pod spec - ztunnel terminates it, not the
|
||||
application - so ci/run-networkpolicy-test.sh holds a NAMED
|
||||
exception for exactly this number, gated on exactly this value.
|
||||
Anything else that is not a containerPort still fails, and so
|
||||
does 15008 itself while the value is off.
|
||||
exception for it. Note what that exception is pinned to: the
|
||||
number 15008 as written in THE TEST, plus an assertion that this
|
||||
chart's values.yaml declares the same default. It is NOT "whatever
|
||||
this value happens to say" - that would let a mistyped port ride
|
||||
in through a configurable field, which is the hole design A of
|
||||
#143 was rejected for.
|
||||
|
||||
READ FOR ztunnel 1.30.6. If Istio changes the port, or the mesh is
|
||||
replaced: the failure is a pod that cannot be reached inside the
|
||||
mesh. Change the default in all 30 charts AND the pinned number in
|
||||
the test - which is the thing that forces someone to look.
|
||||
|
||||
NOT MEASURED, and the chart should not pretend otherwise: that
|
||||
15008 ALONE suffices comes from Istio's documentation. What has
|
||||
@@ -168,7 +200,7 @@ spec:
|
||||
opens. Provable at the first namespace that joins the mesh, and
|
||||
no namespace carries the label yet. */}}
|
||||
- protocol: TCP
|
||||
port: 15008
|
||||
port: {{ int $ambientPort }}
|
||||
{{- end }}
|
||||
{{- if $probeSources }}
|
||||
{{- /* RESTRICTED, because the operator supplied probeSources. The
|
||||
@@ -209,34 +241,64 @@ spec:
|
||||
- ipBlock:
|
||||
cidr: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- if $ambientMesh }}
|
||||
{{- if $ambientEnabled }}
|
||||
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason
|
||||
the switch is one value instead of two. Inside the mesh the
|
||||
kubelet's probes are redirected and no longer arrive from the
|
||||
worker node's address: they arrive from 169.254.7.127, which is
|
||||
therefore the probeSources entry nobody would think to add.
|
||||
the switch is one value instead of two. Inside ISTIO's ambient
|
||||
mesh the kubelet's probes are redirected BY ISTIO and no longer
|
||||
arrive from the worker node's address: they arrive from
|
||||
169.254.7.127, which is therefore the probe source nobody would
|
||||
think to add.
|
||||
|
||||
The two halves are deliberately inseparable. An operator who
|
||||
only opens 15008 by hand has a policy that passes every review -
|
||||
and the moment probeSources is non-empty, every probe is dropped,
|
||||
the pod never becomes Ready, and the symptom reads as a broken
|
||||
WHERE THAT ADDRESS COMES FROM, because it is worth being able to
|
||||
recognise as vendor magic rather than as protocol:
|
||||
|
||||
- It is LINK-LOCAL, 169.254.0.0/16 (RFC 3927) - a range
|
||||
reserved for node-local use that is never routed. Exactly
|
||||
the range one picks a vendor's magic address out of.
|
||||
- Istio's IPv6 counterpart gives the game away:
|
||||
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME
|
||||
DIGITS - 9254:7127 is 169.254.7.127 - plus 1337. Numbers
|
||||
built that way are vendor constants, not protocols.
|
||||
- KUBERNETES DOES NOT KNOW THIS ADDRESS. Neither the
|
||||
NetworkPolicy API nor the kubelet has any notion of it; only
|
||||
Istio's own data plane rewrites the probe source onto it.
|
||||
|
||||
READ FOR ztunnel 1.30.6. If Istio changes it, or the mesh is
|
||||
replaced, THE FAILURE IS SILENT: probes would be dropped, and
|
||||
only where probeSources is non-empty - which is nowhere today.
|
||||
Nothing in this repository or in Kubernetes would report it. That
|
||||
is why the address is a VALUE with a documented default, so it can
|
||||
be corrected without a chart release, and why the test pins the
|
||||
number it expects: changing it means touching 30 values files and
|
||||
the test, and the test is what makes someone look.
|
||||
|
||||
A LIST, not a string, and this is the one case where that does not
|
||||
contradict "the value is a constant, not a configuration": on a
|
||||
DUAL-STACK cluster BOTH entries are needed at the same time, not
|
||||
one of two. This cluster is SingleStack IPv4, so the IPv6
|
||||
counterpart above is left out of the default - an ipBlock of the
|
||||
wrong family is accepted by the API server and matches nothing.
|
||||
Add it alongside, never instead, if the cluster gains IPv6.
|
||||
|
||||
The two halves stay inseparable. An operator who only opens the
|
||||
port by hand has a policy that passes every review - and the
|
||||
moment probeSources is non-empty, every probe is dropped, the pod
|
||||
never becomes Ready, and the symptom reads as a broken
|
||||
application rather than as a policy. That is assertion 4 of
|
||||
ci/run-networkpolicy-test.sh, re-created one value further along.
|
||||
One switch cannot be set halfway.
|
||||
|
||||
Only rendered in this branch, because this is the only branch
|
||||
with a "from" list at all: with probeSources empty the rule is
|
||||
open and needs no peer. The IPv6 counterpart
|
||||
fd16:9254:7127:1337:ffff:ffff:ffff:ffff is deliberately absent -
|
||||
this cluster is SingleStack IPv4, and an ipBlock of the wrong
|
||||
family is accepted by the API server and matches nothing.
|
||||
open and needs no peer.
|
||||
|
||||
UNPROVEN IN OPERATION: probeSources is empty in every chart of
|
||||
this repository (stalwart sets it to the empty list explicitly),
|
||||
so no release reaches this branch today. What CI proves is its
|
||||
render, through the overlay in ci/networkpolicy. */}}
|
||||
{{- range $ambientProbeSources }}
|
||||
- ipBlock:
|
||||
cidr: 169.254.7.127/32
|
||||
cidr: {{ . | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- else }}
|
||||
{{- /* NO "from" SELECTOR, WHICH MEANS OPEN - deliberately, and only
|
||||
|
||||
Reference in new issue
Block a user