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

eBPF LSM
Programlanabilir Güvenlik Politikaları

Linux 5.7+ BPF LSM hook'ları ile kernel güvenlik politikalarını esnek biçimde programlama, SELinux/AppArmor'a alternatif ve tamamlayıcı yaklaşım.

00 BPF LSM Neden?

SELinux ve AppArmor güçlü güvenlik modülleridir; ancak politika dili karmaşıklığı, dinamik güncelleme zorluğu ve gözlemlenebilirlik eksikliği gömülü sistemlerde ciddi engeller oluşturur. BPF LSM bu sorunları köklü biçimde çözer.

Geleneksel LSM Sorunları

SELinux politika karmaşıklığıMilyonlarca kural, tür geçiş tanımları, boolean'lar — öğrenme eğrisi diktir. Özel donanım sürücüleri için politika yazmak haftalar alabilir.
Statik politikaPolitika değişikliği genellikle yeniden derleme veya en azından servis yeniden başlatması gerektirir. Canlı sistemde anlık güncelleme güçtür.
Gözlemlenebilirlik eksikliğiSELinux AVC logları reddedilen işlemleri gösterir, ancak neden reddedildiğini, hangi sürecin hangi bağlamda çalıştığını derinlemesine analiz etmek güçtür.
Gömülü sistem uyumsuzluğuKaynakları kısıtlı sistemlerde tam bir SELinux politika kümesi bellek ve depolama israfına neden olur.

BPF LSM Avantajları

Programlanabilir politikaC benzeri eBPF kodu ile tam programatik esneklik — koşullara, MAP verilerine, sistem durumuna göre dinamik karar verme.
Sıfır yeniden başlatma güncellemesiYeni BPF programı yüklenerek politika anlık değiştirilebilir. Önceki program atomik olarak değiştirilir.
Yerleşik gözlemlenebilirlikBPF MAP'leri ve ring buffer'ları sayesinde her güvenlik kararı, geçen süre, süreç bilgisi kolayca raporlanabilir.
SELinux ile birlikte çalışmaBPF LSM mevcut LSM'lerle yan yana çalışır; ek politika katmanı olarak kullanılabilir.

Gereksinim Özeti

Kernel 5.7+
  CONFIG_BPF_LSM=y
  CONFIG_LSM="lockdown,yama,bpf"  (veya mevcut listeye ,bpf ekle)
  CONFIG_DEBUG_INFO_BTF=y          (CO-RE için)

Kullanıcı Alanı Araçları
  libbpf >= 0.6
  clang >= 12 (BPF hedefi için)
  bpftool
  linux-headers veya vmlinux.h (bpftool btf dump ile)
  

01 LSM Hook Altyapısı

Linux LSM altyapısı, kernel içindeki kritik güvenlik noktalarına hook'lar yerleştirir. BPF LSM bu hook'lara eBPF programları bağlamasına izin vererek mevcut LSM çerçevesini programlanabilir hale getirir.

security_* Hook'ları

Kernel kaynak kodunda security_*() ile başlayan fonksiyonlar LSM hook çağrı noktalarıdır. Bu noktaların her birinde kayıtlı LSM'ler (SELinux, BPF LSM vb.) sırayla çağrılır; herhangi biri ret != 0 döndürürse işlem reddedilir.

HookTetiklenme NoktasıDönüş Değeri
lsm/file_openDosya açılırken0: izin, negatif: reddet
lsm/inode_permissionİzin kontrolünde0: izin, -EACCES: reddet
lsm/socket_connectBağlantı kurulurken0: izin, -EPERM: reddet
lsm/bprm_check_securityexecve() öncesi0: izin, -EACCES: reddet
lsm/task_killSinyal gönderilirken0: izin, -EPERM: reddet
lsm/sb_mountDosya sistemi bağlanırken0: izin, -EPERM: reddet

BPF_PROG_TYPE_LSM

BPF LSM programları BPF_PROG_TYPE_LSM tipinde tanımlanır ve SEC("lsm/hook_adı") bölüm etiketiyle hangi hook'a bağlandıkları belirtilir:

SEC("lsm/file_open")
int BPF_PROG(my_file_open, struct file *file)
{
    /* Güvenlik kararı burada verilir */
    return 0;  /* izin ver */
}

Sleepable ve Non-Sleepable Programlar

Non-sleepable (varsayılan)Çoğu LSM hook'u bu tiptedir. GFP_ATOMIC bellek tahsisi, spinlock uyumlu. Bekleme yapan çağrılar yasaktır.
Sleepable (SEC("lsm.s/..."))Linux 5.11+ ile bazı hook'larda sleepable programlar çalışabilir. bpf_copy_from_user gibi fonksiyonlar kullanılabilir.

Kernel BTF ve CO-RE

BPF LSM programları CO-RE (Compile Once, Run Everywhere) prensibini kullanır. vmlinux.h dosyası kernel BTF bilgisinden üretilir ve tüm kernel veri yapılarını içerir:

# vmlinux.h üret
bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h

02 İlk BPF LSM Programı

libbpf skeleton yaklaşımı, BPF LSM programlarını en az boilerplate kodla derleme ve yükleme imkanı sunar. Aşağıdaki örnek dosya açma işlemlerini izler.

Kernel Tarafı: file_monitor.bpf.c

// file_monitor.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>

char LICENSE[] SEC("license") = "GPL";

/* Olay yapısı -- ring buffer'a yazılacak */
struct event {
    u32  pid;
    u32  uid;
    char comm[16];
    char filename[128];
    int  ret;
};

/* Ring buffer map */
struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 256 * 1024);
} events SEC(".maps");

SEC("lsm/file_open")
int BPF_PROG(monitor_file_open, struct file *file)
{
    struct event *e;
    struct dentry *dentry;
    const unsigned char *name;

    /* Ring buffer'a alan ayır */
    e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
    if (!e)
        return 0;

    e->pid = bpf_get_current_pid_tgid() >> 32;
    e->uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
    bpf_get_current_comm(e->comm, sizeof(e->comm));

    /* Dosya adını oku (CO-RE ile güvenli) */
    dentry = BPF_CORE_READ(file, f_path.dentry);
    name   = BPF_CORE_READ(dentry, d_name.name);
    bpf_probe_read_kernel_str(e->filename,
                              sizeof(e->filename), name);

    e->ret = 0;  /* bu hook izin veriyor */
    bpf_ringbuf_submit(e, 0);

    return 0;  /* erişime izin ver */
}

Kullanıcı Alanı: file_monitor.c

#include <stdio.h>
#include <bpf/libbpf.h>
#include "file_monitor.skel.h"

static int handle_event(void *ctx, void *data, size_t sz)
{
    struct event *e = data;
    printf("PID=%-6u UID=%-4u COMM=%-16s FILE=%s\n",
           e->pid, e->uid, e->comm, e->filename);
    return 0;
}

int main(void)
{
    struct file_monitor_bpf *skel;
    struct ring_buffer      *rb;
    int err;

    /* Skeleton yükle ve BPF programı kernel'e at */
    skel = file_monitor_bpf__open_and_load();
    if (!skel) { fprintf(stderr, "Yükleme hatası\n"); return 1; }

    err = file_monitor_bpf__attach(skel);
    if (err) { fprintf(stderr, "Attach hatası\n"); goto cleanup; }

    /* Ring buffer dinleyici */
    rb = ring_buffer__new(bpf_map__fd(skel->maps.events),
                          handle_event, NULL, NULL);
    if (!rb) { err = -1; goto cleanup; }

    printf("Dosya erişimleri izleniyor...\n");
    while (ring_buffer__poll(rb, 100) >= 0)
        ;  /* sonsuz döngü */

    ring_buffer__free(rb);
cleanup:
    file_monitor_bpf__destroy(skel);
    return err;
}

Derleme

# BPF nesne dosyası derle
clang -O2 -target bpf -D__TARGET_ARCH_arm64 \
    -I/usr/include/bpf \
    -c file_monitor.bpf.c -o file_monitor.bpf.o

