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.
156 lines
8.4 KiB
YAML
156 lines
8.4 KiB
YAML
{{- /*
|
|
NetworkPolicy for satisfactory (Gitea #5).
|
|
|
|
INGRESS ONLY, AND THE MISSING EGRESS SECTION IS THE DECISION IN THIS FILE.
|
|
|
|
Two reasons, and they compound.
|
|
|
|
First, what this server needs outbound is Steam's content delivery, and that is
|
|
not a port list anyone can pin down: the dedicated server downloads and UPDATES
|
|
the game through SteamCMD over TCP 80 and 443 plus the 27015-27050 range, and
|
|
talks to the Steam master servers over UDP 27000-27100 and 4380. Valve changes
|
|
that set, the server re-downloads on every update, and a cap that is wrong
|
|
leaves a server that cannot start - or worse, one that starts and never appears
|
|
in the server browser.
|
|
|
|
Second, AND THIS IS WHY THE DECISION GOES THIS WAY RATHER THAN THE OTHER:
|
|
satisfactory is the ONE chart in this repository with no install-test at all.
|
|
It is in ci/excluded-charts.txt, because the image is multiple GB and the first
|
|
start pulls over 10GB of game files - impractical on a runner. So an egress cap
|
|
here would be the only policy in the sweep with no empirical check whatsoever
|
|
behind it, on the chart whose correct port set is least knowable. Review is the
|
|
only net this chart has, and a review cannot verify a Steam port range.
|
|
|
|
So valkey's rule applies: policyTypes names only what there is something to
|
|
restrict. A deployment that wants an egress cap should write one at release
|
|
level, where the Steam behaviour can actually be observed.
|
|
|
|
What the Ingress half still buys is real: only the three declared game ports
|
|
are reachable, so anything else the image binds is not.
|
|
|
|
WHAT THIS OBJECT IS: a ceiling on reachability, not an authentication layer.
|
|
Only the ports declared below are reachable at all - whatever else the process
|
|
binds, now or after the next app update, is blocked without anyone having to
|
|
notice it exists.
|
|
|
|
WHAT IT IS NOT:
|
|
- EGRESS IS COARSE. A NetworkPolicy matches CIDRs and selectors and NEVER
|
|
hostnames, so "only to the database host" cannot be expressed at all. This
|
|
cluster's CNI is Canal (Flannel plus Calico for policy) and has no FQDN
|
|
rule; Calico's GlobalNetworkSet could express one but would bind this chart
|
|
to Calico, the same argument that kept implementation-specific objects out
|
|
of the Gateway-API sweep (#114). Each egress rule therefore permits its
|
|
PORT to any destination.
|
|
- NOT A BOUNDARY WHERE THE CNI DOES NOT ENFORCE ONE. An unenforced policy is
|
|
accepted by the API server and filters nothing, so everything reads as
|
|
configured while nothing is filtered.
|
|
- THE REASONING IS IN TEMPLATE COMMENTS, not in "#" comments: those are part
|
|
of the rendered object, travel into the cluster and appear in every release
|
|
diff. ci/run-networkpolicy-test.sh fails on a rendered "#".
|
|
|
|
THE PORT NUMBERS BELOW ARE POD PORTS, NEVER NODEPORTS. A NetworkPolicy is
|
|
evaluated at the pod's network interface, after kube-proxy has already
|
|
translated a NodePort to the targetPort, so a NodePort number here would match
|
|
nothing while looking strict - and, being the only rule for that port, would
|
|
block every inbound connection instead. ci/run-networkpolicy-test.sh asserts
|
|
every port here against the containerPorts of the pod spec this policy
|
|
selects.
|
|
*/ -}}
|
|
{{- 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:
|
|
{{- /*
|
|
Selects the StatefulSet's pods. ci/run-networkpolicy-test.sh fails if
|
|
this matches no pod spec of the same render - which is the mistake that
|
|
would otherwise leave a policy in the namespace filtering nothing, and
|
|
its usual cause is a label renamed in the workload template.
|
|
*/}}
|
|
podSelector:
|
|
matchLabels:
|
|
app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs
|
|
policyTypes:
|
|
- Ingress
|
|
ingress:
|
|
{{- /*
|
|
THE GAME PORTS, all three published as NodePorts and all deliberately open: the
|
|
clients are players on the internet and, after NodePort masquerading, the source
|
|
address is a cluster node anyway.
|
|
|
|
The game port appears TWICE, once for each protocol, because the server binds
|
|
it as both - and a policy that named only TCP would be accepted, would look
|
|
complete, and would drop the UDP half of the game traffic. The TCP side is
|
|
additionally what the readiness probe opens a socket against.
|
|
|
|
The numbers go through satisfactory.formatNumber for the same reason they do in
|
|
the Service and the StatefulSet: a port read from values as a float64 renders
|
|
in exponential notation above a certain size, and a NetworkPolicy with "3.0777e
|
|
+04" in it is not valid.
|
|
*/}}
|
|
- ports:
|
|
- protocol: TCP
|
|
port: {{ include "satisfactory.formatNumber" .Values.server.gamePort }}
|
|
- protocol: UDP
|
|
port: {{ include "satisfactory.formatNumber" .Values.server.gamePort }}
|
|
- protocol: TCP
|
|
port: {{ include "satisfactory.formatNumber" .Values.server.messagingPort }}
|
|
{{- 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. The chart's networkPolicy
|
|
block has nothing but enabled. Measured in the render: two exec probes
|
|
and one tcpSocket.
|
|
WHICH RULE IT JOINS:
|
|
the only rule this policy has, the one carrying the game and messaging
|
|
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
|
|
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 }}
|