--- # Default-deny baseline (R-NET-01): deny all ingress + egress, allow only DNS. `kured` is a PLATFORM # namespace, so Kyverno's generator — scoped to `app-namespace=true` — does not produce this; we ship # it explicitly, as crafty/headlamp/homepage/runbooks all do. # # THESE POLICIES DO APPLY TO kured, UNLIKE platform/vpn-ingress'. That component's identical-looking # baseline is honest-but-inert because its DaemonSet runs `hostNetwork: true` and therefore has no # Cilium endpoint for any policy to select. kured's chart defaults to `hostNetwork: false` and we # keep that default (it needs the host PID namespace to reboot, not the host network stack), so it # has a normal pod identity and every rule here is enforced. # # Cilium enforces the UNION of these policies (allow if ANY policy allows), so the CiliumNetworkPolicy # below adds the kube-apiserver path on top of this deny-all floor. apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny namespace: kured labels: homelab.keithjackson.dev/baseline: default-deny spec: podSelector: {} policyTypes: - Ingress - Egress ingress: [] egress: - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: kube-system ports: - protocol: UDP port: 53 - protocol: TCP port: 53 --- # ═══════════════════════════════════════════════════════════════════════════════════════════════ # PROMETHEUS MUST REACH :8080, OR EVERY RULE IN alerts.yaml IS INERT. # ═══════════════════════════════════════════════════════════════════════════════════════════════ # This is not a theoretical hazard in this repo, it is a measured one. From the Longhorn entry in # conformance/tests/l0-static/alert-metrics-have-scrape-targets/check.sh: with the ServiceMonitor # applied and no NetworkPolicy allowance, all three targets were DISCOVERED and every scrape failed # with "context deadline exceeded" after 10.0s — "a drop, not a refusal" — and the TSDB stayed # empty. Target discovery succeeding is therefore NOT evidence the scrape works, and the alerts # would have been quiet rather than broken. KuredNotReporting is the standing detector for exactly # this regression, and it can only work if this policy is correct. # # THE WHOLE `monitoring` NAMESPACE, NOT THE PROMETHEUS POD LABELS — following the same choice # longhorn/networkpolicy-metrics.yaml made and for the same reason: the prometheus-operator owns # those pod labels and rewrites them on upgrade (measured 2026-10-17, the pod carries # `app.kubernetes.io/name: prometheus` plus five operator-managed labels including a # controller-revision-hash), so selecting on them makes the scrape silently fail on a chart bump. # The namespace holds only the monitoring stack. apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: kured-allow-prometheus-scrape namespace: kured labels: app.kubernetes.io/name: kured spec: podSelector: matchLabels: # Set by the chart's `kured.matchLabels` helper (_helpers.tpl:70-71) with release name `kured`. app.kubernetes.io/name: kured policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: monitoring ports: - protocol: TCP # The container port the chart names `metrics` (daemonset.yaml:211-213), from # `configuration.metricsPort`, which we leave at the chart default of 8080 and mirror in # the HelmRelease's `service.port`. port: 8080 --- # Egress allows. kured talks to exactly two things and nothing else; both are required and the # component is useless without either. # # 1. THE KUBE-APISERVER — the lock annotation on its own DaemonSet (`weave.works/kured-node-lock`), # cordon/uncordon (`nodes` patch), the node annotations, listing pods, and creating # `pods/eviction` objects during the drain. Reached via the `kube-apiserver` Cilium entity # rather than an IP: on this cluster the API is fronted by a kube-vip VIP that moves between # nodes on every reboot, so any CIDR-based rule here would break during the exact operation # this component exists to perform. Same construct as platform/headlamp/networkpolicy.yaml. # # 2. PROMETHEUS, on 9090 — the alert gate. The HelmRelease sets # `prometheusUrl: http://kube-prometheus-stack-prometheus.monitoring.svc.cluster.local:9090` # and `alertFilterMatchOnly: true`, so before taking the cluster lock kured asks Prometheus # whether any of LonghornVolumeDegraded / LonghornVolumeFaulted / KubeNodeNotReady / # NodeExporterDown is firing. IF THIS EGRESS IS BLOCKED, kured cannot evaluate the gate. # It treats the query error as "do not proceed" and skips the reboot — which is the correct # fail-safe direction, but it fails SILENTLY and indefinitely: reboots simply stop happening # and nothing goes red except, eventually, KuredRebootPending. Do not remove this rule # thinking it is only for metrics; metrics ingress is the policy above, this is the gate. # # NOTHING ELSE. No egress to the internet (kured pulls no content at runtime), no DNS beyond the # baseline above, no Longhorn or etcd access — kured reads Longhorn's health THROUGH Prometheus # alerts, never from Longhorn's own API, which is why that allow is absent here. apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: name: kured-allow-egress namespace: kured labels: app.kubernetes.io/name: kured spec: endpointSelector: matchLabels: app.kubernetes.io/name: kured egress: - toEntities: - kube-apiserver - toEndpoints: - matchLabels: io.kubernetes.pod.namespace: monitoring app.kubernetes.io/name: prometheus toPorts: - ports: - port: "9090" protocol: TCP