Skip to content

feat(chart): add opt-in NetworkPolicy for the Topograph release#393

Open
resker wants to merge 2 commits into
NVIDIA:mainfrom
resker:chore/chart-networkpolicy
Open

feat(chart): add opt-in NetworkPolicy for the Topograph release#393
resker wants to merge 2 commits into
NVIDIA:mainfrom
resker:chore/chart-networkpolicy

Conversation

@resker

@resker resker commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Description

Adds an opt-in NetworkPolicy (networkPolicy.enabled, default false) 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:

  • Ingress — denied except intra-release traffic (so the node-observer can reach the API server) and the Prometheus scrape namespace when serviceMonitor.enabled.
  • Egress — left unconstrained until extraEgress is set, so enabling the policy cannot 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 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 and docs/engines/k8s.md includes a per-provider egress table. extraIngress appends custom ingress rules.

Validation

  • make chart-test green. helm-unittest covers off-by-default, the whole-release podSelector, the Prometheus ingress rule, and the egress shape.
  • On a representative Kubernetes cluster (Calico CNI) with a namespaced default-deny NetworkPolicy: helm test succeeds 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.

Note: the red govulncheck check is the pre-existing repo-wide stdlib advisory (GO-2026-5856), not from this chart-only PR; it clears on a rebase once #390 lands. All other checks are green.

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.
  • All commits are signed off per DCO (git commit -s).

@github-actions

Copy link
Copy Markdown
Contributor

@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds an opt-in NetworkPolicy to the Topograph Helm chart. The main changes are:

  • A new chart template for release-wide ingress and optional egress policy.
  • New values and schema entries for custom ingress, egress, and DNS selector configuration.
  • Helm unit tests for default rendering, Prometheus ingress, egress, and custom DNS selectors.
  • Kubernetes engine docs and changelog updates for NetworkPolicy usage.

Confidence Score: 4/5

This is close, but the NetworkPolicy validation gaps should be fixed before merging.

  • Empty-effective custom rules can still render into broad allow rules.
  • An explicit empty DNS selector can broaden the DNS egress allow beyond DNS pods.
  • The main render paths are covered, but these boundary cases still need enforcement.

charts/topograph/values.schema.json and charts/topograph/templates/networkpolicy.yaml

Security Review

The NetworkPolicy still has two security-boundary gaps: empty-effective custom rules can open traffic broadly, and an explicit empty DNS selector can allow DNS-port egress to all kube-system pods.

Important Files Changed

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

Comment thread charts/topograph/templates/networkpolicy.yaml Outdated
Comment thread charts/topograph/templates/networkpolicy.yaml
Comment thread charts/topograph/templates/networkpolicy.yaml
@resker
resker force-pushed the chore/chart-networkpolicy branch from f72f590 to ee41396 Compare July 11, 2026 00:12
Comment thread charts/topograph/templates/networkpolicy.yaml
Comment thread charts/topograph/values.schema.json Outdated
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>
@resker
resker force-pushed the chore/chart-networkpolicy branch from ee41396 to 8d2b56e Compare July 13, 2026 19:23
Comment on lines +194 to +198
"items": { "type": "object", "minProperties": 1 }
},
"extraEgress": {
"type": "array",
"items": { "type": "object", "minProperties": 1 }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Empty rules still pass

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.

Comment thread charts/topograph/templates/networkpolicy.yaml Outdated
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>
Comment on lines +198 to +202
"items": { "type": "object", "minProperties": 1 }
},
"extraEgress": {
"type": "array",
"items": { "type": "object", "minProperties": 1 }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security 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 }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security 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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 }}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 }}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chart: optional, provider-aware NetworkPolicy template (opt-in)

2 participants