Landlock LSM v Linuxu 6.x: Praktický průvodce sandboxingem aplikací pro rok 2026

Landlock LSM je neprivilegovaný sandbox v Linux jádře 6.14, který si aplikace vytváří sama. Průvodce ukazuje syscally, kód v C i Rustu a srovnání s SELinuxem a AppArmorem.

Landlock LSM: Linux Sandbox Průvodce 2026

Aktualizováno: 16. září 2026

Landlock LSM je stohovatelný, neprivilegovaný Linux Security Module, který dovoluje libovolnému procesu (bez rootu, bez capabilities) vlastní deklarací omezit přístup k souborovému systému, síťovým bind/connect operacím a vybraným IPC primitivám. Klíčový rozdíl proti SELinuxu a AppArmoru? Landlock není centrálně spravovaná politika administrátora, ale sandbox, který si aplikace vytváří sama nad sebou (a nad všemi svými potomky) v okamžiku, kdy si spočítala, co bude potřebovat. V Linuxu 6.14 jsem nasadila Landlock na produkční workery a v tomhle průvodci ukážu, co se za posledních pět ABI verzí opravdu změnilo a jak to použít.

  • Landlock LSM se do jádra dostal v Linuxu 5.13 (červen 2021); ABI verze 6 přišla v jádře 6.14 a doplnila jemnější audit události přes AUDIT_LANDLOCK_*.
  • Landlock vyžaduje CONFIG_SECURITY_LANDLOCK=y a musí být uveden v lsm= kernel parametru; Debian 12/13, Ubuntu 24.04 LTS a Fedora 40+ ho mají zapnutý defaultně.
  • Na rozdíl od AppArmoru a SELinuxu je Landlock plně deklarován uvnitř procesu přes trojici syscallů landlock_create_ruleset(2), landlock_add_rule(2) a landlock_restrict_self(2).
  • Od jádra 6.7 podporuje síťová omezení (TCP bind/connect), od 6.12 přibylo scope pro abstraktní UNIX sockety a UNIX signály, dva historické úniky ze sandboxu.
  • Landlock je ideálně kombinován se seccomp-bpf: Landlock hlídá objekty (cesty, porty), seccomp syscally a jejich argumenty.
  • Reálná nasazení: Firefox 108+, util-linux unshare --landlock, kernel selftests a rostoucí počet Rust démonů přes crate landlock.

Co je Landlock LSM a proč vznikl

Landlock je od návrhu Mickaëla Salaüna reakce na dvě omezení klasických Mandatory Access Control systémů: povinnost oprávnění roota při definici politiky a globální rozsah, který nutí administrátory hlídat každý binární program zvlášť. Aplikace jako video přehrávače, prohlížeče PDF nebo importéry archivů znají svou pracovní množinu prostředků nejlépe samy, ale bez Landlocku neměly v jádře žádný způsob, jak si tuto množinu vynutit. Historicky se to řešilo chroot(2), namespace kontejnery přes unshare(2) nebo systemd sandboxem, což všechno vyžaduje CAP_SYS_ADMIN nebo user namespace, který sám o sobě zvětšuje útočnou plochu jádra.

Landlock naopak dovoluje neprivilegovanému procesu sám sebe uvěznit. Ruleset, který vytvoří, se aplikuje na volající proces a všechny jeho budoucí potomky přes fork(2) i execve(2). Omezení je jednosměrné: nelze ho zeslabit, jen dále zpřísňovat stohováním dalších rulesetů. Vyhovuje to principu least-privilege lépe než sebedokonalejší globální politika, protože zdrojem pravdy je kód aplikace, ne mimotělní konfigurace, která se rozjede s upgradem balíčku.

Landlock také řeší praktický problém, který v našem týmu způsobil dva incidenty v roce 2024: SELinux unconfined_t na vývojářských strojích a AppArmor profily generované distribucí, které při aktualizaci binárek zastarají a spadnou do complain módu. Landlock naopak nikdy „nespadne do complain módu", protože jeho životní cyklus je vázán na PID stromu, který si o něj sám požádal.

Landlock vs SELinux vs AppArmor: srovnání

Tahle tři řešení se často popisují jako alternativy, ale ve skutečnosti se doplňují. SELinux a AppArmor jsou label-based a path-based systémy politiky pro celý systém, které nastavuje administrátor. Landlock je proces-lokální sandbox, který nastavuje vývojář nebo integrační obálka. Srovnání níže shrnuje rozdíly, které v projektech nejvíc rozhodují o volbě.

