Microsoft eXecution Container (MXC) Documentation

repository·main·Indexed 22 days ago

https://github.com/microsoft/mxc

A cross-platform sandboxing system for securely running untrusted code on Windows, Linux, and macOS. MXC abstracts containment backends—including processcontainer, bubblewrap, seatbelt, and experimental options like wslc and microvm—via a unified JSON configuration schema and a TypeScript SDK (@microsoft/mxc-sdk). The system supports native binary execution, state-aware lifecycle management, and audit mode for policy discovery on Windows.

Tokens
97.4K
Snippets
216
Records
382
Agent score
77%

What's inside Microsoft eXecution Container (MXC)

  1. What is Microsoft eXecution Container (MXC)?

    main
    MXC is a sandboxed code execution system designed to run untrusted code (such as model outputs, plugins, or tools) securely on Windows, Linux, and macOS. It abstracts various containment backends—ranging from OS-native process sandboxes to full Virtual Machines—behind a unified JSON configuration schema and a TypeScript SDK.
  2. What is Permissive Learning Mode (PLM)?

    main

    Permissive Learning Mode (PLM) is a Windows-only trace driver (plm.exe) used to facilitate the transition from an unconstrained workload to an enforced MXC container configuration.

    It works by capturing access-denied events emitted by the Windows permissive sandbox layer. The tool decodes these events into structured findings and automatically merges them into a copy of your existing MXC container configuration. This allows you to generate an Adjusted_<name>.json config file that includes the necessary filesystem.readwritePaths and filesystem.readonlyPaths so that subsequent enforcing runs succeed without access errors.

  3. Understand Bubblewrap filesystem isolation behavior

    main

    Bubblewrap (Bwrap) provides filesystem isolation through namespace enforcement with the following characteristics:

    • Default-deny: If no --bind is specified, there is no access to the host filesystem. The base environment is a curated allowlist (defined by BASELINE_RO_BIND_PATHS) rather than a full bind of the host root (/).
    • Subtree Mounting: Using --bind mounts the entire subtree of the target path.
    • Implicit Traversal: bwrap automatically creates the parent directories of any --bind or --ro-bind destination as empty directories. This allows a path like RW /home/user/project/src to be reachable inside the namespace even if its parent directories are not explicitly bound, without exposing the host's actual parent content.
    • Path Resolution: Path resolution follows a "most-specific-path-wins" rule (longest-prefix) via the filesystem_resolve.rs resolver in wxc_common.
  4. Understand ContainerConfig and backend-specific settings

    main

    A ContainerConfig is the complete configuration for a specific backend. It is generated via createConfigFromPolicy() and can be modified by the user before being passed to spawnSandboxFromConfig().

    Key Rules:

    • One backend per Config: A configuration is exclusive to one backend. For example, a Windows process config contains a processcontainer section but no lxc section. A Linux process config contains an lxc section but no processcontainer section.
    • User-modifiable: Advanced users can override any field in the ContainerConfig before spawning.
    • Schema-defined: Configs follow strict schemas (found in schemas/) and are mirrored by SDK TypeScript types.

    All configs share common sections like filesystem, network, and ui. Backend-specific fields are scoped to their respective sections (e.g., processcontainer.ui for Windows process containment).

    type ContainerConfig =
      | ProcessContainerConfig
      | LxcContainerConfig
      | MicroVmConfig;
  5. Determine where to add a new feature

    main

    Before implementing a feature, use the decision logic to identify which components require updates.

    • Cross-platform security restrictions: Requires updates to SandboxPolicy, the ContainerConfig schema, the TypeScript SDK (@microsoft/mxc-sdk), and the Rust executors.
    • Backend-specific configuration: Requires updates to the ContainerConfig schema, adding a containment type to createConfigFromPolicy, updating SDK defaults, and updating executors.
    • SDK or Executor only: If the feature does not affect security policy or cross-platform configuration, it may only require changes to the TypeScript SDK library or the Rust executors.

    Note: Any change to the Config schema always requires changes to the TypeScript SDK library (@microsoft/mxc-sdk) because the SDK generates the Config.

  6. Understand version coupling for Windows.AI.IsolationSession bindings

    main

    The Rust bindings located in src/backends/isolation_session/bindings/ are generated from a WinMD file. These bindings are strictly coupled to a specific version of the windows crate.

    If the project's windows crate version is upgraded, the bindings crate's build.rs will fail. When this happens, you must regenerate the bindings to match the new crate version. The specific required version for the windows crate is defined in GENERATION_INFO.toml under the target_windows_crate key.

  7. Understand the MXC Network Configuration GA schema

    main

    The MXC Network Configuration is transitioning to a new GA (General Availability) schema. This schema replaces the legacy allowedHosts/blockedHosts/defaultPolicy format with a more structured approach defining egress, ingress, and proxy settings.

    • Egress: Defines how traffic leaves the container. It uses a default policy (e.g., deny), an allow list containing destination CIDRs and specific protocol/port combinations, and a deny list for explicit blocks.
    • Ingress: Controls incoming traffic; for example, setting hostLoopback to deny prevents the container from accessing the host's loopback interface.
    • Proxy: Configures HTTP proxy settings (e.g., http key with a host/port string).
    {
      "network": {
        "egress": {
          "default": "deny",
          "allow": [{ "to": [{ "cidr": "140.82.112.0/20" }], "ports": [{ "protocol": "tcp", "port": 443 }] }],
          "deny": [{ "to": [{ "cidr": "10.0.0.0/8" }] }]
        },
        "ingress": { "hostLoopback": "deny" },
        "proxy": { "http": "127.0.0.1:8080" }
      }
    }
  8. Configure Process Environment and Working Directory in LXC

    main

    The LXC backend implements standard process.cwd and process.env fields:

    Working Directory (process.cwd)

    Implemented via a cd -- "$1" && exec /bin/sh -c "$2" wrapper.

    • An empty string preserves the container's default CWD.
    • Paths with spaces, quotes, $vars, or backticks are passed verbatim via positional arguments to avoid shell escaping issues.
    • Non-existent or unpermitted paths result in a non-zero exit code (typically 1).

    Environment Variables (process.env)

    • Each KEY=VAL entry is passed via the --set-var=KEY=VAL flag to lxc-attach.
    • Replace Semantics: If process.env is non-empty, lxc-exec uses --clear-env to prevent host environment leakage. The process.env values take precedence over the host.
    • Malformed Entries: Entries without = or with an empty key (e.g., "=foo") are silently skipped.
    • Baseline: Even with --clear-env, a small baseline (container, HOME, TERM, a default PATH, USER) is injected by lxc-attach.
  9. Configure Filesystem and Network Policies for LXC

    main

    Filesystem Policy

    Enforced via bind mounts:

    • readwritePaths: Mounted as bind,rw. The script can read and write.
    • readonlyPaths: Mounted as bind,ro. The script can read but not write.
    • deniedPaths: No mount or tmpfs overlay is used; the path is inaccessible.

    Network Policy

    Enforced via iptables/nftables rules on the container's virtual ethernet (veth) interface:

    • defaultPolicy: "block": Default DROP rule.
    • defaultPolicy: "allow": Default ACCEPT rule.
    • allowedHosts: ACCEPT rules for specific IPs/CIDRs.
    • blockedHosts: DROP rules for specific IPs/CIDRs.

    Important Notes:

    • IPv4 Only: Firewall mode only resolves allowedHosts and blockedHosts to IPv4 addresses. IPv6 (AAAA) records and literals are silently dropped.
    • Cleanup: Rules are automatically cleaned up when the container exits if removeRulesOnExit is set to true.
  10. Configure outbound network access (Egress)

    main

    MXC uses a default-deny outbound policy. To allow network access, you must explicitly list destinations in the configuration.

    Key Rules:

    • Default Deny: Unlisted destinations are unreachable. A configuration that mentions nothing grants nothing.
    • Precedence: If a connection matches both an egress.allow rule and an egress.deny rule, the deny rule wins.
    • Address Types: You must use IPv4/IPv6 literals or CIDRs. DNS names are rejected at validation time and cannot be used in rules.
    • Default Behavior:
      • With egress.default: "deny", no matching allow means no outbound access.
      • With egress.default: "allow", no matching deny means unrestricted outbound.

    Platform Enforcement:

    • Windows: Uses WFP (Windows Filtering Platform) on process containers.
    • Linux: Uses a network namespace and iptables on WSLc, LXC, or Bubblewrap backends.
    • macOS: Uses a Seatbelt profile that restricts outbound traffic to the loopback proxy port.
  11. Understand the WSLC two-step lifecycle

    main

    Running Linux containers from Windows via MXC follows a two-step lifecycle pattern to optimize performance and execution:

    1. Pre-pull (One-time per image): Pull the required container image into the SDK cache. This step ensures the image is available locally before execution.
    2. Execute (Any number of times): Run the execution task against the already cached image. This allows for rapid, repeated execution without the overhead of network pulls.
  12. Understand inter-container networking constraints

    main

    Inter-container communication depends heavily on the backend being used:

    • Windows process containers: Supports communication between two AppContainers over host loopback, provided that directional AppContainer loopback-exemption rules are installed for the pair.
    • WSLc / LXC / Bubblewrap: These backends use private network namespaces. 127.0.0.1 is local to the sandbox, meaning sandboxes cannot reach each other via loopback. Inter-container communication requires explicit virtual networking (e.g., veth, bridges, or shared namespaces), which is currently out of scope for GA.
    • macOS (Seatbelt): Seatbelt does not use network namespaces. Processes in different Seatbelt sandboxes share the host loopback and can communicate via 127.0.0.1 or Unix sockets/XPC if their profiles allow it. This is considered host-level IPC rather than isolated container networking.