Skip to content

Commit 411bfeb

Browse files
authored
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.
1 parent 27bc451 commit 411bfeb

File tree

9 files changed

+105
-53
lines changed

9 files changed

+105
-53
lines changed

.github/dotslash-config.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@
2525
"linux-x86_64": { "regex": "^codex-cli-x86_64-unknown-linux-musl\\.zst$", "path": "codex-cli" },
2626
"linux-aarch64": { "regex": "^codex-cli-aarch64-unknown-linux-gnu\\.zst$", "path": "codex-cli" }
2727
}
28+
},
29+
30+
"codex-linux-sandbox": {
31+
"platforms": {
32+
"linux-x86_64": { "regex": "^codex-linux-sandbox-x86_64-unknown-linux-musl\\.zst$", "path": "codex-linux-sandbox" },
33+
"linux-aarch64": { "regex": "^codex-linux-sandbox-aarch64-unknown-linux-gnu\\.zst$", "path": "codex-linux-sandbox" }
34+
}
2835
}
2936
}
3037
}

.github/workflows/rust-release.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,15 @@ jobs:
106106
cp target/${{ matrix.target }}/release/codex-exec "$dest/codex-exec-${{ matrix.target }}"
107107
cp target/${{ matrix.target }}/release/codex-cli "$dest/codex-cli-${{ matrix.target }}"
108108
109+
- if: ${{ matrix.target == 'x86_64-unknown-linux-musl' }} || ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
110+
name: Stage Linux-only artifacts
111+
shell: bash
112+
run: |
113+
cp target/${{ matrix.target }}/release/codex-linux-sandbox "$dest/codex-linux-sandbox-${{ matrix.target }}"
114+
115+
- name: Compress artifacts
116+
shell: bash
117+
run: |
109118
zstd -T0 -19 --rm "$dest"/*
110119
111120
- uses: actions/upload-artifact@v4

codex-rs/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,7 @@ members = [
1515
version = "0.1.0"
1616

1717
[profile.release]
18-
lto = "fat"
18+
lto = "fat"
19+
# Because we bundle some of these executables with the TypeScript CLI, we
20+
# remove everything to make the binary as small as possible.
21+
strip = "symbols"

codex-rs/cli/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ edition = "2021"
77
name = "codex"
88
path = "src/main.rs"
99

10+
[[bin]]
11+
name = "codex-linux-sandbox"
12+
path = "src/linux-sandbox/main.rs"
13+
14+
[lib]
15+
name = "codex_cli"
16+
path = "src/lib.rs"
17+
1018
[dependencies]
1119
anyhow = "1"
1220
clap = { version = "4", features = ["derive"] }

codex-rs/cli/src/landlock.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ use std::process::ExitStatus;
1111

1212
/// Execute `command` in a Linux sandbox (Landlock + seccomp) the way Codex
1313
/// would.
14-
pub(crate) fn run_landlock(
15-
command: Vec<String>,
16-
sandbox_policy: SandboxPolicy,
17-
) -> anyhow::Result<()> {
14+
pub fn run_landlock(command: Vec<String>, sandbox_policy: SandboxPolicy) -> anyhow::Result<()> {
1815
if command.is_empty() {
1916
anyhow::bail!("command args are empty");
2017
}

codex-rs/cli/src/lib.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#[cfg(target_os = "linux")]
2+
pub mod landlock;
3+
pub mod proto;
4+
pub mod seatbelt;
5+
6+
use clap::Parser;
7+
use codex_core::protocol::SandboxPolicy;
8+
use codex_core::SandboxPermissionOption;
9+
10+
#[derive(Debug, Parser)]
11+
pub struct SeatbeltCommand {
12+
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
13+
#[arg(long = "full-auto", default_value_t = false)]
14+
pub full_auto: bool,
15+
16+
#[clap(flatten)]
17+
pub sandbox: SandboxPermissionOption,
18+
19+
/// Full command args to run under seatbelt.
20+
#[arg(trailing_var_arg = true)]
21+
pub command: Vec<String>,
22+
}
23+
24+
#[derive(Debug, Parser)]
25+
pub struct LandlockCommand {
26+
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
27+
#[arg(long = "full-auto", default_value_t = false)]
28+
pub full_auto: bool,
29+
30+
#[clap(flatten)]
31+
pub sandbox: SandboxPermissionOption,
32+
33+
/// Full command args to run under landlock.
34+
#[arg(trailing_var_arg = true)]
35+
pub command: Vec<String>,
36+
}
37+
38+
pub fn create_sandbox_policy(full_auto: bool, sandbox: SandboxPermissionOption) -> SandboxPolicy {
39+
if full_auto {
40+
SandboxPolicy::new_full_auto_policy()
41+
} else {
42+
match sandbox.permissions.map(Into::into) {
43+
Some(sandbox_policy) => sandbox_policy,
44+
None => SandboxPolicy::new_read_only_policy(),
45+
}
46+
}
47+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#[cfg(not(target_os = "linux"))]
2+
fn main() -> anyhow::Result<()> {
3+
eprintln!("codex-linux-sandbox is not supported on this platform.");
4+
std::process::exit(1);
5+
}
6+
7+
#[cfg(target_os = "linux")]
8+
fn main() -> anyhow::Result<()> {
9+
use clap::Parser;
10+
use codex_cli::create_sandbox_policy;
11+
use codex_cli::landlock;
12+
use codex_cli::LandlockCommand;
13+
14+
let LandlockCommand {
15+
full_auto,
16+
sandbox,
17+
command,
18+
} = LandlockCommand::parse();
19+
let sandbox_policy = create_sandbox_policy(full_auto, sandbox);
20+
landlock::run_landlock(command, sandbox_policy)?;
21+
Ok(())
22+
}

codex-rs/cli/src/main.rs

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
#[cfg(target_os = "linux")]
2-
mod landlock;
3-
mod proto;
4-
mod seatbelt;
5-
61
use clap::Parser;
7-
use codex_core::protocol::SandboxPolicy;
8-
use codex_core::SandboxPermissionOption;
2+
use codex_cli::create_sandbox_policy;
3+
use codex_cli::proto;
4+
use codex_cli::seatbelt;
5+
use codex_cli::LandlockCommand;
6+
use codex_cli::SeatbeltCommand;
97
use codex_exec::Cli as ExecCli;
108
use codex_repl::Cli as ReplCli;
119
use codex_tui::Cli as TuiCli;
@@ -63,34 +61,6 @@ enum DebugCommand {
6361
Landlock(LandlockCommand),
6462
}
6563

66-
#[derive(Debug, Parser)]
67-
struct SeatbeltCommand {
68-
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
69-
#[arg(long = "full-auto", default_value_t = false)]
70-
full_auto: bool,
71-
72-
#[clap(flatten)]
73-
pub sandbox: SandboxPermissionOption,
74-
75-
/// Full command args to run under seatbelt.
76-
#[arg(trailing_var_arg = true)]
77-
command: Vec<String>,
78-
}
79-
80-
#[derive(Debug, Parser)]
81-
struct LandlockCommand {
82-
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
83-
#[arg(long = "full-auto", default_value_t = false)]
84-
full_auto: bool,
85-
86-
#[clap(flatten)]
87-
sandbox: SandboxPermissionOption,
88-
89-
/// Full command args to run under landlock.
90-
#[arg(trailing_var_arg = true)]
91-
command: Vec<String>,
92-
}
93-
9464
#[derive(Debug, Parser)]
9565
struct ReplProto {}
9666

@@ -127,7 +97,7 @@ async fn main() -> anyhow::Result<()> {
12797
full_auto,
12898
}) => {
12999
let sandbox_policy = create_sandbox_policy(full_auto, sandbox);
130-
landlock::run_landlock(command, sandbox_policy)?;
100+
codex_cli::landlock::run_landlock(command, sandbox_policy)?;
131101
}
132102
#[cfg(not(target_os = "linux"))]
133103
DebugCommand::Landlock(_) => {
@@ -138,14 +108,3 @@ async fn main() -> anyhow::Result<()> {
138108

139109
Ok(())
140110
}
141-
142-
fn create_sandbox_policy(full_auto: bool, sandbox: SandboxPermissionOption) -> SandboxPolicy {
143-
if full_auto {
144-
SandboxPolicy::new_full_auto_policy()
145-
} else {
146-
match sandbox.permissions.map(Into::into) {
147-
Some(sandbox_policy) => sandbox_policy,
148-
None => SandboxPolicy::new_read_only_policy(),
149-
}
150-
}
151-
}

codex-rs/cli/src/seatbelt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use codex_core::exec::create_seatbelt_command;
22
use codex_core::protocol::SandboxPolicy;
33

4-
pub(crate) async fn run_seatbelt(
4+
pub async fn run_seatbelt(
55
command: Vec<String>,
66
sandbox_policy: SandboxPolicy,
77
) -> anyhow::Result<()> {

0 commit comments

Comments
 (0)