feat: codex-linux-sandbox standalone executable (#740)
This introduces a standalone executable that run the equivalent of the `codex debug landlock` subcommand and updates `rust-release.yml` to include it in the release. The idea is that we will include this small binary with the TypeScript CLI to provide support for Linux sandboxing.
This commit is contained in:
7
.github/dotslash-config.json
vendored
7
.github/dotslash-config.json
vendored
@@ -25,6 +25,13 @@
|
|||||||
"linux-x86_64": { "regex": "^codex-cli-x86_64-unknown-linux-musl\\.zst$", "path": "codex-cli" },
|
"linux-x86_64": { "regex": "^codex-cli-x86_64-unknown-linux-musl\\.zst$", "path": "codex-cli" },
|
||||||
"linux-aarch64": { "regex": "^codex-cli-aarch64-unknown-linux-gnu\\.zst$", "path": "codex-cli" }
|
"linux-aarch64": { "regex": "^codex-cli-aarch64-unknown-linux-gnu\\.zst$", "path": "codex-cli" }
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"codex-linux-sandbox": {
|
||||||
|
"platforms": {
|
||||||
|
"linux-x86_64": { "regex": "^codex-linux-sandbox-x86_64-unknown-linux-musl\\.zst$", "path": "codex-linux-sandbox" },
|
||||||
|
"linux-aarch64": { "regex": "^codex-linux-sandbox-aarch64-unknown-linux-gnu\\.zst$", "path": "codex-linux-sandbox" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
.github/workflows/rust-release.yml
vendored
9
.github/workflows/rust-release.yml
vendored
@@ -106,6 +106,15 @@ jobs:
|
|||||||
cp target/${{ matrix.target }}/release/codex-exec "$dest/codex-exec-${{ matrix.target }}"
|
cp target/${{ matrix.target }}/release/codex-exec "$dest/codex-exec-${{ matrix.target }}"
|
||||||
cp target/${{ matrix.target }}/release/codex-cli "$dest/codex-cli-${{ matrix.target }}"
|
cp target/${{ matrix.target }}/release/codex-cli "$dest/codex-cli-${{ matrix.target }}"
|
||||||
|
|
||||||
|
- if: ${{ matrix.target == 'x86_64-unknown-linux-musl' }} || ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
|
||||||
|
name: Stage Linux-only artifacts
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cp target/${{ matrix.target }}/release/codex-linux-sandbox "$dest/codex-linux-sandbox-${{ matrix.target }}"
|
||||||
|
|
||||||
|
- name: Compress artifacts
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
zstd -T0 -19 --rm "$dest"/*
|
zstd -T0 -19 --rm "$dest"/*
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
|
|||||||
@@ -15,4 +15,7 @@ members = [
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
|
# Because we bundle some of these executables with the TypeScript CLI, we
|
||||||
|
# remove everything to make the binary as small as possible.
|
||||||
|
strip = "symbols"
|
||||||
|
|||||||
@@ -7,6 +7,14 @@ edition = "2021"
|
|||||||
name = "codex"
|
name = "codex"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "codex-linux-sandbox"
|
||||||
|
path = "src/linux-sandbox/main.rs"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "codex_cli"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
|
|||||||
@@ -11,10 +11,7 @@ use std::process::ExitStatus;
|
|||||||
|
|
||||||
/// Execute `command` in a Linux sandbox (Landlock + seccomp) the way Codex
|
/// Execute `command` in a Linux sandbox (Landlock + seccomp) the way Codex
|
||||||
/// would.
|
/// would.
|
||||||
pub(crate) fn run_landlock(
|
pub fn run_landlock(command: Vec<String>, sandbox_policy: SandboxPolicy) -> anyhow::Result<()> {
|
||||||
command: Vec<String>,
|
|
||||||
sandbox_policy: SandboxPolicy,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
if command.is_empty() {
|
if command.is_empty() {
|
||||||
anyhow::bail!("command args are empty");
|
anyhow::bail!("command args are empty");
|
||||||
}
|
}
|
||||||
|
|||||||
47
codex-rs/cli/src/lib.rs
Normal file
47
codex-rs/cli/src/lib.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub mod landlock;
|
||||||
|
pub mod proto;
|
||||||
|
pub mod seatbelt;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use codex_core::protocol::SandboxPolicy;
|
||||||
|
use codex_core::SandboxPermissionOption;
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct SeatbeltCommand {
|
||||||
|
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
|
||||||
|
#[arg(long = "full-auto", default_value_t = false)]
|
||||||
|
pub full_auto: bool,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
pub sandbox: SandboxPermissionOption,
|
||||||
|
|
||||||
|
/// Full command args to run under seatbelt.
|
||||||
|
#[arg(trailing_var_arg = true)]
|
||||||
|
pub command: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser)]
|
||||||
|
pub struct LandlockCommand {
|
||||||
|
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
|
||||||
|
#[arg(long = "full-auto", default_value_t = false)]
|
||||||
|
pub full_auto: bool,
|
||||||
|
|
||||||
|
#[clap(flatten)]
|
||||||
|
pub sandbox: SandboxPermissionOption,
|
||||||
|
|
||||||
|
/// Full command args to run under landlock.
|
||||||
|
#[arg(trailing_var_arg = true)]
|
||||||
|
pub command: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_sandbox_policy(full_auto: bool, sandbox: SandboxPermissionOption) -> SandboxPolicy {
|
||||||
|
if full_auto {
|
||||||
|
SandboxPolicy::new_full_auto_policy()
|
||||||
|
} else {
|
||||||
|
match sandbox.permissions.map(Into::into) {
|
||||||
|
Some(sandbox_policy) => sandbox_policy,
|
||||||
|
None => SandboxPolicy::new_read_only_policy(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
codex-rs/cli/src/linux-sandbox/main.rs
Normal file
22
codex-rs/cli/src/linux-sandbox/main.rs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
eprintln!("codex-linux-sandbox is not supported on this platform.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
use clap::Parser;
|
||||||
|
use codex_cli::create_sandbox_policy;
|
||||||
|
use codex_cli::landlock;
|
||||||
|
use codex_cli::LandlockCommand;
|
||||||
|
|
||||||
|
let LandlockCommand {
|
||||||
|
full_auto,
|
||||||
|
sandbox,
|
||||||
|
command,
|
||||||
|
} = LandlockCommand::parse();
|
||||||
|
let sandbox_policy = create_sandbox_policy(full_auto, sandbox);
|
||||||
|
landlock::run_landlock(command, sandbox_policy)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
#[cfg(target_os = "linux")]
|
|
||||||
mod landlock;
|
|
||||||
mod proto;
|
|
||||||
mod seatbelt;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use codex_core::protocol::SandboxPolicy;
|
use codex_cli::create_sandbox_policy;
|
||||||
use codex_core::SandboxPermissionOption;
|
use codex_cli::proto;
|
||||||
|
use codex_cli::seatbelt;
|
||||||
|
use codex_cli::LandlockCommand;
|
||||||
|
use codex_cli::SeatbeltCommand;
|
||||||
use codex_exec::Cli as ExecCli;
|
use codex_exec::Cli as ExecCli;
|
||||||
use codex_repl::Cli as ReplCli;
|
use codex_repl::Cli as ReplCli;
|
||||||
use codex_tui::Cli as TuiCli;
|
use codex_tui::Cli as TuiCli;
|
||||||
@@ -63,34 +61,6 @@ enum DebugCommand {
|
|||||||
Landlock(LandlockCommand),
|
Landlock(LandlockCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
|
||||||
struct SeatbeltCommand {
|
|
||||||
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
|
|
||||||
#[arg(long = "full-auto", default_value_t = false)]
|
|
||||||
full_auto: bool,
|
|
||||||
|
|
||||||
#[clap(flatten)]
|
|
||||||
pub sandbox: SandboxPermissionOption,
|
|
||||||
|
|
||||||
/// Full command args to run under seatbelt.
|
|
||||||
#[arg(trailing_var_arg = true)]
|
|
||||||
command: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
|
||||||
struct LandlockCommand {
|
|
||||||
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
|
|
||||||
#[arg(long = "full-auto", default_value_t = false)]
|
|
||||||
full_auto: bool,
|
|
||||||
|
|
||||||
#[clap(flatten)]
|
|
||||||
sandbox: SandboxPermissionOption,
|
|
||||||
|
|
||||||
/// Full command args to run under landlock.
|
|
||||||
#[arg(trailing_var_arg = true)]
|
|
||||||
command: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
struct ReplProto {}
|
struct ReplProto {}
|
||||||
|
|
||||||
@@ -127,7 +97,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
full_auto,
|
full_auto,
|
||||||
}) => {
|
}) => {
|
||||||
let sandbox_policy = create_sandbox_policy(full_auto, sandbox);
|
let sandbox_policy = create_sandbox_policy(full_auto, sandbox);
|
||||||
landlock::run_landlock(command, sandbox_policy)?;
|
codex_cli::landlock::run_landlock(command, sandbox_policy)?;
|
||||||
}
|
}
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
DebugCommand::Landlock(_) => {
|
DebugCommand::Landlock(_) => {
|
||||||
@@ -138,14 +108,3 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_sandbox_policy(full_auto: bool, sandbox: SandboxPermissionOption) -> SandboxPolicy {
|
|
||||||
if full_auto {
|
|
||||||
SandboxPolicy::new_full_auto_policy()
|
|
||||||
} else {
|
|
||||||
match sandbox.permissions.map(Into::into) {
|
|
||||||
Some(sandbox_policy) => sandbox_policy,
|
|
||||||
None => SandboxPolicy::new_read_only_policy(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use codex_core::exec::create_seatbelt_command;
|
use codex_core::exec::create_seatbelt_command;
|
||||||
use codex_core::protocol::SandboxPolicy;
|
use codex_core::protocol::SandboxPolicy;
|
||||||
|
|
||||||
pub(crate) async fn run_seatbelt(
|
pub async fn run_seatbelt(
|
||||||
command: Vec<String>,
|
command: Vec<String>,
|
||||||
sandbox_policy: SandboxPolicy,
|
sandbox_policy: SandboxPolicy,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
|||||||
Reference in New Issue
Block a user