diff --git a/kubernetes/infrastructure/network-policies/home-assistant-ns.yaml b/kubernetes/infrastructure/network-policies/home-assistant-ns.yaml index b7033982..84459fa8 100644 --- a/kubernetes/infrastructure/network-policies/home-assistant-ns.yaml +++ b/kubernetes/infrastructure/network-policies/home-assistant-ns.yaml @@ -41,18 +41,14 @@ spec: - ports: - port: "8080" protocol: TCP - # shelly-manager (:8080) is likewise reached only through its own - # Authentik gate outpost. Note this rule and the vscode one are - # port-identical: because this CNP's endpointSelector is {}, a - # `toPorts: 8080` grant here applies to EVERY pod in the namespace that - # listens on 8080, not just the intended one — which is exactly how - # shelly-manager silently rode on the old traefik→:8080 allow written - # for code-server, and why removing that allow in 760e187 took - # shelly-manager's ingress down with it. The `fromEndpoints` selector is - # what keeps these two apart: each gate outpost can only reach its own - # target, enforced on the egress side by the matching CNPs in the - # authentik namespace (vscode-gate-egress.yaml, - # shelly-manager-gate-egress.yaml). + # shelly-manager, behind its own Authentik gate outpost. + # + # CAUTION for every :8080 rule here: this policy's endpointSelector is {}, + # so a toPorts grant on 8080 applies to EVERY pod in the namespace + # listening on that port, not just the intended one. Three apps share + # :8080. What keeps them apart is the fromEndpoints selector plus the + # matching per-gate egress CNPs in the authentik namespace. Changing a + # :8080 rule here affects all of them. - fromEndpoints: - matchLabels: "k8s:io.kubernetes.pod.namespace": authentik @@ -61,11 +57,8 @@ spec: - ports: - port: "8080" protocol: TCP - # esphome (ESPHome Device Builder, :6052) is likewise reached only through - # its own Authentik gate outpost. Unlike the :8080 vscode/shelly rules - # above, :6052 is unique to esphome, so this namespace-wide (endpointSelector - # {}) toPorts grant doesn't silently apply to any other app — the - # fromEndpoints selector still scopes it to the esphome-gate outpost. + # esphome (:6052), behind its own gate outpost. Port is unique to it, so + # the namespace-wide grant does not reach anything else. - fromEndpoints: - matchLabels: "k8s:io.kubernetes.pod.namespace": authentik @@ -74,13 +67,8 @@ spec: - ports: - port: "6052" protocol: TCP - # zigbee2mqtt (Z2M frontend, :8080) is likewise reached only through its own - # Authentik gate outpost. Like the vscode/shelly-manager rules above this is - # the SHARED :8080 (zigbee2mqtt is the third app on that port), so this - # namespace-wide (endpointSelector {}) grant applies to every :8080 pod — the - # fromEndpoints selector scopes it to the zigbee2mqtt-gate outpost, and the - # matching authentik-side zigbee2mqtt-gate-egress.yaml scopes egress to the - # zigbee2mqtt pod specifically. + # zigbee2mqtt frontend, behind its own gate outpost. Shared :8080 — see the + # caution above. - fromEndpoints: - matchLabels: "k8s:io.kubernetes.pod.namespace": authentik @@ -89,11 +77,8 @@ spec: - ports: - port: "8080" protocol: TCP - # zone-configurator (Everything Presence mmWave zone configurator, :42069) - # is likewise reached only through its own Authentik gate outpost. Like - # esphome's :6052, :42069 is unique to it, so this namespace-wide (endpoint - # Selector {}) toPorts grant doesn't silently apply to any other app; the - # fromEndpoints selector scopes it to the zone-configurator-gate outpost. + # zone-configurator (:42069), behind its own gate outpost. Port is unique + # to it. - fromEndpoints: - matchLabels: "k8s:io.kubernetes.pod.namespace": authentik @@ -131,25 +116,16 @@ spec: - fromEntities: - host - remote-node - # World ingress (IoT-VLAN devices via the BGP LoadBalancers) is NOT - # granted namespace-wide here. A blanket `fromEntities: world` on this - # endpointSelector:{} policy exposed EVERY pod in the namespace to the - # untrusted IoT VLAN / any BGP-routed source — vscode (:8080), - # zone-configurator (:42069), otbr, whisper, etc. — not just the two - # LoadBalancers that need it. World is now scoped per-target in the - # dedicated policies at the bottom of this file: world -> mosquitto - # :1883/:8883 and world -> home-assistant :8123 only. (matter-server on - # 10.2.x.226 rides a multus net1 interface that bypasses Cilium policy - # entirely, so it is unaffected either way.) - # ICMP — all standard ICMPv4 and ICMPv6 types from any cluster pod. - # Per Cilium docs, "If any layer 4 policy is specified, then ICMP - # will be blocked unless it's related to a connection that is - # otherwise allowed by the policy." The CT-RELATED path is not - # passing these in practice (Hubble shows ICMPv6 DestinationUnreachable - # being POLICY_DENIED on the egress side of established DNS flows), - # so allow ICMP explicitly. Same shape as commit 3c517a0 — widened - # from the error class to the full standard type set. Cilium's - # ICMPField requires an explicit type per entry; no wildcard. + # Do NOT add a blanket `fromEntities: world` to this policy. With + # endpointSelector {} it exposes every pod in the namespace to the IoT + # VLAN and any BGP-routed source, not just the LoadBalancers that need it. + # World is scoped per target in the dedicated policies at the bottom of + # this file. (matter-server rides a multus interface that bypasses Cilium + # policy entirely and is unaffected either way.) + # ICMP must be allowed explicitly: once any L4 policy exists Cilium blocks + # it unless CT-RELATED passes it, which in practice it does not. Cilium's + # ICMPField takes one type per entry — there is no wildcard, so the list + # has to be spelled out. - fromEndpoints: - matchExpressions: - key: io.kubernetes.pod.namespace @@ -186,10 +162,8 @@ spec: - toEndpoints: - matchLabels: "k8s:io.kubernetes.pod.namespace": apps - # In-cluster InfluxDB: HA's HACS InfluxDB integration writes state - # changes here (replaces the legacy http://influxdb.srv.renner.uno - # target — see kubernetes/apps/influxdb/ha-token-job.yaml for the - # write-bucket token wiring). + # In-cluster InfluxDB: HA's HACS integration writes state changes here. + # Token wiring is in apps/influxdb/ha-token-job.yaml. - toEndpoints: - matchLabels: "k8s:io.kubernetes.pod.namespace": influxdb @@ -213,9 +187,8 @@ spec: - toEntities: - host - remote-node - # World: IoT-VLAN devices, ESPHome, HA Cloud, garage S3 (initial - # tarball seed), legacy InfluxDB on VM 108, third-party integration - # endpoints (Hue, Roomba, Philips, Brother IPP, …). + # World: IoT-VLAN devices, HA Cloud, object storage, and third-party + # integration endpoints on the LAN and internet. - toEntities: - world # ICMP — egress mirror of the ingress ICMP allow above. Lets HA