Skip to content

Add OS-native kernel sandboxing (Landlock/Seatbelt): in-container defense-in-depth + lightweight local mode #396

Description

@dpup

Summary

Add OS-native kernel sandboxing (Landlock on Linux, Seatbelt on macOS) as an enforcement layer for Moat runs. Support it in two modes:

  1. In-container defense-in-depth — apply kernel self-restriction to the agent process inside a Moat container, so a container-boundary breakout still hits an irreversible, deny-by-default capability wall.
  2. Lightweight local mode — a containerless run mode that uses kernel primitives alone (no Docker/Apple container), for fast, low-friction local runs where full container isolation is more than needed.

Motivation

Moat's current isolation model is the container boundary plus the credential-injecting proxy. That's strong, but:

  • Single layer of process isolation. If an agent escapes the container (kernel/container-runtime CVE, misconfig), there's no inner wall. Kernel self-restriction applied to the agent process gives defense-in-depth: even with arbitrary code execution, the agent cannot widen its own restrictions.
  • Startup cost / footprint. Spinning up a container (image selection, mounts, daemon registration) is overhead for quick local runs. A kernel-only mode could start in well under a second with no disk usage, lowering the barrier to "just sandbox this one command."
  • Defense-in-depth aligns with Moat's deny-by-default posture. A curated default blocklist of sensitive paths (SSH keys, ~/.aws, ~/.config/gcloud, shell history, browser cookies) enforced at the kernel level complements the proxy's credential isolation.

How it would work

Linux — Landlock LSM (kernel 5.13+):

  • Agent process calls landlock_restrict_self() after setup, before exec'ing the agent.
  • Restrictions are irreversible (tighten-only) and inherited by all child processes — no userspace escape hatch.
  • Allow-list filesystem access (typically workspace CWD writable, everything else denied) and, on kernel 6.7+ (ABI v4), TCP network rules.

macOS — Seatbelt:

  • Apply a sandbox profile to the agent process for filesystem/network restriction.

Mode 1 (in-container): the container entrypoint applies kernel restrictions to the agent process before handing off. Container boundary = outer wall; Landlock/Seatbelt = inner wall.

Mode 2 (local): moat run skips container creation entirely; the agent runs as a restricted host process. The credential proxy still sits outside the sandbox (localhost), so the existing per-run token injection model carries over unchanged.

Configuration sketch

# moat.yaml
isolation:
  mode: container        # container | local
  kernel_sandbox: true   # apply Landlock/Seatbelt within the chosen mode
  sandbox:
    allow_write: [./]    # paths writable by the agent
    deny_paths:          # blocked even if otherwise readable
      - ~/.ssh
      - ~/.aws
      - ~/.config/gcloud

CLI equivalents (e.g. --mode local, --kernel-sandbox, --allow ./src) for ad-hoc runs.

Known constraints / threat-model notes

These should be documented honestly so users understand the guarantees:

  • Shared kernel in local mode — weaker memory isolation than the container boundary. Local mode trades isolation strength for speed; it is not a replacement for the container in high-risk scenarios.
  • Landlock coverage gaps — historically no UDP filtering (TCP-only until ABI v4 / kernel 6.7); some syscall families (chmod/chown/stat) not restrictable on older kernels. Feature-detect ABI version and degrade gracefully with a clear warning.
  • Network filtering still relies on the proxy — kernel network rules are coarse (per-port/IP); domain-level policy stays with the proxy, which has its own documented bypass surface (domain fronting, DNS tunneling).
  • Windows — Landlock is only available via WSL2; native Windows is out of scope for an initial cut.
  • Kernel-version dependency — Landlock requires Linux 5.13+; older hosts fall back to container-only with a warning.

Scope for a first cut

  • Linux Landlock enforcement applied to the agent process (in-container mode first)
  • Feature-detect Landlock ABI version; warn + degrade on unsupported kernels
  • Default sensitive-path deny-list
  • moat.yaml isolation block + CLI flags
  • macOS Seatbelt support
  • Local (containerless) mode
  • Docs: isolation modes, guarantees, and explicit non-guarantees

Open questions

  • Should local mode be a separate mode: local or a kernel_sandbox-only flag layered onto a "no container" runtime?
  • How does kernel-level network restriction interact with the existing proxy HTTP_PROXY/NO_PROXY routing — do we need to allow-list the loopback proxy explicitly in the Landlock TCP ruleset?
  • Audit logging: should kernel-denied operations be surfaced into the existing audit store?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Fields

    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions