From 75261f039c70e6d4d064056009030a7a01899f1c Mon Sep 17 00:00:00 2001 From: Konstantin Renner Date: Fri, 10 Jul 2026 16:43:26 +0200 Subject: [PATCH] otbr: bind the REST API to the pod address, not 0.0.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Last of the three IoT-VLAN gate/bind bypasses (shelly-manager #142, matter-server #144). otbr's net1 is a macvlan on the IoT VLAN (backbone interface), macvlan bypasses Cilium policy, and REST_HOST=0.0.0.0 bound otbr-agent's REST API on :8081 to all interfaces — so the unauthenticated OTBR REST API was reachable from the whole IoT VLAN. Verified 2026-07-10 from edge-1 (on v30iot): GET http://:8081/ answered (404 at /, but it is the live otbr-agent REST listener). That API can read and modify Thread network configuration, including the network key — i.e. enough to join or disrupt the Thread mesh. Note the reality vs. the old comment: otbr-agent's REST listens on :8081 (governed by REST_HOST / --rest-listen-address); otbr-web sits on 127.0.0.1:80 (loopback, not exposed); the container's declared rest:8080 port has no listener. So REST_HOST is the whole exposure. Set REST_HOST to the pod eth0 address via the downward API (POD_IP=status.podIP; REST_HOST=$(POD_IP)). POD_IP is declared BEFORE REST_HOST because k8s $(VAR) substitution only sees earlier env entries. HA's OTBR integration keeps working via the IPv4 ClusterIP Service (otbr.home-assistant.svc:8081 -> podIP:8081). net1 no longer carries REST. The border router itself is unaffected: it uses net1 through BACKBONE_INTERFACE for mDNS/RA/backbone, independent of where REST listens. Verified: kubectl kustomize builds; rendered env has POD_IP (downward API) before REST_HOST=$(POD_IP); BACKBONE_INTERFACE=net1 unchanged. Baseline before change: state=leader, br=running, mesh 2 routers/8 children/8 neighbors. CAVEAT: this restarts the Thread border router. Expect ~60s of self-healing CASE-session churn on Thread/Matter end devices as the mesh re-forms (per the #141 verification). The dataset-restore race and the protocol-aware probes from #139/#140/#141 mean the pod should come back to leader/running on its own and only report Ready once Thread is up. Post-merge checks to run: GET net1:8081 from the IoT VLAN now fails; REST listens on the pod IP only; HA reaches otbr:8081 via ClusterIP; otbr returns to leader/br running and the mesh reconverges; matter-server's Thread nodes re-subscribe. Co-Authored-By: Claude Opus 4.8 (1M context) --- kubernetes/apps/otbr/deployment.yaml | 30 ++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) 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 -- 2.54.0