# Skeleton üret
bpftool gen skeleton file_monitor.bpf.o \
    name file_monitor_bpf > file_monitor.skel.h

# Kullanıcı alanı derle
gcc -O2 file_monitor.c -o file_monitor -lbpf

03 Dosya Erişim Kontrolü

lsm/inode_permission hook'u, inode bazında okuma/yazma/yürütme izinlerini denetler. Bu hook ile hassas dizinlere erişimi belirli süreçlerle kısıtlamak mümkündür.

İzin Reddetme Örneği

// deny_write.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include <errno.h>

char LICENSE[] SEC("license") = "GPL";

/* Korunan dizin inode numarası (user space'den doldurulur) */
struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, 1);
    __type(key, u32);
    __type(value, u64);
} protected_inode SEC(".maps");

SEC("lsm/inode_permission")
int BPF_PROG(deny_write_to_protected, struct inode *inode,
             int mask)
{
    u32  key = 0;
    u64 *target_ino;
    u64  ino;

    /* Yalnızca yazma girişimlerini denetle */
    if (!(mask & MAY_WRITE))
        return 0;

    target_ino = bpf_map_lookup_elem(&protected_inode, &key);
    if (!target_ino)
        return 0;

    ino = BPF_CORE_READ(inode, i_ino);
    if (ino == *target_ino) {
        bpf_printk("BPF LSM: inode %llu yazma reddedildi\n", ino);
        return -EACCES;
    }

    return 0;
}

Path Filtreleme

Daha esnek bir yaklaşım için lsm/file_open ile dosya adını string karşılaştırma:

SEC("lsm/file_open")
int BPF_PROG(block_etc_shadow, struct file *file)
{
    char fname[32];
    struct dentry *dentry;
    const unsigned char *name;

    dentry = BPF_CORE_READ(file, f_path.dentry);
    name   = BPF_CORE_READ(dentry, d_name.name);
    bpf_probe_read_kernel_str(fname, sizeof(fname), name);

    /* /etc/shadow erişimini root olmayan herkes için reddet */
    if (__builtin_memcmp(fname, "shadow", 6) == 0) {
        u32 uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
        if (uid != 0)
            return -EACCES;
    }

    return 0;
}

İzin Kararı Akışı

open("shadow", O_RDONLY)
        |
        v
VFS katmanı
        |
        v
security_file_open() çağrısı
        |
        +---> SELinux hook  (önce)
        |
        +---> BPF LSM hook  (sonra)
                  |
                  +-- uid != 0 ? --> return -EACCES --> EPERM kullanıcıya
                  |
                  +-- uid == 0 ? --> return 0 ---------> VFS devam eder
  

Whitelist Yaklaşımı

Yalnızca belirli süreçlere belirli dosyalara erişim izni vermek için MAP tabanlı whitelist:

Kara liste (blacklist)Varsayılan izin ver, listedeki koşullarda reddet. Basit uygulamalar için uygundur.
Beyaz liste (whitelist)Varsayılan reddet, listedeki koşullarda izin ver. Güvenlik ilkesi açısından daha güçlüdür; ancak tüm meşru erişimlerin listelenmesi gerekir.

04 Ağ Politikası

lsm/socket_connect hook'u, bir sürecin TCP/UDP bağlantısı kurmasından önce çağrılır. IP adresi ve port bazında filtreleme ile süreç başına ağ politikası uygulanabilir.

Bağlantı Engelleme Örneği

// net_policy.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_endian.h>
#include <errno.h>

char LICENSE[] SEC("license") = "GPL";

/* Yasaklı portlar haritası: port -> 1 */
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 256);
    __type(key, u16);
    __type(value, u8);
} blocked_ports SEC(".maps");

SEC("lsm/socket_connect")
int BPF_PROG(block_ports, struct socket *sock,
             struct sockaddr *address, int addrlen)
{
    struct sockaddr_in *addr4;
    u16 port;
    u8 *blocked;
    char comm[16];

    /* Yalnızca IPv4 TCP/UDP kontrol et */
    if (address->sa_family != AF_INET)
        return 0;

