feat: use Landlock for sandboxing on Linux

This commit is contained in:
Michael Bolin
2025-04-30 14:09:42 -07:00
parent dd1f839f52
commit 364b53900f
3 changed files with 62 additions and 14 deletions

View File

@@ -4,6 +4,7 @@ import type { ParseEntry } from "shell-quote";
import { process_patch } from "./apply-patch.js";
import { SandboxType } from "./sandbox/interface.js";
import { execWithLandlock } from "./sandbox/landlock.js";
import { execWithSeatbelt } from "./sandbox/macos-seatbelt.js";
import { exec as rawExec } from "./sandbox/raw-exec.js";
import { formatCommandForDisplay } from "../../format-command.js";
@@ -42,26 +43,30 @@ export function exec(
sandbox: SandboxType,
abortSignal?: AbortSignal,
): Promise<ExecResult> {
// This is a temporary measure to understand what are the common base commands
// until we start persisting and uploading rollouts
const opts: SpawnOptions = {
timeout: timeoutInMillis || DEFAULT_TIMEOUT_MS,
...(requiresShell(cmd) ? { shell: true } : {}),
...(workdir ? { cwd: workdir } : {}),
};
// Merge default writable roots with any user-specified ones.
const writableRoots = [
process.cwd(),
os.tmpdir(),
...additionalWritableRoots,
];
if (sandbox === SandboxType.MACOS_SEATBELT) {
return execWithSeatbelt(cmd, opts, writableRoots, abortSignal);
}
// SandboxType.NONE (or any other) falls back to the raw exec implementation
return rawExec(cmd, opts, abortSignal);
switch (sandbox) {
case SandboxType.NONE: {
// SandboxType.NONE uses the raw exec implementation.
return rawExec(cmd, opts, abortSignal);
}
case SandboxType.MACOS_SEATBELT: {
// Merge default writable roots with any user-specified ones.
const writableRoots = [
process.cwd(),
os.tmpdir(),
...additionalWritableRoots,
];
return execWithSeatbelt(cmd, opts, writableRoots, abortSignal);
}
case SandboxType.LINUX_LANDLOCK: {
return execWithLandlock(cmd, opts, additionalWritableRoots, abortSignal);
}
}
}
export function execApplyPatch(

View File

@@ -303,6 +303,11 @@ async function getSandbox(runInSandbox: boolean): Promise<SandboxType> {
"Sandbox was mandated, but 'sandbox-exec' was not found in PATH!",
);
}
} else if (process.platform === "linux") {
// TODO: Need to verify that the Landlock sandbox is working. For example,
// using Landlock in a Linux Docker container from a macOS host may not
// work.
return SandboxType.LINUX_LANDLOCK;
} else if (CODEX_UNSAFE_ALLOW_NO_SANDBOX) {
// Allow running without a sandbox if the user has explicitly marked the
// environment as already being sufficiently locked-down.

View File

@@ -0,0 +1,38 @@
import type { ExecResult } from "./sandbox/interface";
import type { SpawnOptions } from "child_process";
import { exec } from "./raw-exec.js";
export function execWithLandlock(
cmd: Array<string>,
opts: SpawnOptions,
userProvidedWritableRoots: ReadonlyArray<string>,
abortSignal?: AbortSignal,
): Promise<ExecResult> {
// TODO(mbolin): Find the arch-appropriate sandbox executable.
const sandboxExecutable = "bin/codex-linux-sandbox-arm64";
const extraSandboxPermissions = userProvidedWritableRoots.flatMap(
(root: string) => ["--sandbox-permission", `disk-write-folder=${root}`],
);
const fullCommand = [
sandboxExecutable,
"--full-auto",
"--sandbox-permission",
"disk-full-read-access",
"--sandbox-permission",
"disk-write-cwd",
"--sandbox-permission",
"disk-write-platform-user-temp-folder",
...extraSandboxPermissions,
"--",
...cmd,
];
return exec(fullCommand, opts, abortSignal);
}