netpol: scope home-assistant world-ingress to the two LBs (security review #sec-2) #125

Merged
k.renner merged 1 commits from feat/mqtt-netpol-scope-world-ingress into master 2026-07-08 06:31:27 +00:00
Owner

Security review — finding #sec-2 (MED-HIGH). First of the one-PR-per-point series.

The home-assistant default-deny-ingress CNP grants fromEntities: world namespace-wide (endpointSelector: {}), so the untrusted IoT VLAN / any BGP-routed source can reach every pod in the namespace on every port — not just the broker. That includes the VSCode editor (:8080), zone-configurator (:42069), otbr web, whisper, matter-server, speech-to-phrase, etc. Only Mosquitto's own auth stands in front of most of them (and the others have no auth at all). I confirmed live that :1883 is reachable from my workstation.

Change

Replace the blanket rule with two endpoint+port-scoped policies (added as multi-doc in the same file; no kustomization change):

  • mosquitto-world-ingress: world → mosquitto :1883, :8883
  • home-assistant-world-ingress: world → home-assistant :8123

Why it's safe

Verified against live Services — the only LoadBalancers in the namespace are home-assistant-bgp (targetPort 8123) and mosquitto-iot (1883/8883); nothing else is world-fronted. External clients can only reach a pod via an LB/NodePort, so no non-LB pod needs world ingress. matter-server's IoT VIP arrives on a multus net1 interface that bypasses Cilium policy, so it's unaffected either way.

kubectl kustomize builds clean. Additive-safe: removes a blanket allow, adds scoped allows covering the same two real paths. Revert = restore the namespace-wide world rule.

Post-merge watch: after Flux reconciles, confirm IoT devices still reach HA:8123 and the broker (Hubble / a quick MQTT connect), and that the other ns pods are no longer world-reachable.

Next in the series: #2 ACL tightening (shellies off homeassistant/#, publishers → write-only), then #3 TLS cutover to :8883 (staged, edge-1 first).

**Security review — finding #sec-2 (MED-HIGH).** First of the one-PR-per-point series. The `home-assistant` `default-deny-ingress` CNP grants `fromEntities: world` **namespace-wide** (`endpointSelector: {}`), so the untrusted IoT VLAN / any BGP-routed source can reach **every pod in the namespace on every port** — not just the broker. That includes the **VSCode editor (:8080)**, zone-configurator (:42069), otbr web, whisper, matter-server, speech-to-phrase, etc. Only Mosquitto's own auth stands in front of most of them (and the others have no auth at all). I confirmed live that :1883 is reachable from my workstation. ## Change Replace the blanket rule with **two endpoint+port-scoped** policies (added as multi-doc in the same file; no kustomization change): - `mosquitto-world-ingress`: world → mosquitto **:1883, :8883** - `home-assistant-world-ingress`: world → home-assistant **:8123** ## Why it's safe Verified against live Services — the **only** LoadBalancers in the namespace are `home-assistant-bgp` (targetPort 8123) and `mosquitto-iot` (1883/8883); nothing else is world-fronted. External clients can only reach a pod via an LB/NodePort, so no non-LB pod needs world ingress. `matter-server`'s IoT VIP arrives on a multus `net1` interface that bypasses Cilium policy, so it's unaffected either way. `kubectl kustomize` builds clean. Additive-safe: removes a blanket allow, adds scoped allows covering the same two real paths. **Revert** = restore the namespace-wide `world` rule. **Post-merge watch:** after Flux reconciles, confirm IoT devices still reach HA:8123 and the broker (Hubble / a quick MQTT connect), and that the other ns pods are no longer world-reachable. Next in the series: **#2 ACL tightening** (shellies off `homeassistant/#`, publishers → write-only), then **#3 TLS cutover to :8883** (staged, edge-1 first).
k.renner added 1 commit 2026-07-08 06:28:52 +00:00
The home-assistant default-deny-ingress CNP granted `fromEntities: world`
namespace-wide (endpointSelector:{}), so the untrusted IoT VLAN / any
BGP-routed source could reach EVERY pod in the namespace on every port —
vscode (:8080), zone-configurator (:42069), otbr, whisper, matter-server,
speech-to-phrase, etc. — when only two pods are actually fronted by
world-facing LoadBalancers.

Replace the blanket rule with two endpoint+port-scoped CiliumNetworkPolicies:
  - world -> mosquitto  :1883, :8883
  - world -> home-assistant :8123
Verified against live Services: home-assistant-bgp (targetPort 8123) and
mosquitto-iot (1883/8883) are the only LoadBalancers in the namespace;
matter-server's IoT VIP rides a multus net1 interface that bypasses Cilium
policy entirely, so it is unaffected.

Security review finding #sec-2. Additive-safe (removes a blanket allow, adds
scoped allows covering the same two real ingress paths); revert = restore the
namespace-wide world rule.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
k.renner merged commit 442dabc936 into master 2026-07-08 06:31:27 +00:00
Sign in to join this conversation.