feat: add debug landlock subcommand comparable to debug seatbelt (#715)

This PR adds a `debug landlock` subcommand to the Codex CLI for testing
how Codex would execute a command using the specified sandbox policy.

Built and ran this code in the `rust:latest` Docker container. In the
container, hitting the network with vanilla `curl` succeeds:

```
$ curl google.com
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
```

whereas this fails, as expected:

```
$ cargo run -- debug landlock -s network-restricted -- curl google.com
curl: (6) getaddrinfo() thread failed to start
```
This commit is contained in:
Michael Bolin
2025-04-28 16:37:05 -07:00
committed by GitHub
parent e7ad9449ea
commit e79549f039
4 changed files with 97 additions and 4 deletions

View File

@@ -1,3 +1,5 @@
#[cfg(target_os = "linux")]
mod landlock;
mod proto;
mod seatbelt;
@@ -58,11 +60,14 @@ struct DebugArgs {
enum DebugCommand {
/// Run a command under Seatbelt (macOS only).
Seatbelt(SeatbeltCommand),
/// Run a command under Landlock+seccomp (Linux only).
Landlock(LandlockCommand),
}
#[derive(Debug, Parser)]
struct SeatbeltCommand {
/// Writable folder for sandbox in full-auto mode (can be specified multiple times).
/// Writable folder for sandbox (can be specified multiple times).
#[arg(long = "writable-root", short = 'w', value_name = "DIR", action = ArgAction::Append, use_value_delimiter = false)]
writable_roots: Vec<PathBuf>,
@@ -75,6 +80,21 @@ struct SeatbeltCommand {
command: Vec<String>,
}
#[derive(Debug, Parser)]
struct LandlockCommand {
/// Writable folder for sandbox (can be specified multiple times).
#[arg(long = "writable-root", short = 'w', value_name = "DIR", action = ArgAction::Append, use_value_delimiter = false)]
writable_roots: Vec<PathBuf>,
/// Configure the process restrictions for the command.
#[arg(long = "sandbox", short = 's')]
sandbox_policy: SandboxModeCliArg,
/// Full command args to run under landlock.
#[arg(trailing_var_arg = true)]
command: Vec<String>,
}
#[derive(Debug, Parser)]
struct ReplProto {}
@@ -103,6 +123,18 @@ async fn main() -> anyhow::Result<()> {
}) => {
seatbelt::run_seatbelt(command, sandbox_policy.into(), writable_roots).await?;
}
#[cfg(target_os = "linux")]
DebugCommand::Landlock(LandlockCommand {
command,
sandbox_policy,
writable_roots,
}) => {
landlock::run_landlock(command, sandbox_policy.into(), writable_roots)?;
}
#[cfg(not(target_os = "linux"))]
DebugCommand::Landlock(_) => {
anyhow::bail!("Landlock is only supported on Linux.");
}
},
}