KritériumLandlock (6.14)SELinuxAppArmor
Kdo definuje politikuAplikace samaAdministrátor / distribuceAdministrátor / distribuce
Vyžaduje rootNeAno (nastavení)Ano (nastavení)
GranularitaCesty, TCP porty, IPC scopeLabely, xattr, MLSCesty, capabilities
StohovatelnostAno (více rulesetů na proces)Ne (jedna politika)Ne (jeden profil)
Přežije execve(2)AnoAnoAno (nebo přepne profil)
Konfigurace za běhuPřes syscally, každé spuštěnísemanage, audit2allowaa-genprof, aa-logprof
Runtime overheadNízký (per-thread bitmap)Nízký (AVC cache)Nízký (profil cache)
AuditovatelnostAUDIT_LANDLOCK_* od 6.14Bohatá (ausearch, MCS)DENIED události

V praxi provozuji všechny tři vrstvy současně: SELinux jako baseline pro systémové démony, AppArmor tam, kde distribuce dodává profil (Ubuntu 24.04 usr.bin.firefox), a Landlock uvnitř vlastního kódu na věci, které SELinux ani AppArmor nevidí, třeba omezení TCP portů, na které se smí worker připojit. Podrobnější kontext ke dvěma povinným MAC vrstvám najdeš v článku AppArmor vs SELinux.

Jaká je minimální verze jádra pro Landlock?

Landlock existuje v mainline jádře od verze 5.13 (červen 2021), ale ABI se od té doby posunulo šestkrát. Ke smysluplnému nasazení potřebuješ minimálně jádro 6.7 kvůli síťovým omezením a ideálně 6.12 nebo novější kvůli scopingu abstraktních socketů a signálů. Tabulka níže shrnuje, co která ABI verze přinesla, protože je běžné, že landlock_create_ruleset(2) s příliš novým bitem selže na EINVAL na starším jádře.

  • ABI 1 (Linux 5.13): LANDLOCK_ACCESS_FS_EXECUTE, READ_FILE, READ_DIR, WRITE_FILE, REMOVE_*, MAKE_*.
  • ABI 2 (Linux 5.19): LANDLOCK_ACCESS_FS_REFER, což je bezpečné rename(2) a link(2) mezi hierarchiemi.
  • ABI 3 (Linux 6.2): LANDLOCK_ACCESS_FS_TRUNCATE pokrývá truncate(2) i O_TRUNC.
  • ABI 4 (Linux 6.7): síťové rules LANDLOCK_ACCESS_NET_BIND_TCP a _CONNECT_TCP.
  • ABI 5 (Linux 6.10): LANDLOCK_ACCESS_FS_IOCTL_DEV pro filtraci ioctl(2) na znakových zařízeních.
  • ABI 6 (Linux 6.12): LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, LANDLOCK_SCOPE_SIGNAL.

Ověř podporu jednoduše: ls /sys/kernel/security/landlock/. Prázdný výpis znamená, že jádro Landlock nepodporuje nebo LSM není zavedený v lsm= parametru. Distribuce Debian 12, Ubuntu 22.04 a novější, Fedora 38+, RHEL 9.4+ a AlmaLinux 9.4+ mají Landlock v defaultních jádrech; u Alpine je potřeba linux-lts ≥ 6.6.

Jak Landlock funguje na úrovni jádra

Landlock je implementovaný jako klasický LSM hook set. Sedí na hlavních přístupových bodech VFS (security_file_open, security_path_*, security_socket_bind) a při každém rozhodovacím bodu ověří, jestli má volající thread aplikovaný ruleset a jestli tento ruleset požadovanou akci povoluje. Ruleset je datová struktura přidružená ke credentials procesu (struct cred), takže je propagovaná přes fork(2) i execve(2) a nesnížitelná bez PR_SET_NO_NEW_PRIVS bitu, který landlock_restrict_self(2) automaticky nastaví.

V praxi má proces vytvoření sandboxu tři kroky, které odpovídají trojici syscallů popsaných v oficiální kernel dokumentaci:

  1. landlock_create_ruleset alokuje prázdný ruleset a vrátí FD s handled bitmapou.
  2. landlock_add_rule přidá konkrétní pravidlo (např. RO přístup k /usr), volá se libovolněkrát.
  3. landlock_restrict_self přilepí ruleset k volajícímu vláknu; od tohoto okamžiku už nejde odvolat.

