Skip to main content

LandlockRuleset

Struct LandlockRuleset 

Source
pub struct LandlockRuleset(/* private fields */);
Expand description

Ruleset Landlock (cf. landlock_create_ruleset(2)).

Encapsule un FD ruleset Landlock qui accumule des règles d’accès filesystem avant d’être appliqué au thread courant via LandlockRuleset::restrict_self.

Les restrictions sont irréversibles pour le fil qui les pose : une fois restrict_self appelé, ce fil ne peut plus voir ses permissions augmenter.

§Ce que la monotonie ne couvre PAS : TSYNC

Cette page affirmait « Landlock est monotone », sans réserve. C’est faux en présence de LandlockRestrictFlags::TSYNC, et la correction est datée du 2026-08-13 après mesure sur un noyau réel : un fil qui pose un domaine avec TSYNC écrase celui de ses fils frères, y compris quand le leur était plus strict. Le noyau le dit — « irrespective of previously established Landlock domains » — et l’expérience le confirme : un fil frère qui ne pouvait rien ouvrir a retrouvé l’accès.

Ce n’est pas une élévation de privilège au sens usuel : les fils d’un processus partagent leur espace d’adressage et n’ont jamais été une frontière de sécurité entre eux. Mais l’intuition « une restriction Landlock ne se retire pas » est fausse à l’échelle du processus, et la croire conduirait à poser TSYNC d’office.

Implementations§

Source§

impl LandlockRuleset

Source

pub fn as_fd(&self) -> BorrowedFd<'_>

Vue empruntée du FD ruleset.

Source

pub fn add_rule_path_beneath( &mut self, path: BorrowedFd<'_>, allowed_access: LandlockAccessFs, ) -> Result<(), Errno>

Ajoute une règle d’accès pour un chemin filesystem et ses descendants.

Wrappeur de landlock_add_rule(2) avec LANDLOCK_RULE_PATH_BENEATH. La règle s’applique au chemin référencé par path et à tous ses descendants. path doit être ouvert avec O_PATH | O_DIRECTORY (ou O_PATH seul pour un fichier).

Sémantique additive : les règles ne peuvent qu’étendre les accès autorisés au sein de ce ruleset. restrict_self appliquera l’intersection de tous les rulesets cumulés.

§Parameters
  • path : FD du chemin cible (typiquement ouvert avec O_PATH).
  • allowed_access : permissions autorisées sur ce chemin et ses descendants.
§Errors
  • EINVAL : allowed_access contient un bit non géré par ce ruleset, ou path n’est pas un FD valide.
  • ENOMEM : mémoire kernel insuffisante.
  • EBADFD : path n’est pas un FD de fichier ou répertoire.
  • EINTR : interruption par signal (ADR-021 convention 2 — remonté tel quel, sans retry automatique).
§Examples
use air_sys_syscall::security::landlock_create_ruleset;
use air_sys_types::security::LandlockAccessFs;
use air_sys_types::fd::BorrowedFd;

let mut ruleset = landlock_create_ruleset(
    LandlockAccessFs::READ_FILE | LandlockAccessFs::EXECUTE,
).expect("create_ruleset");
ruleset.add_rule_path_beneath(path_fd, LandlockAccessFs::READ_FILE)
    .expect("add_rule");
Source

pub fn add_rule_net_port( &mut self, port: u16, allowed_access: LandlockAccessNet, ) -> Result<(), Errno>

Autorise un port TCPlandlock_add_rule(2) avec LANDLOCK_RULE_NET_PORT (ABI v4+, Linux 6.7).

§Parameters
§Ce que cette règle borne, et ce qu’elle ne borne pas

Elle borne le port, jamais l’adresse. Un processus autorisé à se connecter au port 443 peut joindre n’importe quelle machine sur ce port. Landlock n’est pas un pare-feu, et le prendre pour tel donnerait un faux sentiment de confinement réseau.

§Errors
  • EINVAL : le ruleset ne gère pas les accès réseau (il faut l’avoir créé avec landlock_create_ruleset_with_net), ou allowed_access porte un bit inconnu du noyau ;
  • EAFNOSUPPORT / ENOSYS : ABI Landlock antérieure à v4.
Source

pub fn restrict_self(&self) -> Result<(), Errno>

Applique le ruleset au thread courant.

Wrappeur de landlock_restrict_self(2). Irréversible. Après cet appel, le thread ne peut plus accéder aux chemins filesystem non couverts par les règles du ruleset (pour les accès dans handled_access du ruleset).

Prérequis : avoir appelé crate::process::set_no_new_privs ou posséder CAP_SYS_ADMIN.

§Errors
  • EPERM : no_new_privs non positionné et CAP_SYS_ADMIN absent.
  • EINVAL : flags invalides.
  • EINTR : interruption par signal (ADR-021 convention 2 — remonté tel quel, sans retry automatique).
§Examples
use air_sys_syscall::security::landlock_create_ruleset;
use air_sys_syscall::process::set_no_new_privs;
use air_sys_types::security::LandlockAccessFs;

let ruleset = landlock_create_ruleset(LandlockAccessFs::READ_FILE)
    .expect("create_ruleset");
set_no_new_privs().expect("no_new_privs");
ruleset.restrict_self().expect("restrict_self");
Source

pub fn restrict_self_with( &self, flags: LandlockRestrictFlags, ) -> Result<(), Errno>

Applique le ruleset avec des drapeaux (cf. LandlockRestrictFlags).

§Ce que TSYNC change, et pourquoi une fonction séparée n’aurait rien réglé

Sans TSYNC, le domaine ne borne que le fil appelant : dans un processus multifil qui se met lui-même en cage, les fils frères restent dehors, et la cage se contourne en changeant de fil. Avec lui, la configuration s’applique atomiquement à tous les fils.

Les drapeaux ne sont pas des opérations multiplexées (ADR-021 convention 3) : ils se combinent, et un wrapper par combinaison serait une explosion combinatoire sans gain de typage.

§Le noyau refuse ce qu’il ne connaît pas

Un drapeau inconnu de l’ABI courante fait échouer l’appel (EINVAL) — il n’est pas ignoré. La pose de cage étant fail-closed, demander un drapeau trop récent tue le processus : interroger landlock_supported_abi d’abord.

§Errors
  • EPERM : no_new_privs non positionné et CAP_SYS_ADMIN absent.
  • EINVAL : un drapeau inconnu de l’ABI courante.
  • EINTR : interruption par signal (ADR-021 convention 2 — remonté tel quel).

Trait Implementations§

Source§

impl Debug for LandlockRuleset

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.