Author SHA1 Message Date
jklein fb275935c9 docs(README): the ambient-mesh switch and the named exception it costs the render test
Validate Helm Charts / Detect changed charts (pull_request) Successful in 4s
Validate Helm Charts / Resource quantities (pull_request) Successful in 11s
Validate Helm Charts / Selector match (pull_request) Successful in 11s
Validate Helm Charts / HTTPRoute match (pull_request) Successful in 8s
Validate Helm Charts / Bump level (pull_request) Successful in 3s
Validate Helm Charts / YAML scalars (pull_request) Successful in 8s
Validate Helm Charts / NetworkPolicy (pull_request) Successful in 19s
Validate Helm Charts / Build apache-tika (pull_request) Successful in 4s
Validate Helm Charts / Trivy config apache-tika (pull_request) Successful in 10s
Validate Helm Charts / Build bentopdf (pull_request) Successful in 7s
Validate Helm Charts / Build bulwark (pull_request) Successful in 5s
Validate Helm Charts / Build garage (pull_request) Successful in 5s
Validate Helm Charts / Build gotenberg (pull_request) Successful in 7s
Validate Helm Charts / Build keycloak (pull_request) Successful in 6s
Validate Helm Charts / Build minecraft-router (pull_request) Successful in 12s
Validate Helm Charts / Build outline (pull_request) Successful in 5s
Validate Helm Charts / Build paperless-ngx (pull_request) Successful in 5s
Validate Helm Charts / Build calibre-web-automated (pull_request) Successful in 5s
Validate Helm Charts / Build cyberchef (pull_request) Successful in 5s
Validate Helm Charts / Build freesailarr (pull_request) Successful in 6s
Validate Helm Charts / Build homeassistant (pull_request) Successful in 8s
Validate Helm Charts / Build matrix-synapse (pull_request) Successful in 6s
Validate Helm Charts / Build minecraft-server (pull_request) Successful in 5s
Validate Helm Charts / Build patchmon (pull_request) Successful in 4s
Validate Helm Charts / Build rallly (pull_request) Successful in 7s
Validate Helm Charts / Build rss-bridge (pull_request) Successful in 4s
Validate Helm Charts / Build sure (pull_request) Successful in 3s
Validate Helm Charts / Build teamspeak-server (pull_request) Successful in 4s
Validate Helm Charts / Build voucher-vault (pull_request) Successful in 4s
Validate Helm Charts / Build ytdlp-web-player (pull_request) Successful in 6s
Validate Helm Charts / Build zipline (pull_request) Successful in 4s
Validate Helm Charts / Build samba (pull_request) Successful in 5s
Validate Helm Charts / Build satisfactory (pull_request) Successful in 4s
Validate Helm Charts / Build searxng (pull_request) Successful in 4s
Validate Helm Charts / Build stalwart (pull_request) Successful in 4s
Validate Helm Charts / Build template-chart (pull_request) Successful in 4s
Validate Helm Charts / Build urlaubsverwaltung (pull_request) Successful in 5s
Validate Helm Charts / Build valkey (pull_request) Successful in 4s
Validate Helm Charts / Trivy config bentopdf (pull_request) Successful in 11s
Validate Helm Charts / Trivy config bulwark (pull_request) Successful in 13s
Validate Helm Charts / Trivy config calibre-web-automated (pull_request) Successful in 11s
Validate Helm Charts / Trivy config cyberchef (pull_request) Successful in 20s
Validate Helm Charts / Trivy config freesailarr (pull_request) Successful in 13s
Validate Helm Charts / Trivy config garage (pull_request) Successful in 10s
Validate Helm Charts / Trivy config gotenberg (pull_request) Successful in 11s
Validate Helm Charts / Trivy config homeassistant (pull_request) Successful in 13s
Validate Helm Charts / Trivy config keycloak (pull_request) Successful in 11s
Validate Helm Charts / Trivy config matrix-synapse (pull_request) Successful in 20s
Validate Helm Charts / Trivy config minecraft-router (pull_request) Successful in 19s
Validate Helm Charts / Trivy config minecraft-server (pull_request) Successful in 13s
Validate Helm Charts / Trivy config outline (pull_request) Successful in 12s
Validate Helm Charts / Trivy config paperless-ngx (pull_request) Successful in 13s
Validate Helm Charts / Trivy config patchmon (pull_request) Successful in 10s
Validate Helm Charts / Trivy config rss-bridge (pull_request) Successful in 12s
Validate Helm Charts / Trivy config samba (pull_request) Successful in 23s
Validate Helm Charts / Trivy config satisfactory (pull_request) Successful in 13s
Validate Helm Charts / Trivy config searxng (pull_request) Successful in 15s
Validate Helm Charts / Trivy config rallly (pull_request) Successful in 11s
Validate Helm Charts / Trivy config stalwart (pull_request) Successful in 15s
Validate Helm Charts / Trivy config sure (pull_request) Successful in 12s
Validate Helm Charts / Trivy config teamspeak-server (pull_request) Successful in 11s
Validate Helm Charts / Trivy config template-chart (pull_request) Successful in 11s
Validate Helm Charts / Trivy config urlaubsverwaltung (pull_request) Successful in 11s
Validate Helm Charts / Trivy config valkey (pull_request) Successful in 15s
Validate Helm Charts / Trivy config voucher-vault (pull_request) Successful in 10s
Validate Helm Charts / Trivy config ytdlp-web-player (pull_request) Successful in 13s
Validate Helm Charts / Trivy config zipline (pull_request) Successful in 11s
Install-test / Install-test apache-tika (pull_request) Successful in 1m4s
Install-test / Install-test bentopdf (pull_request) Successful in 57s
Install-test / Install-test bulwark (pull_request) Successful in 1m6s
Install-test / Install-test calibre-web-automated (pull_request) Successful in 1m31s
Install-test / Install-test cyberchef (pull_request) Successful in 45s
Install-test / Install-test freesailarr (pull_request) Successful in 3m0s
Install-test / Install-test garage (pull_request) Successful in 1m0s
Install-test / Install-test gotenberg (pull_request) Successful in 1m2s
Install-test / Install-test homeassistant (pull_request) Successful in 1m55s
Install-test / Install-test matrix-synapse (pull_request) Successful in 1m17s
Install-test / Install-test keycloak (pull_request) Successful in 2m8s
Install-test / Install-test minecraft-router (pull_request) Successful in 38s
Install-test / Install-test minecraft-server (pull_request) Successful in 1m25s
Install-test / Install-test outline (pull_request) Successful in 2m0s
Install-test / Install-test paperless-ngx (pull_request) Successful in 2m35s
Install-test / Install-test patchmon (pull_request) Successful in 1m2s
Install-test / Install-test rallly (pull_request) Successful in 1m15s
Install-test / Install-test rss-bridge (pull_request) Successful in 46s
Install-test / Install-test samba (pull_request) Successful in 1m6s
Install-test / Install-test sure (pull_request) Successful in 2m1s
Install-test / Install-test searxng (pull_request) Successful in 52s
Install-test / Install-test stalwart (pull_request) Successful in 1m0s
Install-test / Install-test teamspeak-server (pull_request) Successful in 1m14s
Install-test / Install-test urlaubsverwaltung (pull_request) Successful in 1m31s
Install-test / Install-test valkey (pull_request) Successful in 1m8s
Install-test / Install-test voucher-vault (pull_request) Successful in 1m35s
Install-test / Install-test ytdlp-web-player (pull_request) Successful in 50s
Install-test / Install-test zipline (pull_request) Successful in 1m35s
Validate Helm Charts / Install-test (pull_request) Successful in 38m26s
Gitea #143. Section 15 gets the switch itself - what it renders, why its two
halves are one value, and the two things it does not prove - plus the three
names that overlap as substrings (ingress, networkPolicy.ingress,
networkPolicy.ingressController), because a search for "ingress" matches all
three and one run during this work mistook the third for an inbound escape
hatch on that basis alone.

The render-test section gets assertion 7 as a table row, the re-measured
figures (30 charts, 333 assertions, ~25s), and the six fail paths that were
executed - including the two that are only visible to it: an ungated ipBlock,
which assertion 2 cannot see because it never looks at "from" peers, and a
value declared in the schema but missing from values.yaml, which a --set
render alone would accept.
2026-10-19 13:16:35 +00:00
jklein 8f50ec838b feat(satisfactory): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.

Port half only: this chart offers no networkPolicy.probeSources - its probes
put no packet on the network - so there is no redirected probe to allow and an
ipBlock for one would match nothing while making the rule look like it did
something. ci/run-networkpolicy-test.sh ties that exemption to the absence of
probeSources, not to a chart name.
2026-10-19 13:16:35 +00:00
jklein 805dcb18ac feat(valkey): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.

Port half only: this chart offers no networkPolicy.probeSources - its probes
put no packet on the network - so there is no redirected probe to allow and an
ipBlock for one would match nothing while making the rule look like it did
something. ci/run-networkpolicy-test.sh ties that exemption to the absence of
probeSources, not to a chart name.
2026-10-19 13:16:34 +00:00
jklein 185795d110 feat(minecraft-server): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.

Port half only: this chart offers no networkPolicy.probeSources - its probes
put no packet on the network - so there is no redirected probe to allow and an
ipBlock for one would match nothing while making the rule look like it did
something. ci/run-networkpolicy-test.sh ties that exemption to the absence of
probeSources, not to a chart name.
2026-10-19 13:16:34 +00:00
jklein c3bfc35efe feat(teamspeak-server): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.

Port half only: this chart offers no networkPolicy.probeSources - its probes
put no packet on the network - so there is no redirected probe to allow and an
ipBlock for one would match nothing while making the rule look like it did
something. ci/run-networkpolicy-test.sh ties that exemption to the absence of
probeSources, not to a chart name.
2026-10-19 13:16:34 +00:00
jklein 3c90328b45 feat(samba): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.

Port half only: this chart offers no networkPolicy.probeSources - its probes
put no packet on the network - so there is no redirected probe to allow and an
ipBlock for one would match nothing while making the rule look like it did
something. ci/run-networkpolicy-test.sh ties that exemption to the absence of
probeSources, not to a chart name.
2026-10-19 13:16:34 +00:00
jklein 1880d22c54 feat(stalwart): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:34 +00:00
jklein e9aa94108f feat(minecraft-router): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:34 +00:00
jklein 11b8ad5bb4 feat(keycloak): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:34 +00:00
jklein e97ffaf2d7 feat(garage): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:34 +00:00
jklein dcf825f2eb test(networkpolicy): tie the ambient probe-source half to probeSources, not to a chart name
Gitea #143. Five charts offer no networkPolicy.probeSources - their probes are
exec probes and put no packet on the network - so for them 169.254.7.127/32
would be an ipBlock that matches nothing, which is exactly the strict-looking
empty rule this script exists to catch. Assertion 7 now exempts them on the
absence of that value, the same conditionality assertion 4 and the overlay
guard already use, and records the exemption as a held assertion rather than
as silence.

Also names two limits of assertion 7 in its header: assertions 1, 3, 4 and 5
are not re-run on the switched-on render, and assertion 3's omission is the
interesting one - with probeSources empty, 15008 sits in a rule with no
"from" that neither of assertion 3's two justifications covers. Teaching it a
third would mean writing a rule against a guess, because where ztunnel dials
from cannot be settled until a namespace joins the mesh.
2026-10-19 13:16:34 +00:00
jklein a7ec82447d feat(homeassistant): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:34 +00:00
jklein bd99aaa908 feat(zipline): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:34 +00:00
jklein 7b369fa78d feat(ytdlp-web-player): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:33 +00:00
jklein 51ce5d858c feat(voucher-vault): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:33 +00:00
jklein f3665c117b feat(urlaubsverwaltung): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:33 +00:00
jklein 593c187656 feat(sure): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:33 +00:00
jklein 123c05ffe2 feat(searxng): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:33 +00:00
jklein d4fc26809c feat(rss-bridge): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:33 +00:00
jklein a4372af025 feat(rallly): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:33 +00:00
jklein d4c588344e feat(patchmon): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:25 +00:00
jklein 0e9c2dc782 feat(paperless-ngx): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:25 +00:00
jklein 67f1a10067 feat(outline): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:25 +00:00
jklein cb7672153f feat(matrix-synapse): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:24 +00:00
jklein 4265a50dc9 feat(gotenberg): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:24 +00:00
jklein f9686e37fc feat(freesailarr): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:13 +00:00
jklein a28443c3b8 feat(calibre-web-automated): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:13 +00:00
jklein b80c25316f feat(bulwark): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:13 +00:00
jklein 3fd493ba9a feat(bentopdf): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:13 +00:00
jklein fda1c7c5ff feat(apache-tika): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders against origin/main rather
than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. Ambient does not bypass NetworkPolicy enforcement, so without the
port a pod whose namespace has joined the mesh is unreachable.

The two halves are one value because the second is the one nobody thinks of:
15008 added by hand leaves a policy that passes review and drops every probe
the first time probeSources is narrowed, and a pod that never becomes Ready
reads as a broken application rather than as a policy.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:13 +00:00
jklein 1861966e12 feat(cyberchef): networkPolicy.ingress.ambientMesh
Gitea #143. Additive, default false, so the render is byte-identical to the
previous release - proven by diffing both renders rather than asserted.

Switched on it adds 15008/TCP, ztunnel's HBONE tunnel, to the rule carrying
the probe port, and - inside that rule's source-restricted branch -
169.254.7.127/32, the address the kubelet's probes arrive from once the pod is
in the mesh. The two halves are one value because the second is the one nobody
thinks of: 15008 added by hand leaves a policy that passes review and drops
every probe the first time probeSources is narrowed.

Minor, not major: additive value, unchanged default, unchanged render.
2026-10-19 13:16:12 +00:00
jklein cd2e4c0f15 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.
2026-10-19 13:16:12 +00:00
95 changed files with 301 additions and 4228 deletions

No files matched your search

+1 -1
View File
@@ -120,7 +120,7 @@ jobs:
# invisible there. The value mirrors K3S_IMAGE above (v1.35.5), which is # invisible there. The value mirrors K3S_IMAGE above (v1.35.5), which is
# the cluster this client talks to. # the cluster this client talks to.
# renovate: datasource=github-releases depName=kubernetes/kubernetes # renovate: datasource=github-releases depName=kubernetes/kubernetes
KUBECTL_VERSION: v1.37.1 KUBECTL_VERSION: v1.35.9
# The same expression as runs-on above, written a second time because the # The same expression as runs-on above, written a second time because the
# env context is not available in runs-on - so it cannot be the other way # env context is not available in runs-on - so it cannot be the other way
# round, and reading the label back off the runner is not possible either # round, and reading the label back off the runner is not possible either
+6 -45
View File
@@ -231,7 +231,7 @@ Measured locally: **30 charts, 408 scalars verified, 0 failed**, no cluster. The
| no ingress rule allows every source by accident | `from: []` allows everything, identically to having no `from` at all, while reading in a diff and in `kubectl` output as a restriction — the one mistake in this class that makes a policy *weaker* while looking *stricter* | nobody | | no ingress rule allows every source by accident | `from: []` allows everything, identically to having no `from` at all, while reading in a diff and in `kubectl` output as a restriction — the one mistake in this class that makes a policy *weaker* while looking *stricter* | nobody |
| every port a **network** probe addresses is either unrestricted, or the chart offers `networkPolicy.probeSources` **and** says in `NOTES.txt` what a wrong CIDR costs | the kubelet probes from the IP of the worker node running the pod, which no chart can know. A restricted probe port without that address drops every probe: the pod never becomes `Ready`, the rollout times out, and the symptom reads as a broken application | the install-test, in production's CNI — **but not in CI**, see below | | every port a **network** probe addresses is either unrestricted, or the chart offers `networkPolicy.probeSources` **and** says in `NOTES.txt` what a wrong CIDR costs | the kubelet probes from the IP of the worker node running the pod, which no chart can know. A restricted probe port without that address drops every probe: the pod never becomes `Ready`, the rollout times out, and the symptom reads as a broken application | the install-test, in production's CNI — **but not in CI**, see below |
| **cross-chart egress**: where a chart dials another chart *of this repository*, its egress rule must contain the **pod** ports behind that chart's Services (#136) | an egress rule naming the **Service** port. kube-proxy does the ClusterIP's destination NAT *before* the policy is evaluated at the pod's interface, so the policy sees `podIP:targetPort` and the rule matches nothing wherever `port` and `targetPort` differ | nobody: the symptom is a **timeout**, not `connection refused`, and no error mentions a NetworkPolicy — `paperless-ngx` 14.0.0 shipped it and cost an outage on 2026-10-15 | | **cross-chart egress**: where a chart dials another chart *of this repository*, its egress rule must contain the **pod** ports behind that chart's Services (#136) | an egress rule naming the **Service** port. kube-proxy does the ClusterIP's destination NAT *before* the policy is evaluated at the pod's interface, so the policy sees `podIP:targetPort` and the rule matches nothing wherever `port` and `targetPort` differ | nobody: the symptom is a **timeout**, not `connection refused`, and no error mentions a NetworkPolicy — `paperless-ngx` 14.0.0 shipped it and cost an outage on 2026-10-15 |
| **the ambient-mesh switch** (#143), on two renders per chart plus the chart's declared defaults: those defaults equal the port and `ipBlock` the test **pins**; with the switch off neither appears; with it on the pinned port is present and is still the **only** ingress port that is no `containerPort`, and a source-restricted rule carrying it also carries the `ipBlock` | the named exception decaying into the hole it was carved around. Without the off-half a chart could render the port unconditionally and nothing would notice; without the only-port half the exception becomes the free port key that was rejected; and **without the pin** — now that the port is a value — the exception would permit whatever the value said, so a typo would ride in through a configurable field | nobody — and in production as a pod unreachable in the mesh, or one whose every probe is dropped the first time `probeSources` is narrowed | | **the ambient-mesh switch**, on two renders per chart: with `networkPolicy.ingress.ambientMesh` off, neither `15008` nor `169.254.7.127/32` appears; with it on, `15008` is present and is still the **only** ingress port that is no `containerPort`, and a source-restricted rule carrying it also carries the `ipBlock` (#143) | the named exception decaying into the hole it was carved around. Without the off-half a chart could render `15008` unconditionally and nothing would notice; without the only-port half the exception becomes the free port key that was rejected, and every mistyped ingress port passes with the switch on | nobody — and in production as a pod unreachable in the mesh, or one whose every probe is dropped the first time `probeSources` is narrowed |
Egress ports are otherwise **deliberately not** held against the pod's `containerPort`s: an egress port is a port on the *destination*, and that comparison would fail every chart that reaches a database. Egress ports are otherwise **deliberately not** held against the pod's `containerPort`s: an egress port is a port on the *destination*, and that comparison would fail every chart that reaches a database.
@@ -248,7 +248,7 @@ Four things to know before touching it:
Measured locally: **30 charts, 333 assertions held, 0 failed**, no cluster, ~25 seconds (the ambient-mesh assertion added a second render per chart, and roughly 10 seconds with it). All twelve fail paths were exercised rather than assumed — and one earned its keep immediately: the `probeSources` check was a substring test, so a chart whose key had been **deleted** while the long comment above it still discussed the value passed it. It now looks for the key. The cross-chart assertion added two of those 210, and its own six fail paths were executed the same way: a required port removed from the rule, a wrong number in its place, a peer chart renamed away, the policy's name no longer following the `<release>-<chart>-<kind>` convention, the peers rendering no Service at all (which would make the requirement empty, and is treated as a failure because an empty requirement reads exactly like a satisfied one), and the gated rule made unconditional — which is the failure path of the *gate-off* half, since with the gate off and the rule properly gated the assertion must stay silent. Measured locally: **30 charts, 333 assertions held, 0 failed**, no cluster, ~25 seconds (the ambient-mesh assertion added a second render per chart, and roughly 10 seconds with it). All twelve fail paths were exercised rather than assumed — and one earned its keep immediately: the `probeSources` check was a substring test, so a chart whose key had been **deleted** while the long comment above it still discussed the value passed it. It now looks for the key. The cross-chart assertion added two of those 210, and its own six fail paths were executed the same way: a required port removed from the rule, a wrong number in its place, a peer chart renamed away, the policy's name no longer following the `<release>-<chart>-<kind>` convention, the peers rendering no Service at all (which would make the requirement empty, and is treated as a failure because an empty requirement reads exactly like a satisfied one), and the gated rule made unconditional — which is the failure path of the *gate-off* half, since with the gate off and the rule properly gated the assertion must stay silent.
**The ambient-mesh assertion (#143) was built the same way, and ten of its fail paths were executed**: a second, different port added under the exception (rejected — the exception covers exactly one number); `15008` rendered ungated, caught by assertion 2 in the main loop, which renders the switch at its default; the `ipBlock` rendered ungated, which **only** this assertion can catch, because assertion 2 never looks at `from` peers; the `ipBlock` half deleted while `15008` stayed, i.e. the half-set switch the design exists to make impossible, reachable because `ci/networkpolicy/template-chart.yaml` fills `probeSources`; the policy's name taken off the `<release>-<chart>-<kind>` convention; the value present in the schema but absent from `values.yaml`, which a `--set` render alone would not have noticed; a chart whose **declared default port deviates** (three independent checks fire at once: the declared-defaults comparison, the pinned port missing from the render, and the deviating port not being covered by the exception); a port-only chart **declaring `redirectedProbeSources`**, which reaches no rule there; a **deleted `ambientMesh` object** and one carrying only `enabled: true` (both must *render*, on the chart's own defaults — and the guards that make that work were each shown to be load-bearing by removing them); and a **release-level port override**, which must render and does, while the pinned exception demonstrably does **not** stretch to cover it. The name check earned its keep on the first run rather than in a rehearsal: it read the **directory** name, and `template-chart`'s chart is called `xxx`. **The ambient-mesh assertion (#143) was built the same way, and six of its fail paths were executed**: a second, different port added under the exception (rejected — the exception covers exactly one number); `15008` rendered ungated, caught by assertion 2 in the main loop, which renders the switch at its default; the `ipBlock` rendered ungated, which **only** this assertion can catch, because assertion 2 never looks at `from` peers; the `ipBlock` half deleted while `15008` stayed, i.e. the half-set switch the design exists to make impossible, reachable because `ci/networkpolicy/template-chart.yaml` fills `probeSources`; the policy's name taken off the `<release>-<chart>-<kind>` convention; and the value present in the schema but absent from `values.yaml`, which a `--set` render alone would not have noticed. The name check earned its keep on the first run rather than in a rehearsal: it read the **directory** name, and `template-chart`'s chart is called `xxx`.
**What this test does not and cannot cover, named so the gap is not mistaken for coverage:** that traffic still flows. No ingress controller runs in the install-test cluster, so the path from Traefik to a pod is proven nowhere in this pipeline — and, measured on the PR for #5, **k3s exempts the kubelet's probe traffic from policy**, so a blocked probe path does *not* turn the install-test red either (`cyberchef` became `Ready` in 69s with its probe port restricted to an address no probe can come from). What the install-test *does* prove is enforcement of everything the application itself needs: with `outline`'s DNS egress rule removed, its pod never became `Ready` and the job failed after 5m59s. So the net under this convention is real, but it is an **egress** net — which raises the stakes for assertion 2, since a wrong ingress port is caught mechanically here or not at all. **What this test does not and cannot cover, named so the gap is not mistaken for coverage:** that traffic still flows. No ingress controller runs in the install-test cluster, so the path from Traefik to a pod is proven nowhere in this pipeline — and, measured on the PR for #5, **k3s exempts the kubelet's probe traffic from policy**, so a blocked probe path does *not* turn the install-test red either (`cyberchef` became `Ready` in 69s with its probe port restricted to an address no probe can come from). What the install-test *does* prove is enforcement of everything the application itself needs: with `outline`'s DNS egress rule removed, its pod never became `Ready` and the job failed after 5m59s. So the net under this convention is real, but it is an **egress** net — which raises the stakes for assertion 2, since a wrong ingress port is caught mechanically here or not at all.
@@ -696,18 +696,9 @@ Every port of a workload falls into one of four, and this is the part to get rig
#### Values #### Values
`networkPolicy.enabled`, `.allowedNamespaces`, `.probeSources`, `.ingressController`, `.ingress.ambientMesh.*`, `.egress.extraPorts` — and a chart ships **only the ones that reach a rule**, with `.enabled` and `.ingress.ambientMesh` the two every chart has. `networkPolicy.enabled`, `.allowedNamespaces`, `.probeSources`, `.ingressController`, `.ingress.ambientMesh`, `.egress.extraPorts` — and a chart ships **only the ones that reach a rule**, with `.enabled` and `.ingress.ambientMesh` the two every chart has.
**Three of those names overlap as substrings and are three different things**, which is worth stating because a search for `ingress` matches all of them: top-level `ingress` is the Ingress **object** a chart renders, `networkPolicy.ingress` is the policy's **inbound side**, and `networkPolicy.ingressController` is the **source selector** for the controller's pods — a *sibling* of `networkPolicy.ingress` and no part of it. The 21 charts carrying `ingressController` had **no** inbound escape hatch before #143, and one search run during that work reported them as having one on the strength of the substring alone. **Three of those names overlap as substrings and are three different things**, which is worth stating because a search for `ingress` matches all of them: top-level `ingress` is the Ingress **object** a chart renders, `networkPolicy.ingress` is the policy's **inbound side**, and `networkPolicy.ingressController` is the **source selector** for the controller's pods — a *sibling* of `networkPolicy.ingress` and no part of it. The 21 charts carrying `ingressController` had **no** inbound escape hatch before #143, and one search run during that work reported them as having one on the strength of the substring alone. A chart may add a **named** egress switch where a port depends on something the values cannot see: `paperless-ngx`' `.egress.mailImport` opens IMAPS `993`, off by default, because its mail accounts are rows in its own database and no value says whether a release fetches mail. The bar for one of those: it names what it opens, and it is **not** a second switch for a port some *other* value already decides — that is why the Tika/Gotenberg rule stayed gated on `paperless.files.tikaGotenberg.enabled` and got no policy switch of its own. Two values that can drift apart re-arm the outage the rule was fixed for. A value that silently does nothing is worse than one that is absent, because it reads as a control: `samba` and `satisfactory` carry `enabled` alone (every inbound rule is open to any source), and `teamspeak-server` has `allowedNamespaces` but no `probeSources`.
**And a fourth name, whose confusion risk is the larger one**, because these two are both lists of probe sources and sit two levels apart:
| Name | Whose addresses | Default | Empty means |
| --- | --- | --- | --- |
| `networkPolicy.probeSources` | **yours** — the worker node CIDRs, which no chart can know | `[]` | the probe rule is **open** |
| `networkPolicy.ingress.ambientMesh.redirectedProbeSources` | **Istio's** — the address its data plane rewrites the probe source onto | `[169.254.7.127/32]`, i.e. shipped *filled*, because unlike the node IPs it is a constant the chart **can** know | n/a — clearing it restores the default, deliberately: the two halves of the switch are not separable |
It is named `redirectedProbeSources` and not `probeSources` for exactly that reason: one is where probes come from normally, the other where they come from *after Istio redirects them*. A chart may add a **named** egress switch where a port depends on something the values cannot see: `paperless-ngx`' `.egress.mailImport` opens IMAPS `993`, off by default, because its mail accounts are rows in its own database and no value says whether a release fetches mail. The bar for one of those: it names what it opens, and it is **not** a second switch for a port some *other* value already decides — that is why the Tika/Gotenberg rule stayed gated on `paperless.files.tikaGotenberg.enabled` and got no policy switch of its own. Two values that can drift apart re-arm the outage the rule was fixed for. A value that silently does nothing is worse than one that is absent, because it reads as a control: `samba` and `satisfactory` carry `enabled` alone (every inbound rule is open to any source), and `teamspeak-server` has `allowedNamespaces` but no `probeSources`.
**No chart ships `allowedNamespaces` pre-filled, ever.** A chart that named `mc-router`, or `paperless-jk`, or `stalwart` in its defaults would be baking in an assumption about *this* cluster's namespace layout — which is exactly the kind of claim a chart cannot keep true. So the default is the empty list and the chart is **restrictive**; filling it is the **deployment adaptation**, made where the namespace layout is actually visible. That is why a sweep like this produces a list of release-side one-liners: those are the expected procedure, not a defect list, and the breaking-change major on each chart is what announces them. **No chart ships `allowedNamespaces` pre-filled, ever.** A chart that named `mc-router`, or `paperless-jk`, or `stalwart` in its defaults would be baking in an assumption about *this* cluster's namespace layout — which is exactly the kind of claim a chart cannot keep true. So the default is the empty list and the chart is **restrictive**; filling it is the **deployment adaptation**, made where the namespace layout is actually visible. That is why a sweep like this produces a list of release-side one-liners: those are the expected procedure, not a defect list, and the breaking-change major on each chart is what announces them.
@@ -717,30 +708,7 @@ It is named `redirectedProbeSources` and not `probeSources` for exactly that rea
#### The ambient-mesh switch, and the one named exception in the render test (#143) #### The ambient-mesh switch, and the one named exception in the render test (#143)
**`networkPolicy.ingress.ambientMesh`, in all 30 charts, switched off by default.** Istio runs on this cluster in ambient mode, and Istio's own documentation is explicit that *"ambient does not and will never bypass Kubernetes `NetworkPolicy` enforcement"* — so until this value existed, **no chart here could put a pod into the mesh at all**. Additive, unchanged default, unchanged render: **minor** per chart, and the render was diffed against the previous release per chart rather than asserted. **`networkPolicy.ingress.ambientMesh`, in all 30 charts, default `false`.** Istio runs on this cluster in ambient mode, and Istio's own documentation is explicit that *"ambient does not and will never bypass Kubernetes `NetworkPolicy` enforcement"* — so until this value existed, **no chart here could put a pod into the mesh at all**. Additive, unchanged default, unchanged render: **minor** per chart, and the render was diffed against the previous release per chart rather than asserted.
**The shape, and why both numbers are values rather than literals:**
```yaml
networkPolicy:
ingress:
ambientMesh:
enabled: false
port: 15008
redirectedProbeSources:
- 169.254.7.127/32
```
**Both numbers are Istio *constants*, not standards** — and a value with a documented default is the better home for a vendor constant than a template literal, because an operator can correct it when Istio changes the number without waiting for a chart release. Their provenance, written on each one in every chart rather than assumed:
- **`15008`** is Istio's HBONE port. Not registered with IANA for this, prescribed by no protocol, and **unknown to Kubernetes**. Istio picked it; Istio can change it.
- **`169.254.7.127`** is the address Istio's data plane rewrites the kubelet's probe source onto, and it is recognisable as vendor magic rather than protocol three ways: it is **link-local**, `169.254.0.0/16` (RFC 3927), a range reserved for node-local use and **never routed** — exactly the range one picks a magic address out of; Istio's IPv6 counterpart `fd16:9254:7127:1337:ffff:ffff:ffff:ffff` carries the **same digits** (`9254:7127` *is* `169.254.7.127`) 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 own data plane does.
**Both read for ztunnel 1.30.6.** If Istio changes either, **the failure is silent**: a pod unreachable inside the mesh, or — for the address — probes dropped, and only where `probeSources` is non-empty, which is nowhere today. Nothing in this repository and nothing in Kubernetes would report it. What to re-check then: the port and address Istio's release notes name, this default in all 30 charts, **and** the pinned expectation in `ci/run-networkpolicy-test.sh`.
`redirectedProbeSources` is a **list** and not a string, and this is the one case where that does not contradict "the value is a constant, not a configuration": a **dual-stack** cluster needs **both** of Istio's addresses at the same time, not one of two. That is a nameable case rather than speculation. This cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default — an `ipBlock` of the wrong family is accepted by the API server and matches nothing — and it is documented as the addition to make *alongside*, never *instead*.
**Deleting the object is safe**, per the null-safety rule (#99): every read goes through a fallback reproducing the chart's own declared defaults, so `ambientMesh: null`, or an object carrying only `enabled: true`, renders the documented defaults. Both guards are load-bearing and were executed, not assumed — the direct chained read `$npIngress.ambientMesh.enabled` fails with `nil pointer evaluating interface {}.enabled` on a deleted object, which is the shape of the defect #136 shipped one value away; and without the port fallback a nilled `port` renders **`port: 0`**, an accepted and silently wrong policy.
**It renders two things, and that they are one value is the whole design.** **It renders two things, and that they are one value is the whole design.**
@@ -751,14 +719,7 @@ Point 2 is the one nobody thinks of, which is why the two halves cannot be had s
**The interesting part is the CI, because the obvious implementation was the worst one.** Assertion 2 of `ci/run-networkpolicy-test.sh` holds every ingress port against the selected pod spec's `containerPort`s, and `15008` is deliberately none — ztunnel terminates it, not the application. A free `ingress.extraPorts` key would have had to exempt *operator-supplied ports* from that assertion, which opens it for **all 30 charts**: every mistyped ingress port, the NodePort mistake included, would then pass. So the assertion keeps its edge and 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 interesting part is the CI, because the obvious implementation was the worst one.** Assertion 2 of `ci/run-networkpolicy-test.sh` holds every ingress port against the selected pod spec's `containerPort`s, and `15008` is deliberately none — ztunnel terminates it, not the application. A free `ingress.extraPorts` key would have had to exempt *operator-supplied ports* from that assertion, which opens it for **all 30 charts**: every mistyped ingress port, the NodePort mistake included, would then pass. So the assertion keeps its edge and 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.
**And since the port is a value, the exception cannot be checked against the rendered number.** "Whatever the value says" is design A's hole wearing design C's name: a mistyped port would pass simply by sitting in the configurable field. So **the expectation is pinned in the test** — `AMBIENT_PORT` and `AMBIENT_CIDR` — and assertion 7 checks two things against that statement: **An exception without its own check is the hole it was carved around**, so assertion 7 holds it to five things on **two renders of every chart**: with the switch off, neither `15008` nor the `ipBlock` is present; with it on, `15008` is present, `15008` is still the **only** ingress port that is not a `containerPort`, and wherever the rule carrying it restricts sources it carries the `ipBlock` too. The policy is found by the `<release>-<chart>-` name convention, because an exception must not be granted to an object the test could not identify. It lives in the existing script rather than a new one for the reason #136's cross-chart assertion does, only more so: **the exception weakens assertion 2, so it belongs beside it** — a separate script can be green while the exception it is paired with is wrong, and nothing would connect the two.
1. the render **at chart defaults** produces exactly that port and exactly that `ipBlock`, and
2. **every chart's `values.yaml` declares the same defaults**, read back through `ci/yaml2json` rather than by regex — because a substring test over a commented file is how the `probeSources` check was once fooled by a deleted key whose comment survived.
**A chart that drifts is red.** If Istio changes a constant, it takes 30 values files **and** the two pinned lines — and the pinned lines are what force someone to look. That block is therefore also the single place in the repository that states what we hold Istio's numbers to be, which makes it the answer to "where does `169.254.7.127` come from" as well; the provenance is written there. The renders set **only** `enabled=true` and never a port override, deliberately: the pin is a statement about the defaults *we ship*, not about what a release may choose. An operator overriding the value in their own release is outside this test's reach, as is true of every value here — and that is not a new gap.
**An exception without its own check is the hole it was carved around**, so assertion 7 holds it to six things on **two renders of every chart**: the declared defaults match the pin; with the switch off, neither the pinned port nor the `ipBlock` is present; with it on, the pinned port is present, it is still the **only** ingress port that is not a `containerPort`, and wherever the rule carrying it restricts sources it carries the `ipBlock` too. The probe-source half is conditional on the chart offering `probeSources`: 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. The policy is found by the `<release>-<chart>-` name convention, because an exception must not be granted to an object the test could not identify. It lives in the existing script rather than a new one for the reason #136's cross-chart assertion does, only more so: **the exception weakens assertion 2, so it belongs beside it** — a separate script can be green while the exception it is paired with is wrong, and nothing would connect the two.
**Two things this does not prove, and they are not footnotes.** That `15008` *alone* suffices comes from Istio's documentation; what is measured on this cluster is only which listeners ztunnel opens. And the probe-source half reaches no release, because `probeSources` is empty in every chart (`stalwart` sets it to `[]` explicitly) — CI proves its render through the overlays in `ci/networkpolicy`, not its effect. Both become provable at the first namespace to join the mesh, and **none carries `istio.io/dataplane-mode`** (measured 2026-10-19, 0 of 97). Whether ztunnel needs a source selector of its own is open for the same reason — but it is *not* the unknowable case it looked like: ztunnel runs with its own pod IPs in `istio-system` (no `hostNetwork` on the DaemonSet, Istio 1.30.6), so a peer for it would be a `namespaceSelector` plus `podSelector`, not a node-IP `ipBlock`. **Two things this does not prove, and they are not footnotes.** That `15008` *alone* suffices comes from Istio's documentation; what is measured on this cluster is only which listeners ztunnel opens. And the probe-source half reaches no release, because `probeSources` is empty in every chart (`stalwart` sets it to `[]` explicitly) — CI proves its render through the overlays in `ci/networkpolicy`, not its effect. Both become provable at the first namespace to join the mesh, and **none carries `istio.io/dataplane-mode`** (measured 2026-10-19, 0 of 97). Whether ztunnel needs a source selector of its own is open for the same reason — but it is *not* the unknowable case it looked like: ztunnel runs with its own pod IPs in `istio-system` (no `hostNetwork` on the DaemonSet, Istio 1.30.6), so a peer for it would be a `namespaceSelector` plus `podSelector`, not a node-IP `ipBlock`.
@@ -57,18 +57,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -102,7 +91,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 9998 port: 9998
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -116,22 +105,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -160,7 +138,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -178,29 +156,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -37,28 +37,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -164,85 +164,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+5 -45
View File
@@ -51,18 +51,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -95,7 +84,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 8080 port: 8080
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -109,22 +98,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -168,7 +146,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -186,29 +164,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -411,85 +411,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+5 -45
View File
@@ -57,18 +57,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -103,7 +92,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 3000 port: 3000
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -117,22 +106,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -176,7 +154,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -194,29 +172,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -932,85 +932,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
@@ -52,18 +52,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -98,7 +87,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 8083 port: 8083
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -112,22 +101,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -171,7 +149,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -189,29 +167,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -299,85 +299,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+35 -152
View File
@@ -1261,26 +1261,17 @@ done
# around, so the exception is held to five things at once, on TWO renders of # around, so the exception is held to five things at once, on TWO renders of
# every chart: # every chart:
# #
# the chart's DECLARED DEFAULTS (read back through ci/yaml2json)
# - enabled must default to false, and the port and the probe source must be
# exactly the numbers pinned below. A chart whose default drifted would
# otherwise ship a port no other chart ships while the exception kept being
# granted to the pinned one.
# switch OFF (the main loop's render, reused) # switch OFF (the main loop's render, reused)
# - the pinned port must be ABSENT. Without this half the exception is a hole: a # - 15008 must be ABSENT. Without this half the exception is a hole: a
# chart could render the port unconditionally and nothing would notice, # chart could render the port unconditionally and nothing would notice,
# because assertion 7 would find it present and assertion 2 is where it # because assertion 7 would find it present and assertion 2 is where it
# would have failed. # would have failed.
# - 169.254.7.127/32 must be absent too, for the same reason. # - 169.254.7.127/32 must be absent too, for the same reason.
# switch ON (only "enabled=true" is set, never a port override - the pin is a # switch ON
# statement about the defaults WE ship) # - 15008/TCP must be PRESENT, or the value is decorative.
# - the PINNED port must be PRESENT, or the value is decorative. # - 15008 must be the ONLY ingress port that is not a containerPort. This is
# - the PINNED port must be the ONLY ingress port that is not a # the line between a named exception and design A of #143: every other
# containerPort. This is the line between a named exception and design A # mistyped or NodePort number still fails here, with the switch on.
# of #143: every other mistyped or NodePort number still fails here, with
# the switch on. Note "pinned": since #143 made the port a VALUE, checking
# the exception against the rendered value would permit whatever the value
# said, which is design A's hole under design C's name.
# - wherever the rule carrying 15008 restricts sources, that rule's "from" # - wherever the rule carrying 15008 restricts sources, that rule's "from"
# must contain 169.254.7.127/32. This is the inseparability of the two # must contain 169.254.7.127/32. This is the inseparability of the two
# halves, asserted rather than trusted - and it is REACHABLE, because # halves, asserted rather than trusted - and it is REACHABLE, because
@@ -1323,45 +1314,6 @@ done
# mechanism as assertion 0: the mistake arrives by copy into a NEW chart whose # mechanism as assertion 0: the mistake arrives by copy into a NEW chart whose
# own diff nobody reads for it, which is exactly how garage (#81) and keycloak # own diff nobody reads for it, which is exactly how garage (#81) and keycloak
# (#83) arrived with no policy at all after #5 had been filed. # (#83) arrived with no policy at all after #5 had been filed.
# THE PINNED EXPECTATION, and it is pinned HERE rather than read from the chart
# on purpose (Gitea #143). Both numbers are values with documented defaults, so
# that an operator can correct an Istio constant without waiting for a chart
# release - but an exception that permits "whatever the value says" is design A's
# hole wearing design C's name: a mistyped port would pass simply by sitting in
# the configurable field, and that is the single reason design A was rejected.
#
# So the test states what WE believe Istio's numbers to be, and asserts two
# things against that statement: the render at CHART DEFAULTS produces exactly
# this port and exactly this ipBlock, AND every chart's values.yaml DECLARES
# these same defaults. A chart that drifts is red.
#
# Which makes this block the one place in the repository that says what we hold
# Istio's constants to be - and therefore also the answer to "where does
# 169.254.7.127 come from":
#
# 15008 Istio's HBONE port. Not registered with IANA for this,
# prescribed by no protocol, unknown to Kubernetes. Istio
# picked it and Istio can change it.
# 169.254.7.127/32 the address Istio's data plane rewrites the kubelet's probe
# source onto. LINK-LOCAL (169.254.0.0/16, RFC 3927), a range
# reserved for node-local use and never routed. 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. Neither the NetworkPolicy API nor the kubelet has
# any notion of it.
#
# BOTH READ FOR ztunnel 1.30.6. If Istio changes either, the failure is silent -
# a pod unreachable in the mesh, or probes dropped where probeSources is
# non-empty, which is nowhere today. Changing them means 30 values files AND
# these two lines, and these two lines are what force someone to look.
#
# An operator who overrides the value in their own release is outside this test's
# reach. That is true of every value in this repository and is not a new gap -
# and it is why these renders set ONLY "enabled=true" and never a port override:
# the pin is a statement about the defaults WE ship, not about what a release may
# choose.
AMBIENT_PORT=15008
AMBIENT_CIDR="169.254.7.127/32"
AMBIENT_VALUE="networkPolicy.ingress.ambientMesh" AMBIENT_VALUE="networkPolicy.ingress.ambientMesh"
for CHART in "${CHARTS[@]}"; do for CHART in "${CHARTS[@]}"; do
# A chart whose main-loop render already failed has nothing to compare # A chart whose main-loop render already failed has nothing to compare
@@ -1374,10 +1326,10 @@ for CHART in "${CHARTS[@]}"; do
ROW_FAIL="" ROW_FAIL=""
ON_JSON="$WORK/ambient-$CHART-on.json" ON_JSON="$WORK/ambient-$CHART-on.json"
if ! RCJ_WITH_OVERLAY=1 render_chart_json "$CHART" "$ON_JSON" \ if ! RCJ_WITH_OVERLAY=1 render_chart_json "$CHART" "$ON_JSON" \
--set "$AMBIENT_VALUE.enabled=true"; then --set "$AMBIENT_VALUE=true"; then
sed 's/^/ /' "$WORK/xerr" sed 's/^/ /' "$WORK/xerr"
echo " FAIL $CHART does not render with $AMBIENT_VALUE.enabled=true" echo " FAIL $CHART does not render with $AMBIENT_VALUE=true"
echo "::error title=ambient-mesh $CHART::$CHART does not render with $AMBIENT_VALUE.enabled=true. Since Gitea #143 every chart in this repository offers that value, and networkPolicy carries additionalProperties: false in all of them - so an unknown key does not get ignored, it fails the render. Add the object to values.yaml AND to values.schema.json (schema and template belong together per chart), with enabled: false, port: \$AMBIENT_PORT and redirectedProbeSources: [\$AMBIENT_CIDR], and render that port plus - inside the source-restricted branch - that ipBlock. Copy template-chart/templates/xxx.networkpolicy.yaml." echo "::error title=ambient-mesh $CHART::$CHART does not render with $AMBIENT_VALUE=true. Since Gitea #143 every chart in this repository offers that value, and networkPolicy carries additionalProperties: false in all of them - so an unknown key does not get ignored, it fails the render. Add the key to values.yaml AND to values.schema.json (schema and template belong together per chart), and render the ingress port 15008 plus, inside the source-restricted branch, the ipBlock 169.254.7.127/32. Copy template-chart/templates/xxx.networkpolicy.yaml."
ROW_FAIL=1 ROW_FAIL=1
fi fi
@@ -1393,36 +1345,17 @@ for CHART in "${CHARTS[@]}"; do
ROW_FAIL=1 ROW_FAIL=1
fi fi
# The chart's DECLARED defaults, read with the same parser as everything else
# (ci/yaml2json prints a values file back as JSON). Not a regex over
# values.yaml: the pinned expectation is about numbers and a list, and a
# substring test over a commented file is how the probeSources check in
# assertion 4 was once fooled by a deleted key whose comment survived.
VALUES_JSON="$WORK/$CHART.values.json"
if [ -z "$ROW_FAIL" ]; then
if ! helm template y2j "$YAML2JSON" -f "$ROOT/$CHART/values.yaml" 2>"$WORK/xerr" \
| awk '/^\{/ { print; exit }' >"$VALUES_JSON" || [ ! -s "$VALUES_JSON" ]; then
sed 's/^/ /' "$WORK/xerr"
echo " FAIL $CHART/values.yaml could not be re-read as JSON"
echo "::error title=ambient-mesh $CHART::$CHART/values.yaml could not be re-read by ci/yaml2json, so the chart's DECLARED ambient-mesh defaults could not be compared with the numbers this test pins. That is a parse failure, not a policy finding."
ROW_FAIL=1
fi
fi
if [ -z "$ROW_FAIL" ]; then if [ -z "$ROW_FAIL" ]; then
set +e set +e
python3 - "$CHART" "$RELEASE" "$AMBIENT_VALUE" "$WORK/$CHART.json" "$ON_JSON" \ python3 - "$CHART" "$RELEASE" "$AMBIENT_VALUE" "$WORK/$CHART.json" "$ON_JSON" \
"$ROOT/$CHART" "$CHART_NAME" "$AMBIENT_PORT" "$AMBIENT_CIDR" "$VALUES_JSON" \ "$ROOT/$CHART" "$CHART_NAME" \
>"$WORK/ambient-$CHART.out" 2>"$WORK/ambient-$CHART.pyerr" <<'PY' >"$WORK/ambient-$CHART.out" 2>"$WORK/ambient-$CHART.pyerr" <<'PY'
import json, re, sys import json, re, sys
(chart, release, gate, off_path, on_path, chart_dir, chart_name, chart, release, gate, off_path, on_path, chart_dir, chart_name = sys.argv[1:8]
pinned_port, pinned_cidr, values_json) = sys.argv[1:11]
# Pinned by the caller, never read from the chart - see the long note above PORT = 15008
# AMBIENT_PORT in this script. CIDR = "169.254.7.127/32"
PORT = int(pinned_port)
CIDR = pinned_cidr
lines = [] lines = []
def ok(msg): lines.append("OK|%s: %s" % (chart, msg)) def ok(msg): lines.append("OK|%s: %s" % (chart, msg))
@@ -1518,77 +1451,27 @@ def cidrs_of(rule):
return out return out
# --- The chart DECLARES the pinned defaults ------------------------------- # --- The value is declared, in BOTH places it has to be --------------------
# The render with the switch on already proves the SCHEMA declares the key - an # The render with the switch on already proves the SCHEMA declares it - an
# undeclared key cannot pass additionalProperties: false. What that render # undeclared key cannot pass additionalProperties: false. What that render
# cannot prove is what the chart's own defaults SAY, and since #143 made both # cannot prove is that values.yaml ships the documented default, and a value
# Istio constants configurable, that is the half the pinned exception rests on: # only reachable through --set is not a value an operator finds.
# the exception below is granted to the number THIS SCRIPT names, so a chart
# whose default has drifted away from it must be red here rather than quietly
# carrying a different port than every other chart.
values_yaml = "" values_yaml = ""
try: try:
values_yaml = open("%s/values.yaml" % chart_dir, encoding="utf-8").read() values_yaml = open("%s/values.yaml" % chart_dir, encoding="utf-8").read()
except IOError: except IOError:
pass pass
if re.search(r"^\s*ambientMesh\s*:", values_yaml, re.M):
declared = json.load(open(values_json)) ok("values.yaml ships the ambientMesh key with its default, and the render "
node = (((declared.get("networkPolicy") or {}).get("ingress") or {}) "with %s=true succeeded - which is itself the proof that "
.get("ambientMesh")) "values.schema.json declares it, since networkPolicy carries "
if not isinstance(node, dict): "additionalProperties: false" % gate)
fail("renders with %s.enabled=true - so the schema knows the key - but the "
"chart's own values.yaml declares no ambientMesh OBJECT (found %r). A "
"value that exists only in the schema is reachable through --set and "
"findable nowhere: the defaults are what an operator reads. Ship the "
"object with enabled: false, port: %d and redirectedProbeSources: "
"[%s], as in template-chart." % (gate, node, PORT, CIDR))
else: else:
problems = [] fail("renders with %s=true - so the schema knows the key - but values.yaml "
if node.get("enabled") not in (False, None): "contains no ambientMesh key at all. A value that exists only in the "
problems.append("enabled is %r and must default to false - the switch is " "schema is reachable through --set and findable nowhere: the defaults "
"additive and no release may get it by accident" "are what an operator reads. Add it with its comment, default false, "
% node.get("enabled")) "as in template-chart." % gate)
if node.get("port") != PORT:
problems.append("port defaults to %r, but this test pins Istio's HBONE "
"port at %d" % (node.get("port"), PORT))
# Conditional on the chart offering probeSources, exactly as the
# inseparability check below and assertion 4 are. A chart whose probe rule
# is never source-restricted renders no ipBlock, so this key would reach no
# rule - and a value that silently does nothing reads as a control, which is
# worse than one that is absent (README, "Values, and shipping only the live
# ones"). So for those charts the key must be ABSENT, and that is asserted
# rather than tolerated.
sources = node.get("redirectedProbeSources")
offers_ps = bool(re.search(r"^\s*probeSources\s*:", values_yaml, re.M))
if offers_ps and sources != [CIDR]:
problems.append("redirectedProbeSources defaults to %r, but this test "
"pins Istio's redirected probe source at [%r]"
% (sources, CIDR))
if not offers_ps and sources is not None:
problems.append("redirectedProbeSources is declared (%r) although this "
"chart offers no networkPolicy.probeSources, so its probe "
"rule is never source-restricted and no ipBlock is ever "
"rendered - the key would reach no rule and read as a "
"control that does nothing" % sources)
if problems:
fail("the chart's declared ambient-mesh defaults do not match the "
"numbers ci/run-networkpolicy-test.sh pins: %s. Both are ISTIO "
"CONSTANTS, and they are values precisely so an operator can "
"correct them without a chart release - but the exception assertion "
"2 grants the HBONE port is pinned to the number THIS TEST knows, "
"never to whatever the value says, because a value-driven exception "
"would let a mistyped port ride in through a configurable field. "
"That is the hole design A of #143 was rejected for. So: if Istio "
"changed the constant, change it in all 30 charts AND in the pinned "
"expectation here - the drift is meant to be loud."
% "; ".join(problems))
else:
ok("declares the pinned ambient-mesh defaults verbatim - enabled false, "
"port %d, %s - so the named exception below is granted to a number "
"this chart really ships, and not to whatever a value happened to say"
% (PORT, ("redirectedProbeSources [%s]" % CIDR) if offers_ps else
"and no redirectedProbeSources, correctly, since this chart "
"renders no ipBlock at all"))
off_policies = own_policies(off_path) off_policies = own_policies(off_path)
on_policies = own_policies(on_path) on_policies = own_policies(on_path)
@@ -1613,20 +1496,20 @@ else:
if cidr == CIDR: if cidr == CIDR:
off_cidr_hits.append("%s ingress[%d]" % (name, i)) off_cidr_hits.append("%s ingress[%d]" % (name, i))
if off_hits: if off_hits:
fail("allows ingress port %d with %s.enabled at its default (false): %s. The " fail("allows ingress port %d with %s at its default (false): %s. The "
"exception assertion 2 grants for %d is gated on that value, and " "exception assertion 2 grants for %d is gated on that value, and "
"this is the half that keeps it from being a hole - a port rendered " "this is the half that keeps it from being a hole - a port rendered "
"unconditionally would otherwise be found present by this assertion " "unconditionally would otherwise be found present by this assertion "
"and never held against the pod spec by assertion 2. Gate the port " "and never held against the pod spec by assertion 2. Gate the port "
"on the value." % (PORT, gate, ", ".join(off_hits), PORT)) "on the value." % (PORT, gate, ", ".join(off_hits), PORT))
elif off_cidr_hits: elif off_cidr_hits:
fail("carries the ipBlock %s with %s.enabled at its default (false): %s. The " fail("carries the ipBlock %s with %s at its default (false): %s. The "
"probe-source half of the switch is for the mesh only, where the " "probe-source half of the switch is for the mesh only, where the "
"kubelet's probes are redirected; outside it the address matches " "kubelet's probes are redirected; outside it the address matches "
"nothing and makes the rule read as if it did something." "nothing and makes the rule read as if it did something."
% (CIDR, gate, ", ".join(off_cidr_hits))) % (CIDR, gate, ", ".join(off_cidr_hits)))
else: else:
ok("with %s.enabled at its default the policy allows neither port %d nor the " ok("with %s at its default the policy allows neither port %d nor the "
"ipBlock %s - the negative half of assertion 2's named exception, " "ipBlock %s - the negative half of assertion 2's named exception, "
"without which the exception would be a hole" % (gate, PORT, CIDR)) "without which the exception would be a hole" % (gate, PORT, CIDR))
@@ -1661,18 +1544,18 @@ else:
"the selected pod spec" % (name, i, value)) "the selected pod spec" % (name, i, value))
if not found_port: if not found_port:
fail("renders no ingress port %d with %s.enabled=true, so the value is " fail("renders no ingress port %d with %s=true, so the value is "
"decorative: the chart accepts it, the schema declares it, and the " "decorative: the chart accepts it, the schema declares it, and the "
"policy that comes out is the same one as before. A pod whose " "policy that comes out is the same one as before. A pod whose "
"namespace has joined the ambient mesh is then unreachable, because " "namespace has joined the ambient mesh is then unreachable, because "
"ambient does not bypass NetworkPolicy enforcement." % (PORT, gate)) "ambient does not bypass NetworkPolicy enforcement." % (PORT, gate))
else: else:
ok("with %s.enabled=true the policy allows %s - ztunnel's HBONE tunnel, the one " ok("with %s=true the policy allows %s - ztunnel's HBONE tunnel, the one "
"port assertion 2 grants a named exception for" "port assertion 2 grants a named exception for"
% (gate, ", ".join(found_port))) % (gate, ", ".join(found_port)))
if bad_ports: if bad_ports:
fail("with %s.enabled=true the policy allows ingress port(s) that are NOT " fail("with %s=true the policy allows ingress port(s) that are NOT "
"containerPorts of the selected pod spec and are NOT the one number " "containerPorts of the selected pod spec and are NOT the one number "
"the exception covers (%d): %s. This is the line between a named " "the exception covers (%d): %s. This is the line between a named "
"exception and the free port key that was rejected as design A of " "exception and the free port key that was rejected as design A of "
@@ -1680,7 +1563,7 @@ else:
"including a NodePort number written where a pod port belongs." "including a NodePort number written where a pod port belongs."
% (gate, PORT, "; ".join(bad_ports))) % (gate, PORT, "; ".join(bad_ports)))
elif found_port: elif found_port:
ok("with %s.enabled=true, port %d is still the ONLY ingress port of this chart " ok("with %s=true, port %d is still the ONLY ingress port of this chart "
"that is not a containerPort of the selected pod spec - the exception " "that is not a containerPort of the selected pod spec - the exception "
"covers exactly one number and nothing rode in beside it" "covers exactly one number and nothing rode in beside it"
% (gate, PORT)) % (gate, PORT))
@@ -1716,7 +1599,7 @@ else:
"%s ingress[%d] (peers: %s)" "%s ingress[%d] (peers: %s)"
% (name, i, ", ".join(cidrs_of(rule)) or "no ipBlock at all")) % (name, i, ", ".join(cidrs_of(rule)) or "no ipBlock at all"))
if restricted_without_cidr: if restricted_without_cidr:
fail("with %s.enabled=true the rule carrying port %d restricts its sources but " fail("with %s=true the rule carrying port %d restricts its sources but "
"does not include the ipBlock %s: %s. Inside the mesh the kubelet's " "does not include the ipBlock %s: %s. Inside the mesh the kubelet's "
"probes are redirected and arrive from that address instead of from " "probes are redirected and arrive from that address instead of from "
"the worker node's, so a restricted rule without it drops every " "the worker node's, so a restricted rule without it drops every "
@@ -58,18 +58,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -104,7 +93,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 8080 port: 8080
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -118,22 +107,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -178,7 +156,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -196,29 +174,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -238,85 +238,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
@@ -75,18 +75,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -144,7 +133,7 @@ spec:
port: 6767 port: 6767
- protocol: TCP - protocol: TCP
port: 5007 port: 5007
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -158,22 +147,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -218,7 +196,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -236,29 +214,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -1461,85 +1461,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+5 -45
View File
@@ -61,18 +61,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -159,7 +148,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: {{ $settings.ports.admin | int64 }} port: {{ $settings.ports.admin | int64 }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -173,22 +162,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -217,7 +195,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -235,29 +213,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -625,85 +625,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
@@ -61,18 +61,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -105,7 +94,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 3000 port: 3000
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -119,22 +108,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -163,7 +141,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -181,29 +159,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -37,28 +37,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -189,85 +189,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# REMOTE ASSETS: lets Chromium fetch over TCP 80 and 443, to any # REMOTE ASSETS: lets Chromium fetch over TCP 80 and 443, to any
@@ -63,18 +63,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -105,7 +94,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 8123 port: 8123
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -119,22 +108,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -160,7 +138,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -178,29 +156,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -20,28 +20,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -337,83 +337,5 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
+5 -45
View File
@@ -88,18 +88,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -185,7 +174,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: {{ $settings.ports.management | int64 }} port: {{ $settings.ports.management | int64 }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -199,22 +188,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -243,7 +221,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -261,29 +239,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -1016,85 +1016,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
@@ -56,18 +56,7 @@ WHAT THESE POLICIES DO NOT DO:
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -95,7 +84,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 8008 port: 8008
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -109,22 +98,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -171,7 +149,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -189,29 +167,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -413,85 +413,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports FOR SYNAPSE ONLY. These deliberately do NOT # Additional outbound TCP ports FOR SYNAPSE ONLY. These deliberately do NOT
@@ -61,18 +61,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -124,7 +113,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: {{ .Values.server.apiPort | int64 }} port: {{ .Values.server.apiPort | int64 }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -138,22 +127,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -179,7 +157,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -197,29 +175,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -37,28 +37,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -251,85 +251,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
@@ -57,15 +57,7 @@ selects.
thing as the top-level .Values.ingress (the Ingress object) - both match thing as the top-level .Values.ingress (the Ingress object) - both match
a search for "ingress". */}} a search for "ingress". */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -105,7 +97,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: {{ .Values.server.port | int64 }} port: {{ .Values.server.port | int64 }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's {{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's
HBONE tunnel, the port every inbound connection arrives on once the HBONE tunnel, the port every inbound connection arrives on once the
namespace carries istio.io/dataplane-mode - ambient "does not and will namespace carries istio.io/dataplane-mode - ambient "does not and will
@@ -131,22 +123,11 @@ spec:
and restricted to the namespace otherwise, and the two branches are and restricted to the namespace otherwise, and the two branches are
separate rules in this template - so the port is added twice, or it separate rules in this template - so the port is added twice, or it
would be missing from whichever branch a release happens to render. would be missing from whichever branch a release happens to render.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- end }} {{- end }}
{{- if not (eq .Values.server.service.type "NodePort") }} {{- if not (eq .Values.server.service.type "NodePort") }}
@@ -163,7 +144,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: {{ .Values.server.port | int64 }} port: {{ .Values.server.port | int64 }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's {{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's
HBONE tunnel, the port every inbound connection arrives on once the HBONE tunnel, the port every inbound connection arrives on once the
namespace carries istio.io/dataplane-mode - ambient "does not and will namespace carries istio.io/dataplane-mode - ambient "does not and will
@@ -189,22 +170,11 @@ spec:
and restricted to the namespace otherwise, and the two branches are and restricted to the namespace otherwise, and the two branches are
separate rules in this template - so the port is added twice, or it separate rules in this template - so the port is added twice, or it
would be missing from whichever branch a release happens to render. would be missing from whichever branch a release happens to render.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
from: from:
- namespaceSelector: - namespaceSelector:
+1 -14
View File
@@ -30,21 +30,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
}
}
} }
} }
}, },
+1 -30
View File
@@ -386,36 +386,7 @@ networkPolicy:
# NOT PROVEN: that 15008 alone is enough comes from Istio's documentation; # NOT PROVEN: that 15008 alone is enough comes from Istio's documentation;
# measured on this cluster is only which listeners ztunnel opens, and no # measured on this cluster is only which listeners ztunnel opens, and no
# namespace carries istio.io/dataplane-mode yet. # namespace carries istio.io/dataplane-mode yet.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+5 -45
View File
@@ -58,18 +58,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -104,7 +93,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 3000 port: 3000
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -118,22 +107,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -177,7 +155,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -195,29 +173,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -290,85 +290,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+2 -2
View File
@@ -5,7 +5,7 @@ icon: https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/dev/src-ui/s
type: application type: application
version: 15.1.0+up3.3.1 version: 14.2.0+up3.3.1
appVersion: 3.3.1 appVersion: 3.3.1
maintainers: maintainers:
@@ -56,7 +56,7 @@ dependencies:
# #
# Independent of appVersion: raising it is a patch or minor bump of this # Independent of appVersion: raising it is a patch or minor bump of this
# chart and is never tied to the paperless-ngx image. # chart and is never tied to the paperless-ngx image.
version: "2.0.0+up9.1.2.alpine" version: "1.0.0+up9.1.2.alpine"
# Our own published index, and the READ path only - anonymously readable # Our own published index, and the READ path only - anonymously readable
# (measured), so ci/helm-dependencies.sh needs no credential and was not # (measured), so ci/helm-dependencies.sh needs no credential and was not
# touched. The publish target and its credentials still live in 1Password # touched. The publish target and its credentials still live in 1Password
@@ -86,18 +86,7 @@ that the egress rule below contains their containerPorts.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -132,7 +121,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 8000 port: 8000
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -146,22 +135,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -205,7 +183,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -223,29 +201,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -93,28 +93,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -685,85 +685,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Outbound IMAPS (993) for paperless' mail import. OFF, and it has to be a # Outbound IMAPS (993) for paperless' mail import. OFF, and it has to be a
+5 -45
View File
@@ -52,18 +52,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -99,7 +88,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 3000 port: 3000
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -113,22 +102,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -172,7 +150,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -190,29 +168,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -337,85 +337,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+5 -45
View File
@@ -52,18 +52,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -98,7 +87,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 3000 port: 3000
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -112,22 +101,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -171,7 +149,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -189,29 +167,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -301,85 +301,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
@@ -55,18 +55,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -104,7 +93,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 80 port: 80
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -118,22 +107,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -177,7 +155,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -195,29 +173,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -52,28 +52,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -468,85 +468,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+3 -22
View File
@@ -53,15 +53,7 @@ selects.
thing as the top-level .Values.ingress (the Ingress object) - both match thing as the top-level .Values.ingress (the Ingress object) - both match
a search for "ingress". */}} a search for "ingress". */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -103,7 +95,7 @@ spec:
port: 445 port: 445
- protocol: TCP - protocol: TCP
port: 139 port: 139
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's {{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's
HBONE tunnel, the port every inbound connection arrives on once the HBONE tunnel, the port every inbound connection arrives on once the
namespace carries istio.io/dataplane-mode - ambient "does not and will namespace carries istio.io/dataplane-mode - ambient "does not and will
@@ -126,22 +118,11 @@ spec:
restricted here, and 445 is additionally published as a NodePort. restricted here, and 445 is additionally published as a NodePort.
WHICH RULE IT JOINS: WHICH RULE IT JOINS:
the only rule this policy has, the one carrying 445 and 139. the only rule this policy has, the one carrying 445 and 139.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
egress: egress:
{{- /* {{- /*
+1 -14
View File
@@ -23,21 +23,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
}
}
} }
} }
}, },
+1 -30
View File
@@ -162,36 +162,7 @@ networkPolicy:
# NOT PROVEN: that 15008 alone is enough comes from Istio's documentation; # NOT PROVEN: that 15008 alone is enough comes from Istio's documentation;
# measured on this cluster is only which listeners ztunnel opens, and no # measured on this cluster is only which listeners ztunnel opens, and no
# namespace carries istio.io/dataplane-mode yet. # namespace carries istio.io/dataplane-mode yet.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
@@ -61,15 +61,7 @@ selects.
thing as the top-level .Values.ingress (the Ingress object) - both match thing as the top-level .Values.ingress (the Ingress object) - both match
a search for "ingress". */}} a search for "ingress". */}}
{{- $npIngress := .Values.networkPolicy.ingress | default dict }} {{- $npIngress := .Values.networkPolicy.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -111,7 +103,7 @@ spec:
port: {{ include "satisfactory.formatNumber" .Values.server.gamePort }} port: {{ include "satisfactory.formatNumber" .Values.server.gamePort }}
- protocol: TCP - protocol: TCP
port: {{ include "satisfactory.formatNumber" .Values.server.messagingPort }} port: {{ include "satisfactory.formatNumber" .Values.server.messagingPort }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's {{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's
HBONE tunnel, the port every inbound connection arrives on once the HBONE tunnel, the port every inbound connection arrives on once the
namespace carries istio.io/dataplane-mode - ambient "does not and will namespace carries istio.io/dataplane-mode - ambient "does not and will
@@ -135,21 +127,10 @@ spec:
WHICH RULE IT JOINS: WHICH RULE IT JOINS:
the only rule this policy has, the one carrying the game and messaging the only rule this policy has, the one carrying the game and messaging
ports. ports.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- end }} {{- end }}
+1 -14
View File
@@ -20,21 +20,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
}
}
} }
} }
}, },
+1 -30
View File
@@ -219,34 +219,5 @@ networkPolicy:
# NOT PROVEN: that 15008 alone is enough comes from Istio's documentation; # NOT PROVEN: that 15008 alone is enough comes from Istio's documentation;
# measured on this cluster is only which listeners ztunnel opens, and no # measured on this cluster is only which listeners ztunnel opens, and no
# namespace carries istio.io/dataplane-mode yet. # namespace carries istio.io/dataplane-mode yet.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
+5 -45
View File
@@ -53,18 +53,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -99,7 +88,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 8080 port: 8080
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -113,22 +102,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -172,7 +150,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -190,29 +168,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -514,85 +514,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+2 -2
View File
@@ -37,7 +37,7 @@ type: application
# #
# Note for whoever bumps this next: the suffix tracks appVersion, so a major # Note for whoever bumps this next: the suffix tracks appVersion, so a major
# here keeps the +up<appVersion> it was merged with. # here keeps the +up<appVersion> it was merged with.
version: 3.1.0+upv0.16.26 version: 2.1.0+upv0.16.26
appVersion: "v0.16.26" appVersion: "v0.16.26"
maintainers: maintainers:
@@ -95,7 +95,7 @@ dependencies:
# #
# Independent of appVersion: raising it is a patch or minor bump of this # Independent of appVersion: raising it is a patch or minor bump of this
# chart and is never tied to the Stalwart image. # chart and is never tied to the Stalwart image.
version: "2.0.0+up9.1.2.alpine" version: "1.0.0+up9.1.2.alpine"
# Our own published index, and the READ path only. It is anonymously # Our own published index, and the READ path only. It is anonymously
# readable - measured 2026-10-02: GET .../index.yaml returns 200 without # readable - measured 2026-10-02: GET .../index.yaml returns 200 without
# credentials - so ci/helm-dependencies.sh needs no secret and was not # credentials - so ci/helm-dependencies.sh needs no secret and was not
+5 -45
View File
@@ -68,18 +68,7 @@ unenforced policy is accepted and filters nothing.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := .Values.networkPolicy.ingress | default dict }} {{- $npIngress := .Values.networkPolicy.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
{{- range $group, $spec := fromYaml (include "stalwart.nodes" .) }} {{- range $group, $spec := fromYaml (include "stalwart.nodes" .) }}
{{- $name := printf "%s-%s" (include "stalwart.fullname" $root) $group }} {{- $name := printf "%s-%s" (include "stalwart.fullname" $root) $group }}
--- ---
@@ -124,7 +113,7 @@ spec:
- ports: - ports:
- protocol: {{ $http.protocol | default "TCP" }} - protocol: {{ $http.protocol | default "TCP" }}
port: {{ int $http.containerPort }} port: {{ int $http.containerPort }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -138,22 +127,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if or $probeSources $allowedNs }} {{- if or $probeSources $allowedNs }}
{{- /* {{- /*
@@ -197,7 +175,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -215,29 +193,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -340,28 +340,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -590,85 +590,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
# Egress is restricted BY PORT, not by destination, and the reason is a limit # Egress is restricted BY PORT, not by destination, and the reason is a limit
# of the object rather than laziness: a NetworkPolicy matches ipBlock CIDRs # of the object rather than laziness: a NetworkPolicy matches ipBlock CIDRs
+5 -45
View File
@@ -59,18 +59,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -105,7 +94,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 3000 port: 3000
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -119,22 +108,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -178,7 +156,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -196,29 +174,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -492,85 +492,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
@@ -54,15 +54,7 @@ selects.
thing as the top-level .Values.ingress (the Ingress object) - both match thing as the top-level .Values.ingress (the Ingress object) - both match
a search for "ingress". */}} a search for "ingress". */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -109,7 +101,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: {{ .Values.server.fileTransferPort | int64 }} port: {{ .Values.server.fileTransferPort | int64 }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's {{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's
HBONE tunnel, the port every inbound connection arrives on once the HBONE tunnel, the port every inbound connection arrives on once the
namespace carries istio.io/dataplane-mode - ambient "does not and will namespace carries istio.io/dataplane-mode - ambient "does not and will
@@ -136,22 +128,11 @@ spec:
OR over its rules, so which rule carries the port decides only which OR over its rules, so which rule carries the port decides only which
sources reach it - and an open TCP rule is the honest default while it sources reach it - and an open TCP rule is the honest default while it
is unknown where ztunnel dials from. is unknown where ztunnel dials from.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- /* {{- /*
THE WEB QUERY INTERFACE, and the one rule in this chart with real security THE WEB QUERY INTERFACE, and the one rule in this chart with real security
+1 -14
View File
@@ -30,21 +30,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
}
}
} }
} }
}, },
+1 -30
View File
@@ -166,36 +166,7 @@ networkPolicy:
# NOT PROVEN: that 15008 alone is enough comes from Istio's documentation; # NOT PROVEN: that 15008 alone is enough comes from Istio's documentation;
# measured on this cluster is only which listeners ztunnel opens, and no # measured on this cluster is only which listeners ztunnel opens, and no
# namespace carries istio.io/dataplane-mode yet. # namespace carries istio.io/dataplane-mode yet.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+27 -89
View File
@@ -113,23 +113,7 @@ spec this policy selects.
round of this work already reported ingressController as an inbound round of this work already reported ingressController as an inbound
escape hatch on the strength of the substring alone. */}} escape hatch on the strength of the substring alone. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE, and not as decoration (#99). ambientMesh is an {{- $ambientMesh := $npIngress.ambientMesh }}
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 }} {{- $routed := or .Values.ingress.enabled .Values.httpRoute.enabled }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
@@ -163,36 +147,20 @@ spec:
- protocol: {{ .protocol | default "TCP" }} - protocol: {{ .protocol | default "TCP" }}
port: {{ .containerPort }} port: {{ .containerPort }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh. The default is {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh. 15008/TCP is
15008/TCP: ztunnel's HBONE tunnel, the port every inbound mesh ztunnel's HBONE tunnel, the port every inbound mesh connection
connection arrives on once the namespace carries arrives on once the namespace carries istio.io/dataplane-mode.
istio.io/dataplane-mode. Istio's own documentation is explicit Istio's own documentation is explicit that ambient "does not and
that ambient "does not and will never bypass Kubernetes will never bypass Kubernetes NetworkPolicy enforcement", so
NetworkPolicy enforcement", so without this port a pod in the without this port a pod in the mesh is simply unreachable.
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 It is the one port in this rule that is NOT a containerPort of
the selected pod spec - ztunnel terminates it, not the the selected pod spec - ztunnel terminates it, not the
application - so ci/run-networkpolicy-test.sh holds a NAMED application - so ci/run-networkpolicy-test.sh holds a NAMED
exception for it. Note what that exception is pinned to: the exception for exactly this number, gated on exactly this value.
number 15008 as written in THE TEST, plus an assertion that this Anything else that is not a containerPort still fails, and so
chart's values.yaml declares the same default. It is NOT "whatever does 15008 itself while the value is off.
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 NOT MEASURED, and the chart should not pretend otherwise: that
15008 ALONE suffices comes from Istio's documentation. What has 15008 ALONE suffices comes from Istio's documentation. What has
@@ -200,7 +168,7 @@ spec:
opens. Provable at the first namespace that joins the mesh, and opens. Provable at the first namespace that joins the mesh, and
no namespace carries the label yet. */}} no namespace carries the label yet. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* RESTRICTED, because the operator supplied probeSources. The {{- /* RESTRICTED, because the operator supplied probeSources. The
@@ -241,64 +209,34 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason
the switch is one value instead of two. Inside ISTIO's ambient the switch is one value instead of two. Inside the mesh the
mesh the kubelet's probes are redirected BY ISTIO and no longer kubelet's probes are redirected and no longer arrive from the
arrive from the worker node's address: they arrive from worker node's address: they arrive from 169.254.7.127, which is
169.254.7.127, which is therefore the probe source nobody would therefore the probeSources entry nobody would think to add.
think to add.
WHERE THAT ADDRESS COMES FROM, because it is worth being able to The two halves are deliberately inseparable. An operator who
recognise as vendor magic rather than as protocol: only opens 15008 by hand has a policy that passes every review -
and the moment probeSources is non-empty, every probe is dropped,
- It is LINK-LOCAL, 169.254.0.0/16 (RFC 3927) - a range the pod never becomes Ready, and the symptom reads as a broken
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 application rather than as a policy. That is assertion 4 of
ci/run-networkpolicy-test.sh, re-created one value further along. 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 Only rendered in this branch, because this is the only branch
with a "from" list at all: with probeSources empty the rule is with a "from" list at all: with probeSources empty the rule is
open and needs no peer. 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.
UNPROVEN IN OPERATION: probeSources is empty in every chart of UNPROVEN IN OPERATION: probeSources is empty in every chart of
this repository (stalwart sets it to the empty list explicitly), this repository (stalwart sets it to the empty list explicitly),
so no release reaches this branch today. What CI proves is its so no release reaches this branch today. What CI proves is its
render, through the overlay in ci/networkpolicy. */}} render, through the overlay in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* NO "from" SELECTOR, WHICH MEANS OPEN - deliberately, and only {{- /* NO "from" SELECTOR, WHICH MEANS OPEN - deliberately, and only
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+32 -93
View File
@@ -283,111 +283,50 @@ networkPolicy:
# pods. A SIBLING of this block and NO PART # pods. A SIBLING of this block and NO PART
# OF IT, despite the name. # OF IT, despite the name.
ingress: ingress:
# Opens the pod for Istio's ambient mesh. OFF by default, and additive: with # Opens the pod for Istio's ambient mesh. OFF by default, and additive:
# it off this chart renders byte for byte as it did before the value existed. # 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: # WHAT IT RENDERS, and the two halves are the point:
# #
# 1. The HBONE port inbound (default 15008/TCP) - ztunnel's tunnel. Every # 1. 15008/TCP inbound - ztunnel's HBONE tunnel. Every inbound mesh
# inbound mesh connection arrives on it, and Istio's documentation is # connection arrives on it, and Istio's documentation is explicit that
# explicit that ambient "does not and will never bypass Kubernetes # ambient "does not and will never bypass Kubernetes NetworkPolicy
# NetworkPolicy enforcement". Without this port a pod whose namespace # enforcement". Without this port a pod whose namespace has joined the
# has joined the mesh is unreachable. # mesh is unreachable.
# 2. The redirected probe source (default 169.254.7.127/32) - but ONLY # 2. 169.254.7.127/32 as an additional probe source - but ONLY where
# where networkPolicy.probeSources is non-empty, because only then does # probeSources is non-empty, because only then does the rule have a
# the rule have a "from" list at all. Inside the mesh the kubelet's # "from" list at all. Inside the mesh the kubelet's probes are
# probes are redirected and no longer come from the worker node's # redirected and no longer come from the worker node's address.
# address.
# #
# WHY ONE SWITCH AND NOT TWO. Point 2 is the one nobody thinks of. An # 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 # 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 # 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 # 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 # application rather than as a policy. A switch whose halves can only be
# together cannot be set halfway - which is why the two live in ONE object # had together cannot be set halfway - which is why this is a named
# under one "enabled", and not as separate values that can drift apart. # intention and not a free port list.
# #
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values # TWO THINGS IT DOES NOT PROVE, and they belong next to the value rather
# rather than template literals for exactly that reason: a vendor constant # than in a commit message:
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
# #
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults # - That 15008 alone is enough. That comes from Istio's documentation.
# declared here (#99), so "ambientMesh: null", or an object carrying only # Measured on this cluster is only which listeners ztunnel opens
# "enabled: true", renders the documented defaults instead of failing. # ([::]:15008 inbound, plus its own admin and stats ports). Provable at
ambientMesh: # the first namespace that joins the mesh; none carries
# The switch. Everything else in this object is inert while it is false,
# and the chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: it is not
# registered with IANA for this, no protocol prescribes it, and Kubernetes
# knows nothing about it. Read for ztunnel 1.30.6 (measured on this
# cluster: ztunnel opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh simply cannot be reached. Change
# it here in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately the thing that
# forces someone to look, because the exception it grants this port is
# pinned to the number IT knows and not to whatever this value says. A
# value-driven exception would let a mistyped port through, which is the
# hole design A of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens.
# Provable at the first namespace to join the mesh; none carries
# istio.io/dataplane-mode yet. # istio.io/dataplane-mode yet.
port: 15008 # - That the probe-source half works. probeSources is empty in every
# chart here, so no release reaches that branch. CI proves its render,
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM. # not its effect.
# #
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the # If the policy restricts sources, note that 15008 is restricted with them:
# two are easy to confuse, so: # 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
# networkPolicy.probeSources # open - it runs with its own pod IPs in istio-system (measured: no
# YOUR worker node CIDRs. Empty by default, and empty means the # hostNetwork on the DaemonSet, Istio 1.30.6), so if one turns out to be
# probe rule is OPEN. The chart cannot know these addresses. # needed it is expressible as a namespaceSelector plus podSelector and does
# networkPolicy.ingress.ambientMesh.redirectedProbeSources # not need an unknowable node-IP CIDR.
# ISTIO's rewrite address. Shipped filled, because unlike the node ambientMesh: false
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM, so it is recognisable 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, and this list in all 30 charts
# plus the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and this is the one case where that does not
# contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the
# ipBlock, deliberately: the two halves of this switch are not separable.
# To not open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
@@ -55,18 +55,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -101,7 +90,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 8080 port: 8080
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -115,22 +104,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -174,7 +152,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -192,29 +170,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -311,85 +311,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+3 -22
View File
@@ -36,15 +36,7 @@ a port that is genuinely closed to everything but the namespaces named below.
thing as the top-level .Values.ingress (the Ingress object) - both match thing as the top-level .Values.ingress (the Ingress object) - both match
a search for "ingress". */}} a search for "ingress". */}}
{{- $npIngress := .Values.networkPolicy.ingress | default dict }} {{- $npIngress := .Values.networkPolicy.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -81,7 +73,7 @@ spec:
ports: ports:
- protocol: TCP - protocol: TCP
port: 6379 port: 6379
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's {{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's
HBONE tunnel, the port every inbound connection arrives on once the HBONE tunnel, the port every inbound connection arrives on once the
namespace carries istio.io/dataplane-mode - ambient "does not and will namespace carries istio.io/dataplane-mode - ambient "does not and will
@@ -112,21 +104,10 @@ spec:
expressible as a namespaceSelector plus podSelector and not as an expressible as a namespaceSelector plus podSelector and not as an
unknowable node CIDR. It cannot be settled before a namespace joins the unknowable node CIDR. It cannot be settled before a namespace joins the
mesh, and none has. mesh, and none has.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- end }} {{- end }}
+1 -14
View File
@@ -114,21 +114,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
}
}
} }
} }
}, },
+1 -30
View File
@@ -313,34 +313,5 @@ networkPolicy:
# NOT PROVEN: that 15008 alone is enough comes from Istio's documentation; # NOT PROVEN: that 15008 alone is enough comes from Istio's documentation;
# measured on this cluster is only which listeners ztunnel opens, and no # measured on this cluster is only which listeners ztunnel opens, and no
# namespace carries istio.io/dataplane-mode yet. # namespace carries istio.io/dataplane-mode yet.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
@@ -48,18 +48,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -94,7 +83,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 8000 port: 8000
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -108,22 +97,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -167,7 +145,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -185,29 +163,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -387,85 +387,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
@@ -58,18 +58,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -105,7 +94,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 5000 port: 5000
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -119,22 +108,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -179,7 +157,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -197,29 +175,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -573,85 +573,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
+5 -45
View File
@@ -49,18 +49,7 @@ selects.
for "ingress"; one round of this work already mistook the second for an for "ingress"; one round of this work already mistook the second for an
inbound escape hatch on the strength of the substring. */}} inbound escape hatch on the strength of the substring. */}}
{{- $npIngress := $np.ingress | default dict }} {{- $npIngress := $np.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes {{- $ambientMesh := $npIngress.ambientMesh }}
through a fallback reproducing THIS chart's declared defaults. An
operator who deletes the object, or writes only "enabled: true", must
land on the defaults and not in a nil dereference - #136 shipped exactly
that defect one value away, where the NOTES.txt branch dereferenced
.networkPolicy.egress directly. Clearing the probe-source list restores
the constant rather than removing the ipBlock, deliberately: the two
halves are not separable, and enabled: false is how you open nothing. */}}
{{- $ambient := $npIngress.ambientMesh | default dict }}
{{- $ambientEnabled := $ambient.enabled }}
{{- $ambientPort := $ambient.port | default 15008 }}
{{- $ambientProbeSources := $ambient.redirectedProbeSources | default (list "169.254.7.127/32") }}
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
@@ -96,7 +85,7 @@ spec:
- ports: - ports:
- protocol: TCP - protocol: TCP
port: 3000 port: 3000
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP {{- /* FIRST HALF OF networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP
is ztunnel's HBONE tunnel, the port every inbound connection arrives on is ztunnel's HBONE tunnel, the port every inbound connection arrives on
once the namespace carries istio.io/dataplane-mode - ambient "does not once the namespace carries istio.io/dataplane-mode - ambient "does not
@@ -110,22 +99,11 @@ spec:
second render, that 15008 stays the ONLY such port and that it is absent second render, that 15008 stays the ONLY such port and that it is absent
while the value is false. while the value is false.
AN ISTIO CONSTANT, NOT A STANDARD: 15008 is not registered with
IANA for this, no protocol prescribes it, and Kubernetes knows
nothing about it - Istio picked it for HBONE and Istio can change
it. Hence a VALUE with a documented default rather than a literal
here, so an operator can correct it without a chart release. Read
for ztunnel 1.30.6. The exception ci/run-networkpolicy-test.sh
grants is pinned to the number THAT TEST names, plus a check that
this chart declares the same default - never to whatever the value
says, because that would let a mistyped port ride in through a
configurable field (design A of #143).
NOT MEASURED: that 15008 alone suffices comes from Istio's NOT MEASURED: that 15008 alone suffices comes from Istio's
documentation. Measured on this cluster is only which listeners ztunnel documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}} opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP - protocol: TCP
port: {{ int $ambientPort }} port: 15008
{{- end }} {{- end }}
{{- if $probeSources }} {{- if $probeSources }}
{{- /* {{- /*
@@ -169,7 +147,7 @@ spec:
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: {{ . | quote }}
{{- end }} {{- end }}
{{- if $ambientEnabled }} {{- if $ambientMesh }}
{{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is {{- /* SECOND HALF OF networkPolicy.ingress.ambientMesh, and the reason it is
one value and not two. Inside the mesh the kubelet's probes are one value and not two. Inside the mesh the kubelet's probes are
redirected and no longer arrive from the worker node's address: they redirected and no longer arrive from the worker node's address: they
@@ -187,29 +165,11 @@ spec:
counterpart is deliberately absent - this cluster is SingleStack IPv4, counterpart is deliberately absent - this cluster is SingleStack IPv4,
and an ipBlock of the wrong family is accepted and matches nothing. and an ipBlock of the wrong family is accepted and matches nothing.
WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
it is LINK-LOCAL (169.254.0.0/16, RFC 3927), a range reserved for
node-local use and never routed; Istio's IPv6 counterpart
fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the SAME DIGITS
plus 1337; and KUBERNETES DOES NOT KNOW THE ADDRESS - neither the
NetworkPolicy API nor the kubelet - only Istio's data plane
rewrites the probe source onto it. Read for ztunnel 1.30.6, and if
Istio changes it THE FAILURE IS SILENT: probes dropped, and only
where probeSources is non-empty, which is nowhere today.
A LIST, not a string, and the one case where that does not
contradict "a constant, not a configuration": a DUAL-STACK cluster
needs BOTH entries at once. This cluster is SingleStack IPv4, so
the IPv6 counterpart is left out - an ipBlock of the wrong family
is accepted and matches nothing. Add it ALONGSIDE, never instead.
UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
release reaches this branch. CI proves its render, through the overlays release reaches this branch. CI proves its render, through the overlays
in ci/networkpolicy. */}} in ci/networkpolicy. */}}
{{- range $ambientProbeSources }}
- ipBlock: - ipBlock:
cidr: {{ . | quote }} cidr: 169.254.7.127/32
{{- end }}
{{- end }} {{- end }}
{{- else }} {{- else }}
{{- /* {{- /*
+1 -21
View File
@@ -51,28 +51,8 @@
"additionalProperties": false, "additionalProperties": false,
"properties": { "properties": {
"ambientMesh": { "ambientMesh": {
"description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while enabled is false. Renders the HBONE port inbound AND, where networkPolicy.probeSources is non-empty, the redirected probe source in the same \"from\" list. The two halves live in one object under one switch on purpose: whoever opens the port by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Both numbers are ISTIO CONSTANTS rather than standards, which is why they are values with documented defaults and not template literals - a vendor constant can change, and an operator should be able to correct it without waiting for a chart release. Deleting the whole object is safe: every read falls back to the defaults the chart declares. Unproven in operation: that the HBONE port alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.", "description": "Opens the pod for Istio's ambient mesh; off by default and additive, so the render is byte-identical while it is false. Renders 15008/TCP inbound - ztunnel's HBONE tunnel, which ambient explicitly does not let bypass NetworkPolicy enforcement - AND, where probeSources is non-empty, 169.254.7.127/32 as an additional probe source, because inside the mesh the kubelet's probes are redirected away from the worker node's address. The two halves are inseparable on purpose: whoever opens 15008 by hand has not added the probe address, and the first narrowed probeSources then drops every probe, so the pod never becomes Ready and the symptom reads as a broken application rather than as a policy. Unproven in operation: that 15008 alone suffices comes from Istio's documentation, and no namespace of this cluster carries istio.io/dataplane-mode yet.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"enabled": {
"description": "The switch. Everything else in this object is inert while it is false.",
"type": ["boolean", "null"] "type": ["boolean", "null"]
},
"port": {
"description": "Istio's HBONE port, default 15008. A constant Istio chose, not a standard: not registered with IANA for this, prescribed by no protocol, and unknown to Kubernetes. Read for ztunnel 1.30.6. It is the one ingress port of these charts that is deliberately no containerPort - ztunnel terminates it, not the application - and the exception ci/run-networkpolicy-test.sh grants it is pinned to the number THAT TEST knows, plus an assertion that this chart declares the same default. Not to whatever this value says: a value-driven exception would let a mistyped port ride in through a configurable field.",
"type": ["integer", "null"],
"minimum": 1,
"maximum": 65535
},
"redirectedProbeSources": {
"description": "Sources the kubelet's probes arrive from once Istio has redirected them, default [169.254.7.127/32]. NOT the same value as networkPolicy.probeSources one level up: that one holds YOUR worker node CIDRs, is empty by default and means OPEN when empty, and the chart cannot know those addresses; this one holds ISTIO's rewrite address and ships filled, because unlike the node IPs it is a constant the chart can know. Recognisable as vendor magic rather than protocol: it is link-local (169.254.0.0/16, RFC 3927, never routed), Istio's IPv6 counterpart fd16:9254:7127:1337:ffff:ffff:ffff:ffff carries the same digits plus 1337, and Kubernetes itself knows the address in neither the NetworkPolicy API nor the kubelet. A list and not a string because a dual-stack cluster needs BOTH entries at once; this cluster is SingleStack IPv4, so the IPv6 counterpart is left out of the default - an ipBlock of the wrong family is accepted and matches nothing. If Istio changes the address the failure is SILENT: probes dropped, and only where probeSources is non-empty.",
"type": ["array", "null"],
"items": {
"type": "string"
}
}
}
} }
} }
}, },
+1 -79
View File
@@ -281,85 +281,7 @@ networkPolicy:
# IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio # IPs in istio-system (measured: no hostNetwork on the DaemonSet, Istio
# 1.30.6), so if one proves necessary it is expressible as a # 1.30.6), so if one proves necessary it is expressible as a
# namespaceSelector plus podSelector and needs no unknowable node CIDR. # namespaceSelector plus podSelector and needs no unknowable node CIDR.
# BOTH NUMBERS BELOW ARE ISTIO CONSTANTS, NOT STANDARDS, and they are values ambientMesh: false
# rather than template literals for exactly that reason: a vendor constant
# can change, and an operator should be able to correct it without waiting
# for a chart release. Their provenance is written on each one.
#
# DELETING THIS OBJECT IS SAFE. Every read falls back to the defaults
# declared here (#99), so "ambientMesh: null", or an object carrying only
# "enabled: true", renders the documented defaults instead of failing.
ambientMesh:
# The switch. Everything else here is inert while it is false, and the
# chart renders exactly as it did before the object existed.
enabled: false
# ISTIO'S HBONE PORT. A constant Istio chose, not a standard: not
# registered with IANA for this, prescribed by no protocol, and unknown to
# Kubernetes. Read for ztunnel 1.30.6 (measured on this cluster: ztunnel
# opens [::]:15008 as its inbound listener).
#
# If Istio changes it, a pod in the mesh cannot be reached. Change it here
# in all 30 charts AND in the pinned expectation inside
# ci/run-networkpolicy-test.sh - the test is deliberately what forces
# someone to look, because the exception it grants this port is pinned to
# the number IT knows and not to whatever this value says. A value-driven
# exception would let a mistyped port through, which is the hole design A
# of #143 was rejected for.
#
# NOT MEASURED: that this port ALONE suffices comes from Istio's
# documentation. Measured here is only which listeners ztunnel opens, and
# no namespace carries istio.io/dataplane-mode yet.
port: 15008
# SOURCES THE KUBELET'S PROBES ARRIVE FROM ONCE ISTIO HAS REDIRECTED THEM.
#
# NOT THE SAME VALUE AS networkPolicy.probeSources one level up, and the
# two are easy to confuse, so:
#
# networkPolicy.probeSources
# YOUR worker node CIDRs. Empty by default, and empty means the
# probe rule is OPEN. The chart cannot know these addresses.
# networkPolicy.ingress.ambientMesh.redirectedProbeSources
# ISTIO's rewrite address. Shipped filled, because unlike the node
# IPs it is a constant the chart CAN know. Only rendered where the
# rule above is restricted, i.e. where probeSources is non-empty.
#
# WHERE 169.254.7.127 COMES FROM - vendor magic, not protocol:
#
# - 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 it 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, THE FAILURE IS SILENT:
# probes would be dropped, and only where probeSources is non-empty - which
# is nowhere in this repository today - so nothing here and nothing in
# Kubernetes would report it. What to re-check then: the address Istio's
# release notes name for probe redirection, this list in all 30 charts, and
# the pinned expectation in ci/run-networkpolicy-test.sh.
#
# A LIST, not a string, and the one case where that does not contradict "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 is left out - 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.
#
# Clearing this list restores the default rather than removing the ipBlock,
# deliberately: the two halves of this switch are not separable. To not
# open the port at all, set enabled: false.
#
# UNPROVEN IN OPERATION: probeSources is empty in every chart here, so no
# release reaches this branch. CI proves the render, not the effect.
redirectedProbeSources:
- 169.254.7.127/32
egress: egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and # Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and