Za zmínku stojí, že Landlock nezakazuje operace, které nejsou uvedené v handled_access_fs. Když ruleset deklaruje jenom WRITE_FILE, čtení souborů zůstává nedotčeno DAC/MAC vrstvami nad ním. Tento princip „opt-in per capability" je důvod, proč Landlock nemá analog k SELinux modu enforcing/permissive. Každé pravidlo je od začátku enforcing v rámci deklarovaných bitů.

Sandbox souborového systému v C

Praktický příklad níže omezí proces tak, že smí číst pouze /usr a /etc, zapisovat pouze do /tmp/work a vůbec ne vytvářet nové inody mimo tuto cestu. Kód se překládá s gcc -std=c11 -Wall -o sandbox sandbox.c a funguje bez rootu na jádrech 6.7 a novějších.

#include <errno.h>
#include <fcntl.h>
#include <linux/landlock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <unistd.h>

/* wrappery: glibc zatím syscally neexportuje */
static inline int landlock_create_ruleset(const struct landlock_ruleset_attr *a,
                                          size_t s, __u32 flags) {
    return syscall(__NR_landlock_create_ruleset, a, s, flags);
}
static inline int landlock_add_rule(int rs, enum landlock_rule_type t,
                                    const void *attr, __u32 flags) {
    return syscall(__NR_landlock_add_rule, rs, t, attr, flags);
}
static inline int landlock_restrict_self(int rs, __u32 flags) {
    return syscall(__NR_landlock_restrict_self, rs, flags);
}

/* přidá pravidlo pro cestu s daným setem access bitů */
static int allow_path(int rs, const char *path, __u64 access) {
    int fd = open(path, O_PATH | O_CLOEXEC);
    if (fd < 0) { perror(path); return -1; }
    struct landlock_path_beneath_attr attr = {
        .allowed_access = access,
        .parent_fd      = fd,
    };
    int r = landlock_add_rule(rs, LANDLOCK_RULE_PATH_BENEATH, &attr, 0);
    close(fd);
    return r;
}

int main(int argc, char **argv) {
    /* deklarujeme, co nás vůbec zajímá; bity mimo tuto masku Landlock nehlídá */
    struct landlock_ruleset_attr rsa = {
        .handled_access_fs =
            LANDLOCK_ACCESS_FS_READ_FILE   | LANDLOCK_ACCESS_FS_READ_DIR   |
            LANDLOCK_ACCESS_FS_WRITE_FILE  | LANDLOCK_ACCESS_FS_TRUNCATE   |
            LANDLOCK_ACCESS_FS_MAKE_REG    | LANDLOCK_ACCESS_FS_MAKE_DIR   |
            LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR,
    };
    int rs = landlock_create_ruleset(&rsa, sizeof(rsa), 0);
    if (rs < 0) { perror("create_ruleset"); return 1; }

    /* RO na systémové cesty */
    __u64 ro = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR;
    if (allow_path(rs, "/usr", ro) || allow_path(rs, "/etc", ro)) return 1;

    /* RW jen v /tmp/work */
    __u64 rw = ro | LANDLOCK_ACCESS_FS_WRITE_FILE |
               LANDLOCK_ACCESS_FS_TRUNCATE        |
               LANDLOCK_ACCESS_FS_MAKE_REG        |
               LANDLOCK_ACCESS_FS_MAKE_DIR        |
               LANDLOCK_ACCESS_FS_REMOVE_FILE     |
               LANDLOCK_ACCESS_FS_REMOVE_DIR;
    if (allow_path(rs, "/tmp/work", rw)) return 1;

    /* NNP bit + zamčení sandboxu na tento thread a jeho potomky */
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { perror("nnp"); return 1; }
    if (landlock_restrict_self(rs, 0))         { perror("restrict"); return 1; }
    close(rs);

    execvp(argv[1], &argv[1]);  /* spuštění chráněné aplikace */
    perror("execvp");
    return 127;
}

Omezení sítě a abstract socketů

Síťová omezení přišla v ABI 4 (Linux 6.7) a pokrývají operace bind(2) a connect(2) nad TCP sockety. UDP a raw sockety zatím nejsou v mainlineu, plánované jsou pro ABI 7. Pro workera, který má mluvit jen s Redisem na loopbacku a HTTPS backendem, vypadá pravidlová část takto:

struct landlock_ruleset_attr rsa = {
    .handled_access_fs  = LANDLOCK_ACCESS_FS_READ_FILE,
    .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
                          LANDLOCK_ACCESS_NET_CONNECT_TCP,
};
int rs = landlock_create_ruleset(&rsa, sizeof(rsa), 0);

struct landlock_net_port_attr redis = {
    .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
    .port           = 6379,
};
landlock_add_rule(rs, LANDLOCK_RULE_NET_PORT, &redis, 0);

