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.
200 lines
9.2 KiB
YAML
200 lines
9.2 KiB
YAML
{{- /*
|
|
NetworkPolicy for teamspeak-server (Gitea #5).
|
|
|
|
Three ports, two of them NodePorts facing the internet and one that is purely
|
|
internal - so this chart shows the public/internal split without the probe
|
|
problem getting in the way of either.
|
|
|
|
voice (UDP) NodePort, open. The clients are on the internet.
|
|
fileTransfer (TCP) NodePort, open, and also the port all three probes open
|
|
a socket against - justified twice.
|
|
web query (10080) NO Service at all, no probe, so it is RESTRICTED to this
|
|
namespace plus allowedNamespaces. That is the rule worth
|
|
having here: the query interface is administrative.
|
|
|
|
NOTE THE PORT NUMBERS. This chart happens to use the SAME number for the pod
|
|
port and the NodePort (server.voicePort, server.fileTransferPort are both), so
|
|
the usual NodePort mistake is invisible here - the number is right either way.
|
|
That is a coincidence of this chart's values and not a rule; do not copy the
|
|
pattern to a chart where the two differ.
|
|
|
|
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 }}
|
|
{{- $np := .Values.networkPolicy }}
|
|
{{- $ns := .Release.Namespace }}
|
|
{{- $allowedNs := $np.allowedNamespaces | default list }}
|
|
{{- /* 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 := $np.ingress | default dict }}
|
|
{{- $ambientMesh := $npIngress.ambientMesh }}
|
|
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
|
|
{{- /*
|
|
Egress is listed because a voice server's outbound needs are small: DNS and
|
|
HTTPS. The uncertainty is named in the rule itself rather than hidden - see the
|
|
note on TeamSpeak's accounting ports.
|
|
*/}}
|
|
- Egress
|
|
ingress:
|
|
{{- /*
|
|
THE VOICE PORT, UDP, published as a NodePort and deliberately open: the clients
|
|
are on the internet, and after masquerading the source address is a cluster
|
|
node anyway.
|
|
|
|
PROTOCOL MATTERS AS MUCH AS THE NUMBER HERE. Voice is UDP, and a rule written
|
|
for TCP on the same number would be accepted, would look complete, and would
|
|
drop every voice packet - which is why ci/run-networkpolicy-test.sh checks the
|
|
protocol against the containerPort's and not just the number.
|
|
*/}}
|
|
- ports:
|
|
- protocol: UDP
|
|
port: {{ .Values.server.voicePort | int64 }}
|
|
{{- /*
|
|
THE FILE-TRANSFER PORT, TCP, also a NodePort and also open. It is additionally
|
|
the port all three probes open a TCP socket against, so it is the probe path as
|
|
well - open for two independent reasons.
|
|
*/}}
|
|
- ports:
|
|
- protocol: TCP
|
|
port: {{ .Values.server.fileTransferPort | int64 }}
|
|
{{- if $ambientMesh }}
|
|
{{- /* 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
|
|
tcpSocket probes, so these ARE network probes - they are simply never
|
|
restricted here.
|
|
WHICH RULE IT JOINS:
|
|
the file-transfer rule, the policy's open TCP rule. The voice rule is
|
|
UDP and the rule on 10080 is restricted to the namespace. A policy is an
|
|
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
|
|
is unknown where ztunnel dials from.
|
|
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: 15008
|
|
{{- end }}
|
|
{{- /*
|
|
THE WEB QUERY INTERFACE, and the one rule in this chart with real security
|
|
value. It has no Service and no probe, so it can be restricted, and it is: this
|
|
namespace plus allowedNamespaces. The query interface is administrative, and
|
|
before this object the only thing keeping it off the network was that nothing
|
|
published it.
|
|
*/}}
|
|
- ports:
|
|
- protocol: TCP
|
|
port: 10080
|
|
from:
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
{{- /*
|
|
Set by Kubernetes itself on every namespace (>= 1.21), so this needs no
|
|
labelling work on a namespace this chart does not own.
|
|
*/}}
|
|
kubernetes.io/metadata.name: {{ $ns }}
|
|
{{- range $allowedNs }}
|
|
- namespaceSelector:
|
|
matchLabels:
|
|
kubernetes.io/metadata.name: {{ . | quote }}
|
|
{{- end }}
|
|
egress:
|
|
{{- /*
|
|
DNS, unconditionally. There is no value to switch this off, and that is
|
|
deliberate: a knob whose only other setting breaks every name lookup in
|
|
the pod is a trap and not a feature.
|
|
*/}}
|
|
- ports:
|
|
- protocol: UDP
|
|
port: 53
|
|
- protocol: TCP
|
|
port: 53
|
|
{{- /*
|
|
Outbound HTTPS, for the server's own update and accounting calls. To any
|
|
destination - hostnames cannot appear here.
|
|
|
|
IF THE SERVER REFUSES TO START AFTER THIS POLICY ARRIVES, THIS IS THE RULE TO
|
|
WIDEN: TeamSpeak's accounting service has historically used ports of its own
|
|
(TCP 2008, UDP 2010 on the 3.x line), and this chart cannot know what the 6.x
|
|
beta does. The install-test covers this chart, so a startup that depends on a
|
|
blocked port shows up there rather than in production - but a license or
|
|
accounting check that merely degrades would not.
|
|
*/}}
|
|
- ports:
|
|
- protocol: TCP
|
|
port: 443
|
|
{{- with $np.egress }}
|
|
{{- with .extraPorts }}
|
|
{{- /*
|
|
Ports only, to any destination - see the header. This is the operator's
|
|
escape hatch from the egress cap, and the only alternative to switching
|
|
the whole policy off.
|
|
*/}}
|
|
- ports:
|
|
{{- range . }}
|
|
- protocol: TCP
|
|
port: {{ int . }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|