feat(chart): add opt-in NetworkPolicy for the Topograph release#393
feat(chart): add opt-in NetworkPolicy for the Topograph release#393resker wants to merge 2 commits into
Conversation
|
🌿 Preview your docs: https://nvidia-preview-pull-request-393.docs.buildwithfern.com/topograph |
Greptile SummaryThis PR adds an opt-in NetworkPolicy to the Topograph Helm chart. The main changes are:
Confidence Score: 4/5This is close, but the NetworkPolicy validation gaps should be fixed before merging.
charts/topograph/values.schema.json and charts/topograph/templates/networkpolicy.yaml
|
| Filename | Overview |
|---|---|
| charts/topograph/templates/networkpolicy.yaml | Adds the NetworkPolicy template, including release-wide selection, ingress allows, optional egress, and DNS selector rendering. |
| charts/topograph/values.schema.json | Adds schema support for the new NetworkPolicy values and custom rule arrays. |
| charts/topograph/values.yaml | Documents defaults and examples for enabling the policy, setting custom DNS selectors, and supplying egress rules. |
| charts/topograph/tests/networkpolicy_test.yaml | Adds Helm tests for the main NetworkPolicy render paths. |
| docs/engines/k8s.md | Documents the chart policy, default-deny behavior, provider egress targets, and DNS selector override. |
Reviews (4): Last reviewed commit: "feat(chart): make NetworkPolicy DNS pod ..." | Re-trigger Greptile
f72f590 to
ee41396
Compare
Ship a bundled NetworkPolicy, off by default (networkPolicy.enabled), covering all three components (API server, node-observer, node-data-broker) selected by the release instance label. When enabled, ingress is denied except intra-release traffic (so the node-observer can reach the API server to trigger regeneration) and, when serviceMonitor.enabled, the Prometheus scrape namespace. Egress stays unconstrained until networkPolicy.extraEgress is set, so enabling the policy does not break egress on clusters without a default-deny. When extraEgress is set, an Egress policyType is added with DNS and intra-release allows plus the supplied rules. Because the kube-apiserver address and the provider's topology-source endpoint are cluster-specific, they are operator-supplied via extraEgress and are required under default-deny egress. networkPolicy.extraIngress appends custom ingress rules. Adds a values.schema.json entry, a helm-unittest suite, and documents the policy (including the NMC/Calico namespace-list alternative) in the Kubernetes engine docs. Signed-off-by: Rob Esker <resker@nvidia.com>
ee41396 to
8d2b56e
Compare
| "items": { "type": "object", "minProperties": 1 } | ||
| }, | ||
| "extraEgress": { | ||
| "type": "array", | ||
| "items": { "type": "object", "minProperties": 1 } |
There was a problem hiding this comment.
minProperties: 1 rejects only the literal {} case. A rule like networkPolicy.extraIngress: [{ ports: [] }] or networkPolicy.extraEgress: [{ ports: [] }] still has one property, passes schema validation, and is rendered into the policy. With no effective peers or ports, Kubernetes can treat the rule as open traffic in that direction, so the intended deny policy can still be bypassed with schema-valid values. Please validate that custom rules contain the required non-empty from/to and/or ports fields for the direction being configured.
Scope the DNS egress allow rule via networkPolicy.dnsPodSelector instead
of hardcoding k8s-app: kube-dns, so clusters whose CoreDNS carries a
different label (e.g. app.kubernetes.io/name: coredns) can allow DNS
without falling back to a broad extraEgress rule.
Default is empty in values.yaml so a Helm value override replaces the
selector cleanly; the template falls back to { k8s-app: kube-dns } when
unset. A non-empty default would deep-merge with an override and AND both
labels, matching no DNS pod.
Signed-off-by: Rob Esker <resker@nvidia.com>
| "items": { "type": "object", "minProperties": 1 } | ||
| }, | ||
| "extraEgress": { | ||
| "type": "array", | ||
| "items": { "type": "object", "minProperties": 1 } |
There was a problem hiding this comment.
Empty-effective rules pass
minProperties: 1 only rejects the literal {} case. Values such as extraIngress: [{ from: [] }], extraIngress: [{ ports: [] }], extraEgress: [{ to: [] }], or extraEgress: [{ ports: [] }] still pass schema validation and render verbatim. Kubernetes treats empty or omitted from/to as all sources or destinations, and empty or omitted ports as all ports, so these values can still open the selected pods to broad ingress or egress. Please validate the effective rule shape for each direction, not only that the rule object has one key.
| kubernetes.io/metadata.name: kube-system | ||
| podSelector: | ||
| matchLabels: | ||
| {{- toYaml (.Values.networkPolicy.dnsPodSelector | default (dict "k8s-app" "kube-dns")) | nindent 14 }} |
There was a problem hiding this comment.
Empty selector broadens DNS An explicit
networkPolicy.dnsPodSelector: {} override bypasses this fallback and renders an empty matchLabels under the DNS podSelector. Kubernetes treats an empty pod selector as matching every pod in the selected namespace, so the DNS egress rule allows TCP and UDP 53 to any kube-system pod instead of only DNS pods. Please reject an empty DNS selector or treat it the same as unset before rendering.
| # cidr: 10.96.0.1/32 | ||
| # ports: | ||
| # - protocol: TCP | ||
| # port: 6443 |
There was a problem hiding this comment.
This example combines the Kubernetes Service IP with the usual control-plane endpoint port. In-cluster client-go uses KUBERNETES_SERVICE_HOST/KUBERNETES_SERVICE_PORT, normally the Service IP on 443; kubectl get endpoints returns an endpoint IP normally on 6443. Depending on where the CNI evaluates policy relative to DNAT, operators may need either Service-IP:443 or endpoint-IP:6443, but Service-IP:6443 matches neither path and can leave all three components unable to reach the API after Egress isolation is enabled. Please correct this example and explain the CNI-dependent alternative consistently with the docs example/table.
| ports: | ||
| - protocol: TCP | ||
| port: {{ .Values.service.port }} | ||
| {{- end }} |
There was a problem hiding this comment.
When ingress.enabled or gatewayAPI.enabled is used, requests normally originate from an ingress/gateway controller in another namespace. This policy permits only same-release pods and, conditionally, the ServiceMonitor namespace, so enabling networkPolicy makes those chart-provided routes unreachable unless the operator happens to add the correct extraIngress rule. Since the controller selector is cluster-specific, please at least document this required extraIngress configuration beside the Ingress/HTTPRoute guidance and add combination tests; otherwise two supported chart features silently conflict.
| - from: | ||
| - namespaceSelector: | ||
| matchLabels: | ||
| kubernetes.io/metadata.name: {{ .Values.serviceMonitor.namespace }} |
There was a problem hiding this comment.
serviceMonitor.namespace controls where the ServiceMonitor object is created, not necessarily the namespace of the Prometheus pod that performs the scrape. A central Prometheus can watch ServiceMonitors in other namespaces, so this selector can still block metrics even though serviceMonitor.enabled is correctly configured. Please use a separately configurable scraper namespace/selector (or require extraIngress and document it) rather than treating the ServiceMonitor resource namespace as the traffic source.
Description
Adds an opt-in
NetworkPolicy(networkPolicy.enabled, defaultfalse) covering all three chart components, so the chart can provision the network access its own workloads need on clusters that enforce a default-deny posture. Closes #383.The policy selects the whole release by instance label. When enabled:
serviceMonitor.enabled.extraEgressis set, so enabling the policy cannot break egress on clusters without a default-deny. WhenextraEgressis set, anEgresspolicyType is added with DNS and intra-release allows plus the supplied rules.Because Kubernetes NetworkPolicy egress cannot target hostnames, the kube-apiserver and the provider's topology-source endpoint are operator-supplied via
extraEgress(required under default-deny). Rather than auto-derive these per provider (their endpoints are hostnames/URLs, not addressable in a portable policy), the chart stays generic anddocs/engines/k8s.mdincludes a per-provider egress table.extraIngressappends custom ingress rules.Validation
make chart-testgreen. helm-unittest covers off-by-default, the whole-release podSelector, the Prometheus ingress rule, and the egress shape.helm testsucceeds under deny-all (release pods reach the API server), while a non-release pod is correctly blocked — confirming the intra-release allows are both sufficient and enforcing.Checklist
git commit -s).