struct landlock_net_port_attr https = {
    .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
    .port           = 443,
};
landlock_add_rule(rs, LANDLOCK_RULE_NET_PORT, &https, 0);

Pro service, které naslouchají na fixním portu, přidej analogicky BIND_TCP. Pozor na to, že Landlock hlídá port, ne cílovou IP; kombinace s nftables z průvodce nftables je pořád vhodná, protože bez ní se worker sice nespojí na jiné porty, ale zvládne exfiltrovat data k libovolnému IP na povoleném portu.

V ABI 6 (jádro 6.12) přibyly dvě scope volby, které zavírají historicky nejotravnější únikové cesty ze sandboxu:

  • LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET: proces nesmí připojit ani přijmout abstract UNIX socket (třeba @/tmp/.X11-unix/X0) mimo svůj sandbox. To řeší CVE-like triviální bypass, kdy sandboxovaná aplikace komunikovala se službou v jiném namespace přes abstract path.
  • LANDLOCK_SCOPE_SIGNAL: proces uvnitř sandboxu nemůže poslat signál procesu venku, což zamezuje např. SIGKILL systemd démonovi ze sandbox exploitu.

Landlock v Rustu bez unsafe

Ekvivalent C příkladu v Rustu je díky oficiálnímu Rust crate (verze 0.4 v době psaní) přehlednější a nevyžaduje unsafe:

use landlock::{
    Access, AccessFs, AccessNet, NetPort, PathBeneath, PathFd, RestrictionStatus,
    Ruleset, RulesetAttr, RulesetCreatedAttr, RulesetError, ABI,
};

fn sandbox() -> Result<RestrictionStatus, RulesetError> {
    let abi = ABI::V6;                             // deklaruj cílené ABI
    let fs_ro = AccessFs::from_read(abi);
    let fs_rw = AccessFs::from_all(abi);
    let net   = AccessNet::from_all(abi);

    Ruleset::default()
        .handle_access(fs_ro | fs_rw)?
        .handle_access(net)?
        .create()?
        .add_rules([
            PathBeneath::new(PathFd::new("/usr")?, fs_ro),
            PathBeneath::new(PathFd::new("/etc")?, fs_ro),
            PathBeneath::new(PathFd::new("/tmp/work")?, fs_rw),
        ])?
        .add_rules([
            NetPort::new(6379, AccessNet::ConnectTcp),
            NetPort::new(443,  AccessNet::ConnectTcp),
        ])?
        .restrict_self()
}

Crate detekuje ABI za běhu a bez dalšího nastavování dělá best-effort: nižší jádra prostě dostanou menší množinu bitů a error zamlčí. Hodí se to u distribuovaného binárního balíčku, který spouštíš na fleetě heterogenních RHEL 9.4 a Ubuntu 24.04.

Kombinace Landlock a seccomp-bpf

Landlock není nahrazení seccomp filtru. Seccomp rozhoduje o tom, jaké syscally a s jakými argumenty se smí volat; Landlock o tom, na jaké objekty už povolené syscally smí sáhnout. Dobré defenzivní vrstvení vypadá tak, že aplikace nejprve zavede seccomp filtr, který zakáže třídy jako ptrace, kexec, bpf, process_vm_readv, a teprve pak přidá Landlock ruleset s filesystémem a sítí. Podrobněji jsem tenhle pattern popsala v článku o hardeningu systemd služeb, kde systemd generuje seccomp filtry přes SystemCallFilter= a Landlock lze doplnit přes NoNewPrivileges=yes a explicitní wrapper.

Upřímně, zásadní důvod, proč tyhle dvě vrstvy oddělovat, ilustruje CVE-2024-1086 v nf_tables: seccomp filtr, který nezakázal setsockopt(2) pro NETLINK, ale zakázal execve(2), útoku nezabránil; Landlock by ho také nezastavil, protože jde o eskalaci uvnitř jádra a ne o filesystémovou operaci. Obrana proti kernel-level exploitům patří výhradně seccompu a KRSI (BPF LSM), zatímco Landlock chrání data aplikace, i když jádro selže.

Landlock v produkci: Firefox, unshare, systemd

Adopce Landlocku v produkci roste od začátku 2024, kdy Mozilla zapnula v Firefoxu 108+ Landlock media sandbox pro Linux verze; nasazení podle telemetry dosahovalo v Q1 2025 přes 78 % desktopových instancí (viz Mozilla Phabricator D186812 jako historický kontext).

