Files
helm-charts/teamspeak-server/values.yaml
T
jklein bc580ee291 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 17:56:57 +00:00

185 lines
8.0 KiB
YAML

# Scale the whole chart down without uninstalling it: scaleDown: true renders
# every Deployment/StatefulSet with replicas: 0 (and suspends CronJobs) and
# takes precedence over any per-workload replicas value. PVCs, Secrets and
# Services stay in place; scaleDown: false brings the workloads back up.
scaleDown: false
# Replica count for this workload; 0 is allowed and scales it down.
# The chart-global scaleDown flag takes precedence.
replicas: 1
server:
# UDP voice port and TCP file transfer port. Each value is used as the
# container port, the service port and the NodePort, and is passed to the
# server via TSSERVER_DEFAULT_PORT / TSSERVER_FILE_TRANSFER_PORT so the
# server always listens on the exposed port (file transfer requires the
# in-container port to match the externally reachable port).
voicePort: 30987
fileTransferPort: 30033
resources:
# Limits are optional: memory and ephemeral-storage limits default to
# limitFactor x the request, an explicitly set limit always wins, and a
# CPU limit is only set when configured explicitly under "limits".
limitFactor: 3
requests:
cpu: 0.2
memory: 100Mi
ephemeral-storage: 100Mi
# Extra environment variables for the server container, appended to the ones
# the chart always sets (TSSERVER_LICENSE_ACCEPTED, TSSERVER_DEFAULT_PORT,
# TSSERVER_FILE_TRANSFER_PORT). Values are rendered through tpl and support
# the value, secretKeyRef and configMapKeyRef forms:
# - name: SOME_ENV
# value: "{{ .Release.Name }}"
# - name: FROM_SECRET
# valueFrom:
# secretKeyRef:
# name: "some-secret"
# key: "someKey"
# - name: FROM_CONFIGMAP
# valueFrom:
# configMapKeyRef:
# name: "some-configmap"
# key: "someKey"
env: []
livenessProbe:
# The voice service is UDP and cannot be probed; probe the TCP
# file-transfer port instead.
tcpSocket:
port: file-transfer
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
tcpSocket:
port: file-transfer
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
# The startup probe gates liveness/readiness, so those need no
# initialDelaySeconds. Startup budget = periodSeconds * failureThreshold = 150s.
startupProbe:
tcpSocket:
port: file-transfer
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 30
securityContext:
pod:
runAsNonRoot: true
# The teamspeak6-server image runs as the non-root user "teamspeak"
# (uid/gid 9987); fsGroup 9987 keeps the data volume writable.
runAsUser: 9987
runAsGroup: 9987
fsGroup: 9987
fsGroupChangePolicy: Always
container:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
capabilities:
drop:
- ALL
add: []
storage:
size: 25Gi
storageClass: longhorn
# ---------------------------------------------------------------------------
# NetworkPolicy (Gitea #5). On by default. The reference implementation with
# the full reasoning is template-chart; the convention is in README, "Chart
# style guidelines", and the operator's view on this chart's wiki page.
# ---------------------------------------------------------------------------
networkPolicy:
# NOTE WHAT IS NOT HERE: probeSources. The port the probes address is the
# file-transfer port, and its rule is open for an independent reason - it is
# a NodePort facing the internet. So probeSources would reach no rule, and a
# value that silently does nothing reads as a control it is not.
# allowedNamespaces IS here, because the web-query rule uses it.
#
# WHAT THIS POLICY ALLOWS, and the three ports differ:
# - voice (server.voicePort, UDP): NodePort, OPEN - clients are on the
# internet. Note the protocol: a TCP rule on the same number would be
# accepted and would drop every voice packet.
# - fileTransfer (server.fileTransferPort, TCP): NodePort, open, and also
# the probe port.
# - web query (10080): RESTRICTED to this namespace plus
# allowedNamespaces. No Service, no probe, and administrative - this is
# the rule with real value here.
# Outbound: DNS and TCP 443.
#
# IF THE SERVER REFUSES TO START, WIDEN THE EGRESS RULE FIRST. 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 - but a check that merely degrades would not.
#
# A coincidence worth not copying: this chart uses the same number for the
# pod port and the NodePort, so the usual NodePort mistake is invisible here.
# That is a property of these values, not a rule.
enabled: true
# Namespaces whose pods may reach this workload, in addition to the release's
# own namespace, which is always allowed. Empty means: own namespace only.
#
# Matched on the "kubernetes.io/metadata.name" label Kubernetes sets on every
# namespace itself, so no labelling work is needed on a namespace this chart
# does not own - and no typo protection either: a name that does not exist
# matches nothing and is silently ineffective. Note the granularity: an entry
# opens the port to EVERY pod in that namespace.
allowedNamespaces: []
# The policy's INBOUND side. Mind the three names that overlap as substrings,
# because a search for "ingress" matches all of them and they are different
# things:
#
# ingress (top level) the Ingress OBJECT this chart
# renders - host, TLS, className
# networkPolicy.ingress this block: what the policy lets IN
# networkPolicy.ingressController the SOURCE SELECTOR for the ingress
# controller's pods. A SIBLING of this block
# and NO PART OF IT, despite the name.
ingress:
# Opens the pod for Istio's ambient mesh (Gitea #143). OFF by default and
# additive: while it is false this chart renders byte for byte as it did
# before the value existed.
#
# It renders 15008/TCP inbound - ztunnel's HBONE tunnel. Every inbound mesh
# connection arrives on it, and ambient "does not and will never bypass
# Kubernetes NetworkPolicy enforcement", so without this port a pod whose
# namespace has joined the mesh is unreachable.
#
# ONE HALF SHORTER THAN ELSEWHERE, deliberately. In other charts 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 has 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. An ipBlock for one would match nothing while making the
# rule look like it did something, which is the kind of strict-looking empty
# rule ci/run-networkpolicy-test.sh exists to catch; that script ties the
# exemption to the absence of probeSources rather than to a chart name.
#
# NOT PROVEN: that 15008 alone is enough comes from Istio's documentation;
# measured on this cluster is only which listeners ztunnel opens, and no
# namespace carries istio.io/dataplane-mode yet.
ambientMesh: false
egress:
# Additional outbound TCP ports. DNS (UDP and TCP 53) is always allowed and
# has no switch - a knob whose only other setting breaks every name lookup
# in the pod is a trap, not a feature.
#
# PORTS, NOT DESTINATIONS, and that is a limit of the object rather than
# laziness: a NetworkPolicy matches CIDRs and selectors and never hostnames,
# so "only to the database host" cannot be written at all. Each entry
# permits its port to ANY destination. What the cap still buys is real: a
# compromised pod cannot open arbitrary outbound connections.
#
# TCP 2008 and UDP 2010 are the entries to try if the server will not start
# or will not register - see the note above.
extraPorts: []