Gondolin Agent Sandbox

repository·main·Indexed 23 days ago

https://github.com/earendil-works/gondolin

An Alpine Linux sandbox for running untrusted code via local Linux micro-VMs. It provides programmable network and filesystem control, enforcing host-side policies on network egress, secret access, and filesystem interactions. Supports QEMU as the default VMM and an experimental krun backend. Features include HTTP hooks with secret substitution, outbound SSH proxying with host allowlisting, and a CLI for managing VM sessions, snapshots, and attachments.

Tokens
51.4K
Snippets
97
Records
255
Agent score
81%

What's inside @earendil-works/gondolin

  1. Gondolin Feature Highlights

    main

    Gondolin provides several advanced capabilities for managing agent sandboxes:

    • Micro-VMs: Local, disposable Linux environments.
    • Network Control: Programmable HTTP/TLS egress policy via allowlists and request/response hooks.
    • Secret Injection: Inject secrets into the guest without exposing them to the guest environment directly.
    • VFS Control: Programmable Virtual File System (VFS) mounts using JavaScript.
    • Ingress Gateway: Expose guest HTTP services to the host using --listen or vm.enableIngress().
    • Session Management: Attach to running shells, use SSH (host-to-guest or proxied guest-to-upstream), and take disk snapshots to resume later.
    • Customization: Build custom Alpine-based images and configure DNS behavior (synthetic, trusted, open) and rootfs modes (readonly, memory, cow).
  2. Use @earendil-works/gondolin-krun-runner-darwin-arm64 as a platform dependency

    main

    The @earendil-works/gondolin-krun-runner-darwin-arm64 package provides a prebuilt gondolin-krun-runner specifically for macOS on arm64 architecture.

    End-users do not typically install this package directly; instead, it is consumed as an optional platform-specific dependency by the main @earendil-works/gondolin package.

  3. Understand the Node VFS (vendored) implementation

    main

    The node:vfs implementation in this directory is vendored from upstream Node.js (specifically pull request #61478). It is used as a pre-release API for virtual file system operations within Gondolin.

    Because this is a vendored implementation, it includes specific Gondolin-specific hardening and extensions that differ from the upstream Node.js source. These modifications are marked with GONDOLIN_VENDORED_NODE_VFS_PATCH or XXX(patch): to facilitate synchronization with upstream changes.

  4. Configure DNS Modes in Gondolin

    main

    DNS behavior is controlled via a mode setting. This determines how the host handles UDP port 53 traffic.

    Available Modes:

    • synthetic (default): No upstream DNS. The host replies directly with synthetic answers for A / AAAA queries. This prevents using DNS as an egress channel.
    • trusted: The host forwards queries to the host's trusted resolvers. It validates that payloads are standard DNS queries to prevent UDP tunneling.
    • open: UDP/53 is forwarded directly to the guest's target IP without payload validation. This enables DNS-like UDP tunneling.

    CLI Options:

    • --dns MODE: Set mode to synthetic, trusted, or open.
    • --dns-trusted-server IP: (Repeatable) Adds an IPv4 upstream resolver (required for trusted mode).
  5. Requirements for the krun VMM backend

    main

    When using vmm=krun, the following requirements must be met:

    • Image Manifest Assets: The image manifest must include krun assets: assets.krunKernel and optionally assets.krunInitrd.
    • Experimental Status: The krun backend is experimental and has lower feature parity compared to the QEMU backend.
  6. Handle TLS MITM CA certificates

    main

    Gondolin uses a local Certificate Authority (CA) to perform TLS MITM (Man-in-the-Middle) interception for HTTP hooks.

    • Storage: The CA is generated under ~/.cache/gondolin/ssl (or XDG_CACHE_HOME).
    • Injection: The CA cert is injected into the guest at /etc/gondolin/mitm/ca.crt.
    • Guest Installation: Guest init scripts attempt to install the cert via update-ca-certificates and publish a merged runtime bundle at /run/gondolin/ca-certificates.crt.

    Security Guidance:

    • Treat the CA private key as sensitive.
    • For per-run isolation, configure mitmCertDir to point to a temporary directory.
  7. How Gondolin sandboxing works

    main

    Gondolin provides lightweight micro-VM sandboxes (using QEMU by default or an optional libkrun backend) that boot in under a second on macOS or Linux.

    Unlike standard compute sandboxing, Gondolin provides programmatic control over the network stack and virtual filesystem via JavaScript. This allows you to:

    • Control network egress (which hosts the sandbox can reach).
    • Protect secrets from exfiltration by binding specific secrets to specific allowed hosts.
    • Control filesystem access and persistence for the agent.
  8. How programmable filesystem mounts work

    main

    Gondolin provides programmable storage through a Virtual File System (VFS) architecture. The guest sees two types of storage:

    1. Image-backed root filesystem: Typically ephemeral.
    2. Host-provided mounts: Programmable paths (e.g., /workspace) managed by the host.

    For programmable mounts, the guest uses a FUSE daemon called sandboxfs. Every filesystem operation (read, write, etc.) is converted into an RPC message over virtio-serial and sent to the host. The host's FsRpcService then dispatches these operations to configured VFS providers, such as:

    • MemoryProvider
    • RealFSProvider
    • ReadonlyProvider

    This allows the guest to interact with normal POSIX paths while the host controls the actual data persistence and access logic.

  9. Compare QEMU and krun VM backends

    main

    Gondolin supports two VM backends for its agent sandbox:

    • qemu: The default backend. It offers broader feature support and allows for more granular hardware configuration (CPU models, acceleration, machine types).
    • krun: An experimental backend that uses libkrun via host/krun-runner. It is optimized for performance but has stricter architectural constraints.

    Key Differences

    Featureqemukrun
    ArchitectureGuest arch can differ from hostGuest arch must match host arch
    Hardware TuningSupports sandbox.cpu, sandbox.machineType, sandbox.accelThese options are rejected; hardware is implicit per OS
    Kernel RequirementsStandard kernel/initrd/rootfsRequires a libkrunfw-compatible kernel
    Rootfs Memory ModeUses backend snapshot mode (unless rootfs.size is set)Uses a temporary qcow2 overlay via qemu-img (ephemeral writes, not RAM-backed)

    Recommendation

    Use qemu for general development and production unless you specifically need to test or use the krun backend.

  10. Use Secret Injection for secure credential handling

    main

    Gondolin provides a mechanism to inject secrets into HTTP requests without exposing them to the guest environment.

    1. Guest Side: The guest environment contains only placeholders for secrets.
    2. Mediation: When the guest sends an HTTP request, the host scans the headers for these placeholders.
    3. Substitution: If the destination host is permitted by the secret's policy, the host replaces the placeholder with the actual secret value in the outbound request.
    4. Security: If the destination is not allowed, the request is blocked. This ensures real secret values never reside in the guest's memory, environment, or filesystem.
  11. How Gondolin handles secrets in the guest VM

    main

    Gondolin uses a placeholder-based model to prevent real secret values from ever entering the guest VM environment. Instead of passing real tokens into the VM's environment variables, the host generates high-entropy placeholders.

    The Workflow:

    1. The host generates placeholders (e.g., <random-marker>.<normalized_secret_name>).
    2. You pass both env (containing the placeholders) and httpHooks into VM.create(...).
    3. The guest only sees the placeholders in its environment variables.
    4. On outbound HTTP requests, the host intercepts the request and replaces the placeholders with the real secret values, but only if the destination host is in the secret's allowed host list.

    Placeholder Modes:

    • secretPlaceholderMode: "shared" (Default): All secrets share one random marker, resulting in placeholders like <random-marker>.<normalized_secret_name>.
    • secretPlaceholderMode: "unique": Each secret gets its own unique, fully random placeholder like GONDOLIN_SECRET_<random>.