Skip to content

feat(jailer): add Landlock LSM sandboxing via --landlock flag#5771

Open
pavitrabhalla wants to merge 11 commits intofirecracker-microvm:mainfrom
superserve-ai:pavitrabhalla/issue-5513-impl
Open

feat(jailer): add Landlock LSM sandboxing via --landlock flag#5771
pavitrabhalla wants to merge 11 commits intofirecracker-microvm:mainfrom
superserve-ai:pavitrabhalla/issue-5513-impl

Conversation

@pavitrabhalla
Copy link
Copy Markdown

@pavitrabhalla pavitrabhalla commented Mar 18, 2026

Summary

This PR implements the feature requested in #5513 — adding opt-in Landlock LSM support to the Firecracker jailer as a defense-in-depth mechanism.

What it does

Adds a --landlock flag to the jailer. When passed:

  1. Before pivot_root: a Landlock ruleset is created and a PathFd is opened on the jail directory, capturing its inode
  2. Right before exec: restrict_self() is called, enforcing the ruleset on the jailed Firecracker process

The ruleset grants all filesystem access rights within the jail directory and denies everything outside. This means if Firecracker escapes the pivot_root chroot via a kernel exploit, Landlock independently prevents access to files outside the jail — enforced by a separate LSM path in the kernel.

The flag is opt-in — existing deployments are completely unaffected.

Why Landlock on top of chroot?

pivot_root + chroot lives in the kernel's VFS layer and can be bypassed by a guest-triggered kernel exploit. Landlock is enforced by a separate LSM subsystem and independently blocks filesystem access, so it provides defense-in-depth even if pivot_root is defeated. The inode-based rules (not path-based) also survive the pivot_root boundary.

Verification

Tested on kernel 6.8.0-1048-gcp (Landlock V1/V2/V3 supported). strace confirms the correct syscalls are made:

Without --landlock — no Landlock syscalls:

+++ exited with 0 +++

With --landlock — all 4 Landlock syscalls succeed:

landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION) = 4
landlock_create_ruleset({handled_access_fs=LANDLOCK_ACCESS_FS_EXECUTE|...|LANDLOCK_ACCESS_FS_MAKE_SYM, ...}, 24, 0) = 4
landlock_add_rule(4, LANDLOCK_RULE_PATH_BENEATH, {allowed_access=..., parent_fd=3}, 0) = 0
landlock_restrict_self(4, 0) = 0
+++ exited with 0 +++

parent_fd=3 is the PathFd opened before pivot_root — confirming the inode reference survives the chroot boundary.

Changes

  • src/jailer/src/landlock.rs (new): prepare_ruleset() and enforce()
  • src/jailer/src/env.rs: wires Landlock into run() — before chroot and before exec
  • src/jailer/src/main.rs: --landlock CLI flag + JailerError::Landlock variant
  • src/jailer/Cargo.toml: landlock = "0.4" dependency
  • tests/framework/jailer.py: landlock parameter in JailerContext
  • CHANGELOG.md: entry under [Unreleased] → Added

License Acceptance

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

PR Checklist

  • I have read and understand CONTRIBUTING.md.
  • I have run tools/devtool checkbuild --all to verify that the PR passes build checks on all supported architectures.
  • I have run tools/devtool checkstyle to verify that the PR passes the automated style checks.
  • I have described what is done in these changes, why they are needed, and how they are solving the problem in a clear and encompassing way.
  • I have updated any relevant documentation (both in code and in the docs) in the PR.
  • I have mentioned all user-facing changes in CHANGELOG.md.
  • If a specific issue led to this PR, this PR closes the issue.
  • When making API changes, I have followed the Runbook for Firecracker API changes.
  • I have tested all new and changed functionalities in unit tests and/or integration tests.
  • I have linked an issue to every new TODO.

  • This functionality cannot be added in rust-vmm.

pavitrabhalla and others added 8 commits March 18, 2026 17:06
Add opt-in `--landlock` flag to the jailer that uses the Linux Landlock
LSM (kernel >= 5.13) as a defense-in-depth mechanism. When enabled, a
Landlock ruleset granting full filesystem access within the jail
directory is prepared before pivot_root (capturing the inode) and
enforced right before exec, so restrictions are inherited by the jailed
Firecracker process.

If Firecracker escapes the pivot_root chroot via a kernel exploit,
Landlock independently prevents access to files outside the jail
directory. The flag fails loudly if the kernel does not support
Landlock, since the user explicitly opted in.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Pavitra Bhalla <pavitra@superserve.ai>
Break up method chain closures that exceeded the 100-character line
limit enforced by rustfmt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Pavitra Bhalla <pavitra@superserve.ai>
Document the new --landlock CLI flag under the [Unreleased] Added
section of CHANGELOG.md.

Signed-off-by: Pavitra Bhalla <pavitra@superserve.ai>
The ArgVals struct initializer in env.rs tests did not include the
landlock field added by the parent commit, causing a compilation error.

Signed-off-by: Pavitra Bhalla <pavitra@superserve.ai>
Normalize markdown list formatting in CHANGELOG.md.

Signed-off-by: Pavitra Bhalla <pavitra@superserve.ai>
Clippy's assertions_on_result_states lint forbids assert!(x.is_ok())
and assert!(x.is_err()); use .unwrap() and .unwrap_err() instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Pavitra Bhalla <pavitra@superserve.ai>
Signed-off-by: Pavitra Bhalla <pavitra@superserve.ai>
Signed-off-by: Pavitra Bhalla <pavitra@superserve.ai>
/// Returns [`JailerError::Landlock`] if the kernel does not support Landlock (kernel < 5.13),
/// if `jail_dir` cannot be opened, or if any ruleset syscall fails.
pub fn prepare_ruleset(jail_dir: &Path) -> Result<RulesetCreated, JailerError> {
let abi = ABI::V1;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't pin to ABI::V1 but to the latest that you actually tested on a machine. Restricting only to ABI::V1 will limit the features to only the initial ones, whereas if you take the greatest version you'll get all the ones supported by the running system (i.e. best-effort). See the documentation. BTW, this version should regularly be incremented once the new version is tested on a kernel supporting it.

let mut parts = release.split('.');
let major: i32 = parts.next().and_then(|s| s.parse().ok()).unwrap_or(0);
let minor: i32 = parts.next().and_then(|s| s.parse().ok()).unwrap_or(0);
major > 5 || (major == 5 && minor >= 13)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't check kernel version. Landlock may be available/backported to older kernels. Landlock provides a dedicated interface to check which ABI version is supported by the running kernel. You should use LandlockStatus instead, but keep in mind that there are only a few valid use case to check this support (testing makes sense here) because the normal use of the Rust crate automatically follows a best-effort approach.

.takes_value(false)
.help("Print the binary version number."),
)
.arg(Argument::new("landlock").takes_value(false).help(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option should probably reflect the goal, not (only) the mechanism. Landlock is gaining new feature over time and the current option doesn't reflect the related description.

pavitrabhalla and others added 2 commits March 18, 2026 16:02
…tation

- Use ABI::V4 instead of V1 for best-effort support on newer kernels
- Add official Landlock docs link to module comment
- Replace uname() kernel version check with CompatLevel::HardRequirement
- Rename --landlock flag to --landlock-restrict-fs to reflect the goal

Signed-off-by: Pavitra Bhalla <pavitra@superserve.ai>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Pavitra Bhalla <pavitra@superserve.ai>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@pavitrabhalla
Copy link
Copy Markdown
Author

@l0kod Thanks for the feedback. I updated everything as per your comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants