diff --git a/kubernetes/apps/otbr/deployment.yaml b/kubernetes/apps/otbr/deployment.yaml index 6792573d..2ebf0b84 100644 --- a/kubernetes/apps/otbr/deployment.yaml +++ b/kubernetes/apps/otbr/deployment.yaml @@ -206,13 +206,31 @@ spec: - name: FIREWALL value: "0" # The upstream entrypoint defaults REST_HOST=127.0.0.1 and - # passes --rest-listen-address $REST_HOST to otbr-agent, so - # the REST API on :8081 was only reachable from inside the - # pod. Bind it to all interfaces so the ClusterIP Service - # path (HA's otbr integration → otbr.home-assistant.svc:8081) - # works. + # passes --rest-listen-address $REST_HOST to otbr-agent. HA's + # OTBR integration reaches the REST API via the ClusterIP + # Service (otbr.home-assistant.svc:8081), so it must not be + # loopback-only — but it must NOT be 0.0.0.0 either. + # + # net1 is a macvlan on the IoT VLAN (backbone interface, + # below), and macvlan traffic bypasses Cilium policy. A + # 0.0.0.0 bind therefore exposed the unauthenticated OTBR REST + # API — which can read/modify Thread network config, including + # the network key — to the whole IoT VLAN (verified 2026-07-10: + # GET http://:8081/ from the VLAN answered). Bind it to + # the pod eth0 address instead: the ClusterIP path still + # works, net1 no longer carries the REST API. POD_IP must be + # declared before REST_HOST — k8s $(VAR) substitution only sees + # earlier env entries. The Thread border router itself is + # unaffected: it uses net1 via BACKBONE_INTERFACE for + # mDNS/RA/backbone, independent of where REST listens. This is + # the otbr twin of the shelly-manager (#142) and matter-server + # (#144) fixes. + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP - name: REST_HOST - value: "0.0.0.0" + value: "$(POD_IP)" ports: - name: web containerPort: 8081