    addr4 = (struct sockaddr_in *)address;
    port  = bpf_ntohs(addr4->sin_port);

    blocked = bpf_map_lookup_elem(&blocked_ports, &port);
    if (blocked && *blocked) {
        bpf_get_current_comm(comm, sizeof(comm));
        bpf_printk("BPF LSM: %s port %u'e bağlantı reddedildi\n",
                   comm, port);
        return -EPERM;
    }

    return 0;
}

Kullanıcı Alanından Port Listesi Güncelleme

// net_policy_user.c (basitleştirilmiş)
#include <bpf/libbpf.h>
#include "net_policy.skel.h"

int main(void)
{
    struct net_policy_bpf *skel;
    u16 port;
    u8  val = 1;

    skel = net_policy_bpf__open_and_load();
    net_policy_bpf__attach(skel);

    /* Telnet portunu engelle */
    port = 23;
    bpf_map__update_elem(skel->maps.blocked_ports,
                         &port, sizeof(port),
                         &val,  sizeof(val),
                         BPF_ANY);

    /* FTP portunu engelle */
    port = 21;
    bpf_map__update_elem(skel->maps.blocked_ports,
                         &port, sizeof(port),
                         &val, sizeof(val),
                         BPF_ANY);

    pause();  /* sinyal gelene kadar bekle */
    net_policy_bpf__destroy(skel);
    return 0;
}

cgroup ile Birleştirme

Süreç bazlı değil konteyner/uygulama grubu bazlı politika için cgroup ID kullanılır:

SEC("lsm/socket_connect")
int BPF_PROG(cgroup_net_policy, struct socket *sock,
             struct sockaddr *address, int addrlen)
{
    u64 cgroup_id = bpf_get_current_cgroup_id();

    /* Yalnızca kısıtlı cgroup'a uygula (cgroup ID harici ayarlanır) */
    if (cgroup_id == RESTRICTED_CGROUP_ID) {
        /* ... politika uygula ... */
    }
    return 0;
}

Desteklenen Ağ Hook'ları

HookTetiklenmeParametre
lsm/socket_createSocket oluşturmadafamily, type, protocol
lsm/socket_connectBağlantı kurulurkensocket, sockaddr
lsm/socket_bindPorta bind sırasındasocket, sockaddr
lsm/socket_sendmsgMesaj göndermedesocket, msghdr
lsm/socket_recvmsgMesaj almadasocket, msghdr

05 Süreç Denetimi

lsm/bprm_check_security hook'u, execve() sistem çağrısının güvenlik denetiminde tetiklenir. Bu hook sayesinde hangi binary'nin çalıştırılabileceği, hash doğrulaması ve imza kontrolü gibi politikalar uygulanabilir.

execve İzleme

// exec_monitor.bpf.c
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>

char LICENSE[] SEC("license") = "GPL";

struct exec_event {
    u32  pid;
    u32  ppid;
    u32  uid;
    char comm[16];
    char filename[128];
};

struct {
    __uint(type, BPF_MAP_TYPE_RINGBUF);
    __uint(max_entries, 512 * 1024);
} exec_events SEC(".maps");

SEC("lsm/bprm_check_security")
int BPF_PROG(monitor_exec, struct linux_binprm *bprm)
{
    struct exec_event *e;
    struct task_struct *task;
    const char *fname;

    e = bpf_ringbuf_reserve(&exec_events, sizeof(*e), 0);
    if (!e) return 0;

    task  = bpf_get_current_task_btf();
    e->pid  = BPF_CORE_READ(task, pid);
    e->ppid = BPF_CORE_READ(task, real_parent, pid);
    e->uid  = bpf_get_current_uid_gid() & 0xFFFFFFFF;
    bpf_get_current_comm(e->comm, sizeof(e->comm));

    fname = BPF_CORE_READ(bprm, filename);
    bpf_probe_read_kernel_str(e->filename,
                              sizeof(e->filename), fname);

    bpf_ringbuf_submit(e, 0);
    return 0;
}

Binary Whitelist ile Çalıştırma Engeli

