Skip to content

Security: riedel/nix-opencode-with-mitm

Security

SECURITY.md

Security Report & Threat Model

Overview

This document describes the threat model, security findings, and recommendations for the jailed opencode sandbox implemented in flake.nix.


Threat Model

Assets to Protect

  1. Host filesystem - Prevent unauthorized read/write access outside the project directory
  2. Host network - Prevent direct network access; only allow whitelisted hosts via proxy
  3. Host processes & namespaces - Prevent interference with host processes or namespace escape
  4. Host secrets - Prevent access to credentials, tokens, or sensitive data outside permitted paths
  5. Future run integrity - Prevent the jailed process from influencing or breaking future sandbox runs

Trusted Components

Component Source Trust Level
jail-nix sourcehut:~alexdavid/jail-nix High - core sandbox primitive
nixpkgs github:NixOS/nixpkgs/nixos-unstable High - standard Nix packages
ip2unix github:nixcloud/ip2unix Medium - traffic redirection
mitmproxy nixpkgs Medium - runs on host, enforces allowlist
opencode github:numtide/llm-agents.nix Low - the agent being jailed

Adversary Capabilities

The adversary (jailed opencode process) can:

  • Execute arbitrary commands inside the jail
  • Read/write all mounted paths
  • Attempt symlink attacks, namespace escapes, kernel exploits
  • Try to influence environment variables or wrapper behavior
  • Attempt to corrupt state that affects future runs

The adversary cannot (by design):

  • Access the host network directly (network namespace unshared)
  • Access /nix/store writeable (read-only mount)
  • Create new sessions (no-new-session)
  • Access paths not explicitly bind-mounted

Security Findings

Finding 1: Full CWD Mount (mount-cwd)

Severity: Medium (by design, but worth documenting)

Location: flake.nix:57-59

commonJailOptionsNoNet = with jail.combinators; [
  time-zone
  no-new-session
  mount-cwd  # <-- Host's current working directory is mounted
];

Impact:

  • The agent has full read/write access to the entire project directory
  • Any file in CWD can be read, modified, or deleted
  • If CWD is /home/user/ or similar, large portions of home directory are exposed

Recommendation:

  • Mount only specific subdirectories instead of full CWD
  • Consider read-only mounts for directories that shouldn't be modified
  • Document that CWD must be a trusted project directory

Finding 2: Opencode Config Paths Mounted Read-Write

Severity: Medium (by design for functionality)

Location: flake.nix:92-97

opencodeConfigPaths = [
  "~/.config/opencode"
  "~/.local/share/opencode"
  "~/.local/state/opencode"
];

Impact:

  • Agent can read API keys, tokens, and credentials stored in opencode config
  • Agent can modify opencode configuration, affecting future runs
  • Agent can inject malicious config or state that persists across jail sessions

Recommendation:

  • Mount config directories as read-only if opencode doesn't need to write them
  • Audit what credentials are stored in these paths
  • Consider separate config profiles for jailed vs. host opencode

Finding 3: Shared Temp Directory /tmp/jailed-agents

Severity: Low-Medium (DoS vector, limited path manipulation)

Location: flake.nix:296-305, 371-375

Details:

  • Host wrapper creates /tmp/jailed-agents for Unix socket + mitmproxy config
  • Jail has read/write access to this directory and mitmproxy-conf subdirectory
  • Host cleanup uses rm -rf "$SOCK_DIR" on exit

Attack Scenarios:

  1. Symlink attack on mitmproxy-conf: Agent creates mitmproxy-conf as symlink to another directory. Due to mkdir -p and set -euo pipefail, mitmproxy startup will fail (DoS to next run).
  2. Socket manipulation: Agent could potentially delete or rename proxy.sock during execution.
  3. Hosts file manipulation: Agent can modify $SOCK_DIR/hosts but this is overwritten at startup.

Why Path Traversal is Limited:

  • rm -rf on a symlink only removes the symlink, not the target
  • mkdir -p "$MITM_CONF_DIR" fails if final component is a symlink
  • set -euo pipefail ensures failures abort the wrapper

Recommendation:

  • Use per-run random temp directories: mktemp -d /tmp/jailed-agents.XXXXXX
  • Pass SOCK_DIR dynamically to add-runtime instead of hardcoding
  • Validate/clear $SOCK_DIR contents before starting mitmproxy

Finding 4: OPENCODE_DISABLE_JAIL Escape Hatch

Severity: Medium (footgun if misconfigured)

Location: flake.nix:431-439

if [ "${OPENCODE_DISABLE_JAIL:-0}" = "1" ]; then
  # Runs opencode on HOST, only wrapped with ip2unix
  exec ip2unix ... -- opencode "$@"
else
  # Runs jailed opencode
  ${jailedOpencodeViaUnixSocket { }}/bin/jailed-opencode-ip2unix "$@"
fi

Impact:

  • If OPENCODE_DISABLE_JAIL=1 is set in host environment, jail is disabled
  • Agent cannot toggle this from inside jail (child cannot change parent env)
  • Dangerous if set in shell profile, systemd unit, or config by accident

Recommendation:

  • Rename to OPENCODE_UNSAFE_DISABLE_JAIL to signal danger
  • Add warning log output when this is enabled
  • Never set in production or automated environments

Finding 5: Dev Shell Provides Unsandboxed Opencode

Severity: Low (documented behavior, but easy to misuse)

Location: flake.nix:467-476

devShells.default = pkgs.mkShell {
  packages = [
    pkgs.nixd
    pkgs.nixfmt
    pkgs.statix
    llm-agents.packages.${system}.opencode  # <-- Unsandboxed opencode
    ip2unixPkg
    mitmproxyPkg
  ];
};

Impact:

  • Running nix develop then opencode bypasses all jail protections
  • User may accidentally use unsandboxed opencode thinking it's jailed

Recommendation:

  • Document that nix run .#opencode-with-mitm is the only jailed entrypoint
  • Consider removing opencode from dev shell or renaming it (e.g., opencode-unsafe)

Finding 6: Network Isolation Depends on Namespace Unsharing

Severity: Low (architectural assumption)

Details:

  • Jail uses jail-nix to unshare network namespace
  • No direct verification that network namespace is truly isolated
  • If jail-nix has a bug, network isolation could fail

Mitigation:

  • Startup assertions test that direct network fails (curl www.example.com)
  • Even if namespace escapes, mitmproxy allowlist still enforces host whitelist
  • Multiple layers of defense (jail + proxy + allowlist)

Recommendation:

  • Keep startup assertions enabled in production
  • Periodically audit jail-nix for namespace handling bugs

Finding 7: Fixed Socket Path Enables Cross-Run Persistence

Severity: Low

Location: flake.nix:367-375

SOCK_DIR="/tmp/jailed-agents"  # Fixed path

Impact:

  • Attacker-controlled files in /tmp/jailed-agents persist across runs
  • Could be used for DoS or influence behavior of next run
  • /tmp is typically world-writable, enabling cross-user attacks on multi-user systems

Recommendation:

  • Use per-run random directories
  • Set restrictive permissions on SOCK_DIR (chmod 700)
  • Consider using $XDG_RUNTIME_DIR instead of /tmp

Attack Surface Summary

Attack Vector Likelihood Impact Mitigation Status
Direct network access Low High ✅ Namespace unsharing + startup assertions
Network via non-allowed host Low Medium ✅ mitmproxy --allow-hosts
Host filesystem escape Low High ✅ jail-nix bind mounts only
Symlink attack on /tmp Medium Low ⚠️ Partially mitigated (DoS possible)
Config manipulation Medium Medium ⚠️ By design (read-write mounts)
CWD data exfiltration High Medium ⚠️ By design (mount-cwd)
Jail disable via env var Low High ⚠️ Footgun documented
Dev shell bypass Medium High ⚠️ Documented
jail-nix vulnerability Low Critical ⚠️ Trusted component assumption

Recommendations Summary

High Priority

  1. Use per-run temp directories instead of fixed /tmp/jailed-agents
  2. Remove or restrict mount-cwd for the ip2unix jail variant if full project access isn't needed
  3. Audit opencode config for sensitive data; consider read-only mounts where possible

Medium Priority

  1. Rename OPENCODE_DISABLE_JAIL to signal danger (OPENCODE_UNSAFE_DISABLE_JAIL)
  2. Add validation of $SOCK_DIR contents before mitmproxy startup
  3. Set restrictive permissions on temp directories (chmod 700)

Low Priority

  1. Document allowed entrypoints clearly (only nix run .#opencode-with-mitm)
  2. Consider removing opencode from dev shell or renaming it
  3. Add telemetry/logging when jail is disabled

Conclusion

The sandbox provides reasonable isolation against network access and host filesystem escape, with defense-in-depth via:

  • Namespace unsharing (jail-nix)
  • Unix socket-based proxy (ip2unix)
  • Host allowlist enforcement (mitmproxy)
  • Startup assertions

The main risks are:

  1. By-design host access (CWD, opencode config) - acceptable if understood
  2. Temp directory manipulation - can cause DoS, limited path traversal
  3. Configuration footguns (OPENCODE_DISABLE_JAIL, dev shell)

The jail is not suitable for:

  • Running untrusted code that may exploit kernel vulnerabilities
  • Protecting against a determined adversary with root access
  • Multi-tenant environments without additional hardening

For typical use (running AI agents on local projects with network allowlisting), the current design provides appropriate isolation with acceptable tradeoffs for usability.

There aren't any published security advisories