From c4053d5ffda27ad5d37fb994e0eb5512ac192e25 Mon Sep 17 00:00:00 2001 From: Renn F Date: Sat, 18 Jul 2026 07:28:14 +0200 Subject: [PATCH] fix(security): bind orchestrator :8000 to loopback (GHSA-4f7g-w95g-5q2c) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both deploy composes published the orchestrator API on 0.0.0.0:8000, so any host that could reach the machine hit the control plane directly — past nginx and, in the default header-trust posture, with no credential: read/write runtime settings and X-Agent-Role: ceo spawn/stop. nginx reaches the API over the internal Docker network, so a routable host publish is never needed; bind it to 127.0.0.1. On-host debugging and normal panel operation are unchanged; off-host access must go through nginx + cloud auth. --- CHANGELOG.md | 4 ++++ docker-compose.registry.yml | 7 ++++++- docker-compose.yaml | 7 ++++++- docker-compose.yml | 7 ++++++- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3abc8995..972e70da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ## [Unreleased] +### Security + +- **Orchestrator API is no longer published on a routable host interface (GHSA-4f7g-w95g-5q2c).** Both deploy composes published the orchestrator's `:8000` on `0.0.0.0`, so anyone who could reach the host hit the control plane directly — bypassing nginx and, in the default header-trust posture (`ROBOCO_AGENT_AUTH_REQUIRED` unset, cloud auth off), reading/writing runtime settings and spoofing `X-Agent-Role: ceo` to spawn/stop agents with no credential. The publish is now bound to `127.0.0.1`; nginx reaches the API over the internal Docker network, so normal operation and on-host debugging are unchanged, while off-host access must go through nginx + cloud auth. The header-trust design itself is unchanged (it stays the deliberate local-no-login panel path); this closes the unintended off-host reachability that gave it teeth. + ## [0.25.0] - 2026-07-16 ### Added diff --git a/docker-compose.registry.yml b/docker-compose.registry.yml index 27acb84d..3dee1c1e 100644 --- a/docker-compose.registry.yml +++ b/docker-compose.registry.yml @@ -291,7 +291,12 @@ services: - data - render ports: - - "8000:8000" + # Loopback-only: the orchestrator API is an unauthenticated control plane + # in header-trust mode. nginx reaches it over the internal network, so it + # never needs a routable host publish; remote access goes via nginx + cloud + # auth. A 0.0.0.0 publish exposed spawn/stop and settings-write with no + # credential to anyone who could reach the host (GHSA-4f7g-w95g-5q2c). + - "127.0.0.1:8000:8000" environment: # DB network isolation is LIVE in this file (postgres/redis on the # data-only network): suppress the legacy prod-creds gate-env diff --git a/docker-compose.yaml b/docker-compose.yaml index abcb18dd..ee034c80 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -415,7 +415,12 @@ services: - data - render ports: - - "8000:8000" + # Loopback-only: the orchestrator API is an unauthenticated control plane + # in header-trust mode. nginx reaches it over the internal network, so it + # never needs a routable host publish; remote access goes via nginx + cloud + # auth. A 0.0.0.0 publish exposed spawn/stop and settings-write with no + # credential to anyone who could reach the host (GHSA-4f7g-w95g-5q2c). + - "127.0.0.1:8000:8000" environment: # Database (use container name, not localhost) ROBOCO_DATABASE_HOST: roboco-postgres diff --git a/docker-compose.yml b/docker-compose.yml index abcb18dd..ee034c80 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -415,7 +415,12 @@ services: - data - render ports: - - "8000:8000" + # Loopback-only: the orchestrator API is an unauthenticated control plane + # in header-trust mode. nginx reaches it over the internal network, so it + # never needs a routable host publish; remote access goes via nginx + cloud + # auth. A 0.0.0.0 publish exposed spawn/stop and settings-write with no + # credential to anyone who could reach the host (GHSA-4f7g-w95g-5q2c). + - "127.0.0.1:8000:8000" environment: # Database (use container name, not localhost) ROBOCO_DATABASE_HOST: roboco-postgres