/* İzin verilen binary adları MAP'i */
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 64);
    __type(key, char[128]);
    __type(value, u8);
} allowed_binaries SEC(".maps");

SEC("lsm/bprm_check_security")
int BPF_PROG(whitelist_exec, struct linux_binprm *bprm)
{
    char filename[128];
    const char *fname;
    u8 *allowed;

    /* Yalnızca uid=1000 süreçlere uygula */
    u32 uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
    if (uid != 1000)
        return 0;

    fname = BPF_CORE_READ(bprm, filename);
    bpf_probe_read_kernel_str(filename, sizeof(filename), fname);

    allowed = bpf_map_lookup_elem(&allowed_binaries, filename);
    if (!allowed) {
        bpf_printk("BPF LSM: %s calistirilmasi reddedildi\n",
                   filename);
        return -EACCES;
    }

    return 0;
}

Parent-Child İlişkisi Denetimi

Güvenilir olmayan bir sürecin alt süreç (fork+exec) başlatmasını engelleme senaryosu gömülü sistemlerde yaygındır. Örneğin, bir ağ servisi yalnızca önceden tanımlanmış yardımcı programları çalıştırabilir:

bpf_get_current_task_btf()Mevcut görevin task_struct işaretçisini BTF türüyle döner; parent, UID, GID alanlarına CO-RE ile erişilebilir.
BPF_CORE_READ(task, real_parent, pid)Üst sürecin PID'ini güvenli biçimde okur.
BPF_CORE_READ(bprm, filename)Çalıştırılmak istenen binary'nin tam yolunu döner.

06 MAP ile Dinamik Politika

BPF MAP'leri, kullanıcı alanından kernel içindeki BPF programlarına veri aktarmanın temel mekanizmasıdır. Bu sayede güvenlik politikaları çalışma anında güncellenebilir.

Kara Liste MAP'i

// blacklist.bpf.c -- PID bazlı kara liste
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <errno.h>

char LICENSE[] SEC("license") = "GPL";

/* PID kara listesi: pid -> reason_code */
struct {
    __uint(type, BPF_MAP_TYPE_HASH);
    __uint(max_entries, 1024);
    __type(key,   u32);
    __type(value, u32);
} pid_blacklist SEC(".maps");

SEC("lsm/file_open")
int BPF_PROG(blacklist_pid_file, struct file *file)
{
    u32 pid = bpf_get_current_pid_tgid() >> 32;
    u32 *reason;

    reason = bpf_map_lookup_elem(&pid_blacklist, &pid);
    if (reason) {
        bpf_printk("BPF LSM: PID %u kara listede (reason=%u)\n",
                   pid, *reason);
        return -EACCES;
    }
    return 0;
}

Kullanıcı Alanından Politika Güncelleme

// policy_manager.c
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "blacklist.skel.h"

static int add_to_blacklist(struct blacklist_bpf *skel, u32 pid, u32 reason)
{
    return bpf_map__update_elem(skel->maps.pid_blacklist,
                                &pid,    sizeof(pid),
                                &reason, sizeof(reason),
                                BPF_ANY);
}

static int remove_from_blacklist(struct blacklist_bpf *skel, u32 pid)
{
    return bpf_map__delete_elem(skel->maps.pid_blacklist,
                                &pid, sizeof(pid), 0);
}

int main(int argc, char **argv)
{
    struct blacklist_bpf *skel;
    u32 pid, reason;

    skel = blacklist_bpf__open_and_load();
    blacklist_bpf__attach(skel);

    /* PID 1234'ü kara listeye al */
    add_to_blacklist(skel, 1234, 42);

    /* Kullanıcı komutlarını dinle (basit REPL) */
    while (scanf("add %u %u", &pid, &reason) == 2)
        add_to_blacklist(skel, pid, reason);

    blacklist_bpf__destroy(skel);
    return 0;
}

MAP Türleri ve Kullanım Senaryoları

