Tüm eğitimler
TEKNİK REHBERGÖMÜLÜ LİNUXGÜVENLİK2026

Landlock
Kullanıcı Alanı Erişim Kontrolü

Linux 5.13+ Landlock LSM ile süreç kendi erişim haklarını kısıtlar; gömülü uygulama sandbox'ı, minimal ayrıcalık ilkesi uygulaması ve daemon güvenlik hapsı.

00 Landlock Neden?

Landlock, Linux 5.13'te ana çekirdeğe dahil edilen ve süreçlerin kendi erişim haklarını programatik olarak kısıtlamasına olanak tanıyan bir LSM (Linux Security Module) mekanizmasıdır. Geleneksel güvenlik modellerinde kısıtlamalar dışarıdan (root, sistem yöneticisi) uygulanırken Landlock bu kısıtlamayı uygulamanın kendisine bırakır.

Gömülü sistemlerde daemon güvenliği kritik bir konudur: bir saldırgan savunmasız bir sistem servisini (MQTT broker, HTTP sunucusu, CANBUS daemon) ele geçirirse tüm dosya sistemine erişim kazanabilir. Landlock bu saldırı yüzeyini dramatik biçimde daraltır; daemon yalnızca gerçekten ihtiyaç duyduğu dizinlere ve ağ işlemlerine erişebilir.

Landlock öncesi — saldırı yüzeyi:
  Güvenlik açığı bulunan daemon
        |
        v
  Tüm dosya sistemine erişim
  /etc/passwd, /root/.ssh, /var/lib/...
  Tüm ağ portlarına bağlanma
  [saldırgan tam kontrolü ele geçirir]

Landlock sonrası — saldırı yüzeyi:
  Güvenlik açığı bulunan daemon
        |
        v
  Yalnızca /var/lib/mqtt/ okuma/yazma
  Yalnızca port 1883'te dinleme
  Başka hiçbir dizin veya port erişilemez
  [saldırgan etkisi kapsamlı biçimde sınırlandırılır]
  

seccomp ile karşılaştırma: seccomp sistem çağrısı düzeyinde filtre uygular (hangi syscall'ların kullanılabileceğini kısıtlar); Landlock ise kaynak erişimi düzeyinde çalışır (hangi dosya ve ağ kaynaklarına erişilebileceğini kısıtlar). İki mekanizma birbirini tamamlar ve sıkı güvenlik politikaları için birlikte kullanılabilir.

ÖzellikseccompLandlockSELinux/AppArmor
Kısıtlama düzeyiSyscallKaynak erişimiKaynak + syscall
Politika konumuUygulama içiUygulama içiSistem geneli
Kök gerektiriyor muHayır (PR_SET_NO_NEW_PRIVS)HayırEvet (politika yüklemek için)
Kernel versiyonu3.5+5.13+Çok eski
Yapılandırma karmaşıklığıOrtaDüşükYüksek
Gömülü kullanım kolaylığıİyiÇok iyiOrta

01 Landlock Kavramları

Landlock üç temel kavram üzerine inşa edilmiştir: ruleset (kural kümesi), rule (kural) ve handled_access_fs bayrakları. Bu kavramları anlamak doğru bir sandbox politikası tasarlamak için gereklidir.

Ruleset (kural kümesi): Bir Landlock politikasının en üst düzey nesnesidir. Ruleset, hangi erişim türlerinin bu politika tarafından yönetileceğini tanımlar. Örneğin "bu ruleset dosya yazma ve dosya çalıştırma erişimlerini yönetir" şeklinde bir tanım yapılır.

Rule (kural): Belirli bir kaynak (dosya yolu, ağ portu) için izin verilen erişim türlerini belirtir. Ruleset varsayılan olarak her şeyi reddeder; kurallar yalnızca izin verilen kaynakları açıkça tanımlar.

Handled access bayrakları: Ruleset'in aktif olarak yönettiği erişim türleridir. Bir erişim türü handled olarak işaretlenmemişse Landlock o erişim türü üzerinde herhangi bir kısıtlama uygulamaz.

Landlock politika modeli:

  landlock_create_ruleset(handled_access_fs = OKUMA | YAZMA | ÇALISTIRMA)
        |
        v
  Ruleset oluşturuldu (her şey varsayılan olarak reddedildi)
        |
        +-- landlock_add_rule(ruleset, "/var/lib/app", OKUMA | YAZMA)
        +-- landlock_add_rule(ruleset, "/usr/bin/app", OKUMA | ÇALISTIRMA)
        +-- landlock_add_rule(ruleset, "/lib",          OKUMA)
        |
        v
  landlock_restrict_self(ruleset)
        |
        v
  Süreç artık yalnızca tanımlı kaynaklara erişebilir
  

Landlock ABI versiyonu zaman içinde genişlemiştir: ABI 1 (kernel 5.13) temel dosya sistemi kısıtlarını, ABI 2 (kernel 5.19) sembolik bağ oluşturma desteğini, ABI 3 (kernel 6.2) inode başına kural sayısı artışını, ABI 4 (kernel 6.7) ağ kısıtlarını getirmiştir. Uygulamanızın desteklediği ABI'yi tespit etmek için landlock_create_ruleset syscall'ını özel bir flag ile çağırabilirsiniz.

/* Desteklenen Landlock ABI versiyonunu tespit et */
#include <linux/landlock.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdio.h>

int landlock_get_abi_version(void) {
    int abi = syscall(SYS_landlock_create_ruleset,
                      NULL, 0,
                      LANDLOCK_CREATE_RULESET_VERSION);
    return abi;
}

int main(void) {
    int abi = landlock_get_abi_version();
    if (abi < 0) {
        perror("Landlock desteklenmiyor");
        return 1;
    }
    printf("Landlock ABI versiyonu: %d\n", abi);
    return 0;
}

02 Temel Kullanım

Landlock politikası uygulamak üç syscall ile gerçekleşir: landlock_create_ruleset, landlock_add_rule ve landlock_restrict_self. Bu bölümde eksiksiz bir C örneği üzerinden temel kullanım gösterilmektedir.

Önemli ön koşul: landlock_restrict_self çağrısından önce sürecin no_new_privs bayrağını ayarlaması gerekir. Bu, Landlock kısıtlamalarını setuid ikilileri aracılığıyla atlatma girişimlerini engeller.

/* landlock_demo.c — temel Landlock kullanımı */
#define _GNU_SOURCE
#include <linux/landlock.h>
#include <sys/syscall.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

/* Landlock syscall sarmalayıcıları (glibc henüz eklemediyse) */
static int landlock_create_ruleset(
        const struct landlock_ruleset_attr *attr,
        size_t size, uint32_t flags) {
    return syscall(SYS_landlock_create_ruleset, attr, size, flags);
}

static int landlock_add_rule(int ruleset_fd,
        enum landlock_rule_type type,
        const void *attr, uint32_t flags) {
    return syscall(SYS_landlock_add_rule, ruleset_fd, type, attr, flags);
}

static int landlock_restrict_self(int ruleset_fd, uint32_t flags) {
    return syscall(SYS_landlock_restrict_self, ruleset_fd, flags);
}

/* Yardımcı: bir dizine okuma/yazma erişimi ekle */
static int add_path_rule(int ruleset_fd,
                         const char *path,
                         uint64_t allowed_access) {
    struct landlock_path_beneath_attr attr = {
        .allowed_access = allowed_access,
    };
    int fd = open(path, O_PATH | O_CLOEXEC);
    if (fd < 0) {
        fprintf(stderr, "Yol acilamadi: %s: %s\n", path, strerror(errno));
        return -1;
    }
    attr.parent_fd = fd;
    int ret = landlock_add_rule(ruleset_fd,
                                LANDLOCK_RULE_PATH_BENEATH,
                                &attr, 0);
    close(fd);
    return ret;
}

int main(void) {
    /* 1. Adım: Ruleset oluştur */
    struct landlock_ruleset_attr ruleset_attr = {
        .handled_access_fs =
            LANDLOCK_ACCESS_FS_READ_FILE   |
            LANDLOCK_ACCESS_FS_WRITE_FILE  |
            LANDLOCK_ACCESS_FS_READ_DIR    |
            LANDLOCK_ACCESS_FS_EXECUTE,
    };

    int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
                                             sizeof(ruleset_attr), 0);
    if (ruleset_fd < 0) {
        perror("landlock_create_ruleset basarisiz");
        return 1;
    }

    /* 2. Adım: İzin verilen kaynakları tanımla */
    /* Yalnızca /tmp dizinine okuma+yazma izni */
    if (add_path_rule(ruleset_fd, "/tmp",
            LANDLOCK_ACCESS_FS_READ_FILE |
            LANDLOCK_ACCESS_FS_WRITE_FILE |
            LANDLOCK_ACCESS_FS_READ_DIR) < 0) {
        close(ruleset_fd);
        return 1;
    }

    /* /usr/bin dizinine yalnızca okuma+çalıştırma izni */
    if (add_path_rule(ruleset_fd, "/usr/bin",
            LANDLOCK_ACCESS_FS_READ_FILE |
            LANDLOCK_ACCESS_FS_EXECUTE   |
            LANDLOCK_ACCESS_FS_READ_DIR) < 0) {
        close(ruleset_fd);
        return 1;
    }

    /* 3. Adım: no_new_privs bayragini ayarla (zorunlu) */
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
        perror("prctl PR_SET_NO_NEW_PRIVS basarisiz");
        close(ruleset_fd);
        return 1;
    }

    /* 4. Adım: Kısıtlamayı uygula */
    if (landlock_restrict_self(ruleset_fd, 0) < 0) {
        perror("landlock_restrict_self basarisiz");
        close(ruleset_fd);
        return 1;
    }
    close(ruleset_fd);

    printf("Landlock kısıtlaması aktif.\n");
    printf("/tmp/test.txt yazma deneyi...\n");

    /* Bu izin verildi: /tmp altında */
    int fd = open("/tmp/test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd >= 0) {
        write(fd, "merhaba\n", 8);
        close(fd);
        printf("OK: /tmp/test.txt yazildi\n");
    }

    /* Bu reddedilecek: /etc izin verilmedi */
    fd = open("/etc/passwd", O_RDONLY);
    if (fd < 0)
        printf("REDDEDILDI: /etc/passwd erisilemez (beklenen)\n");
    else {
        printf("HATA: /etc/passwd erisilebildi!\n");
        close(fd);
    }

    return 0;
}
# Derleme
gcc -o landlock_demo landlock_demo.c

# Çalıştır
./landlock_demo

03 Dosya Sistemi Kısıtları

Landlock dosya sistemi erişim bayrakları, uygulamanın hangi işlemleri hangi dizinlerde yapabileceğini ince tanecikli (fine-grained) şekilde belirler. Her bayrağın anlamını ve tipik kullanım senaryosunu bu bölümde ele alacağız.

LANDLOCK_ACCESS_FS_* bayraklarının tamamı:

READ_FILEDosya içeriğini oku (open+read); kütüphane yüklemek için gerekli
WRITE_FILEDosya içeriğine yaz (open+write); log ve veri dosyaları için gerekli
EXECUTEDosyayı çalıştır (execve); yalnızca binary yürütme gerektiren dizinlerde açılır
READ_DIRDizin içeriğini listele (opendir/readdir); dizin gezinimi için gerekli
REMOVE_DIRDizin sil (rmdir); geçici dizin temizliği için gerekebilir
REMOVE_FILEDosya sil (unlink); log rotasyonu, geçici dosya temizliği
MAKE_CHARKarakter aygıt dosyası oluştur (mknod); nadiren gerekli
MAKE_DIRAlt dizin oluştur (mkdir); dinamik dizin yapısı gerektiren uygulamalar
MAKE_REGYeni düzenli dosya oluştur; yazma işlemleri için CREATE bayrağı ile birlikte gerekli
MAKE_SOCKUnix domain soket dosyası oluştur; IPC kullanan daemon'lar için gerekli
MAKE_FIFOİsimli boru (FIFO) oluştur
MAKE_BLOCKBlok aygıt dosyası oluştur
MAKE_SYMSembolik bağ oluştur (kernel 5.19+)
REFERDosyayı başka bir dizine taşı/yeniden adlandır (kernel 5.19+)
TRUNCATEDosyayı kırp/boyutunu değiştir (kernel 6.2+)

