Skip to content

bug(policy): invalid landlock.compatibility values silently fall back to best_effort #2356

Description

@cdiddy77

Agent Diagnostic

  • Skills loaded: create-github-issue
  • OpenShell version tested: source-level review of main at 8cf2673c (local CLI on PATH is 0.0.72, but the finding is read from current main, not from a running sandbox)
  • Latest release checked: v0.0.86 (gh release list) — the code path below is unchanged on main as of this commit
  • Known fixes reviewed: searched main for every hard_requirement reference (16 hits); the only string comparison is the one below, and no validation exists in openshell-policy or openshell-server/src/grpc/validation.rs
  • Possible duplicates reviewed: searched open/closed issues for landlock and for compatibility validation policy. Closest is supervisor: Landlock hard_requirement aborts with "incompatible directory-only access-rights: ReadDir" for real directories on RHEL 9.6 (kernel 5.14) ABI #2218 (Landlock hard_requirement aborting on RHEL 9.6 kernel 5.14 ABI), which is about ABI compatibility, not value parsing. No duplicate found.
  • Findings: LandlockDef.compatibility is deserialized as a bare String with no value validation. The proto→runtime conversion is a single equality check against "hard_requirement", with everything else falling through to BestEffort.
  • Remaining reason for filing: the failure direction is always toward less enforcement. A typo in a security-relevant field produces a sandbox that reads as strictly configured but runs permissively, with no error, warning, or log line.

Description

Actual behavior: Any value of landlock.compatibility other than the exact string hard_requirement is silently treated as best_effort. A policy containing compatibility: hard-requirement (hyphen), compatibility: strict, or compatibility: HARD_REQUIREMENT is accepted without complaint and runs in best_effort mode — meaning Landlock failures degrade to no filesystem restriction instead of aborting startup.

#[serde(deny_unknown_fields)] catches unknown keys, so landlock: { compatibilty: ... } is rejected. It does nothing for unknown values.

Expected behavior: An unrecognized compatibility value is rejected at policy parse/validation time with an error naming the field and the accepted values. Fail closed on a security control, or at minimum emit a warning — never silently downgrade enforcement.

Reproduction Steps

  1. Write a sandbox policy containing:
    filesystem_policy:
    read_only: [/usr, /lib]read_write: [/sandbox, /tmp]landlock:
    compatibility: hard-requirement # hyphen instead of underscore
  2. Create a sandbox with this policy.
  3. Observe it is accepted with no error or warning.
  4. On a host where Landlock is unavailable or a listed path cannot be opened, the sandbox starts anyway with reduced (or zero) filesystem restriction, rather than aborting as hard_requirement would.

Environment

Logs

Parsing accepts any string:

// crates/openshell-policy/src/lib.rs:73-76#[derive(Debug,Serialize,Deserialize)]#[serde(deny_unknown_fields)]structLandlockDef{#[serde(default, skip_serializing_if = "String::is_empty")]compatibility:String,}

The only place the value is interpreted — everything that is not an exact match falls through to BestEffort:

// crates/openshell-core/src/policy.rs:141-150implFrom<ProtoLandlockPolicy>forLandlockPolicy{fnfrom(proto:ProtoLandlockPolicy) -> Self{let compatibility = if proto.compatibility == "hard_requirement"{LandlockCompatibility::HardRequirement}else{LandlockCompatibility::BestEffort};Self{ compatibility }}}

Suggested Fix

Parse into the existing LandlockCompatibility enum rather than a String, either via #[serde(rename_all = "snake_case")] on the enum or a TryFrom<&str> that errors on unrecognized input, and surface that error through gateway policy validation. Docs enumerate exactly two accepted values (docs/reference/policy-schema.mdx:92), so tightening the parser matches documented behavior rather than changing it.

Related

  • A second, separate gap found alongside this one: when both filesystem_policy.read_only and read_write are empty, prepare() returns Ok(None) and Landlock is a complete no-op even under hard_requirement (crates/openshell-supervisor-process/src/sandbox/linux/landlock.rs:145-147). Arguably hard_requirement with no paths should be a configuration error. Filing separately if maintainers agree it is distinct.

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:acceptedA maintainer decided OpenShell should pursue this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions