Files
helm-charts/valkey/templates/valkey.networkpolicy.yaml
T
jklein 320b4ef2c5 refactor(valkey): ambientMesh becomes an object carrying Istio's two constants
Gitea #143, maintainer's instruction. The HBONE port and the redirected probe
source are ISTIO CONSTANTS, not standards, so they move from template literals
into values with documented defaults - an operator can then correct them when
Istio changes a number, without waiting for a chart release.

One object under one "enabled", so the two halves still cannot be set
separately; two values could drift apart, which is what this design exists to
prevent. Every read is null-safe (#99), so a deleted object or one carrying
only "enabled: true" falls back to the defaults declared here rather than
dereferencing nil - the defect #136 shipped one value away.

Provenance now written on each constant: 15008 is Istio's HBONE port, not
IANA-registered for this and unknown to Kubernetes; 169.254.7.127 is link-local
(RFC 3927, never routed), its IPv6 counterpart carries the same digits plus
1337, and neither the NetworkPolicy API nor the kubelet knows the address -
only Istio's data plane rewrites the probe source onto it. Both read for
ztunnel 1.30.6, and a change in either fails silently.

The exception in ci/run-networkpolicy-test.sh stays pinned to the numbers that
test names, and additionally asserts this chart declares the same defaults -
never "whatever the value says", which would let a mistyped port in through a
configurable field.

No version change: nothing is published yet, the default is still off, and the
render at defaults is byte-identical - re-diffed against origin/main.

No redirectedProbeSources here, in neither values.yaml nor the schema: 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, which is worse than one that is
absent. ci/run-networkpolicy-test.sh asserts that absence rather than
tolerating it.
2026-10-19 17:57:01 +00:00

133 lines
7.2 KiB
YAML

{{- /*
NetworkPolicy for valkey (Gitea #5).
THIS CHART AND stalwart ARE THE TWO THAT HAD A POLICY BEFORE #5, so the
convention was derived from them rather than imposed on them. What changed here
is the FORM and not a single rule: the reasoning moved out of "#" comments,
which are part of the rendered object and travel into the cluster, into template
comments, which render nothing. ci/run-networkpolicy-test.sh now fails on a
rendered "#", and this file was one of the two that failed it.
TWO THINGS THAT DELIBERATELY DID NOT CHANGE, because they are right and the
convention was written to accommodate them:
1. INGRESS ONLY. This instance opens no outbound connection of its own - no
replication, no cluster bus - so an egress rule here would constrain nothing
while making the object look stricter than it is. policyTypes names only
what there is something to restrict. homeassistant and satisfactory invoke
the same rule in the other direction, for outbound traffic nobody can
enumerate.
2. 6379 IS RESTRICTED BY SOURCE, with no probeSources value anywhere in this
chart - and that combination is only safe because of a detail worth knowing:
ALL THREE PROBES HERE ARE EXEC PROBES. An exec probe runs inside the
container through the kubelet's runtime and puts no packet on the network, so
there is no kubelet traffic to let through and nothing to accommodate. A
chart with an httpGet or tcpSocket probe on this port could NOT restrict it
this way - it would need the worker node's IP, which no chart can know, and
the pod would never become Ready without it. minecraft-server is tight for
the same reason; every web app in the sweep is not.
So this policy is what the rest of the rollout aims at and mostly cannot reach:
a port that is genuinely closed to everything but the namespaces named below.
*/ -}}
{{- if .Values.networkPolicy.enabled }}
{{- /* networkPolicy.ingress - the policy's INBOUND side, and not the same
thing as the top-level .Values.ingress (the Ingress object) - both match
a search for "ingress". */}}
{{- $npIngress := .Values.networkPolicy.ingress | default dict }}
{{- /* NULL-SAFE ON PURPOSE (#99): ambientMesh is an OBJECT, so every read goes
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
kind: NetworkPolicy
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}-networkpolicy
labels:
app: {{ .Release.Name }}-{{ .Chart.Name }}-networkpolicy
spec:
podSelector:
matchLabels:
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
policyTypes:
- Ingress
ingress:
{{- /*
One rule with a list of sources. Note the shape: an empty "from" would allow
EVERYTHING, so the namespaces are always spelled out - the release's own
namespace first, then whatever networkPolicy.allowedNamespaces adds. An
empty "from: []" is worse still, because it allows everything while reading
as a restriction; ci/run-networkpolicy-test.sh rejects that form outright.
*/}}
- from:
- namespaceSelector:
matchLabels:
{{- /*
Set by Kubernetes itself on every namespace (>= 1.21), so this
needs no labelling work on the target namespaces.
*/}}
kubernetes.io/metadata.name: {{ .Release.Namespace }}
{{- range .Values.networkPolicy.allowedNamespaces }}
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: {{ . | quote }}
{{- end }}
ports:
- protocol: TCP
port: 6379
{{- if $ambientEnabled }}
{{- /* networkPolicy.ingress.ambientMesh (Gitea #143). 15008/TCP is ztunnel's
HBONE tunnel, the port every inbound connection arrives on once the
namespace carries istio.io/dataplane-mode - ambient "does not and will
never bypass Kubernetes NetworkPolicy enforcement", so without it a pod
in the mesh is unreachable.
It is the one port here that is NOT a containerPort of the selected pod
spec: ztunnel terminates it, not the application. ci/run-networkpolicy-
test.sh holds a NAMED exception for exactly this number, gated on exactly
this value, and proves on a second render that it stays the only such
port and that it is absent while the value is false.
THE PORT HALF ONLY, and that is not an omission. Elsewhere this switch
also adds 169.254.7.127/32 as a probe source, because inside the mesh the
kubelet's probes are redirected away from the worker node's address.
This chart offers no networkPolicy.probeSources at all, so the rule
carrying the probe port is never restricted by source - and an
unrestricted rule needs no probe address. Measured in the render: three
exec probes, which run inside the container through the kubelet's
runtime and put no packet on the network at all.
WHICH RULE IT JOINS:
the only rule this policy has, the one carrying 6379. That rule is
ALWAYS restricted to the release's own namespace plus allowedNamespaces,
so 15008 is restricted with it - and whether ztunnel is reached by those
peers is the open question of #143. ztunnel runs with its own pod IPs in
istio-system (measured 2026-10-19: no hostNetwork on the DaemonSet,
Istio 1.30.6), so if a peer of its own proves necessary it is
expressible as a namespaceSelector plus podSelector and not as an
unknowable node CIDR. It cannot be settled before a namespace joins the
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
documentation. Measured on this cluster is only which listeners ztunnel
opens. Provable at the first namespace to join the mesh; none has. */}}
- protocol: TCP
port: {{ int $ambientPort }}
{{- end }}
{{- end }}