Tipik bir daemon için minimum izin kümesi örneği:

/*
 * Minimal daemon izin kümesi:
 * - /etc/myapp/   : yapılandırmayı oku
 * - /var/lib/myapp: durum verisi okuma/yazma
 * - /var/log/myapp: log dosyaları yaz
 * - /tmp          : geçici dosyalar
 * - /lib, /usr/lib: kütüphaneleri oku/çalıştır
 * - /usr/bin      : yardımcı ikililer
 */

struct {
    const char *path;
    uint64_t   access;
} rules[] = {
    { "/etc/myapp",       FS_READ },
    { "/var/lib/myapp",   FS_READ | FS_WRITE | FS_MAKE_REG | FS_REMOVE_FILE },
    { "/var/log/myapp",   FS_WRITE | FS_MAKE_REG | FS_REMOVE_FILE },
    { "/tmp",             FS_READ | FS_WRITE | FS_MAKE_REG | FS_REMOVE_FILE },
    { "/lib",             FS_READ | FS_EXECUTE },
    { "/usr/lib",         FS_READ | FS_EXECUTE },
    { "/usr/bin",         FS_READ | FS_EXECUTE },
    { NULL, 0 }
};

/* Buradaki FS_READ, FS_WRITE vb. kısaltmalardır;
 * gerçek kodda LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR
 * gibi tam bayrak adları kullanılmalıdır. */

Dikkat gerektiren nokta: Bir dizine WRITE_FILE izni vermek yeni dosya oluşturmayı kapsamaz. Yeni dosya oluşturabilmek için MAKE_REG bayrağı da gereklidir. Benzer şekilde var olan bir dosyayı silmek için REMOVE_FILE, alt dizin silmek için REMOVE_DIR ayrıca belirtilmelidir.

04 Ağ Kısıtları

Linux 6.7 ile birlikte Landlock, TCP ağ operasyonlarını da kısıtlayabilir hale gelmiştir. LANDLOCK_ACCESS_NET_BIND_TCP ve LANDLOCK_ACCESS_NET_CONNECT_TCP bayrakları ile sürecin hangi portlarda dinleyebileceği ve bağlanabileceği kontrol altına alınır.

Ağ kısıtları dosya sistemi kısıtlarından bağımsızdır; aynı ruleset içinde her ikisi birlikte tanımlanabilir veya ayrı ruleset'ler oluşturulabilir.

/* Landlock ağ kısıtlaması örneği (kernel 6.7+) */
#define _GNU_SOURCE
#include <linux/landlock.h>
#include <sys/syscall.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

/* ABI versiyonunu kontrol et */
static int check_landlock_abi(void) {
    return syscall(SYS_landlock_create_ruleset,
                   NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
}

int apply_network_sandbox(void) {
    int abi = check_landlock_abi();
    if (abi < 4) {
        fprintf(stderr, "Ag kisitlari icin ABI 4 gerekli (kernel 6.7+), mevcut: %d\n", abi);
        return -1;
    }

    struct landlock_ruleset_attr rs_attr = {
        .handled_access_net =
            LANDLOCK_ACCESS_NET_BIND_TCP    |
            LANDLOCK_ACCESS_NET_CONNECT_TCP,
    };

    int ruleset_fd = syscall(SYS_landlock_create_ruleset,
                             &rs_attr, sizeof(rs_attr), 0);
    if (ruleset_fd < 0) {
        perror("ruleset olusturulamadi");
        return -1;
    }

    /* Yalnızca port 1883 (MQTT) üzerinde dinlemeye izin ver */
    struct landlock_net_port_attr net_attr = {
        .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
        .port = 1883,
    };
    if (syscall(SYS_landlock_add_rule, ruleset_fd,
                LANDLOCK_RULE_NET_PORT, &net_attr, 0) < 0) {
        perror("net kural eklenemedi (bind 1883)");
        close(ruleset_fd);
        return -1;
    }

    /* Port 5432 (PostgreSQL) bağlantısına izin ver */
    net_attr.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP;
    net_attr.port = 5432;
    if (syscall(SYS_landlock_add_rule, ruleset_fd,
                LANDLOCK_RULE_NET_PORT, &net_attr, 0) < 0) {
        perror("net kural eklenemedi (connect 5432)");
        close(ruleset_fd);
        return -1;
    }

    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
        perror("prctl basarisiz");
        close(ruleset_fd);
        return -1;
    }

    if (syscall(SYS_landlock_restrict_self, ruleset_fd, 0) < 0) {
        perror("restrict_self basarisiz");
        close(ruleset_fd);
        return -1;
    }

    close(ruleset_fd);
    printf("Ag sandbox aktif: yalnizca bind:1883 ve connect:5432\n");
    return 0;
}

