Skip to content

Maven host-agent: container→host code-execution afwegen en hardenen #44

Description

@ericwout-overheid

Context

Naar aanleiding van een rondgaande post over een Copilot CLI die in een Docker-container tóch host-root kreeg (user in de docker group + gemounte Docker-socket = root-equivalent), is de huidige sandbox tegen het licht gehouden.

Goed nieuws: die specifieke aanvalsketen geldt niet voor onze sandbox:

  • Geen Docker-socket gemount (compose.yml mount alleen claude-home + projects).
  • Geen docker CLI / docker group in de container; geen DinD; geen --privileged.
  • User claude is non-root, sudo beperkt tot precies één script (init-firewall.sh).
  • Enige caps: NET_ADMIN/NET_RAW (firewall).

Het reële aandachtspunt: de Maven host-agent

claude-sandbox/host-agents/maven/maven_agent.py is per ontwerp een container→host code-execution-bridge: een MCP-server die mvn draait namens Claude in de container. Dit is bewust (rootless Docker in de container kan geen sibling-containers starten voor Testcontainers).

Directe OS-command-injectie via de tool-args is afgevangen (subprocess.run met list-vorm, geen shell=True, vaste argv[0]). Maar willekeurige host-code via Maven is triviaal:

  1. Maven-goals zelf — bv. exec-maven-plugin:exec met -Dexec.executable=/bin/sh.
  2. pom.xml — de projects-map is een gedeeld bind-mount; Claude controleert de pom.xml die de host uitvoert. Een plugin gebonden aan de validate-phase draait bij élke goal.
  3. mvnw-wrapper — ligt PROJECT_DIR in de gedeelde map, dan kan Claude mvnw schrijven/overschrijven; de agent voert dat direct uit als host-user (niet eens "via maven").

Daarnaast: op Linux bindt de agent op 0.0.0.0:7777 zonder authenticatie (run.sh waarschuwt hier al voor) → ook het netwerk kan dit misbruiken.

Gevolg: wat Claude in de sandbox kan, kan het via deze bridge uitvoeren als de host-user die run.sh startte. Draait die user in de docker group of heeft die sudo, dan is host-escalatie alsnog mogelijk — niet via de container, maar via de host-user.

Voorstel: opties afwegen + documenteren

Goedkope hardening (past bij het realistische dreigingsbeeld — Claude rogue / prompt-injection)

  • Draai run.sh als dedicated least-privilege host-user (niet in docker group, geen sudo) — documenteren in docs/maven-mcp-agent.md.
  • Adviseer host-maven-projecten buiten de gedeelde projects-map (Claude kan dan geen pom.xml/mvnw schrijven die de host draait).
  • Linux: bind default op 127.0.0.1 of firewall poort 7777; waarschuwing aanscherpen.
  • Overweeg een egress-firewall + ephemeral werkdir voor de agent.

Sterkere isolatie (alleen als runner echt onvertrouwde code draait)

  • Optie C — sysbox: runner als container met geïsoleerde Docker-in-container (eigen dockerd, raakt host-daemon niet). Testcontainers werkt, host-escalatie dicht. Praktisch Linux-only, recente kernel.
  • Optie D — microVM (Kata/Firecracker): runner in lichtgewicht VM met eigen kernel; container- én kernel-escape blijven in de VM. Vereist KVM/nested virt; meer resources.

⚠️ Niet doen: runner-container mét gemounte Docker-socket (om Testcontainers te laten werken). Dat reproduceert exact de Copilot-bug (socket = host-root).

Afweging op threat model

Adversary Maatregel
Claude rogue / prompt-injection (semi-vertrouwd) Goedkope hardening — ~90% winst, lage kosten
Onvertrouwde code, Linux-host Optie C (sysbox)
Volledig vijandige code, multi-tenant/CI, kernel-escape in scope Optie D (microVM)

Platform-nuance

run.sh draait native op het echte OS (ook op Mac/Windows — niet in de Docker-VM). C en D gaan uit van een Linux-host met moderne kernel / KVM; voor Mac/Windows-gebruikers niet zomaar inzetbaar. Mee te wegen.

Definition of done

  • Afweging vastgelegd als ADR of SECURITY.md-sectie.
  • Goedkope hardening-stappen doorgevoerd + gedocumenteerd in docs/maven-mcp-agent.md.
  • Beslissing genomen over C/D (nu doen / later / out-of-scope).

Metadata

Metadata

Assignees

No one assigned

    Labels

    dockerPull requests that update docker codeenhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions