diff --git a/crates/openshell-driver-podman/src/container.rs b/crates/openshell-driver-podman/src/container.rs index 4378d0a6bb..4f502da8bc 100644 --- a/crates/openshell-driver-podman/src/container.rs +++ b/crates/openshell-driver-podman/src/container.rs @@ -1494,6 +1494,13 @@ pub fn build_isolation_specs( openshell_core::sandbox_env::ADMITTED_ISOLATION_BACKEND.into(), openshell_sandbox_backend::BACKEND_NAME.into(), ); + // The supervisor runs as the resolved non-root workload identity. Keep + // generated interception CA material under its writable /run tmpfs rather + // than the root-owned default at /etc/openshell-tls. + supervisor.env.insert( + openshell_core::sandbox_env::PROXY_TLS_DIR.into(), + "/run/openshell/proxy-tls".into(), + ); supervisor.user = user; supervisor.groups = input .identity @@ -1801,6 +1808,14 @@ mod tests { .contains(&"nocopy".into()) ); assert_eq!(specs.supervisor.entrypoint, vec!["/openshell-supervisor"]); + assert_eq!( + specs + .supervisor + .env + .get(openshell_core::sandbox_env::PROXY_TLS_DIR) + .map(String::as_str), + Some("/run/openshell/proxy-tls") + ); } fn json_struct(value: Value) -> prost_types::Struct { diff --git a/crates/openshell-sandbox/src/network_broker.rs b/crates/openshell-sandbox/src/network_broker.rs index 6b01cc0967..d4972465c6 100644 --- a/crates/openshell-sandbox/src/network_broker.rs +++ b/crates/openshell-sandbox/src/network_broker.rs @@ -41,6 +41,10 @@ const DNS_RELAY_ADDRESS: SocketAddr = SocketAddr::V4(std::net::SocketAddrV4::new const RELAY_CONNECT_TIMEOUT: Duration = Duration::from_secs(5); const NETWORK_DECISION_TIMEOUT: Duration = Duration::from_secs(30); +fn retry_notification_receive(error: &io::Error) -> bool { + error.kind() == io::ErrorKind::Interrupted || error.raw_os_error() == Some(libc::ENOENT) +} + #[derive(Debug)] struct PendingOpenSlot(Arc); @@ -271,7 +275,12 @@ impl NetworkBroker { while broker_healthy.load(Ordering::Acquire) { let notification = match listener.receive() { Ok(notification) => notification, - Err(error) if error.kind() == io::ErrorKind::Interrupted => continue, + // ENOENT is a documented seccomp user-notification + // race: the target thread exited or its blocked + // syscall was interrupted while the kernel was + // preparing the notification. It does not mean the + // listener itself is unhealthy. + Err(error) if retry_notification_receive(&error) => continue, Err(error) => { tracing::error!(%error, "sandbox network broker listener failed"); broker_healthy.store(false, Ordering::Release); @@ -1754,6 +1763,19 @@ mod tests { use std::io::{Read as _, Write as _}; use std::os::unix::net::{UnixListener, UnixStream}; + #[test] + fn notification_receive_retries_interrupted_and_disappeared_targets() { + assert!(retry_notification_receive(&io::Error::from( + io::ErrorKind::Interrupted + ))); + assert!(retry_notification_receive(&io::Error::from_raw_os_error( + libc::ENOENT + ))); + assert!(!retry_notification_receive(&io::Error::from_raw_os_error( + libc::EBADF + ))); + } + #[test] fn relay_rejects_descriptor_replaced_after_policy_decision() { let metadata = SocketMetadata {