Introduktion
Linux-kernen er fundamentet for millioner af servere, cloud-instanser, IoT-enheder og desktop-systemer verden over. Men med den udbredelse følger også en angrebsflade, der bare bliver større og større. I 2025 blev der registreret hele 5.530 CVE'er (Common Vulnerabilities and Exposures) relateret til Linux-kernen — en stigning på 28 % i forhold til året før. Det svarer til 8-9 nye sårbarheder om dagen. Ærligt, det tal overraskede mig, da jeg først så det.
Den eksponentielle vækst skyldes dels, at Linux-kerneteamet blev en CVE Numbering Authority i starten af 2024, hvilket har forbedret gennemsigtigheden markant. Men det understreger også behovet for robuste sikkerhedsmekanismer på applikationsniveau.
Traditionelt har Linux-administratorer brugt sikkerhedsmoduler som SELinux og AppArmor til at håndhæve adgangskontrol. De er effektive, men kræver root-privilegier, komplekse politikker og central administration. Hvad nu, hvis enhver applikation — uanset privilegieniveau — bare kunne isolere sig selv i en sikker sandbox?
Det er præcis det, Landlock LSM (Linux Security Module) tilbyder. Landlock er en stackbar sikkerhedsmodul, der giver uprivilegerede processer mulighed for at begrænse deres egne adgangsrettigheder til filsystemet, netværket og interproces-kommunikation — uden root, uden containere og uden komplekse systemkonfigurationer. Så lad os dykke ned i alle aspekter af Landlock: fra arkitektur og ABI-versioner til praktisk implementering, værktøjer og bedste praksis.
Hvad er Landlock LSM?
Landlock er et stackbart Linux Security Module, der har været inkluderet i Linux-kernen siden version 5.13 (udgivet i juni 2021). I modsætning til SELinux og AppArmor, der kræver privilegeret adgang til konfiguration, er Landlock designet fra bunden til at være tilgængeligt for enhver proces — også dem, der kører uden root-privilegier.
Kerneegenskaber
- Uprivilegeret sandboxing: Enhver proces kan oprette og håndhæve sin egen sikkerhedspolitik uden root-adgang. Det gør det muligt for udviklere at integrere sandboxing direkte i deres applikationer.
- Stackbar arkitektur: Landlock fungerer ved siden af eksisterende sikkerhedsmoduler. Det erstatter ikke SELinux eller AppArmor, men tilføjer et ekstra sikkerhedslag oven på de eksisterende politikker.
- Deny-by-default model: Når en proces aktiverer en Landlock-politik, nægtes al adgang til de ressourcer, der er omfattet af politikken, medmindre der eksplicit er tilføjet en regel, der tillader det.
- Uigenkaldelighed: Når en Landlock-begrænsning er aktiveret, kan den aldrig fjernes. En proces kan kun tilføje yderligere restriktioner. Det er faktisk en ret elegant sikkerhedsgaranti.
- Minimal kerne-kode: Implementeringen består af cirka 2.000 linjer kode (
security/landlock/*), hvilket gør den overskuelig at auditere og vedligeholde. - Arvelighed: Underprocesser (
fork()/clone()) arver automatisk forælderprocessens Landlock-begrænsninger, så sandboxen forbliver intakt.
Forskellen fra SELinux og AppArmor
SELinux og AppArmor er kraftfulde, systemdækkende sikkerhedsmoduler, men de opererer fundamentalt anderledes end Landlock:
- SELinux kræver komplekse politikker skrevet i et særligt politiksprog, administreres af root og kræver labelbaseret adgangskontrol på alle systemobjekter.
- AppArmor bruger sti-baserede profiler, der også skal konfigureres af en administrator med root-adgang.
- Landlock kræver ingen systemkonfiguration. En applikation kan selv definere og håndhæve sine begrænsninger ved hjælp af tre simple systemkald. Ikke flere, ikke færre.
Tænk på Landlock som "defense-in-depth" på applikationsniveau. Det interfererer aldrig med andre adgangskontrolmekanismer, men tilføjer kun yderligere restriktioner oven på det, der allerede er på plads.
Landlock ABI-versioner og kernelunderstøttelse
Landlock har gennemgået en markant evolution siden sin introduktion i Linux 5.13. Hver ny ABI-version (Application Binary Interface) har udvidet funktionaliteten med nye adgangsrettigheder og kontrolmuligheder. Det er afgørende at forstå ABI-versionerne, da de bestemmer, hvilke funktioner der er tilgængelige på et givet system.
| ABI-version | Linux-kerne | Nye funktioner |
|---|---|---|
| ABI v1 | Linux 5.13 | Grundlæggende filsystemadgangsrettigheder: EXECUTE, WRITE_FILE, READ_FILE, READ_DIR, REMOVE_DIR, REMOVE_FILE, MAKE_CHAR, MAKE_DIR, MAKE_REG, MAKE_SOCK, MAKE_FIFO, MAKE_BLOCK, MAKE_SYM |
| ABI v2 | Linux 5.19 | LANDLOCK_ACCESS_FS_REFER — tillader flytning og hard-linking af filer mellem forskellige mapper (file reparenting) |
| ABI v3 | Linux 6.2 | LANDLOCK_ACCESS_FS_TRUNCATE — kontrol over filafkortning via truncate(2), ftruncate(2), creat(2) og open(2) med O_TRUNC |
| ABI v4 | Linux 6.7 | Netværksrestriktioner: LANDLOCK_ACCESS_NET_BIND_TCP og LANDLOCK_ACCESS_NET_CONNECT_TCP til styring af TCP-socket-operationer |
| ABI v5 | Linux 6.12 | LANDLOCK_ACCESS_FS_IOCTL_DEV — kontrol over ioctl(2)-kald på karakter- og blokenheder |
| ABI v6 | Linux 6.14 | IPC-scoping: LANDLOCK_SCOPE_SIGNAL og LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET til begrænsning af interproces-kommunikation |
For at tjekke den tilgængelige ABI-version på dit system kan du bruge følgende kommando:
# Tjek Landlock ABI-version via /sys
cat /sys/kernel/security/landlock/abi_version
# Alternativt via systemkald i C:
# landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION)
Hvis filen /sys/kernel/security/landlock/abi_version ikke eksisterer, er Landlock enten ikke aktiveret i kernen eller slet ikke understøttet på dit system. Du kan aktivere det via kerneparameteren lsm=landlock,... eller sikre det via kerneconfig-optionen CONFIG_SECURITY_LANDLOCK=y.
Sådan fungerer Landlock
Landlock-sandboxing bygger på tre systemkald, der tilsammen danner en simpel men kraftfuld arbejdsgang. Lad os se på hvert systemkald og den overordnede flow.
De tre systemkald
1. landlock_create_ruleset()
Første trin er at oprette et regelsæt (ruleset), der definerer, hvilke adgangstyper der skal kontrolleres. Man angiver de "håndterede adgangsrettigheder" (handled_access_fs, handled_access_net, scoped), og Landlock returnerer en fil-deskriptor til regelsættet.
struct landlock_ruleset_attr {
__u64 handled_access_fs; // Filsystemrettigheder der kontrolleres
__u64 handled_access_net; // Netværksrettigheder der kontrolleres
__u64 scoped; // IPC-scoping flags
};
int ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
Alle adgangstyper, der er angivet i handled_access_fs og handled_access_net, vil blive nægtet som standard, medmindre der eksplicit tilføjes en regel, der tillader dem. Det er deny-by-default i praksis.
2. landlock_add_rule()
Næste trin er at tilføje specifikke regler, der tillader adgang til bestemte ressourcer. Der findes to regeltyper:
LANDLOCK_RULE_PATH_BENEATH: Tillader specifikke filsystemoperationer under en given mappe eller fil.LANDLOCK_RULE_NET_PORT: Tillader specifikke netværksoperationer på en given TCP-port.
// Tilføj filsystemregel
struct landlock_path_beneath_attr path_rule = {
.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR,
.parent_fd = open("/usr", O_PATH | O_CLOEXEC),
};
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &path_rule, 0);
// Tilføj netværksregel
struct landlock_net_port_attr net_rule = {
.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
.port = 443,
};
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, &net_rule, 0);
3. landlock_restrict_self()
Sidste trin er at håndhæve regelsættet på den kaldende tråd (og alle fremtidige underprocesser). Inden dette kald skal man typisk indstille PR_SET_NO_NEW_PRIVS via prctl() for at forhindre, at sandboxede processer kan opnå nye privilegier via setuid-programmer.
// Forhindre privilegieeskalering
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
// Håndhæv regelsættet
landlock_restrict_self(ruleset_fd, 0);
// Luk regelsæt-deskriptoren (begrænsningerne forbliver aktive)
close(ruleset_fd);
Multi-lag politikstabling
En af Landlocks mest kraftfulde funktioner er politikstabling. Flere regelsæt kan stables oven på hinanden, og for at en handling skal være tilladt, skal alle lag tillade den (AND-logik). Det muliggør en lagdelt sikkerhedsmodel, som i praksis ser sådan ud:
- Et første lag kan give bred adgang til
/usrog/tmp. - Et andet lag (tilføjet senere, f.eks. efter init) kan indsnævre adgangen yderligere til kun
/usr/share. - Resultatet er, at kun
/usr/shareer tilgængeligt, da begge lag skal tillade adgangen.
Maksimalt 16 lag kan stables per tråd. Hvert lag er uigenkaldeligt og kan kun tilføje yderligere restriktioner.
Praktisk implementering i C
Nu til den sjove del — rigtig kode. Følgende er et komplet C-eksempel, der demonstrerer oprettelsen af en Landlock-sandbox med filsystem- og netværksrestriktioner. Programmet giver læseadgang til /usr, læse- og skriveadgang til /tmp, og tillader kun udgående TCP-forbindelser til port 443 (HTTPS).
#define _GNU_SOURCE
#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>
/* Wrapper-funktioner til Landlock-systemkald */
static inline int
landlock_create_ruleset(const struct landlock_ruleset_attr *attr,
size_t size, __u32 flags)
{
return syscall(__NR_landlock_create_ruleset, attr, size, flags);
}
static inline int
landlock_add_rule(int ruleset_fd, enum landlock_rule_type type,
const void *attr, __u32 flags)
{
return syscall(__NR_landlock_add_rule, ruleset_fd, type, attr, flags);
}
static inline int
landlock_restrict_self(int ruleset_fd, __u32 flags)
{
return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
}
/* Hjælpefunktion til at tilføje filsystemregel */
static int add_path_rule(int ruleset_fd, __u64 allowed_access,
const char *path)
{
int fd = open(path, O_PATH | O_CLOEXEC);
if (fd < 0) {
fprintf(stderr, "Kunne ikke åbne sti: %s: %s\n",
path, strerror(errno));
return -1;
}
struct landlock_path_beneath_attr attr = {
.allowed_access = allowed_access,
.parent_fd = fd,
};
int ret = landlock_add_rule(ruleset_fd,
LANDLOCK_RULE_PATH_BENEATH, &attr, 0);
close(fd);
if (ret < 0) {
fprintf(stderr, "Kunne ikke tilføje regel for %s: %s\n",
path, strerror(errno));
return -1;
}
return 0;
}
/* Hjælpefunktion til at tilføje netværksregel */
static int add_net_rule(int ruleset_fd, __u64 allowed_access,
__u64 port)
{
struct landlock_net_port_attr attr = {
.allowed_access = allowed_access,
.port = port,
};
int ret = landlock_add_rule(ruleset_fd,
LANDLOCK_RULE_NET_PORT, &attr, 0);
if (ret < 0) {
fprintf(stderr, "Kunne ikke tilføje netværksregel for port %llu: %s\n",
(unsigned long long)port, strerror(errno));
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
int ret;
/* Trin 1: Detekter ABI-version */
int abi_version = landlock_create_ruleset(NULL, 0,
LANDLOCK_CREATE_RULESET_VERSION);
if (abi_version < 0) {
if (errno == EOPNOTSUPP || errno == ENOSYS) {
fprintf(stderr, "Landlock er ikke understøttet af kernen.\n");
fprintf(stderr, "Forsøg at aktivere det med: "
"lsm=landlock,... kerneparameter\n");
} else {
perror("landlock_create_ruleset(VERSION)");
}
return 1;
}
printf("Landlock ABI-version: %d\n", abi_version);
/* Trin 2: Definer håndterede adgangsrettigheder */
/* Filsystemrettigheder (ABI v1-v3) */
__u64 fs_access =
LANDLOCK_ACCESS_FS_EXECUTE |
LANDLOCK_ACCESS_FS_WRITE_FILE |
LANDLOCK_ACCESS_FS_READ_FILE |
LANDLOCK_ACCESS_FS_READ_DIR |
LANDLOCK_ACCESS_FS_REMOVE_DIR |
LANDLOCK_ACCESS_FS_REMOVE_FILE |
LANDLOCK_ACCESS_FS_MAKE_CHAR |
LANDLOCK_ACCESS_FS_MAKE_DIR |
LANDLOCK_ACCESS_FS_MAKE_REG |
LANDLOCK_ACCESS_FS_MAKE_SOCK |
LANDLOCK_ACCESS_FS_MAKE_FIFO |
LANDLOCK_ACCESS_FS_MAKE_BLOCK |
LANDLOCK_ACCESS_FS_MAKE_SYM;
/* Tilføj REFER hvis ABI >= 2 */
if (abi_version >= 2)
fs_access |= LANDLOCK_ACCESS_FS_REFER;
/* Tilføj TRUNCATE hvis ABI >= 3 */
if (abi_version >= 3)
fs_access |= LANDLOCK_ACCESS_FS_TRUNCATE;
/* Netværksrettigheder (kræver ABI >= 4) */
__u64 net_access = 0;
if (abi_version >= 4)
net_access = LANDLOCK_ACCESS_NET_BIND_TCP |
LANDLOCK_ACCESS_NET_CONNECT_TCP;
struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = fs_access,
.handled_access_net = net_access,
};
/* Trin 3: Opret regelsæt */
int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
sizeof(ruleset_attr), 0);
if (ruleset_fd < 0) {
perror("landlock_create_ruleset");
return 1;
}
/* Trin 4: Tilføj filsystemregler */
/* Læseadgang til /usr (læs filer og mapper, eksekvér) */
__u64 usr_access = LANDLOCK_ACCESS_FS_READ_FILE |
LANDLOCK_ACCESS_FS_READ_DIR |
LANDLOCK_ACCESS_FS_EXECUTE;
if (add_path_rule(ruleset_fd, usr_access, "/usr") != 0)
return 1;
/* Læs- og skriveadgang til /tmp */
__u64 tmp_access = LANDLOCK_ACCESS_FS_READ_FILE |
LANDLOCK_ACCESS_FS_READ_DIR |
LANDLOCK_ACCESS_FS_WRITE_FILE |
LANDLOCK_ACCESS_FS_MAKE_REG |
LANDLOCK_ACCESS_FS_MAKE_DIR |
LANDLOCK_ACCESS_FS_REMOVE_FILE |
LANDLOCK_ACCESS_FS_REMOVE_DIR;
if (abi_version >= 3)
tmp_access |= LANDLOCK_ACCESS_FS_TRUNCATE;
if (add_path_rule(ruleset_fd, tmp_access, "/tmp") != 0)
return 1;
/* Trin 5: Tilføj netværksregler (kun HTTPS udgående) */
if (abi_version >= 4) {
if (add_net_rule(ruleset_fd,
LANDLOCK_ACCESS_NET_CONNECT_TCP, 443) != 0)
return 1;
printf("Netværksregel tilføjet: TCP connect port 443\n");
} else {
printf("Advarsel: Netværksrestriktioner kræver ABI >= 4 "
"(fundet: %d)\n", abi_version);
}
/* Trin 6: Aktiver NO_NEW_PRIVS og håndhæv sandbox */
ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
if (ret) {
perror("prctl(PR_SET_NO_NEW_PRIVS)");
close(ruleset_fd);
return 1;
}
ret = landlock_restrict_self(ruleset_fd, 0);
close(ruleset_fd);
if (ret) {
perror("landlock_restrict_self");
return 1;
}
printf("Landlock-sandbox er nu aktiv!\n");
printf("Tilladte operationer:\n");
printf(" - Læs og eksekvér: /usr\n");
printf(" - Læs og skriv: /tmp\n");
printf(" - TCP connect: port 443 kun\n");
/* Trin 7: Eksekvér den sandboxede applikation */
if (argc > 1) {
execvp(argv[1], &argv[1]);
perror("execvp");
return 1;
}
/* Demo: Test adgang */
FILE *f = fopen("/tmp/landlock_test.txt", "w");
if (f) {
fprintf(f, "Denne fil blev oprettet i sandboxen.\n");
fclose(f);
printf("Succes: Skrev til /tmp/landlock_test.txt\n");
}
f = fopen("/etc/passwd", "r");
if (!f) {
printf("Forventet: Kan ikke læse /etc/passwd (%s)\n",
strerror(errno));
} else {
printf("Uventet: Kunne læse /etc/passwd!\n");
fclose(f);
}
return 0;
}
Kompilér og kør programmet:
gcc -Wall -o landlock_sandbox landlock_sandbox.c
./landlock_sandbox
# Eller med en kommando:
./landlock_sandbox /bin/ls /tmp
Bemærk, at programmet degraderer med ynde ved ældre kerner: det detekterer ABI-versionen og tilpasser de håndterede rettigheder derefter. Det er en vigtig best practice for produktionsklar kode — og noget jeg vil anbefale at gøre fra dag ét.
Filsystemadgangsrettigheder
Landlock tilbyder en finkornet kontrol over filsystemoperationer. Rettighederne er opdelt i to kategorier: filrettigheder (der gælder for individuelle filer) og mapperettigheder (der gælder for mapper og deres indhold).
Filrettigheder
| Konstant | ABI | Beskrivelse |
|---|---|---|
LANDLOCK_ACCESS_FS_EXECUTE |
v1 | Eksekvér en fil (via execve(2) og lignende) |
LANDLOCK_ACCESS_FS_WRITE_FILE |
v1 | Åbn en fil til skrivning (kræver ofte også TRUNCATE) |
LANDLOCK_ACCESS_FS_READ_FILE |
v1 | Åbn en fil med læseadgang |
LANDLOCK_ACCESS_FS_TRUNCATE |
v3 | Afkort filer via truncate(2), ftruncate(2), creat(2) eller open(2) med O_TRUNC |
LANDLOCK_ACCESS_FS_IOCTL_DEV |
v5 | Udfør ioctl(2)-kald på karakter- og blokenheder |
Mapperettigheder
| Konstant | ABI | Beskrivelse |
|---|---|---|
LANDLOCK_ACCESS_FS_READ_DIR |
v1 | Åbn en mappe eller oplist dens indhold |
LANDLOCK_ACCESS_FS_REMOVE_DIR |
v1 | Fjern en tom mappe eller omdøb den |
LANDLOCK_ACCESS_FS_REMOVE_FILE |
v1 | Unlink (fjern) eller omdøb en fil |
LANDLOCK_ACCESS_FS_MAKE_CHAR |
v1 | Opret en karakter-enhedsfil |
LANDLOCK_ACCESS_FS_MAKE_DIR |
v1 | Opret en ny mappe |
LANDLOCK_ACCESS_FS_MAKE_REG |
v1 | Opret en regulær fil |
LANDLOCK_ACCESS_FS_MAKE_SOCK |
v1 | Opret en UNIX-domænesocket |
LANDLOCK_ACCESS_FS_MAKE_FIFO |
v1 | Opret en named pipe (FIFO) |
LANDLOCK_ACCESS_FS_MAKE_BLOCK |
v1 | Opret en blok-enhedsfil |
LANDLOCK_ACCESS_FS_MAKE_SYM |
v1 | Opret et symbolsk link |
LANDLOCK_ACCESS_FS_REFER |
v2 | Tillad hard-linking og flytning af filer mellem mapper (nægtet som standard) |
Vigtige bemærkninger om fil- vs. mapperettigheder
Der er en vigtig skelnen her, som det er værd at dvæle ved:
- Filrettigheder (EXECUTE, WRITE_FILE, READ_FILE, TRUNCATE, IOCTL_DEV) gælder kun for regulære filer og enheder. De kan angives i regler, der peger på både filer og mapper — i sidstnævnte tilfælde gælder de for alle filer under mappen.
- Mapperettigheder (READ_DIR, REMOVE_DIR, MAKE_*, REFER) gælder kun for mapper og kan kun angives i regler, der peger på mapper.
- Peger reglen på en specifik fil, gælder rettigheden kun for den pågældende fil. Peger den på en mappe, gælder rettigheden for alle filer i hele undertræet.
Netværksadgangskontrol
Med ABI version 4 (Linux 6.7) fik Landlock mulighed for at kontrollere TCP-netværksadgang. Det var en stor milepæl, fordi det gør det muligt at begrænse, hvilke porte en sandboxet proces kan binde til og oprette forbindelser til.
Tilgængelige netværksrettigheder
| Konstant | Beskrivelse |
|---|---|
LANDLOCK_ACCESS_NET_BIND_TCP |
Tillad binding af en TCP-socket til en specifik lokal port |
LANDLOCK_ACCESS_NET_CONNECT_TCP |
Tillad oprettelse af en aktiv TCP-forbindelse til en specifik fjernport |
Eksempler på netværksrestriktioner
Her er et par eksempler på, hvordan netværksregler oprettes i praksis:
/* Tillad en webserver at binde til port 80 og 443 */
struct landlock_net_port_attr bind_http = {
.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
.port = 80,
};
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, &bind_http, 0);
struct landlock_net_port_attr bind_https = {
.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
.port = 443,
};
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, &bind_https, 0);
/* Tillad udgående forbindelser til en database på port 5432 */
struct landlock_net_port_attr connect_db = {
.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
.port = 5432,
};
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT, &connect_db, 0);
Netværksreglerne fungerer efter samme deny-by-default-princip som filsystemreglerne: Når LANDLOCK_ACCESS_NET_BIND_TCP eller LANDLOCK_ACCESS_NET_CONNECT_TCP er inkluderet i handled_access_net, nægtes alle bind- eller connect-operationer, medmindre der er en specifik regel, der tillader den pågældende port.
Bemærk: Netværksrestriktionerne er i øjeblikket begrænset til TCP. UDP, SCTP og andre protokoller er endnu ikke understøttede, men der arbejdes på at udvide understøttelsen i fremtidige kerneversioner. Det er en begrænsning, man bør have in mente.
IPC-scoping
ABI version 6 introducerede IPC-scoping (Inter-Process Communication), og det er ærligt talt en af de funktioner, jeg synes fortjener mere opmærksomhed. Den giver Landlock mulighed for at begrænse kommunikation mellem processer — noget der er særligt vigtigt for at forhindre, at en sandboxet proces kan påvirke processer uden for sit sikkerhedsdomæne.
Tilgængelige IPC-scoping flags
| Konstant | Beskrivelse |
|---|---|
LANDLOCK_SCOPE_SIGNAL |
Begrænser signalafsendelse: En sandboxet proces kan kun sende signaler til processer inden for samme Landlock-domæne eller et underdomæne. Signaler til processer uden for sandboxen blokeres. |
LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
Begrænser forbindelser via abstrakte UNIX-sockets: En sandboxet proces kan kun forbinde til abstrakte UNIX-sockets, der tilhører processer i samme eller et underordnet Landlock-domæne. |
Brug af IPC-scoping
IPC-scoping aktiveres via scoped-feltet i landlock_ruleset_attr:
struct landlock_ruleset_attr attr = {
.handled_access_fs = /* ... filsystemrettigheder ... */,
.handled_access_net = /* ... netværksrettigheder ... */,
.scoped = LANDLOCK_SCOPE_SIGNAL |
LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET,
};
int ruleset_fd = landlock_create_ruleset(&attr, sizeof(attr), 0);
/* ... tilføj regler ... */
landlock_restrict_self(ruleset_fd, 0);
Med IPC-scoping aktiveret kan en sandboxet proces ikke længere:
- Sende
SIGKILL,SIGTERMeller andre signaler til processer uden for sin sandbox. - Oprette forbindelse til abstrakte UNIX-domænesockets, der tilhører processer i et andet sikkerhedsdomæne.
Det er særligt værdifuldt i multi-tenant-miljøer, hvor flere applikationer kører på samme vært, og man ønsker at forhindre lateral bevægelse mellem sandboxede processer.
Praktiske værktøjer: Landrun og Island
Selvom Landlock API'et er relativt simpelt, kan det godt være lidt besværligt at implementere fra bunden for hver applikation. Heldigvis findes der et par rigtig gode værktøjer, der gør det nemt at bruge Landlock-sandboxing uden at skrive C-kode.
Landrun
Landrun er et letvægtigt CLI-værktøj skrevet i Go, der gør det praktisk at sandboxe enhver kommando med finkornet filsystem- og netværksadgangskontrol. Det kræver ingen root-privilegier, ingen containere og ingen SELinux/AppArmor-konfiguration. Kort sagt: det er Landlock gjort tilgængeligt for alle.
Grundlæggende brug
# Giv læseadgang med eksekveringsrettigheder til /usr,
# læs-/skriveadgang til /tmp, og opret en fil
landrun --rox /usr --rw /tmp touch /tmp/testfil
# Kun læseadgang - skrivning nægtes
landrun --rox /usr touch /tmp/testfil
# Resultat: touch: cannot touch '/tmp/testfil': Permission denied
# Oplist hjemmebiblioteket med minimumsrettigheder
landrun --rox /usr/bin/ls --rox /usr/lib --ro /home ls /home
# Start en interaktiv shell i en sandbox
landrun --rox /usr/ --ro /lib,/lib64 /usr/bin/bash
Netværksrestriktioner med Landrun
# Tillad kun HTTPS-udgående forbindelser
landrun --rox /usr/ --ro /etc --connect-tcp 443 \
curl https://eksempel.dk
# Webserver bundet til specifikke porte
landrun --rox /usr/bin --ro /lib,/lib64,/var/www \
--rwx /var/log --bind-tcp 80,443 \
/usr/bin/nginx
# Debugging med strace
landrun --rox /usr strace -f -e trace=all ls
Landrun-kommandolinjeflags
| Flag | Beskrivelse |
|---|---|
--ro <sti> |
Giv kun læseadgang til den angivne sti |
--rox <sti> |
Læseadgang med eksekveringsrettigheder |
--rw <sti> |
Læse- og skriveadgang |
--rwx <sti> |
Fuld læse-, skrive- og eksekveringsadgang |
--bind-tcp <port> |
Tillad TCP-binding til de angivne porte |
--connect-tcp <port> |
Tillad udgående TCP-forbindelser til de angivne porte |
--env <VAR> |
Videregiv miljøvariabel til den sandboxede proces |
--best-effort |
Degradér med ynde på ældre kerner |
--ldd |
Tilføjer automatisk nødvendige delte biblioteker |
--add-exec |
Tilføj automatisk den eksekverbare fils sti |
Island
Island er et andet sandboxing-værktøj, der er udviklet af Landlock-teamet selv. I modsætning til Landrun, der primært bruges interaktivt på kommandolinjen, fokuserer Island på TOML-baserede profiler og kontekstbevidst aktivering.
Islands kernekoncepter
- Zero-code integration: Kører eksisterende binære filer uden modifikation.
- TOML-baseret konfiguration: Sikkerhedspolitikker defineres i deklarative TOML-filer i stedet for kode.
- Kontekstbevidst aktivering: Profiler aktiveres automatisk baseret på den aktuelle arbejdsmappe. Når du navigerer ind i et projektbibliotek, anvender Island automatisk den tilhørende sikkerhedsprofil. Ret smart, faktisk.
Profilstruktur
Island-profiler gemmes i ~/.config/island/profiles/<n>/ og består af:
profile.toml: Definerer aktiveringskonteksten, miljøvariable og isoleringsindstillinger.landlock/: Indeholder sikkerhedspolitikfiler (f.eks. filsystemadgangsregler) i Landlock Config-formatet.
# Eksempel: ~/.config/island/profiles/webprojekt/profile.toml
[context]
when_beneath = ["/home/bruger/projekter/webapp"]
[environment]
NODE_ENV = "development"
PORT = "3000"
[workspace]
writable = true
Miljøintegration giver kontekstbevidsthed: Variablerne ISLAND_CONTEXT_BENEATH_[0-9]+ eksponerer alle when_beneath-stier relateret til den aktuelt anvendte profil, og profilspecifikke miljøvariable injiceres automatisk.
Island er stadig under aktiv udvikling, men det repræsenterer en spændende retning for brugervenlig Landlock-sandboxing med deklarativ konfiguration.
Integration med systemd-tjenester
En af de mest praktiske anvendelser af Landlock er at sandboxe systemd-tjenester i produktion. Ved at kombinere Landlock med systemd kan du give hver tjeneste præcis de rettigheder, den behøver — og ikke en eneste mere.
Landrun med systemd
Den simpleste metode er at bruge Landrun som en wrapper i en systemd-servicefil:
# /etc/systemd/system/nginx-landrun.service
[Unit]
Description=Nginx webserver med Landlock-sandbox via Landrun
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/landrun \
--rox /usr/bin,/usr/lib \
--ro /etc/nginx,/etc/ssl,/etc/passwd,/etc/group,/etc/nsswitch.conf \
--rwx /var/log/nginx \
--rwx /var/cache/nginx \
--bind-tcp 80,443 \
/usr/bin/nginx -g 'daemon off;'
Restart=always
User=nginx
Group=nginx
# Kombinér med systemds egne sikkerhedsfunktioner
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Aktivér og start tjenesten:
sudo systemctl daemon-reload
sudo systemctl enable nginx-landrun
sudo systemctl start nginx-landrun
sudo systemctl status nginx-landrun
Programmatisk integration
For tjenester, der er bygget med Landlock-understøttelse direkte i applikationen, kan systemd-servicefilen være enklere:
# /etc/systemd/system/min-applikation.service
[Unit]
Description=Min applikation med indbygget Landlock-sandbox
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/min-applikation --enable-landlock
Restart=on-failure
User=appbruger
Group=appgruppe
# Systemd-sikkerhedshærdning der komplementerer Landlock
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/var/lib/min-applikation /var/log/min-applikation
ProtectHome=true
PrivateTmp=true
ProtectKernelModules=true
ProtectKernelTunables=true
RestrictNamespaces=true
[Install]
WantedBy=multi-user.target
Kommende systemd-integration via LandlockConfig
Der er også et igangværende arbejde med at integrere Landlock direkte i systemd via en ny LandlockConfig-nøgle i [Service]-sektionen. Denne funktion, der bygger på liblandlockconfig-biblioteket, vil gøre det muligt at pege på en mappe med TOML-filer, der definerer Landlock-politikken:
# Fremtidig systemd-integration (under udvikling)
[Service]
LandlockConfig=/etc/landlock/nginx/
Denne tilgang vil forenkle konfigurationen betydeligt og gøre Landlock-sandboxing til en førsteklasses borger i systemd-økosystemet. Det ser jeg personligt meget frem til.
Best practices og sikkerhedsanbefalinger
For at få mest muligt ud af Landlock-sandboxing, er her de anbefalinger, jeg mener er vigtigst:
1. Anvend "leaf-level" rettigheder
Undgå at give brede rettigheder til rodmapper. Jo mere specifik du er med dine stier, desto stærkere er sandboxen. Giv f.eks. adgang til /usr/share/min-app i stedet for hele /usr.
2. Defense-in-depth
Landlock bør aldrig være dit eneste sikkerhedslag. Kombinér det med:
- seccomp-bpf: Begræns tilgængelige systemkald (Landlock kontrollerer ressourcer, seccomp kontrollerer operationer).
- Linux namespaces: Isolér processens syn på filsystem, netværk, PID'er osv.
- SELinux/AppArmor: Systemdækkende politikker som et overordnet sikkerhedslag.
- systemd-hærdning:
NoNewPrivileges,ProtectSystem,PrivateTmpm.fl.
3. ABI-versionsdetektion og graceful degradation
Antag aldrig en bestemt ABI-version. Detektér altid versionen ved kørselstid og tilpas dine håndterede rettigheder. Hvis en kerne ikke understøtter netværksrestriktioner (ABI < 4), bør din applikation stadig fungere — bare uden den pågældende beskyttelse.
int abi = landlock_create_ruleset(NULL, 0,
LANDLOCK_CREATE_RULESET_VERSION);
if (abi < 0) {
/* Landlock ikke tilgængeligt - kør uden sandbox
eller afslut, afhængigt af sikkerhedskrav */
if (errno == EOPNOTSUPP)
fprintf(stderr, "Advarsel: Landlock ikke understøttet\n");
/* ... */
}
/* Tilpas rettigheder baseret på ABI-version */
__u64 fs_rights = BASE_FS_RIGHTS;
if (abi >= 2) fs_rights |= LANDLOCK_ACCESS_FS_REFER;
if (abi >= 3) fs_rights |= LANDLOCK_ACCESS_FS_TRUNCATE;
/* ... osv. */
4. Respektér de 16 lags grænse
Landlock tillader maksimalt 16 stablede lag per tråd. Planlæg din lagdeling omhyggeligt, og undgå at oprette unødvendige lag. I de fleste tilfælde er 1-3 lag mere end tilstrækkeligt.
5. Håndtér fejl korrekt
Tjek altid returværdier fra alle tre systemkald. Håndtér særligt:
EOPNOTSUPP: Landlock er ikke aktiveret i kernen.EINVAL: Ugyldige parametre (f.eks. ukendte flags på en ældre kerne).E2BIG: For mange stablede lag (over 16).
6. Test grundigt
Test din sandbox med både positive og negative tilfælde. Verificér, at tilladte operationer virker, og at forbudte operationer nægtes med de forventede fejlmeddelelser (EACCES). Det lyder banalt, men det overses overraskende tit.
7. Dokumentér din sikkerhedspolitik
Beskriv tydeligt, hvilke rettigheder din applikation behøver og hvorfor. Det letter vedligeholdelse og sikkerhedsgennemgang — og din fremtidige dig selv vil takke dig for det.
Landlock vs. andre sikkerhedsmekanismer
For at forstå, hvornår Landlock er det rigtige valg, er det nyttigt at sammenligne det med andre Linux-sikkerhedsmekanismer.
| Egenskab | Landlock | SELinux | AppArmor | seccomp-bpf | Containere/Namespaces |
|---|---|---|---|---|---|
| Kræver root | Nej | Ja | Ja | Nej* | Typisk ja |
| Kontroltype | Ressource-adgang (filer, porte, IPC) | Label-baseret MAC | Sti-baseret MAC | Systemkald-filtrering | Fuld isolation |
| Granularitet | Fil-/mappe-niveau, port-niveau | Objekt-niveau | Sti-niveau | Systemkald-niveau | Procesgruppe-niveau |
| Stackbar | Ja (op til 16 lag) | Nej (eksklusiv) | Nej (eksklusiv) | Ja | Ja (nested) |
| Kompleksitet | Lav | Høj | Middel | Middel-høj | Høj |
| Applikationsstyret | Ja | Nej | Nej | Ja | Nej |
| Netværkskontrol | TCP (fra ABI v4) | Fuld | Begrænset | Systemkald-niveau | Fuld (netns) |
| Overhead | Minimalt | Lavt | Lavt | Minimalt | Varierende |
* seccomp-bpf kræver PR_SET_NO_NEW_PRIVS eller CAP_SYS_ADMIN
Hvornår skal du bruge hvad?
- Landlock: Ideel når en applikation selv vil begrænse sin adgang til filsystem, netværk og IPC. Perfekt til applikationer, der processerer utroværdigt input, plugins eller tredjepartskode. Intet behov for root eller systemkonfiguration.
- SELinux/AppArmor: Brug til systemdækkende, obligatorisk adgangskontrol (MAC), der håndhæves af administratorer. Velegnet til servermiljøer med stringente compliance-krav.
- seccomp-bpf: Brug til at filtrere tilgængelige systemkald. Komplementerer Landlock godt: Landlock kontrollerer hvilke ressourcer der kan tilgås, seccomp kontrollerer hvilke operationer der kan udføres.
- Containere/Namespaces: Brug til fuld isolation af applikationer, særligt i multi-tenant-miljøer. Giver stærkest isolation, men med størst overhead og kompleksitet.
Den stærkeste sikkerhed opnås ved at kombinere flere mekanismer: Landlock + seccomp-bpf + namespaces + SELinux/AppArmor udgør en robust defense-in-depth-strategi.
Fremtiden for Landlock
Landlock er under aktiv udvikling, og der er flere spændende forbedringer på vej.
Rust-integration i kernen
Med Rusts stigende tilstedeværelse i Linux-kernen (fra Linux 6.1) er der mulighed for, at dele af Landlock-infrastrukturen fremover kan drage fordel af Rusts hukommelsessikkerhed. Rust-landlock-biblioteket (rust-landlock) giver allerede i dag en sikker Rust-API til Landlock fra brugerrum, og det arbejde informerer potentielle kerne-sideimplementeringer.
Udvidede adgangskontroller
Flere områder er under overvejelse for fremtidige ABI-versioner:
- UDP-understøttelse: Udvidelse af netværksrestriktioner til at omfatte UDP-sockets, hvilket vil dække DNS-opslag og andre protokoller.
- Udvidet ioctl-kontrol: Mere granuleret kontrol over enhedsspecifikke ioctl-kommandoer.
- Audit-understøttelse: Forbedret logning og revision af Landlock-hændelser (arbejdet er allerede påbegyndt i Linux 6.14).
- Filsystemtopologi: Potentiel kontrol over mount-operationer og filsystem-topologi-ændringer.
Signerede eBPF-programmer
Landlock var oprindeligt designet med eBPF-integration in mente. Selvom den nuværende implementering bruger en mere traditionel LSM-tilgang, er der forskning i signerede eBPF-programmer, der kan give endnu mere fleksibel og verificerbar sandboxing. I dette paradigme kan en betroet tredjepart signere eBPF-programmer, som kernen derefter kan indlæse med sikkerhed for programmets integritet.
Systemd-førsteklasses integration
Som nævnt er der et aktivt arbejde med at integrere Landlock direkte i systemd via LandlockConfig-direktivet. Når dette er færdiggjort, vil det være trivielt at tilføje Landlock-sandboxing til enhver systemd-tjeneste via deklarativ konfiguration.
Håndtering af "weird files"
I Linux 6.14 blev der tilføjet forbedret håndtering af såkaldte "weird files" — filer, der kan være synlige efter filsystemkorruption. Landlock håndterer nu disse filer korrekt, hvilket styrker robustheden i sikkerhedsmodellen.
Konklusion
Landlock LSM repræsenterer et paradigmeskifte i Linux-sikkerhed. For første gang kan enhver applikation — uanset privilegieniveau — oprette en robust, uigenkaldelig sikkerhedssandbox uden root-adgang, uden containere og uden kompleks systemkonfiguration. Med den stadigt stigende strøm af Linux-kernesårbarheder (5.530 CVE'er alene i 2025) er applikationsniveau-sandboxing ikke længere en luksus — det er en nødvendighed.
Landlocks styrker taler for sig selv:
- Simpelt API: Kun tre systemkald at lære og anvende.
- Uprivilegeret: Ingen root krævet, hvilket muliggør sandboxing af enhver brugerproces.
- Stackbar: Fungerer ved siden af SELinux, AppArmor og andre LSM'er som et ekstra sikkerhedslag.
- Uigenkaldelig: Når sandboxen er aktiv, kan den aldrig fjernes — kun styrkes.
- Bred dækning: Filsystem, netværk og IPC-kontrol i en samlet ramme.
- Lav overhead: Minimal påvirkning af systemets ydelse.
Uanset om du er applikationsudvikler, systemadministrator eller sikkerhedsarkitekt, bør Landlock være en del af din værktøjskasse. Start i dag: tjek din kerneversion (uname -r), verificér Landlock-understøttelse (cat /sys/kernel/security/landlock/abi_version), og begynd at sandboxe dine applikationer — enten programmatisk via C/Rust API'et eller med værktøjer som Landrun og Island. Hver ekstra sandboxet applikation er et skridt mod en sikrere Linux-infrastruktur.