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:
jklein committed 2026-10-14 13:55:50 +00:00
1 parent c9edbb1750
commit 1001de620a
4 files changed
+388

No files matched your search

+51
View File
@@ -9,6 +9,57 @@
"description": "Helm's conventional cross-chart values. Unused by this chart, kept open so an umbrella install cannot break on it.",
"type": "object"
},
"networkPolicy": {
"description": "The chart's NetworkPolicy (Gitea #5), on by default. A ceiling on reachability and not an authentication layer: only the declared container ports are reachable at all, egress is capped by PORT (a NetworkPolicy matches CIDRs and selectors, never hostnames, so a destination host cannot be expressed), and ingress is capped by source for every port no network probe touches. Only a boundary where the cluster's CNI enforces policies - an unenforced policy is accepted by the API server and filters nothing. The template's header carries the full list of what it does not do.",
"type": "object",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"allowedNamespaces": {
"description": "Namespaces whose pods may reach this workload, in addition to the release's own namespace, which is always allowed. Matched on the kubernetes.io/metadata.name label Kubernetes sets on every namespace itself, so no labelling work is needed - and no typo protection either: a name that does not exist matches nothing and is silently ineffective. An entry opens the port to EVERY pod in that namespace; for the ingress controller use ingressController instead.",
"type": ["array", "null"],
"items": {
"type": "string"
}
},
"probeSources": {
"description": "Source CIDRs allowed to reach the port the network probes address. EMPTY MEANS OPEN, not blocked: with no entry that rule is rendered without a \"from\" selector. The kubelet's probes come from the IP of the worker node running the pod, an address the chart cannot know and must not guess - a guessed CIDR that excludes the real node blocks every probe and the pod never becomes Ready, which reads as a broken application rather than as a policy. Setting this restricts the rule, which is why the chart adds the own namespace, ingressController and allowedNamespaces to the same rule.",
"type": ["array", "null"],
"items": {
"type": "string"
}
},
"ingressController": {
"description": "The ingress controller allowed to reach the routed port once probeSources restricts it; rendered only while ingress.enabled or httpRoute.enabled is true. The two keys become ONE \"from\" entry and are therefore an AND - pods carrying podLabels IN namespace - which is the whole point: the controller's namespace in allowedNamespaces would open the port to every pod in kube-system. The default is measured on this cluster (DaemonSet rke2-traefik in kube-system, which also serves the Gateway API here), not assumed; a policy written against the wrong controller is accepted, looks strict and silently blocks all web traffic.",
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"namespace": {
"type": ["string", "null"]
},
"podLabels": {
"description": "Label set the controller's pods carry, rendered verbatim as a podSelector's matchLabels. Kept open because the chart does not interpret the keys.",
"type": ["object", "null"]
}
}
},
"egress": {
"type": ["object", "null"],
"additionalProperties": false,
"properties": {
"extraPorts": {
"description": "Additional outbound TCP ports, to ANY destination - ports and not destinations, because a NetworkPolicy cannot express a hostname. DNS is always allowed and has no switch. Without an entry here an outbound port the chart does not already derive is blocked, which is the cap's whole purpose and also its upgrade risk.",
"type": ["array", "null"],
"items": {
"type": "integer"
}
}
}
}
}
},
"scaleDown": {
"type": "boolean"
},