Ağ kısıt bayrakları:

LANDLOCK_ACCESS_NET_BIND_TCPTCP portunda dinleme (bind + listen); sunucu süreçler için gerekli
LANDLOCK_ACCESS_NET_CONNECT_TCPTCP bağlantısı kurma (connect); istemci bağlantıları için gerekli

Önemli sınırlamalar: Landlock ağ kısıtları yalnızca TCP'yi kapsar; UDP, Unix domain soketleri veya ham soketler (raw sockets) için Landlock ağ kısıtları uygulanamaz. Bu erişimleri kısıtlamak için seccomp ile birlikte kullanmak gerekmektedir. Ayrıca halihazırda açık olan soket bağlantıları Landlock politikası uygulandıktan sonra da çalışmaya devam eder; politika yalnızca yeni bağlantı girişimlerini etkiler.

05 Kalıtım ve fork

Landlock kısıtlamaları süreç kalıtımı yoluyla child süreçlere aktarılır. fork() ile oluşturulan alt süreçler, üst sürecin Landlock politikasını devralır ve bu politika daraltılabilir ancak genişletilemez.

Bu davranış güvenlik açısından kritik bir garantidir: kısıtlı bir süreç fork() ile bir child oluşturduğunda, o child daha az kısıtlı olamaz. Alt süreç ek kısıtlamalar ekleyebilir ancak mevcut kısıtlamaları kaldıramaz.

Landlock kalıtım modeli:

  Ana süreç (kısıtsız)
       |
       |  landlock_restrict_self (politika A: /tmp, /var/lib/app)
       v
  Ana süreç (politika A aktif)
       |
       |  fork()
       |
       +----------> Child süreç (politika A devralındı)
                         |
                         |  landlock_restrict_self (politika B: yalnızca /tmp)
                         v
                    Child süreç (politika A + B: kesişim = /tmp)
                    [Politika A'yı kaldıramaz, yalnızca daraltabilir]
  

no_new_privs zorunluluğu: landlock_restrict_self çağrısından önce prctl(PR_SET_NO_NEW_PRIVS, 1, ...) ayarlanmalıdır. Bu bayrak da kalıtımla child süreçlere geçer; bir kez ayarlandığında geri alınamaz. Amaç: setuid veya setcap ikilileri çalıştırarak Landlock kısıtlamalarını atlatmayı engellemektir.

/* Çok işlemli daemon — güvenli kalıtım örneği */
#include <sys/prctl.h>
#include <unistd.h>
#include <stdio.h>

void apply_worker_sandbox(void);  /* ek kısıtlamalar */

int main(void) {
    /* 1. Adım: Ana süreç için Landlock kur (geniş izinler) */
    setup_landlock_main();

    /* 2. Adım: Worker fork'larını başlat */
    for (int i = 0; i < WORKER_COUNT; i++) {
        pid_t pid = fork();
        if (pid == 0) {
            /* Child: ana politikayı devraldı, daha da kısıt */
            apply_worker_sandbox();
            worker_loop();
            _exit(0);
        }
    }

    /* Ana süreç: kabul döngüsü */
    accept_loop();
    return 0;
}

exec() ve Landlock: execve() sonrasında Landlock kısıtlamaları korunur. Yeni çalıştırılan ikili, önceki süreçten kalan politikayı devralır. Bu davranış sayesinde bir sarmalayıcı (wrapper) script veya launcher süreci Landlock'u kurup ardından asıl programı exec() ile başlatabilir; asıl program Landlock API'sini hiç çağırmak zorunda kalmaz.

/* Landlock launcher: politikayı kur, ardından programı exec et */
int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Kullanim: %s PROGRAM [ARGS...]\n", argv[0]);
        return 1;
    }

    /* Politikayı kur */
    if (setup_landlock_policy() < 0)
        return 1;

    /* Asıl programı başlat — politika devredilir */
    execvp(argv[1], &argv[1]);
    perror("execvp basarisiz");
    return 1;
}

06 Gömülü Daemon Sandboxing

Gerçek dünya gömülü senaryolarında MQTT broker, hafif web sunucusu ve CANBUS daemon uygulamaları için Landlock sandbox politikaları tasarlanmaktadır. Her uygulamanın ihtiyaç duyduğu kaynaklar dikkatle analiz edilmelidir.

MQTT Broker (Mosquitto) Sandbox: Mosquitto tipik olarak yapılandırma dosyasını, kalıcı mesaj veritabanını ve PID dosyasını kullanır; ağda yalnızca 1883 (MQTT) ve 8883 (MQTT/TLS) portlarında dinler.

/* mosquitto_sandbox.c — Mosquitto için Landlock sarmalayıcı */
#include <linux/landlock.h>
#include <sys/syscall.h>
#include <sys/prctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

#define FS_R   (LANDLOCK_ACCESS_FS_READ_FILE | LANDLOCK_ACCESS_FS_READ_DIR)
#define FS_RW  (FS_R | LANDLOCK_ACCESS_FS_WRITE_FILE | \
                LANDLOCK_ACCESS_FS_MAKE_REG | LANDLOCK_ACCESS_FS_REMOVE_FILE)
#define FS_RX  (FS_R | LANDLOCK_ACCESS_FS_EXECUTE)

static const struct {
    const char *path;
    uint64_t    access;
} mosquitto_rules[] = {
    { "/etc/mosquitto",        FS_R   },  /* yapılandırma */
    { "/var/lib/mosquitto",    FS_RW  },  /* kalıcı veritabanı */
    { "/var/log/mosquitto",    FS_RW  },  /* log dosyaları */
    { "/var/run",              FS_RW  },  /* PID dosyası */
    { "/tmp",                  FS_RW  },  /* geçici dosyalar */
    { "/lib",                  FS_RX  },  /* sistem kütüphaneleri */
    { "/usr/lib",              FS_RX  },  /* uygulama kütüphaneleri */
    { "/usr/sbin",             FS_RX  },  /* mosquitto ikilisi */
    { "/etc/ssl",              FS_R   },  /* TLS sertifikaları */
    { "/dev/urandom",          FS_R   },  /* rassal sayı üretici */
    { NULL, 0 }
};

int apply_mosquitto_sandbox(void) {
    /* Tam uygulama için önceki bölümlerdeki yardımcı işlevler kullanılır */
    return 0; /* kısaltılmış örnek */
}

CANBUS daemon sandbox: SocketCAN kullanan bir CAN daemon genellikle yapılandırma dizinine okuma, log dizinine yazma ve CAN soketlerine erişim ister. Ağ soketleri (SOCK_RAW CAN) Landlock ağ kısıtlamalarının kapsamı dışındadır; bu kısıtlama için seccomp filtreleri kullanılmalıdır.

/* can_daemon_sandbox.c — CAN daemon için minimal politika */
static const struct {
    const char *path;
    uint64_t    access;
} can_daemon_rules[] = {
    { "/etc/can-daemon",       FS_R   },  /* yapılandırma */
    { "/var/lib/can-daemon",   FS_RW  },  /* durum verisi */
    { "/var/log/can-daemon",   FS_RW  },  /* günlükler */
    { "/tmp",                  FS_RW  },  /* geçici dosyalar */
    { "/lib",                  FS_RX  },  /* kütüphaneler */
    { "/usr/lib",              FS_RX  },  /* kütüphaneler */
    { "/dev",                  FS_R   },  /* aygıt dosyası okuma (vcan0 vb.) */
    { NULL, 0 }
    /* Not: CAN ham soketleri ağ kısıtları yerine seccomp ile yönetilir */
};

Web sunucusu sandbox (lighttpd örneği):

static const struct {
    const char *path;
    uint64_t    access;
} lighttpd_rules[] = {
    { "/etc/lighttpd",         FS_R   },  /* yapılandırma */
    { "/var/www/html",         FS_R   },  /* statik web içeriği */
    { "/var/log/lighttpd",     FS_RW  },  /* erişim ve hata logları */
    { "/var/run",              FS_RW  },  /* PID dosyası, FastCGI soketleri */
    { "/tmp",                  FS_RW  },  /* geçici dosyalar */
    { "/lib",                  FS_RX  },
    { "/usr/lib",              FS_RX  },
    { "/usr/sbin",             FS_RX  },
    { "/etc/ssl",              FS_R   },  /* HTTPS sertifikaları */
    { "/etc/mime.types",       FS_R   },  /* MIME türleri */
    { NULL, 0 }
};

07 Mevcut Araçlarla Entegrasyon

Landlock, systemd servis birimlerindeki ek güvenlik seçenekleri, bubblewrap sandbox aracı ve diğer Linux güvenlik mekanizmaları ile birlikte kullanılabilir.

systemd servis birimi ile Landlock: systemd 254+ sürümünde LandlockAccessFS ve ilgili direktifler eklenmiştir. Bu sayede C kodu yazmadan systemd birim dosyası üzerinden Landlock politikası tanımlanabilir. Ancak eski systemd sürümlerinde (gömülü dağıtımlarda yaygın) bu direktifler yoktur; bu durumda servis başlatma betiği içinden Landlock uygulanması gerekir.

# /etc/systemd/system/mqtt-broker.service — systemd 254+ özelliği
[Unit]
Description=MQTT Broker (Mosquitto)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=mosquitto
Group=mosquitto
ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf

# Landlock dosya sistemi kısıtları (systemd 254+)
# Tüm dosya sistemi erişimini kısıtla
LandlockInodesAccessFilesystems=read execute

# Güvenlik katmanlarını birleştir
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/var/lib/mosquitto /var/log/mosquitto

[Install]
WantedBy=multi-user.target

Eski systemd sürümleri için ExecStartPre yaklaşımı:

# /etc/systemd/system/mqtt-broker.service — eski systemd uyumlu
[Service]
User=mosquitto
NoNewPrivileges=yes
ExecStartPre=/usr/sbin/mqtt-sandbox-setup
ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf

bubblewrap ile karşılaştırma: bubblewrap (bwrap), namespace tabanlı izolasyon sağlar ve daha kapsamlı bir sandbox oluşturabilir. Landlock ise daha hafif ve uygulama içine entegre bir çözümdür. İkisi farklı sorunları çözer ve birlikte kullanılabilir.

ÖzellikLandlockbubblewrap
MekanizmaLSM (çekirdek yetki bayrakları)Linux namespace (mount, pid, net vb.)
Uygulama değişikliğiGerekli (syscall çağrısı)Gerekmez (dışarıdan sarmalama)
Çekirdek gereksinimleriLinux 5.13+, CONFIG_SECURITY_LANDLOCK=yNamespace desteği (çoğu çekirdekte mevcut)
OverheadÇok düşükOrta (namespace kurulum maliyeti)
Ağ yalıtımıYalnızca TCP port (6.7+)Tam ağ namespace yalıtımı
Gömülü uygunlukYüksekOrta (namespace desteği gerektirir)

Landlock ve seccomp kombinasyonu gömülü güvenlik için güçlü bir temel oluşturur: seccomp syscall yüzeyini minimize eder, Landlock ise erişilebilir kaynakları kısıtlar. İkisi birlikte kullanıldığında saldırı yüzeyi çok katmanlı biçimde daraltılır.

/* Landlock + seccomp kombinasyonu */
int secure_daemon_init(void) {
    /* 1. Landlock: dosya sistemi ve ağ kısıtları */
    if (apply_landlock_policy() < 0)
        return -1;

    /* 2. seccomp: gereksiz syscall'ları kapat */
    if (apply_seccomp_filter() < 0)
        return -1;

    /* 3. Artık güvenli ortamda çalışıyoruz */
    return 0;
}

08 Hata Ayıklama

Landlock politikası uygulandıktan sonra reddedilen erişimleri teşhis etmek için strace ile syscall izleme, EACCES/EPERM hata ayrımı ve audit log mekanizmasından yararlanılır.

strace ile Landlock syscall'larını izle:

# Landlock syscall'larını izle
strace -e trace=landlock_create_ruleset,landlock_add_rule,landlock_restrict_self \
    ./myapp

# Tüm dosya erişimlerini ve hataları izle
strace -e trace=openat,open,read,write,execve \
    -e fault=openat:error=EACCES \
    ./myapp 2>&1 | grep -E "EACCES|EPERM"

# Daha ayrıntılı: reddedilen tüm sistem çağrıları
strace -P /etc/passwd ./myapp 2>&1

EACCES ve EPERM ayrımı:

EACCES (13)Landlock politikası tarafından reddedildi; politikayı genişletmeniz gerekiyor
EPERM (1)Yetki yetersizliği — no_new_privs ayarlanmamış veya farklı bir güvenlik mekanizması reddetti
/* Hata yönetimi örneği */
int fd = open("/path/to/file", O_RDONLY);
if (fd < 0) {
    if (errno == EACCES) {
        fprintf(stderr, "Landlock reddi: %s\n", strerror(errno));
        /* Landlock politikasında ilgili dizini kontrol et */
    } else if (errno == EPERM) {
        fprintf(stderr, "Yetki hatasi: %s\n", strerror(errno));
        /* no_new_privs veya başka bir LSM kontrolü */
    } else {
        perror("Beklenmedik hata");
    }
}

Linux audit ile Landlock olaylarını kaydet:

# auditd kurulumu
sudo apt-get install auditd

# Landlock reddiyatlarını kaydet
sudo auditctl -a always,exit -F arch=b64 \
    -S landlock_create_ruleset,landlock_add_rule,landlock_restrict_self \
    -k landlock_ops

# Redleri izle
sudo ausearch -k landlock_ops | aureport --file

# Gerçek zamanlı audit izleme
sudo tail -f /var/log/audit/audit.log | grep landlock

Politika geliştirme iş akışı:

Politika geliştirme adımları:

  1. Uygulamayı strace altında çalıştır
     strace -e trace=openat,connect ./myapp 2>trace.log

  2. Erişilen kaynakları analiz et
     grep "openat" trace.log | awk '{print $2}' | sort -u

  3. Minimal Landlock politikası oluştur
     (yalnızca adım 2'de tespit edilen kaynaklar)

  4. Politika ile test et
     ./myapp 2>test.log
     grep "EACCES\|EPERM" test.log

  5. Eksik izinleri ekle ve adım 4'e dön

  6. Production öncesi güvenlik incelemesi:
     izin verilen kaynaklar gerçekten gerekli mi?
  

Çekirdek yapılandırması doğrulama: Landlock'un aktif olup olmadığını ve hangi ABI versiyonunun desteklendiğini doğrulamak için:

# Landlock çekirdek yapılandırmasını kontrol et
zcat /proc/config.gz | grep LANDLOCK
# Beklenen çıktı:
# CONFIG_SECURITY_LANDLOCK=y

# LSM listesini kontrol et
cat /sys/kernel/security/lsm
# Çıktıda "landlock" görünmelidir

# ABI versiyonunu C'den değil shell'den test et
python3 -c "
import ctypes, os
libc = ctypes.CDLL(None, use_errno=True)
# SYS_landlock_create_ruleset = 444 (x86_64)
abi = libc.syscall(444, None, 0, 1)
print(f'Landlock ABI: {abi}')
"

Landlock desteği yoksa (CONFIG_SECURITY_LANDLOCK ayarlanmamış veya LSM listesinde "landlock" yok) uygulamanızın Landlock olmadan da çalışabilmesi için ABI kontrolünü zorunlu adım yerine isteğe bağlı güvenlik katmanı olarak tasarlamanız önerilir. Bu yaklaşım farklı kernel versiyonlarında çalışan taşınabilir bir gömülü uygulama için doğru pratiktir.