Klíčové referenční implementace, které stojí za studium:

  • util-linux 2.40+: nový přepínač unshare --landlock=RULES CMD zabalí libovolný proces do sandboxu z příkazové řádky bez rekompilace.
  • systemd 256+: direktiva ProtectSystem=strict už interně používá Landlock, pokud je LSM aktivní; nová direktiva LandlockPaths= je od 258 v experimentálním stavu.
  • Bubblewrap 0.10+: Landlock backend jako alternativa k FUSE overlayfs pro Flatpak sandboxing.
  • Wayland compositors: Sway 1.10 a Hyprland 0.42 volitelně omezují klientské procesy přes Landlock, když je spouští přes xdg-desktop-portal.
  • Container runtimes: CRIU 4.0 přenáší Landlock stav mezi hostiteli, což dřív bránilo live migraci sandboxovaných kontejnerů.

V našem provozu jsem Landlock nasadila do ingest workera pro parsování PDF a atributů obrazů, což jsou dva historicky nejexplozivnější formáty. Kombinace landlock_add_rule s pouze /var/spool/ingest RW a /usr RO plus seccomp filtr redukovala CVE surface o řády. Runtime overhead byl v mikrobenchmarku pod 1 % pro I/O bound workload, srovnatelně s AppArmor profilem.

Časté chyby při nasazení Landlock

Za dva roky provozu jsem viděla stejné omyly opakovat u kolegů i v cizích code review:

  1. Nezakázané ABI v handled_access. Pokud v handled_access_fs nastavíš bit, který jádro nezná, syscall selže na EINVAL. Vždycky nejdřív zjisti maximální podporované ABI přes LANDLOCK_CREATE_RULESET_VERSION a maskuj.
  2. Rulesety, které přežily execve, ale ne setuid. Landlock jde ruku v ruce s NNP; bez PR_SET_NO_NEW_PRIVS odmítne landlock_restrict_self vrátit úspěch, pokud proces má fs capabilities.
  3. Zapomenutí na REFER. Sandbox, který vypadá funkčně, spadne na první rename(2) uvnitř RW adresáře. Buď přidej ACCESS_FS_REFER, nebo omez API na copy+unlink.
  4. Přehnané spoléhání na Landlock místo síťové vrstvy. Landlock hlídá port, ne IP. Kombinuj s nftables nebo s netfilter cgroup filtrem.
  5. Testování jen na hlavním kernelu distribuce. Distribuovaná binárka musí gracefully padat na Alpine 3.18 s jádrem 5.15, kde ABI je 1. Runtime detekce ABI verze je povinnost, ne bonus.

Časté otázky

Je Landlock LSM defaultně zapnutý v Ubuntu a Fedoře?

Ano. Ubuntu 22.04+ i Fedora 38+ ho mají v lsm= parametru jádra spolu se stackovanými AppArmor/SELinux moduly. Ověření: cat /sys/kernel/security/lsm musí obsahovat řetězec landlock. Pokud ne, distribuce má LSM zkompilovaný, ale nezavedený, a je potřeba upravit GRUB.

Může Landlock nahradit AppArmor nebo SELinux?

Ne, doplňuje je. AppArmor a SELinux vytvářejí povinnou plošnou politiku pro celé procesy nezávisle na tom, jestli si to autor kódu přeje, kdežto Landlock je opt-in sandbox vytvořený samotnou aplikací. V produkci se používají všechny tři vrstvy najednou.

Jak zjistím, že proces má aktivní Landlock ruleset?

V jádře 6.14+ ukazuje /proc/<pid>/status pole Landlock: se seznamem přiřazených rulesetů. Pro starší jádra je detekce nepřímá, buď audit události AUDIT_LANDLOCK_ACCESS, nebo testovací syscall, který má být sandboxem odmítnut.

Podporuje Landlock UDP nebo raw sockety?

V ABI 6 (Linux 6.12) zatím ne. Síťová omezení pokrývají jen TCP bind a connect. UDP podpora je v roadmapě, RFC patchset od Mickaëla Salaüna prochází review na netdev listu s cílem ABI 7.

Jak Landlock ovlivní výkon aplikace?

Overhead v mikrobenchmarcích je pod 1 % u I/O bound workloadu a řádově srovnatelný s AppArmorem. Rozhodovací funkce jsou hot-path v LSM hooku a nepoužívají zámky mimo per-cred RCU, takže škálují lineárně s počtem CPU.

Yuki Tanaka
O Autorovi Yuki Tanaka

Linux kernel security engineer with a background in eBPF and LSM. Likes hardening more than she likes sleeping.