refactor(orchestrator): fail closed unconditionally, drop topology opt-out
tracker-policy-pr / check-pr (pull_request) Successful in 7s
test / integration-docker (pull_request) Successful in 15s
test / unit (pull_request) Successful in 39s
test / integration-firecracker (pull_request) Successful in 3m55s
test / coverage (pull_request) Successful in 22s
test / publish-infra (pull_request) Has been skipped
prd-number / assign-numbers (push) Failing after 21s
test / integration-docker (push) Successful in 22s
lint / lint (push) Successful in 56s
Update Quality Badges / update-badges (push) Successful in 53s
test / unit (push) Successful in 1m52s
test / integration-firecracker (push) Successful in 5m2s
test / coverage (push) Successful in 16s
test / publish-infra (push) Successful in 1m56s
tracker-policy-pr / check-pr (pull_request) Successful in 7s
test / integration-docker (pull_request) Successful in 15s
test / unit (pull_request) Successful in 39s
test / integration-firecracker (pull_request) Successful in 3m55s
test / coverage (pull_request) Successful in 22s
test / publish-infra (pull_request) Has been skipped
prd-number / assign-numbers (push) Failing after 21s
test / integration-docker (push) Successful in 22s
lint / lint (push) Successful in 56s
Update Quality Badges / update-badges (push) Successful in 53s
test / unit (push) Successful in 1m52s
test / integration-firecracker (push) Successful in 5m2s
test / coverage (push) Successful in 16s
test / publish-infra (push) Successful in 1m56s
Remove the empty-key opt-out codex flagged: ControlPlaneProvisioning no longer lets the orchestrator start without a signing key when a backend declares an "isolated" topology. Host separation is not the safety condition — the gateway (and any other caller) must reach the control-plane listener for /resolve, so an open orchestrator would still grant them full `cli`. The signing key is now mandatory for every backend. Drops the now-purposeless Topology/COLOCATED machinery (no backend declared a non-default topology) and the contractual test_orchestrator_key_allows_empty_when_isolated. Updates the PRD's invariant 4 and lifecycle docstring accordingly. Docs/behaviour only otherwise. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit was merged in pull request #482.
This commit is contained in:
@@ -59,9 +59,8 @@ class Orchestrator(abc.ABC):
|
||||
|
||||
# The shared control-plane auth provisioning contract (#476). Every backend
|
||||
# gets its signing key + gateway token through this one seam rather than
|
||||
# re-deriving the wiring; the default topology is co-located + fail-closed,
|
||||
# so a backend need not redeclare it (a truly isolated control plane would
|
||||
# override this with a different `Topology`).
|
||||
# re-deriving the wiring; it is fail-closed for every backend — the
|
||||
# orchestrator never starts without its signing key.
|
||||
provisioning: ControlPlaneProvisioning = ControlPlaneProvisioning()
|
||||
|
||||
def ensure_built(self) -> None:
|
||||
|
||||
+11
-38
@@ -26,7 +26,7 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
|
||||
from . import orchestrator_auth
|
||||
from .orchestrator_auth import ROLE_GATEWAY
|
||||
@@ -39,8 +39,8 @@ from .paths import (
|
||||
|
||||
|
||||
class ProvisioningError(RuntimeError):
|
||||
"""A control-plane auth invariant would be violated (e.g. starting a
|
||||
co-located control plane without its signing key — which would run OPEN)."""
|
||||
"""A control-plane auth invariant would be violated (e.g. starting the
|
||||
orchestrator without its signing key — which would run OPEN)."""
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -99,31 +99,6 @@ CONTROL_PLANE = TrustDomain(
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Topology:
|
||||
"""Where a backend runs the two planes, so the provisioning seam can decide
|
||||
whether an open control plane is dangerous — the backend declares this
|
||||
instead of hardcoding the decision in its launcher.
|
||||
|
||||
`data_plane_shares_control_host` — the gateway runs on the same host/VM as
|
||||
the orchestrator, so an open orchestrator would hand the co-located gateway
|
||||
full `cli`. The default, and what makes the signing key mandatory. Every
|
||||
current backend is co-located (docker/macOS: two containers on one host;
|
||||
firecracker: two VMs on one host, agents L3-isolated); an isolated control
|
||||
plane on a separate trusted host may declare False.
|
||||
|
||||
`combined_guest` — both planes in one guest (the retired combined infra VM).
|
||||
Informational; kept so a future combined backend declares it."""
|
||||
|
||||
data_plane_shares_control_host: bool = True
|
||||
combined_guest: bool = False
|
||||
|
||||
|
||||
# The default posture: data plane co-located with the control plane. Fail-closed
|
||||
# — the signing key is mandatory. A backend opts out only by declaring otherwise.
|
||||
COLOCATED = Topology(data_plane_shares_control_host=True)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ControlPlaneProvisioning:
|
||||
"""The one seam every backend launcher uses to provision control-plane auth,
|
||||
@@ -131,22 +106,22 @@ class ControlPlaneProvisioning:
|
||||
round: the orchestrator gets the raw key (`orchestrator_key`), the gateway
|
||||
gets a minted `gateway` token (`gateway_token`), the host CLI mints its own
|
||||
`cli` token from the same host-canonical key, and the orchestrator never
|
||||
starts open where its gateway is co-located."""
|
||||
starts open."""
|
||||
|
||||
domain: TrustDomain = CONTROL_PLANE
|
||||
topology: Topology = field(default=COLOCATED)
|
||||
|
||||
def orchestrator_key(self) -> str:
|
||||
"""The raw signing key the orchestrator process must receive (carry it in
|
||||
`domain.key_env`). Fail-closed: raises rather than return "" for a
|
||||
co-located topology, since an empty key runs the server open and hands
|
||||
the co-located gateway full `cli`."""
|
||||
`domain.key_env`). Fail-closed: raises rather than return "", since an
|
||||
empty key runs the server open — and being on a separate host does not
|
||||
stop the gateway from reaching the control plane (it must, for
|
||||
`/resolve`), so an open orchestrator would treat that gateway as `cli`."""
|
||||
key = self.domain.signing_key()
|
||||
if not key and self.topology.data_plane_shares_control_host:
|
||||
if not key:
|
||||
raise ProvisioningError(
|
||||
f"refusing to start the {self.domain.name} orchestrator without "
|
||||
"a signing key: its gateway shares this host/VM, so an open "
|
||||
"orchestrator would grant that gateway full `cli` (#476)"
|
||||
"a signing key: an open orchestrator authenticates no one and "
|
||||
"grants every caller that reaches it full `cli` (#476)"
|
||||
)
|
||||
return key
|
||||
|
||||
@@ -161,7 +136,5 @@ __all__ = [
|
||||
"ProvisioningError",
|
||||
"TrustDomain",
|
||||
"CONTROL_PLANE",
|
||||
"Topology",
|
||||
"COLOCATED",
|
||||
"ControlPlaneProvisioning",
|
||||
]
|
||||
|
||||
@@ -45,7 +45,9 @@ key.
|
||||
pre-minted `gateway` token it can't rewrite into `cli`;
|
||||
3. the host CLI's `cli` token is minted from the same key, so it stays valid
|
||||
across co-running backends;
|
||||
4. the control plane never runs open where its data plane shares the host/VM.
|
||||
4. the orchestrator never runs open — the signing key is mandatory, with no
|
||||
topology opt-out (a separate host does not stop a caller from reaching the
|
||||
control-plane listener, so it cannot make open mode safe).
|
||||
|
||||
## Non-goals
|
||||
|
||||
@@ -72,9 +74,8 @@ signed by one service's key neither carries nor verifies another service's role.
|
||||
|
||||
**`ControlPlaneProvisioning`** is the seam the backends call.
|
||||
`orchestrator_key()` returns the raw key for the control-plane process
|
||||
(fail-closed: it raises rather than hand back an empty key that would run the
|
||||
server open where the data plane is co-located). `gateway_token()` mints the
|
||||
gateway's token. Each backend applies these through its own transport —
|
||||
(fail-closed for every backend: it raises rather than hand back an empty key that
|
||||
would run the server open). `gateway_token()` mints the gateway's token. Each backend applies these through its own transport —
|
||||
docker/macOS inject env vars, firecracker pushes over SSH — but none re-derives
|
||||
*which* key or role.
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ from bot_bottle.trust_domain import (
|
||||
CONTROL_PLANE,
|
||||
ControlPlaneProvisioning,
|
||||
ProvisioningError,
|
||||
Topology,
|
||||
TrustDomain,
|
||||
)
|
||||
|
||||
@@ -78,23 +77,16 @@ class TestControlPlaneProvisioning(unittest.TestCase):
|
||||
with patch("bot_bottle.trust_domain.host_signing_key", return_value="key"):
|
||||
self.assertEqual("key", prov.orchestrator_key())
|
||||
|
||||
def test_orchestrator_key_fail_closes_when_colocated_and_empty(self) -> None:
|
||||
# Invariant 4: a co-located backend must never start the control plane
|
||||
# without a key (it would run OPEN and hand the co-located data plane
|
||||
# full `cli`).
|
||||
prov = ControlPlaneProvisioning() # default topology: co-located
|
||||
def test_orchestrator_key_fail_closes_when_empty(self) -> None:
|
||||
# Invariant 4: the orchestrator must never start without a key — it would
|
||||
# run OPEN and grant every caller that reaches it full `cli`. There is no
|
||||
# topology opt-out: a separate host does not stop the gateway (or any
|
||||
# other caller) from reaching the control-plane listener.
|
||||
prov = ControlPlaneProvisioning()
|
||||
with patch("bot_bottle.trust_domain.host_signing_key", return_value=""):
|
||||
with self.assertRaises(ProvisioningError):
|
||||
prov.orchestrator_key()
|
||||
|
||||
def test_orchestrator_key_allows_empty_when_isolated(self) -> None:
|
||||
# A genuinely isolated control plane (no co-located data plane) may run
|
||||
# without a key — the only topology where open mode is permissible.
|
||||
prov = ControlPlaneProvisioning(
|
||||
topology=Topology(data_plane_shares_control_host=False))
|
||||
with patch("bot_bottle.trust_domain.host_signing_key", return_value=""):
|
||||
self.assertEqual("", prov.orchestrator_key())
|
||||
|
||||
def test_gateway_token_is_a_verifiable_gateway_role_token(self) -> None:
|
||||
prov = ControlPlaneProvisioning()
|
||||
with patch("bot_bottle.trust_domain.host_signing_key", return_value="k"):
|
||||
|
||||
Reference in New Issue
Block a user