Skip to content

feat(fast_io,daemon): Landlock LSM defense-in-depth for daemon receiver (SEC-1.p)#4702

Open
oferchen wants to merge 1 commit into
masterfrom
feat/fast-io-landlock-sec-1-p
Open

feat(fast_io,daemon): Landlock LSM defense-in-depth for daemon receiver (SEC-1.p)#4702
oferchen wants to merge 1 commit into
masterfrom
feat/fast-io-landlock-sec-1-p

Conversation

@oferchen
Copy link
Copy Markdown
Owner

Summary

Wires Linux Landlock LSM into the daemon receiver path as defense-in-depth above the SEC-1 *at syscall chain. Implements the audit recommendation from PR #4699. After apply_module_privilege_restrictions returns, the receiver thread engages a kernel-enforced allowlist over the module root: every filesystem syscall the thread (and any process inherited from it) issues outside that root is rejected by the kernel with EACCES regardless of which API the caller used. The sandbox is inherited by the name converter and pre/post-xfer-exec hook processes, closing the missed-call-site / future-regression gap that pure call-site routing cannot guarantee on its own.

  • crates/fast_io/Cargo.toml adds the optional landlock = "0.4" dep behind a new landlock Cargo feature, Linux-only, mirroring the iouring-send-zc opt-in pattern at lines 110-117 of the same file.
  • crates/fast_io/src/landlock.rs builds the ruleset with Ruleset::default() + set_compatibility(CompatLevel::BestEffort) and requests AccessFs::from_all(ABI::V3) unconditionally so the crate silently drops REFER (v1) and TRUNCATE (v1/v2) on older kernels. Returns LandlockOutcome::{Enforced(RulesetStatus), Unavailable, Error} so the daemon can log the actual ABI tier the kernel honoured.
  • crates/fast_io/src/landlock_stub.rs ships the same public surface for non-Linux targets and feature-disabled builds; every call short-circuits to Unavailable so the wire-in stays target-agnostic.
  • crates/daemon/Cargo.toml depends on fast_io (stub on non-Linux, real Landlock dep on Linux) so the call site in transfer.rs compiles unconditionally.
  • crates/daemon/src/daemon/sections/module_access/transfer.rs adds two helpers:
    • validate_client_paths_in_module rejects client-supplied --temp-dir / --partial-dir / --backup-dir paths that resolve outside the module root. Per the audit's recommendation in section 10, this is REJECT over widening the allowlist; widening the writable surface to honour an attacker-supplied prefix undermines the whole point of the sandbox and rsync's own chroot mode behaves the same way.
    • engage_landlock_sandbox engages the kernel allowlist immediately after apply_module_privilege_restrictions returns. Probe failure or setup error is logged at WARN but does not abort the connection; the SEC-1 *at chain remains the primary defense even when Landlock is unavailable.
  • SECURITY.md adds a SEC-1.p entry documenting the kernel-version matrix and the reject-vs-widen decision.

Kernel-version downgrade strategy implemented

  • v3 on 6.2+: READ / WRITE / CREATE / DELETE / RENAME / SYMLINK / REFER / TRUNCATE.
  • v2 on 5.19+: drops TRUNCATE.
  • v1 on 5.13+: drops TRUNCATE and REFER.
  • < 5.13: LandlockOutcome::Unavailable; SEC-1 *at helpers remain the sole defense. Logged once per connection at INFO.

set_compatibility(CompatLevel::BestEffort) lets the crate strip rights the kernel does not understand, so the same call site works on every supported kernel without runtime branching.

Audit recommendation alignment

Coordination

Lock-file requirement

Cargo.lock requires cargo update -p landlock (or cargo generate-lockfile) to register the new optional crate and its transitive deps before --locked CI jobs will pass. The lock update was deliberately not run from this branch per the consolidate-cargo standing rule on agents; it should be added in a follow-up commit before merge.

Test plan

  • CI fmt+clippy passes on the lock-updated branch.
  • CI nextest (stable) passes on Linux with the daemon's forwarded landlock feature.
  • CI Windows + macOS + Linux musl all build through the stub.
  • Unit tests in crates/fast_io/src/landlock.rs::tests cover the four scenarios called out by the audit: is_supported agreement with ABI::query, in-module write succeeds, out-of-module write fails with PermissionDenied, empty allowlist denies all writes, and a second restrict call does not relax the sandbox.
  • Manual daemon smoke: start oc-rsyncd on a kernel >= 6.2, push from a client with --temp-dir=/tmp, observe the daemon log line rejected --temp-dir='/tmp' ... outside module root and the wire-protocol @ERROR reply.

…er (SEC-1.p)

Layers a kernel-enforced allowlist above the SEC-1 *at* syscall chain in the
daemon receiver path. Once `restrict_to_module_paths` succeeds, every
filesystem syscall the connection thread issues outside the module root is
rejected by the kernel with EACCES, regardless of which API the caller used.
The sandbox is inherited by the name converter and pre/post-xfer-exec hook
processes, closing the "missed call site / future regression" gap that pure
call-site routing cannot guarantee on its own.

- `crates/fast_io/Cargo.toml`: optional `landlock = "0.4"` dep behind a new
  `landlock` Cargo feature (Linux-only, mirrors the `iouring-send-zc` opt-in
  pattern).
- `crates/fast_io/src/landlock.rs`: real implementation using
  `Ruleset::default()` + `set_compatibility(BestEffort)` so the helper
  requests `AccessFs::from_all(ABI::V3)` unconditionally and the crate
  silently drops REFER (v1) and TRUNCATE (v1/v2) on older kernels. The
  returned `LandlockOutcome` carries the `RulesetStatus` so callers can log
  the actual enforcement level.
- `crates/fast_io/src/landlock_stub.rs`: identical public surface for non-
  Linux targets and feature-disabled builds; every call short-circuits to
  `Unavailable` so cross-platform callers compile without `#[cfg]` branching.
- `crates/daemon/Cargo.toml`: depend on `fast_io` (stub on non-Linux, real
  Landlock dep on Linux) so the wire-in stays target-agnostic.
- `crates/daemon/src/daemon/sections/module_access/transfer.rs`:
  - `validate_client_paths_in_module` rejects client-supplied `--temp-dir`
    / `--partial-dir` / `--backup-dir` paths that resolve outside the
    module root (audit-recommended REJECT over allowlist widening).
  - `engage_landlock_sandbox` engages the kernel allowlist immediately
    after `apply_module_privilege_restrictions` returns. Probe failure or
    setup error is logged but does not abort the connection; the SEC-1
    *at* chain remains the primary defense.
- `SECURITY.md`: SEC-1.p entry documenting kernel-version matrix and the
  reject-vs-widen decision.

NOTE: `Cargo.lock` requires `cargo update -p landlock` to register the new
optional crate before `--locked` CI jobs will pass. Local cargo is
deliberately not run from this agent per the consolidate-cargo standing
rule; the lock update lands in a follow-up commit before merge.
@github-actions github-actions Bot added the enhancement New feature or request label May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant