Skip to content

Commit 240032e

Browse files
committed
feat(sandbox): Landlock TCP port restriction in Platform mode
Add Landlock ABI v4 TCP connect restriction for Platform mode. When the kernel supports ABI v4, only the proxy port (default 3128) is allowed for outbound TCP connections. On older kernels, BestEffort compat level silently degrades -- the rule has no effect but the proxy still works cooperatively. Both handle_access(ConnectTcp) and add_rule(NetPort) use the ? operator since BestEffort guarantees they succeed on all kernel versions. Signed-off-by: Ladislav Smola <lsmola@redhat.com>
1 parent b44b196 commit 240032e

1 file changed

Lines changed: 35 additions & 4 deletions

File tree

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

Lines changed: 35 additions & 4 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: declare intent to handle TCP connect access.
188+
// BestEffort compat level: silently succeeds on kernels without
189+
// ABI v4 (restriction has no effect but doesn't break anything).
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,28 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result<Option<P
207216
}
208217
}
209218

219+
if matches!(policy.network.mode, NetworkMode::Platform) {
220+
let proxy_port = policy
221+
.network
222+
.proxy
223+
.as_ref()
224+
.and_then(|p| p.http_addr)
225+
.map_or(3128_u16, |addr| addr.port());
226+
227+
// BestEffort compat level: add_rule silently succeeds even if
228+
// ABI v4 is unavailable. The rule has no effect on older kernels,
229+
// but the proxy still works cooperatively.
230+
ruleset = ruleset
231+
.add_rule(NetPort::new(proxy_port, AccessNet::ConnectTcp))
232+
.into_diagnostic()?;
233+
debug!(
234+
port = proxy_port,
235+
"Landlock TCP connect rule added (proxy port only, \
236+
effective on kernels with ABI v4+)"
237+
);
238+
rules_applied += 1;
239+
}
240+
210241
if rules_applied == 0 {
211242
return Err(miette::miette!(
212243
"Landlock ruleset has zero valid paths — all {} path(s) failed to open. \
@@ -215,7 +246,7 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result<Option<P
215246
));
216247
}
217248

218-
let skipped = total_paths - rules_applied;
249+
let skipped = total_paths.saturating_sub(rules_applied);
219250
openshell_ocsf::ocsf_emit!(
220251
openshell_ocsf::ConfigStateChangeBuilder::new(crate::ocsf_ctx())
221252
.severity(openshell_ocsf::SeverityId::Informational)

0 commit comments

Comments
 (0)