feat(template-chart): the reference NetworkPolicy, on by default
The reference implementation for Gitea #5: one NetworkPolicy per workload, networkPolicy.enabled defaulting to true, plus the values block, the schema entry and the NOTES.txt section that go with it. The shape is taken from the two charts that already had one - valkey and stalwart - rather than invented, because they carry the expensive lessons: policyTypes only where there is something to restrict, namespaces always spelled out because an empty "from" allows everything, kubernetes.io/metadata.name so no foreign namespace needs labelling, POD ports and never NodePorts, egress by port because the object cannot express a hostname, and the probe path left open by default because the kubelet probes from the node's own IP. Three things are written down rather than assumed. The ingress source is measured on the cluster and is NOT what the issue body assumes: ingress-nginx is gone, the only IngressClass is traefik, and the controller is the DaemonSet rke2-traefik in kube-system - which also serves the Gateway API here, so one source covers both the Ingress and the HTTPRoute path. Egress stays coarse: with Canal (Flannel plus Calico) there is no FQDN rule, and Calico's GlobalNetworkSet would bind every chart to Calico, the same argument that kept implementation-specific objects out of #114. And a policy is only a boundary where the CNI enforces one - unenforced, it is accepted and filters nothing. All reasoning sits in Go template comments, not in "#" comments: those are part of the rendered object and travel into the cluster. Writing this header also found the second-order trap, which is now in the header itself - prose inside a template comment must not spell out the comment's own closing delimiter, or the parser ends the comment early and the remaining prose is emitted as manifest content. Part of #5
This commit is contained in:
4 files changed
+388
No files matched your search
@@ -174,3 +174,120 @@ httpRoute:
|
||||
# is how a migration from the Ingress runs: the HTTPRoute takes a second
|
||||
# hostname while the Ingress keeps ingress.domain.
|
||||
hostnames: []
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# NetworkPolicy (Gitea #5) — the reference block. Copy it with the template.
|
||||
# ---------------------------------------------------------------------------
|
||||
networkPolicy:
|
||||
# ON BY DEFAULT, which is the decision this whole rollout rests on: a
|
||||
# NetworkPolicy that ships switched off is reached only when somebody
|
||||
# remembers to switch it on, and then the issue is closed while nothing is
|
||||
# filtered.
|
||||
#
|
||||
# READ THIS BEFORE RELYING ON IT: a NetworkPolicy is only a boundary if the
|
||||
# cluster's CNI enforces policies. Where it does not, the API server still
|
||||
# accepts the object and everything looks configured while nothing is
|
||||
# filtered - an unenforced policy is a false sense of security, not a weak
|
||||
# one. Verify on the cluster (try to connect from an unrelated namespace)
|
||||
# rather than reading it off this value.
|
||||
#
|
||||
# WHAT IS ACTUALLY CAPPED, and what is not, is written out in the template's
|
||||
# header - read it once before assuming this value means "the workload is
|
||||
# isolated". In short: only the declared ports are reachable, egress is
|
||||
# capped by PORT (never by host - the object cannot express a hostname), and
|
||||
# the probed port stays open inside the cluster until probeSources is set.
|
||||
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 by the "kubernetes.io/metadata.name" label, which Kubernetes sets on
|
||||
# every namespace itself (>= 1.21), so no labelling work is needed on a
|
||||
# namespace this chart does not own - but also no typo protection: a
|
||||
# namespace name that does not exist matches nothing and is silently
|
||||
# ineffective.
|
||||
#
|
||||
# NOTE THE GRANULARITY: an entry here opens the port to EVERY pod in that
|
||||
# namespace. For the ingress controller use ingressController below instead,
|
||||
# which pins the source down to the controller's pods.
|
||||
allowedNamespaces: []
|
||||
|
||||
# Source CIDRs allowed to reach the port the probes address. This is the
|
||||
# single most dangerous value in this block, in both directions, so it is
|
||||
# spelled out in full.
|
||||
#
|
||||
# EMPTY MEANS OPEN, NOT BLOCKED. With no entry the rule for that port is
|
||||
# rendered WITHOUT a "from" selector, so the port is reachable from anywhere
|
||||
# the CNI routes to - which, since this port carries no NodePort, means from
|
||||
# inside the cluster and not from the internet.
|
||||
#
|
||||
# That is the deliberate choice between two bad defaults. The kubelet's
|
||||
# liveness/readiness/startup probes originate from the IP of the WORKER NODE
|
||||
# running the pod, an address this chart cannot know and must not guess: a
|
||||
# guessed CIDR that excludes the real node addresses blocks every probe, the
|
||||
# pod never becomes Ready, the rollout times out, and the symptom reads as a
|
||||
# broken application rather than as a policy. Shipping "open" instead is
|
||||
# honest about what it does.
|
||||
#
|
||||
# Narrow it by listing the worker node IPs (e.g. ["10.0.1.11/32",
|
||||
# "10.0.1.12/32"]) — and mind the companion trap: once this list is
|
||||
# non-empty the rule IS restricted, which also cuts off everything else that
|
||||
# reaches this port. The chart adds the release's own namespace, the
|
||||
# ingressController below and allowedNamespaces to the same rule for exactly
|
||||
# that reason.
|
||||
#
|
||||
# A probe that is an "exec" probe is NOT affected by any of this: it runs
|
||||
# inside the container through the kubelet's runtime and puts no packet on
|
||||
# the network, which is why a chart with exec-only probes (valkey) can
|
||||
# restrict its port by source with no probeSources at all.
|
||||
probeSources: []
|
||||
|
||||
# The ingress controller allowed to reach the routed port once probeSources
|
||||
# restricts it. Rendered ONLY while ingress.enabled or httpRoute.enabled is
|
||||
# true, and only inside the restricted branch - with the default (open) rule
|
||||
# the controller reaches the port anyway and naming it would be noise.
|
||||
#
|
||||
# The two keys are rendered as ONE "from" entry, which makes them an AND:
|
||||
# pods carrying podLabels IN namespace. That is the point of having them
|
||||
# here at all - putting the controller's namespace into allowedNamespaces
|
||||
# instead would open the port to every pod in kube-system.
|
||||
#
|
||||
# THE DEFAULT IS MEASURED ON THIS CLUSTER, NOT ASSUMED (2026-10-14):
|
||||
# ingress-nginx is gone (no DaemonSet, no pods), the only IngressClass is
|
||||
# "traefik" and it is the default, and the controller is the DaemonSet
|
||||
# rke2-traefik in kube-system with the pod label
|
||||
# app.kubernetes.io/name=rke2-traefik. The same Traefik also serves the
|
||||
# Gateway API here (GatewayClass "traefik", controller
|
||||
# traefik.io/gateway-controller, no pods of its own in the Gateway's
|
||||
# namespace), so this one source covers BOTH the Ingress and the HTTPRoute
|
||||
# path.
|
||||
#
|
||||
# A policy written against the WRONG controller is the expensive mistake in
|
||||
# this block: it is accepted, it looks strict, and it silently blocks all
|
||||
# web traffic. Check the cluster before changing these two keys, and note
|
||||
# that ingress.className says nothing about them - the class names the
|
||||
# controller's implementation, not the pods its traffic comes from.
|
||||
ingressController:
|
||||
namespace: kube-system
|
||||
podLabels:
|
||||
app.kubernetes.io/name: rke2-traefik
|
||||
|
||||
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 ipBlock CIDRs and pod/namespace
|
||||
# selectors, never hostnames, so "only to the database host" cannot be
|
||||
# written at all. Each entry here permits its port to ANY destination.
|
||||
# What the cap still buys is real: a compromised pod cannot open arbitrary
|
||||
# outbound connections.
|
||||
#
|
||||
# Add what the deployment actually needs: 443 for an outbound HTTPS call
|
||||
# (an OIDC provider's discovery document, an S3 endpoint, an app that
|
||||
# fetches), 5432 for Postgres, 3306 for MariaDB, 6379 for Valkey/Redis,
|
||||
# 587 or 465 for SMTP submission. A chart that KNOWS its outbound port
|
||||
# from its own values should derive it in the template instead of asking
|
||||
# for it twice (stalwart does this with dataStore.postgres.port).
|
||||
extraPorts: []
|
||||
Reference in new issue
Block a user