landlock

Trait Compatible

source
pub trait Compatible: Sized + OptionCompatLevelMut {
    // Provided methods
    fn set_compatibility(self, level: CompatLevel) -> Self { ... }
    fn set_best_effort(self, best_effort: bool) -> Self
       where Self: Sized { ... }
}
Expand description

Properly handles runtime unsupported features.

This guarantees consistent behaviors across crate users and runtime kernels even if this crate get new features. It eases backward compatibility and enables future-proofness.

Landlock is a security feature designed to help improve security of a running system thanks to application developers. To protect users as much as possible, compatibility with the running system should then be handled in a best-effort way, contrary to common system features. In some circumstances (e.g. applications carefully designed to only be run with a specific set of kernel features), it may be required to error out if some of these features are not available and will then not be enforced.

Provided Methods§

source

fn set_compatibility(self, level: CompatLevel) -> Self

To enable a best-effort security approach, Landlock features that are not supported by the running system are silently ignored by default, which is a sane choice for most use cases. However, on some rare circumstances, developers may want to have some guarantees that their applications will not run if a certain level of sandboxing is not possible. If we really want to error out when not all our requested requirements are met, then we can configure it with set_compatibility().

The Compatible trait is implemented for all object builders (e.g. Ruleset). Such builders have a set of methods to incrementally build an object. These build methods rely on kernel features that may not be available at runtime. The set_compatibility() method enables to control the effect of the following build method calls starting after the set_compatibility() call. Such effect can be:

Taking Ruleset as an example, the handle_access() build method returns a Result that can be Err(RulesetError) with a nested CompatError. Such error can only occur with a running Linux kernel not supporting the requested Landlock accesses and if the current compatibility level is CompatLevel::HardRequirement. However, such error is not possible with CompatLevel::BestEffort nor CompatLevel::SoftRequirement.

The order of this call is important because it defines the behavior of the following build method calls that return a Result. If set_compatibility(CompatLevel::HardRequirement) is called on an object, then a CompatError may be returned for the next method calls, until the next call to set_compatibility(). This enables to change the behavior of a set of build method calls, for instance to be sure that the sandbox will at least restrict some access rights.

New objects inherit the compatibility configuration of their parents, if any. For instance, Ruleset::create() returns a RulesetCreated object that inherits the Ruleset’s compatibility configuration.

§Example with SoftRequirement

Let’s say an application legitimately needs to rename files between directories. Because of previous Landlock limitations, this was forbidden with the first version of Landlock, but it is now handled starting with the second version. For this use case, we only want the application to be sandboxed if we have the guarantee that it will not break a legitimate usage (i.e. rename files). We then create a ruleset which will either support file renaming (thanks to AccessFs::Refer) or silently do nothing.

use landlock::*;

fn ruleset_handling_renames() -> Result<RulesetCreated, RulesetError> {
    Ok(Ruleset::default()
        // This ruleset must either handle the AccessFs::Refer right,
        // or it must silently ignore the whole sandboxing.
        .set_compatibility(CompatLevel::SoftRequirement)
        .handle_access(AccessFs::Refer)?
        // However, this ruleset may also handle other (future) access rights
        // if they are supported by the running kernel.
        .set_compatibility(CompatLevel::BestEffort)
        .handle_access(AccessFs::from_all(ABI::V5))?
        .create()?)
}
§Example with HardRequirement

Security-dedicated applications may want to ensure that an untrusted software component is subject to a minimum of restrictions before launching it. In this case, we want to create a ruleset which will at least support all restrictions provided by the first version of Landlock, and opportunistically handle restrictions supported by newer kernels.

use landlock::*;

fn ruleset_fragile() -> Result<RulesetCreated, RulesetError> {
    Ok(Ruleset::default()
        // This ruleset must either handle at least all accesses defined by
        // the first Landlock version (e.g. AccessFs::WriteFile),
        // or the following handle_access() call must return a wrapped
        // AccessError<AccessFs>::Incompatible error.
        .set_compatibility(CompatLevel::HardRequirement)
        .handle_access(AccessFs::from_all(ABI::V1))?
        // However, this ruleset may also handle new access rights
        // (e.g. AccessFs::Refer defined by the second version of Landlock)
        // if they are supported by the running kernel,
        // but without returning any error otherwise.
        .set_compatibility(CompatLevel::BestEffort)
        .handle_access(AccessFs::from_all(ABI::V5))?
        .create()?)
}
source

fn set_best_effort(self, best_effort: bool) -> Self
where Self: Sized,

👎Deprecated: Use set_compatibility() instead

Cf. set_compatibility():

  • set_best_effort(true) translates to set_compatibility(CompatLevel::BestEffort).

  • set_best_effort(false) translates to set_compatibility(CompatLevel::HardRequirement).

Object Safety§

This trait is not object safe.

Implementors§