Skip to content

Commit 9ec5718

Browse files
committed
feat(sandbox): Landlock TCP port restriction in Platform mode
When Platform mode is active, apply Landlock ABI v4 network rules to restrict TCP connect to only the proxy port (default 3128). This makes the loopback CONNECT proxy mandatory at the kernel level -- a process calling connect() to any other port gets EACCES before any packet is created. This closes the cooperative proxy gap: without this, processes ignoring HTTP_PROXY could bypass the proxy and connect directly. With this, enforcement is at the kernel LSM layer (Tier 1), same as Landlock filesystem restrictions. Graceful degradation: if the kernel does not support Landlock ABI v4 (RHEL 9.5 or earlier), the network rules are silently skipped and enforcement falls back to the cooperative proxy + NetworkPolicy. 1 file changed, +43/-3 lines. Compiles, tests pass, clippy clean. Ref: NVIDIA#899
1 parent 421a0a7 commit 9ec5718

1 file changed

Lines changed: 43 additions & 3 deletions

File tree

‎crates/openshell-sandbox/src/sandbox/linux/landlock.rs‎

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
//! Landlock filesystem sandboxing.
55
6-
use crate::policy::{LandlockCompatibility, SandboxPolicy};
6+
use crate::policy::{LandlockCompatibility, NetworkMode, SandboxPolicy};
77
use landlock::{
8-
ABI, Access, AccessFs, CompatLevel, Compatible, PathBeneath, PathFd, PathFdError, Ruleset,
9-
RulesetAttr, RulesetCreatedAttr,
8+
ABI, Access, AccessFs, AccessNet, CompatLevel, Compatible, NetPort, PathBeneath, PathFd,
9+
PathFdError, Ruleset, RulesetAttr, RulesetCreatedAttr,
1010
};
1111
use miette::{IntoDiagnostic, Result};
1212
use std::path::{Path, PathBuf};
@@ -184,6 +184,15 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result<Option<P
184184
.handle_access(access_all)
185185
.into_diagnostic()?;
186186

187+
// Platform mode: also handle TCP connect access so Landlock can
188+
// restrict which ports the agent connects to. Requires ABI v4.
189+
// The compat_level handles graceful degradation on older kernels.
190+
if matches!(policy.network.mode, NetworkMode::Platform) {
191+
ruleset = ruleset
192+
.handle_access(AccessNet::ConnectTcp)
193+
.into_diagnostic()?;
194+
}
195+
187196
let mut ruleset = ruleset.create().into_diagnostic()?;
188197
let mut rules_applied: usize = 0;
189198

@@ -207,6 +216,37 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result<Option<P
207216
}
208217
}
209218

219+
// Platform mode: restrict TCP connect to the proxy port only.
220+
// Uses Landlock ABI v4 (LANDLOCK_ACCESS_NET_CONNECT_TCP). If the
221+
// kernel doesn't support ABI v4, the rule is silently skipped
222+
// (best_effort) and enforcement falls back to NetworkPolicy.
223+
if matches!(policy.network.mode, NetworkMode::Platform) {
224+
let proxy_port = policy
225+
.network
226+
.proxy
227+
.as_ref()
228+
.and_then(|p| p.http_addr)
229+
.map_or(3128_u16, |addr| addr.port());
230+
231+
match ruleset.add_rule(NetPort::new(proxy_port, AccessNet::ConnectTcp)) {
232+
Ok(r) => {
233+
ruleset = r;
234+
debug!(
235+
port = proxy_port,
236+
"Landlock allow TCP connect (proxy port only)"
237+
);
238+
rules_applied += 1;
239+
}
240+
Err(e) => {
241+
debug!(
242+
error = %e,
243+
"Landlock TCP port restriction unavailable (ABI v4 required), \
244+
falling back to cooperative proxy enforcement"
245+
);
246+
}
247+
}
248+
}
249+
210250
if rules_applied == 0 {
211251
return Err(miette::miette!(
212252
"Landlock ruleset has zero valid paths — all {} path(s) failed to open. \

0 commit comments

Comments
 (0)