fix(missions): egress from a subnet of their own, so the host can police it
docs/MISSION-EGRESS.md measured that a mission container reaches the entire
tailnet and SSH on its own host, and left the remediation unapplied. Applying
it on 2026-09-18 found why five iptables lines were never going to be enough:
missions egressed from clawmates_edge, the SERVER's network, and the server
needs the tailnet — Beszel on architect, Ollama for the local backend, the
node daemons for exec-test and node-placed terminals. A tailnet drop scoped to
172.23/16 cut the server off from architect:8090 inside a minute.
Missions now egress from clawmates_missions, 172.25.0.0/16, pinned so the
firewall can name it and declared in both compose files with the same shape
edge has. Compose v1 does not create a network no service uses, so on gw-04
it was created by hand with compose's own labels; the server's attach failure
message now says to check for it. core is unchanged: the door and API are
still reached over 172.20.
The policy itself (/usr/local/sbin/clawmates-egress.sh on gw-04, systemd unit
+ drop-ins on docker and tailscaled) lives in mangle/PREROUTING with
--ctstate NEW. Two earlier placements failed measurably: filter/FORWARD loses
to tailscaled re-inserting ts-forward above it on every restart, and
raw/PREROUTING runs before conntrack, so it dropped the server's replies to
tailnet clients and took the API off 100.102.112.85:8088. Verified from the
mission subnet (tailnet, host ssh, link-local blocked; public and core open),
from edge (tailnet open, ssh blocked), and inbound from tank; and proved to
survive restarting both daemons.
Also: deploy/compose/docker-compose.override.yml is tracked now. It holds the
fixes for the five local bring-up gaps and every credential in it is a
${VAR:?} reference, and it had lived on one laptop that lost a volume this
week.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WZb5A2kfVfjpdwSochkuHz
This commit is contained in:
co-authored by
Claude Opus 5
parent
1678452a93
commit
869c3adcb7
@@ -323,11 +323,24 @@ pub fn runtime_auth_mode() -> RuntimeAuth {
|
||||
}
|
||||
}
|
||||
|
||||
/// Docker networks the runtime container must be attached to.
|
||||
/// - `clawmates_core`: talks to the server + database
|
||||
/// - `clawmates_edge`: has egress for outbound provider calls
|
||||
/// Docker networks the mission runtime container must be attached to.
|
||||
/// - `clawmates_core`: talks to the server (skills door, API) + database
|
||||
/// - `clawmates_missions`: has egress for outbound provider calls and fetches
|
||||
///
|
||||
/// Missions used to egress from `clawmates_edge`, the same network as the
|
||||
/// SERVER. That made the host's egress policy impossible to scope: a mission
|
||||
/// agent runs model-generated shell over content it fetched from the open web
|
||||
/// and must not reach the tailnet, but the server on the same subnet must —
|
||||
/// Beszel, Ollama, the node daemons. Measured 2026-09-18: the tailnet rule
|
||||
/// written for missions cut the server off from architect:8090 within the
|
||||
/// minute. A subnet of their own is what lets `clawmates-egress.sh` on gw-04
|
||||
/// drop tailnet/private/link-local/ssh for missions and nothing else.
|
||||
///
|
||||
/// The network is declared by compose (prod: /opt/clawmates/docker-compose.yml;
|
||||
/// local: deploy/compose/docker-compose.yml) with the same shape `edge` has —
|
||||
/// not internal, so it routes off the host.
|
||||
const CORE_NETWORK: &str = "clawmates_core";
|
||||
const EDGE_NETWORK: &str = "clawmates_edge";
|
||||
const EDGE_NETWORK: &str = "clawmates_missions";
|
||||
|
||||
/// Host path (as seen by the docker engine, NOT the server container)
|
||||
/// where the mission's checkouts live. Matches the mount source used
|
||||
@@ -832,7 +845,8 @@ impl MissionRuntimeProvisioner {
|
||||
{CORE_NETWORK} is internal and has no route off the host, so \
|
||||
this mission would run with no egress at all: every provider \
|
||||
call and every fetch would fail while the phase still \
|
||||
reported completion"
|
||||
reported completion. Is the `missions` network declared in \
|
||||
the compose file and created (`docker network ls`)?"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user