fix(security): scope Compose volumes per project (ExploitGym-inspired) - #4
jeremy1392 wants to merge 1 commit into
Conversation
|
Thanks for this — the storage-scoping argument holds up and most of it is ready to land. One check needs rework first: it rejects Compose shapes the benchmark corpus actually ships, in a way no operator setting can lift. To make that concrete and reproducible, I put your commit on top of current Reproducing
uv sync --extra all
PYTHONPATH=vendor/xrlenv .venv/bin/python -m pytest \
vendor/xrlenv/tests/unit/control/test_compose_corpus_compatibility.py -qNo benchmark cache, no Docker, no network needed. (With What breaks and why it is unconditional
# seta: {890,892,973,1133,1198} — 2 mounts each
# example: https://github.com/camel-ai/seta-env/blob/683748c43e0b8ddd68ee5ceb75d28af3bea85d71/Harbor-Dataset/1198/environment/docker-compose.yaml#L12
- ${HOST_VERIFIER_LOGS_PATH}:${ENV_VERIFIER_LOGS_PATH}
- ${HOST_AGENT_LOGS_PATH}:${ENV_AGENT_LOGS_PATH}
# terminalworld: {tw_15324,tw_313581,tw_529592}
# example: https://huggingface.co/datasets/EuniAI/TerminalWorld/blob/main/artifacts/tw_15324.tar.gz
- ${DOCKER_SOCKET_PATH:-/var/run/docker.sock}:/var/run/docker.sock
# terminalworld: tw_291556
- ${WORKSPACE_DIR}:/workspaceThat second shape is worth dwelling on, because it cuts both ways. Its effective value is known statically — it resolves to But it also shows your concern is not hypothetical. Today that mount is not vetted at all. The reason this is worth blocking on, rather than a tuning question, is the tier.
This check emits tier 3, so there is no What I'd suggest insteadThe concern is real and I don't want it dropped — vetting a source of But blanket rejection is a strictly worse trade than it looks: it takes nine tasks offline and still doesn't vet the mount, it only refuses it. Resolve interpolation control-plane-side using the same environment the node will use, then vet the resolved value. That is better than both the status quo and this PR:
That closes the hole you found, and closes it more thoroughly, without the collateral. The rest of the change is goodVerified against all 59 cached documents: Also worth calling out, because I under-credited it on first read: the One documentation note: this PR removes the docstring line asserting "The corpus's 7 multi-service tasks pass under the operator's existing One process noteThis PR touches only |
Problem and behavior
Independent Compose projects can attach the same backing store even when their project names differ. The current control-plane gate checks service host binds but does not inspect top-level named-volume definitions or
volumes_from. This change rejects those storage declarations before image resolution, placement, rollout recording, or a node command.For example,
volumes: {cache: {name: shared-cache}}now raisesKwargsPolicyViolationnamingvolumes.cache.name. Withvolumes: {cache: {}}, Compose continues allocating a project-local cache and services within that project can share it normally.Changes
volumes_from, tmpfs, and anonymous volumes. Fix target-only anonymous mounts being mistaken for host binds.ExploitGym connection and author's research
This PR builds on Jeremy Canale's independent case study, "Hardening the OpenAI ExploitGym environment". The study reconstructs the OpenAI / Hugging Face incident from public reports and proposes a containment architecture covering shared dependency infrastructure, per-run identities, restricted egress, and independent shutdown controls.
The principle carried into this contribution is that isolating an agent's container is insufficient if shared writable infrastructure remains available as a communication channel. This PR applies that principle to Beagle's Compose storage: keep writable volumes local to each independent project while preserving intentional sharing between services within that project. The regression tests and real Docker smoke test validate this specific implementation; the broader architecture in the case study remains a proposal requiring its own validation.
The underlying incident evidence is documented in OpenAI's account of unauthorized communication through shared infrastructure. Docker documents that explicit volume names bypass project scoping and volume drivers can attach shared backing stores.
Validation
git diff --checkpassed.scripts/gateway/gateway_proxy.pyand Windows' lack offcntl. The affected XRLEnv tests were run separately on Linux, including both existing POSIX process-group tests.Scope and compatibility
Existing deployments using shared/external/custom Compose storage must migrate to project-local volumes initialized from immutable inputs. These checks have no privileged/host-path-policy override.
Leave
project_nameunset for independent executions; the coordinator already generates a fresh name for each acquire. Explicit project names retain existing semantics and callers must keep them unique. This does not isolate network services, operator-allowed ordinary host binds, other execution backends, or host-privileged workloads, and does not change XRLEnv's trusted-workload model.This PR branches directly from upstream main and is independent of #3 (confirmed local-container cleanup).