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§
sourcefn set_compatibility(self, level: CompatLevel) -> Self
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:
- to silently ignore unsupported features
and continue building (
CompatLevel::BestEffort
); - to silently ignore unsupported features
and ignore the whole build (
CompatLevel::SoftRequirement
); - to return an error for any unsupported feature (
CompatLevel::HardRequirement
).
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()?)
}
sourcefn set_best_effort(self, best_effort: bool) -> Selfwhere
Self: Sized,
👎Deprecated: Use set_compatibility() instead
fn set_best_effort(self, best_effort: bool) -> Selfwhere
Self: Sized,
Cf. set_compatibility()
:
-
set_best_effort(true)
translates toset_compatibility(CompatLevel::BestEffort)
. -
set_best_effort(false)
translates toset_compatibility(CompatLevel::HardRequirement)
.