Files
codex/codex-rs/uds/src/daemon_directory.rs
T
Eric Traut 49305d74b4 Isolate app-server Unix sockets from filesystem-restricted commands (#45984)
## Why

Network access and Unix-socket allowlists must not let commands with filesystem restrictions reach the privileged app-server RPC transport.

## What changed

- Bind Unix control sockets in a fixed, user-owned directory with mode `0700`, independent of environment settings, and expose the advertised paths as symlinks. Preserve existing parent permissions, reject unsafe parents, and serialize socket setup and publication.
- Mask the socket directory in Linux bubblewrap sandboxes after each bind that exposes it. Reject host mount aliases and nested mounts that compromise isolation.
- Deny access to the directory and outbound connections to its sockets in macOS Seatbelt policies, including when network access or Unix-socket allowlists grant broader access.
- Require bubblewrap for filesystem-restricted Linux execution. Users with `features.use_legacy_landlock` enabled must disable it for these policies.

## Testing

Add regression coverage for direct and symlink socket access, hardlink attempts, Linux host-process links and bind-mount aliases, and continued use of unrelated and sandbox-local sockets. Add transport coverage for parent permissions, concurrent restart after a stale symlink, and cleanup that preserves a replacement at the advertised path.

GitOrigin-RevId: 53372c27eea278d964f2cf68aed24323ef3b7082
2026-09-16 16:56:41 +00:00

40 lines
1.5 KiB
Rust

//! The host-local rendezvous root for privileged app-server RPC sockets.
//!
//! Every listener uses this root, which sandboxes hide even before a daemon
//! starts. It must not depend on HOME, TMPDIR, CODEX_HOME, or command settings.
use std::fs;
use std::io;
use std::os::unix::fs::DirBuilderExt;
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
/// Returns the fixed executor-local directory that every sandbox must hide.
pub fn shared_daemon_socket_directory() -> io::Result<PathBuf> {
// Resolve the system alias /tmp -> /private/tmp on macOS.
let temporary_root = fs::canonicalize("/tmp")?;
let uid = unsafe { libc::geteuid() };
Ok(temporary_root.join(format!("codex-daemon-{uid}")))
}
/// Creates the reserved directory, rejecting symlinks and unsafe existing owners or modes.
pub fn prepare_shared_daemon_socket_directory() -> io::Result<PathBuf> {
let directory = shared_daemon_socket_directory()?;
match fs::DirBuilder::new().mode(0o700).create(&directory) {
Ok(()) => {}
Err(error) if error.kind() == io::ErrorKind::AlreadyExists => {}
Err(error) => return Err(error),
}
let metadata = fs::symlink_metadata(&directory)?;
if !metadata.is_dir()
|| metadata.uid() != unsafe { libc::geteuid() }
|| metadata.mode() & 0o777 != 0o700
{
return Err(io::Error::new(
io::ErrorKind::PermissionDenied,
"app-server socket directory must be a user-owned directory with mode 0700",
));
}
Ok(directory)
}