MAP TürüKullanım SenaryosuAvantaj
BPF_MAP_TYPE_HASHPID/UID kara-beyaz listeO(1) arama, dinamik boyut
BPF_MAP_TYPE_ARRAYTek yapılandırma değeriÇok hızlı erişim, sabit boyut
BPF_MAP_TYPE_RINGBUFOlay raporlamaDüşük overhead, sıralı iletim
BPF_MAP_TYPE_LPM_TRIEIP adresi prefix eşlemeCIDR blok filtreleme
BPF_MAP_TYPE_PERCPU_HASHPerformans sayaçlarıCPU başına kilit yok

Atomik Politika Geçişi

MAP güncellemeleri atomiktir; okuma ile güncelleme arasında tutarsız durum oluşmaz. Büyük politika geçişlerinde BPF_F_LOCK bayrağı ile spin kilit koruması eklenebilir.

07 Gömülü Güvenlik Senaryosu

Gerçek bir IoT cihazı örneği: MQTT telemetri servisi yalnızca bulut sunucusuna (belirli IP:port) bağlanabilmeli, diğer tüm ağ bağlantıları engellenmelidir. Yetkisiz binary çalıştırma da kısıtlanmıştır.

Senaryo Gereksinimleri

Ağ kısıtlamasımqtt-client süreci yalnızca 192.168.1.100:8883 adresine bağlanabilir. Diğer tüm dış bağlantılar reddedilir.
Süreç kısıtlamasıBelirli kullanıcı (uid=1001) altında yalnızca /usr/bin/mqtt-client çalıştırılabilir.
Dosya koruması/etc/mqtt/certs/ dizinine yalnızca mqtt-client erişebilir, diğer süreçler okuyamaz.
Dinamik güncellemeSunucu IP değiştiğinde politika yeniden başlatma olmadan güncellenebilmelidir.

iot_policy.bpf.c

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_endian.h>
#include <errno.h>

char LICENSE[] SEC("license") = "GPL";

/* Yapılandırma: izin verilen sunucu IP ve port */
struct iot_config {
    __be32 allowed_ip;
    __be16 allowed_port;
    u32    mqtt_uid;
};

struct {
    __uint(type, BPF_MAP_TYPE_ARRAY);
    __uint(max_entries, 1);
    __type(key, u32);
    __type(value, struct iot_config);
} iot_cfg SEC(".maps");

/* Ağ politikası */
SEC("lsm/socket_connect")
int BPF_PROG(iot_net_policy, struct socket *sock,
             struct sockaddr *address, int addrlen)
{
    struct sockaddr_in *addr4;
    struct iot_config  *cfg;
    u32 key = 0, uid;

    if (address->sa_family != AF_INET)
        return 0;

    cfg = bpf_map_lookup_elem(&iot_cfg, &key);
    if (!cfg) return 0;

    uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
    if (uid != cfg->mqtt_uid)
        return 0;  /* diğer kullanıcılara dokunma */

    addr4 = (struct sockaddr_in *)address;

    /* İzin verilen IP+port kontrolü */
    if (addr4->sin_addr.s_addr == cfg->allowed_ip &&
        addr4->sin_port         == cfg->allowed_port)
        return 0;

    bpf_printk("IoT LSM: uid=%u yetkisiz baglanti engellendi\n",
               uid);
    return -EPERM;
}

/* Dosya erişim politikası */
SEC("lsm/file_open")
int BPF_PROG(iot_file_policy, struct file *file)
{
    char fname[32];
    struct dentry *dentry;
    const unsigned char *name;
    u32 uid;

    dentry = BPF_CORE_READ(file, f_path.dentry);
    name   = BPF_CORE_READ(dentry, d_name.name);
    bpf_probe_read_kernel_str(fname, sizeof(fname), name);

    /* "certs" dizinine erişimi kısıtla */
    if (__builtin_memcmp(fname, "certs", 5) == 0) {
        struct iot_config *cfg;
        u32 key = 0;
        cfg = bpf_map_lookup_elem(&iot_cfg, &key);
        uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;
        if (cfg && uid != cfg->mqtt_uid)
            return -EACCES;
    }
    return 0;
}

Politika Güncelleme (Sunucu IP Değişimi)

/* Yeni sunucu adresini yükle -- yeniden başlatma gerekmez */
struct iot_config new_cfg = {
    .allowed_ip   = htonl(0xC0A80165),  /* 192.168.1.101 */
    .allowed_port = htons(8883),
    .mqtt_uid     = 1001,
};
u32 key = 0;
bpf_map__update_elem(skel->maps.iot_cfg,
                     &key, sizeof(key),
                     &new_cfg, sizeof(new_cfg),
                     BPF_ANY);

08 Hata Ayıklama ve Doğrulama

BPF LSM programlarını doğrulamak ve hata ayıklamak için bpftool, bpftrace ve kernel verifier mesajları temel araçlardır. CAP_BPF yetki gereksinimleri de dikkate alınmalıdır.

bpftool ile Program Listeleme

# Yüklü BPF programlarını listele
bpftool prog list

# LSM programlarını filtrele
bpftool prog list | grep lsm

# Belirli programın detayları
bpftool prog show id 42

# Programın BPF bytecode'unu göster
bpftool prog dump xlated id 42

# JIT sonrası makine kodu
bpftool prog dump jited id 42

bpftrace ile Anlık İzleme

# Tüm LSM hook çağrılarını say
bpftrace -e 'kprobe:security_file_open { @[comm] = count(); }'

# BPF LSM reddedilen işlemleri izle
bpftrace -e 'kretprobe:bpf_lsm_file_open /retval != 0/ {
    printf("DENY: %s ret=%d\n", comm, retval);
}'

# execve çağrılarını izle
bpftrace -e 'tracepoint:syscalls:sys_enter_execve {
    printf("exec: %s %s\n", comm, str(args->filename));
}'

Verifier Hataları ve Çözümleri

invalid mem access 'scalar'NULL pointer dereference olasılığı. bpf_map_lookup_elem sonucu NULL kontrolü yapın.
back-edge from insn X to YDöngüde sonsuz çalışma riski. Döngü sayısı sınırlı olmalı; bounded loop (Linux 5.3+) kullanın.
program too largeBPF talimat limiti (varsayılan 1M). Büyük programları ayrı fonksiyonlara bölün, bpf_tail_call kullanın.
cannot call sleepableNon-sleepable hook içinde uyku gerektirecek yardımcı çağrısı. SEC("lsm.s/...") ile sleepable programa dönüştürün.
R0 !read_okFonksiyon dönüş değeri okunmadan kullanılmış. Her dönüş değeri kontrol edilmeli.

CAP_BPF Gereksinimleri

# BPF LSM programı yüklemek için gereken yetkiler:
# CAP_BPF      -- BPF programı yükleme
# CAP_MAC_ADMIN -- LSM hook'a program bağlama

# Test amaçlı (üretimde kaçının):
setcap cap_bpf,cap_mac_admin+eip ./my_lsm_tool

# systemd servis olarak (güvenli yol):
# [Service]
# AmbientCapabilities=CAP_BPF CAP_MAC_ADMIN
# CapabilityBoundingSet=CAP_BPF CAP_MAC_ADMIN

BPF Log Seviyesi

# bpf_printk çıktılarını göster
cat /sys/kernel/debug/tracing/trace_pipe

# Verifier ayrıntılı log (yükleme hatası durumunda)
# libbpf ile:
libbpf_set_print(LIBBPF_DEBUG, ...);

Performans Ölçümü

BPF LSM hook'larının gecikme etkisini ölçmek için perf ya da bpftrace kullanılabilir. Kritik yolda çalışan hook'lar (file_open, socket_connect) mikrosaniye mertebesinde etkiye sahip olabilir; MAP erişimlerini en aza indirin ve gereksiz bpf_printk çağrılarını üretimde devre dışı bırakın.

# file_open hook gecikmesini ölç
bpftrace -e '
    kprobe:bpf_lsm_file_open  { @start[tid] = nsecs; }
    kretprobe:bpf_lsm_file_open /@start[tid]/ {
        @latency = hist(nsecs - @start[tid]);
        delete(@start[tid]);
    }
'