Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX GÜVENLİK 2026

seccomp & Capabilities
Linux Süreç Sandbox

Sistem çağrısı filtreleme, capability kümeleri ve ayrıcalık düşürme teknikleriyle üretime hazır Linux süreç güvenlik katmanı.

00 Linux güvenlik katmanları

Linux, süreçleri kısıtlamak için birbirini tamamlayan dört farklı mekanizma sunar. Bu mekanizmalar derinlemesine savunma (defense in depth) ilkesini uygular.

Dört katman

Kullanıcı alanı süreci
        │
        ▼
┌─────────────────────────────────────────────┐
│  1. DAC  — uid/gid dosya izinleri (chmod)   │
│     "Bu süreç hangi dosyalara erişebilir?"  │
├─────────────────────────────────────────────┤
│  2. MAC  — SELinux / AppArmor               │
│     "Bu süreç hangi label'lara erişebilir?" │
├─────────────────────────────────────────────┤
│  3. Capabilities — ayrıntılı ayrıcalıklar  │
│     "Bu süreç hangi ayrıcalıklı işlemleri  │
│      yapabilir?"                            │
├─────────────────────────────────────────────┤
│  4. seccomp — sistem çağrısı filtresi       │
│     "Bu süreç hangi syscall'ları çağırabilir?"│
└─────────────────────────────────────────────┘
        │
        ▼
   Kernel (syscall arayüzü)

Neden birden fazla katman

DAC tek başına yetersizroot (uid=0) tüm DAC kısıtlamalarını aşar; capabilities bunu parçalara böler
MAC bypass riskiKernel güvenlik açığı SELinux/AppArmor'u atlayabilir; seccomp kernel arayüzünü kısıtlar
Capability ile exploit riskiCapability olan süreç bile tehlikeli syscall'ları çağırabilir; seccomp bunu engeller
Derinlemesine savunmaBirden fazla katman, tek bir açığın yeterli olmamasını sağlar

Tehdit modeli: privilege escalation

Saldırı vektörüDACCapabilitiesseccomp
Dosya okuma yetkisi yokEngellerKısmenEtkisiz
setuid binary istismarıEtkisizEngeller (drop)Kısmen
Kernel exploit via syscallEtkisizEtkisizEngeller
ptrace ile süreç enjeksiyonuKısmenCAP_SYS_PTRACE drop ileptrace syscall filtrele
Network raw socketEtkisizCAP_NET_RAW drop ilesocket() filtrele

Bu bölümde

  • DAC → MAC → Capabilities → seccomp: dört bağımsız katman
  • Her katman farklı bir saldırı vektörünü engeller
  • Embedded sistemlerde en etkili yaklaşım: capabilities drop + seccomp-bpf

01 Linux capabilities

Linux capabilities, geleneksel root/non-root ayrımını onlarca ayrıntılı ayrıcalığa böler. Her süreç veya dosya belirli capability'lere sahip olabilir.

Capability kümeleri

KümeAçıklama
Permitted (CapPrm)Sürecin sahip olabileceği maksimum capability kümesi; effective'e eklenebilir
Effective (CapEff)Şu an aktif olan capability'ler; kernel bu kümeye göre erişim kontrolü yapar
Inheritable (CapInh)exec() sonrasında child sürece aktarılacak capability'ler
Bounding (CapBnd)Sürecin asla kazanamayacağı capability'leri kısıtlayan üst sınır
Ambient (CapAmb)Non-root exec() için inheritable'ı destekleyen ek küme (kernel 4.3+)

Önemli capability'ler

CAP_NET_BIND_SERVICE1024 altı port'a bağlanma; web sunucusu için root yerine bu yeterli
CAP_NET_ADMINAğ arayüzü, routing, iptables yönetimi — güçlü ve dikkatli kullanılmalı
CAP_SYS_TIMESistem saatini değiştirme (settimeofday, adjtimex)
CAP_SYS_PTRACEBaşka süreçleri izleme; strace, gdb gibi araçlar için gerekli
CAP_DAC_OVERRIDEDosya izinlerini atlama — root'a en yakın; dikkatle ver
CAP_SETUID / CAP_SETGIDuid/gid değiştirme; setuid binary'ler bu capability'ye ihtiyaç duyar
CAP_SYS_ADMINYüzlerce farklı ayrıcalıklı işlem; neredeyse root'a eşdeğer, kullanmaktan kaçın

Capability'leri okuma ve yönetme

bash
# libcap-dev ve libcap2-bin kurulumu
apt-get install libcap2-bin

# Mevcut sürecin capability'lerini göster
getpcaps $$

# Belirli bir PID'nin capability'lerini göster
getpcaps 1234

# /proc/PID/status ile okuma (ham hexadecimal)
grep Cap /proc/$$/status
# CapInh: 0000000000000000
# CapPrm: 0000003fffffffff
# CapEff: 0000003fffffffff
# CapBnd: 0000003fffffffff
# CapAmb: 0000000000000000

# Hex değeri okunabilir capability listesine çevir
capsh --decode=0000003fffffffff

# Dosya capability'lerini gör (setcap ile ayarlananlar)
getcap /usr/bin/ping
# /usr/bin/ping = cap_net_raw+ep

# Dosyaya capability ata (root/sudo gerekir)
setcap cap_net_bind_service+ep /usr/local/bin/myserver

# Dosya capability'lerini kaldır
setcap -r /usr/local/bin/myserver

Bu bölümde

  • 5 capability kümesi: permitted, effective, inheritable, bounding, ambient
  • getpcaps/getcap ile okuma, setcap ile dosyaya atama
  • CAP_SYS_ADMIN neredeyse root'a eşdeğer — daima daha spesifik capability kullan

02 Capability dropping

Bir daemon başlangıçta root yetkisiyle başlar (port bağlamak için), ardından gereksiz tüm capability'leri düşürerek minimum ayrıcalıkla çalışmaya devam eder.

prctl() ile capability yönetimi

C
#include <sys/prctl.h>
#include <sys/capability.h>
#include <unistd.h>
#include <grp.h>

/* Root olarak başla, port'a bağlan, sonra ayrıcalıkları düşür */
int drop_privileges(uid_t target_uid, gid_t target_gid) {
    cap_t caps;

    /* 1. Yalnızca ihtiyaç duyulan capability'leri tut */
    caps = cap_from_text("cap_net_bind_service=ep");
    if (cap_set_proc(caps) != 0) {
        perror("cap_set_proc");
        cap_free(caps);
        return -1;
    }
    cap_free(caps);

    /* 2. Supplementary group'ları ayarla */
    if (setgroups(0, NULL) != 0) {
        perror("setgroups");
        return -1;
    }

    /* 3. gid değiştir */
    if (setresgid(target_gid, target_gid, target_gid) != 0) {
        perror("setresgid");
        return -1;
    }

    /* 4. uid değiştir */
    if (setresuid(target_uid, target_uid, target_uid) != 0) {
        perror("setresuid");
        return -1;
    }

    /* 5. No new privileges: exec() ile yeni capability kazanılamaz */
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
        perror("prctl(PR_SET_NO_NEW_PRIVS)");
        return -1;
    }

    return 0;
}

PR_SET_SECUREBITS

SECBIT_NOROOTuid=0 olan sürecin otomatik olarak tüm capability'leri almasını engeller
SECBIT_NO_SETUID_FIXUPsetuid() çağrıları sırasında capability kümelerinin otomatik güncellenmesini engeller
SECBIT_KEEP_CAPSsetuid() sonrasında effective capability'lerin korunmasını sağlar (geçici)
SECBIT_NO_NEW_PRIVSsecurebit olarak PR_SET_NO_NEW_PRIVS — kalıcı ve devredilebilir
C
#include <sys/prctl.h>
#include <linux/securebits.h>

/* Root bile olsa yeni capability kazanılamaz */
prctl(PR_SET_SECUREBITS,
      SECBIT_NOROOT | SECBIT_NOROOT_LOCKED |
      SECBIT_NO_SETUID_FIXUP | SECBIT_NO_SETUID_FIXUP_LOCKED |
      SECBIT_KEEP_CAPS_LOCKED);

/* Bounding set'ten tüm tehlikeli capability'leri kaldır */
for (int cap = 0; cap <= CAP_LAST_CAP; cap++) {
    if (cap == CAP_NET_BIND_SERVICE) continue; /* İhtiyacımız var */
    prctl(PR_CAPBSET_DROP, cap, 0, 0, 0);
}

Ambient capability — non-root exec

Ambient capability kümesi (kernel 4.3+), non-root bir sürecin exec() çağrısı sırasında child sürece capability aktarmasını sağlar. Sistemd socket activation ile birlikte kullanışlıdır.

C
#include <sys/prctl.h>
#include <sys/capability.h>

/* Ambient capability ekle (permitted ve inheritable'da da olmalı) */
/* Önce inheritable'a ekle */
cap_t caps = cap_get_proc();
cap_value_t cap_list[] = { CAP_NET_BIND_SERVICE };
cap_set_flag(caps, CAP_INHERITABLE, 1, cap_list, CAP_SET);
cap_set_proc(caps);
cap_free(caps);

/* Sonra ambient'e ekle */
prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0);

/* Şimdi exec() ile başlatılan child uid=www-data olsa bile */
/* CAP_NET_BIND_SERVICE'e sahip olur */

Bu bölümde

  • cap_set_proc() + setresuid/gid: root'tan non-root geçişinde capability dropping
  • PR_SET_NO_NEW_PRIVS: exec() ile ayrıcalık kazanımını kalıcı olarak engeller
  • SECBIT_NOROOT: uid=0 olsa bile otomatik capability ataması olmaz
  • Ambient capability: non-root child'a belirli capability'leri aktarma (kernel 4.3+)

03 seccomp strict mode

seccomp strict modu, bir süreci yalnızca dört sistem çağrısına (read, write, exit, sigreturn) kısıtlar. Sandbox güvenlik gerektiren, önceden veri işleyen uygulamalar için idealdir.

Strict mode nasıl çalışır

prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT)
        │
        ▼
  İzin verilen syscall'lar:
    read(), write(), exit(), sigreturn()
        │
  Diğer tüm syscall'lar:
        ▼
    SIGKILL (anında sonlanma)
C
#include <sys/prctl.h>
#include <linux/seccomp.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>

int main(void) {
    int fd;
    char buf[4096];
    ssize_t n;

    /* Dosyayı strict mode ÖNCE aç */
    fd = open("/etc/hostname", O_RDONLY);
    if (fd < 0) {
        write(STDERR_FILENO, "open failed\n", 12);
        return 1;
    }

    /* Strict mode'u etkinleştir */
    /* Bundan sonra read, write, exit, sigreturn dışında her şey SIGKILL */
    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT) != 0) {
        /* prctl başarısız; kernel desteği yok */
        return 1;
    }

    /* Şu an sadece read/write/exit/sigreturn yapabiliriz */
    n = read(fd, buf, sizeof(buf) - 1);
    if (n > 0) {
        buf[n] = '\0';
        write(STDOUT_FILENO, buf, n);
    }

    /* _exit() exit syscall'ını çağırır — izinli */
    _exit(0);
}

Derleme ve kernel gereksinimleri

bash
# Derleme
gcc -O2 -o sandboxed_reader sandboxed_reader.c

# Kernel konfigürasyonu (zaten çoğu dağıtımda aktif)
# CONFIG_SECCOMP=y
# CONFIG_SECCOMP_FILTER=y

# Kernel desteğini kontrol et
grep CONFIG_SECCOMP /boot/config-$(uname -r)
# CONFIG_SECCOMP=y
# CONFIG_SECCOMP_FILTER=y

# Çalışma zamanı desteğini kontrol et
cat /proc/sys/kernel/seccomp/actions_avail
# kill_process kill_thread trap errno trace log allow

Strict mode sınırlamaları

Çok kısıtlayıcıGerçek uygulamalar onlarca farklı syscall kullanır; strict mode pratik değil
Hazırlık gerektirirTüm dosya/soket işlemleri strict mode aktive edilmeden önce tamamlanmalı
malloc() çalışmazbrk/mmap syscall'larına ihtiyaç duyar; strict modda bu çağrılar öldürücü
Kullanım alanıKriptografik hesaplama, sıkıştırma/açma motorları gibi isolated hesaplama görevleri

Bu bölümde

  • Strict mode: yalnızca read, write, exit, sigreturn — diğerleri SIGKILL
  • Tüm I/O işlemleri aktif etmeden önce tamamlanmalı
  • Gerçek uygulamalar için seccomp-bpf daha esnek ve pratiktir

04 seccomp-bpf filtre yazımı

seccomp-bpf, BPF (Berkeley Packet Filter) programlarını syscall filtresi olarak kullanır. Her syscall için izin ver, reddet veya özel eylem kararı verilebilir.

BPF filtre yapısı

C
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/audit.h>
#include <sys/syscall.h>
#include <sys/prctl.h>
#include <stddef.h>

/* BPF makroları seccomp için */
#define VALIDATE_ARCH \
    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, \
             offsetof(struct seccomp_data, arch)), \
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0), \
    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL_PROCESS)

#define LOAD_SYSCALL_NR \
    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, \
             offsetof(struct seccomp_data, nr))

#define ALLOW_SYSCALL(name) \
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SYS_##name, 0, 1), \
    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)

#define BLOCK_SYSCALL(name) \
    BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SYS_##name, 0, 1), \
    BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EPERM)

static int install_filter(void) {
    struct sock_filter filter[] = {
        /* 1. Mimari doğrula */
        VALIDATE_ARCH,
        /* 2. Syscall numarasını yükle */
        LOAD_SYSCALL_NR,
        /* 3. İzin verilen syscall'lar */
        ALLOW_SYSCALL(read),
        ALLOW_SYSCALL(write),
        ALLOW_SYSCALL(open),
        ALLOW_SYSCALL(openat),
        ALLOW_SYSCALL(close),
        ALLOW_SYSCALL(fstat),
        ALLOW_SYSCALL(mmap),
        ALLOW_SYSCALL(mprotect),
        ALLOW_SYSCALL(munmap),
        ALLOW_SYSCALL(brk),
        ALLOW_SYSCALL(exit_group),
        ALLOW_SYSCALL(futex),
        /* 4. Tüm diğerlerini reddet — EPERM ile (crash yerine hata döner) */
        BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EPERM),
    };

    struct sock_fprog prog = {
        .len = sizeof(filter) / sizeof(filter[0]),
        .filter = filter,
    };

    /* No new privs zorunlu (CAP_SYS_ADMIN olmadan) */
    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0)
        return -1;

    /* Filtreyi yükle */
    if (prctl(PR_SET_SECCOMP, SECCOMP_SET_MODE_FILTER, &prog) != 0)
        return -1;

    return 0;
}

SECCOMP_RET eylem türleri

EylemDeğerDavranış
SECCOMP_RET_ALLOW0x7fff0000Syscall'a izin ver
SECCOMP_RET_ERRNO0x00050000 | errnoSyscall'ı reddet, errno döndür
SECCOMP_RET_KILL_THREAD0x00000000Çağıran thread'i SIGSYS ile öldür
SECCOMP_RET_KILL_PROCESS0x80000000Tüm süreci öldür (kernel 4.14+)
SECCOMP_RET_TRAP0x00030000SIGSYS gönder (signal handler tetiklenir)
SECCOMP_RET_TRACE0x7ff00000ptrace ile izleyen sürece bildir
SECCOMP_RET_LOG0x7ffc0000Audit log'a yaz, syscall'a izin ver

Syscall argümanı filtreleme

BPF filtresi, syscall numarasının yanı sıra argümanları da inceleyebilir. Bu özellikle socket() çağrısında soket türünü kısıtlamak için kullanılır.

C
/* Yalnızca AF_INET TCP socket'lerine izin ver, raw socket'leri engelle */
/* socket(domain, type, protocol) — arg0=domain, arg1=type */

#define LOAD_ARG0_LOW32 \
    BPF_STMT(BPF_LD | BPF_W | BPF_ABS, \
             offsetof(struct seccomp_data, args[0]))

/* socket syscall için argüman denetimi */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SYS_socket, 0, 10),
/* arg0 = AF_INET (2) mi? */
LOAD_ARG0_LOW32,
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AF_INET, 0, 4),
/* arg1 = SOCK_STREAM (1) veya SOCK_DGRAM (2) mi? */
BPF_STMT(BPF_LD | BPF_W | BPF_ABS,
         offsetof(struct seccomp_data, args[1])),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SOCK_STREAM, 1, 0),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, SOCK_DGRAM, 0, 1),
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW),
/* AF_INET ama SOCK_RAW veya diğerleri — reddet */
BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ERRNO | EPERM),

Bu bölümde

  • BPF filtresi: struct sock_filter dizisi, prctl(PR_SET_SECCOMP, SECCOMP_SET_MODE_FILTER)
  • Önce PR_SET_NO_NEW_PRIVS gerekli (CAP_SYS_ADMIN olmadan yükleme için)
  • SECCOMP_RET_ERRNO: güvenli reddetme, KILL_PROCESS: sert sonlandırma
  • args[0]–args[5]: syscall argümanlarını filtrelemek mümkün

05 libseccomp ile kolay filtre

libseccomp, ham BPF kodu yazmadan yüksek seviyeli API ile seccomp filtresi oluşturur. Taşınabilir kod yazmayı kolaylaştırır ve mimari farkını soyutlar.

Temel libseccomp API

C
#include <seccomp.h>
#include <errno.h>

/*
 * Derleme: gcc -O2 -o app app.c -lseccomp
 * Embedded için statik: gcc -O2 -static -o app app.c -l:libseccomp.a
 */

int setup_seccomp(void) {
    scmp_filter_ctx ctx;
    int rc;

    /* Varsayılan eylem: izin verilmeyen tüm syscall'ları EPERM ile reddet */
    ctx = seccomp_init(SCMP_ACT_ERRNO(EPERM));
    if (ctx == NULL)
        return -1;

    /* İzin verilen syscall'ları tek tek ekle */
    rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
    rc |= seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);

    if (rc != 0) {
        seccomp_release(ctx);
        return -1;
    }

    /* Filtreyi kernel'e yükle */
    rc = seccomp_load(ctx);
    seccomp_release(ctx);
    return rc;
}

Argüman tabanlı kural ekleme

C
/* Yalnızca AF_INET + SOCK_STREAM socket'lerine izin ver */
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 2,
    SCMP_A0(SCMP_CMP_EQ, AF_INET),
    SCMP_A1(SCMP_CMP_EQ, SOCK_STREAM));

/* open() sadece O_RDONLY ile */
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 1,
    SCMP_A1(SCMP_CMP_MASKED_EQ, O_ACCMODE, O_RDONLY));

/* ioctl sadece FIONREAD komutuna izin ver */
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ioctl), 1,
    SCMP_A1(SCMP_CMP_EQ, FIONREAD));

/* Karşılaştırma operatörleri:
   SCMP_CMP_EQ    — eşit
   SCMP_CMP_NE    — eşit değil
   SCMP_CMP_LT    — küçük
   SCMP_CMP_LE    — küçük eşit
   SCMP_CMP_GT    — büyük
   SCMP_CMP_GE    — büyük eşit
   SCMP_CMP_MASKED_EQ — mask & arg == datum */

Filtreyi dosyaya yazma (analiz / dağıtım)

bash
# C API ile BPF byte code'u dosyaya aktar (analiz için)
# seccomp_export_bpf(ctx, fd);

# seccomp-tools ile filtre içeriğini oku
apt-get install ruby-seccomp-tools   # veya gem install seccomp-tools
seccomp-tools dump ./my_sandboxed_app
# Örnek çıktı:
#  line  CODE  JT   JF      K
# =================================
#  0000: 0x20 0x00 0x00 0x00000004  ld  [4]  ; arch
#  0001: 0x15 0x00 0x04 0xc000003e  jeq ARCH_X86_64 ; true: continue, false: +4
#  ...

# strace ile hangi syscall'ların kullanıldığını bul (filtre yazmadan önce)
strace -c ./my_app 2>&1 | tail -20

Embedded için önerilen libseccomp kullanımı

Statik linklemeKüçük embedded rootfs'te -l:libseccomp.a ile statik derle; ek .so gerekmez
strace -c ile analizFiltre yazmadan önce uygulamanın kullandığı tüm syscall'ları say: strace -c ./app
SCMP_ACT_LOGGeliştirme sırasında SCMP_ACT_ERRNO yerine SCMP_ACT_LOG kullan — log al, engelleme
Çok thread dikkatFiltre her thread'e ayrı uygulanır; thread oluşturulmadan önce yükleme önerilir

Bu bölümde

  • seccomp_init(SCMP_ACT_ERRNO) + seccomp_rule_add + seccomp_load — üç adım
  • Argüman tabanlı kural: SCMP_A0(SCMP_CMP_EQ, değer) ile spesifik filtreleme
  • strace -c ile önce syscall envanteri, sonra izin listesi oluştur

06 Systemd sandbox

Systemd, servis birimlerine seccomp, capability ve namespacing kısıtlamaları eklemeyi kod değişikliği gerektirmeden sağlar. Modern embedded dağıtımlar için güçlü bir güvenlik katmanıdır.

Temel güvenlik direktifleri

systemd unit
[Service]
ExecStart=/usr/bin/myserver

# ──────────────────────────────────────────
# Capability kısıtlamaları
# ──────────────────────────────────────────
# Yalnızca bu capability'lere izin ver (geri kalanlar bounding set'ten kaldırılır)
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_NET_ADMIN

# Ambient capability ekle (non-root servis için port < 1024 bağlama)
AmbientCapabilities=CAP_NET_BIND_SERVICE

# ──────────────────────────────────────────
# Ayrıcalık yükseltmeyi engelle
# ──────────────────────────────────────────
NoNewPrivileges=yes

# ──────────────────────────────────────────
# Kimlik
# ──────────────────────────────────────────
User=myserver
Group=myserver
DynamicUser=yes     # Çalışma zamanında geçici uid/gid ata

# ──────────────────────────────────────────
# Dosya sistemi kısıtlamaları
# ──────────────────────────────────────────
ProtectSystem=strict          # /usr, /boot, /etc salt okunur
ProtectHome=yes               # /home, /root erişilemez
PrivateTmp=yes                # Özel /tmp
ReadWritePaths=/var/lib/myserver /run/myserver
ReadOnlyPaths=/etc/myserver

# ──────────────────────────────────────────
# Syscall filtreleme
# ──────────────────────────────────────────
SystemCallFilter=@system-service       # Servis için gerekli syscall kümesi
SystemCallFilter=~@privileged @resources  # Tehlikeli grupları çıkar
SystemCallErrorNumber=EPERM            # Yasaklı syscall SIGKILL yerine EPERM döndürür

# ──────────────────────────────────────────
# Namespace izolasyonu
# ──────────────────────────────────────────
PrivateNetwork=no              # Ağa ihtiyaç var
PrivateDevices=yes             # /dev erişimini kısıtla
ProtectKernelTunables=yes      # /proc/sys yazmaya kapat
ProtectKernelModules=yes       # modprobe/rmmod engelle
ProtectKernelLogs=yes          # /dev/kmsg, /proc/kmsg kapat
ProtectClock=yes               # Sistem saati değiştirme engelle
RestrictNamespaces=yes         # Yeni namespace oluşturmayı engelle
LockPersonality=yes            # personality() syscall'ını kısıtla
MemoryDenyWriteExecute=yes     # W+X bellek eşlemesi engelle (JIT olmayan servisler için)

SystemCallFilter grupları

Grupİçerik
@basic-ioread, write, close, lseek, dup, pipe, …
@network-iosocket, connect, bind, accept, send, recv, …
@system-serviceDaemon için gerekli temel syscall kümesi (geniş)
@privilegedmount, chown, mknod, ptrace, … — genellikle engellenmeli
@resourcessetrlimit, nice, ioprio_set, … — performans ayarları
@raw-ioiopl, ioperm, pciconfig_read — donanım erişimi
@rebootreboot, kexec_load — sistemi yeniden başlatma
@clocksettimeofday, clock_settime, adjtimex

Güvenlik skorunu kontrol etme

bash
# Servis güvenlik skorunu analiz et (systemd 240+)
systemd-analyze security myserver.service

# Örnek çıktı:
# NAME                        DESCRIPTION                              EXPOSURE
# ✓ NoNewPrivileges=          Service processes cannot gain new privs   0.0
# ✓ SystemCallFilter=         System call filtering                     0.0
# ✗ MemoryDenyWriteExecute=  Memory regions can be written and exec    0.1
# ...
# → Overall exposure level: 2.1 OK 🟢

# Birim dosyasını doğrula
systemd-analyze verify /etc/systemd/system/myserver.service

Bu bölümde

  • SystemCallFilter=@system-service ~@privileged: kod değişikliği olmadan seccomp
  • CapabilityBoundingSet + NoNewPrivileges: capability dropping unit dosyasından
  • ProtectSystem=strict + PrivateTmp: dosya sistemi izolasyonu
  • systemd-analyze security: güvenlik puan tablosu

07 Container entegrasyonu

Docker ve Kubernetes, container süreçleri için seccomp profili ve capability kısıtlamaları destekler. OCI runtime spec üzerinden standart bir arayüz sunar.

Docker seccomp profili

Docker varsayılan olarak yaklaşık 44 tehlikeli syscall'ı engelleyen bir seccomp profili uygular. Özel profil JSON formatında tanımlanır.

json
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": [
    "SCMP_ARCH_X86_64",
    "SCMP_ARCH_AARCH64"
  ],
  "syscalls": [
    {
      "names": [
        "read", "write", "open", "openat", "close",
        "fstat", "stat", "lstat", "mmap", "mprotect",
        "munmap", "brk", "rt_sigaction", "rt_sigprocmask",
        "rt_sigreturn", "ioctl", "access", "pipe", "select",
        "sched_yield", "mremap", "msync", "mincore", "madvise",
        "socket", "connect", "accept", "sendto", "recvfrom",
        "bind", "listen", "getsockname", "getpeername",
        "setsockopt", "getsockopt", "clone", "fork", "execve",
        "exit", "wait4", "kill", "uname", "fcntl", "flock",
        "fsync", "getdents", "getcwd", "chdir", "rename",
        "mkdir", "rmdir", "unlink", "readlink", "chmod",
        "gettimeofday", "getrlimit", "getuid", "getgid",
        "setuid", "setgid", "geteuid", "getegid", "getppid",
        "getpgrp", "setsid", "setgroups", "getgroups",
        "exit_group", "futex", "set_tid_address",
        "clock_gettime", "clock_getres", "clock_nanosleep",
        "epoll_wait", "epoll_ctl", "epoll_create1",
        "timerfd_create", "timerfd_settime", "timerfd_gettime",
        "signalfd4", "eventfd2", "pipe2", "inotify_init1",
        "inotify_add_watch", "sendmsg", "recvmsg",
        "getrandom", "copy_file_range"
      ],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}
bash
# Özel seccomp profili ile container çalıştır
docker run --security-opt seccomp=/etc/docker/seccomp-myapp.json myimage

# Capability dropping ile çalıştır
docker run \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  --security-opt no-new-privileges \
  myimage

# Seccomp tamamen devre dışı (geliştirme/test)
docker run --security-opt seccomp=unconfined myimage

Kubernetes seccomp profili

yaml
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  securityContext:
    # Pod seviyesinde seccomp (Kubernetes 1.19+ GA)
    seccompProfile:
      type: Localhost
      localhostProfile: profiles/myapp-seccomp.json
    runAsNonRoot: true
    runAsUser: 1000

  containers:
  - name: myapp
    image: myapp:1.0
    securityContext:
      # Container seviyesinde capability
      capabilities:
        drop:
        - ALL
        add:
        - NET_BIND_SERVICE
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      seccompProfile:
        type: RuntimeDefault   # Container runtime varsayılan profili

OCI seccomp profili konumları

RuntimeVarsayılan profil konumu
Docker (containerd)/etc/docker/seccomp.json
containerd/etc/containerd/config.toml → seccomp_profile
CRI-O/etc/crio/seccomp.json
Kubernetes node/var/lib/kubelet/seccomp/profiles/

Bu bölümde

  • Docker: --security-opt seccomp=profil.json + --cap-drop ALL --cap-add ...
  • Kubernetes: pod.spec.securityContext.seccompProfile + capabilities.drop/add
  • Profil konumu: /var/lib/kubelet/seccomp/profiles/ (node yerel)

08 Hata ayıklama

seccomp ve capability sorunları genellikle EACCES veya EPERM hataları olarak ortaya çıkar. doğru araçlarla hangi syscall veya capability'nin engellendiğini tespit etmek mümkündür.

/proc/PID/status capability alanları

bash
# Mevcut kabuğun capability durumu
grep Cap /proc/$$/status
# CapInh: 0000000000000000
# CapPrm: 0000000000000000
# CapEff: 0000000000000000
# CapBnd: 000001ffffffffff
# CapAmb: 0000000000000000

# Hex değeri okunabilir isme çevir
capsh --decode=000001ffffffffff

# seccomp durumunu göster
grep Seccomp /proc/$$/status
# Seccomp: 2    (0=yok, 1=strict, 2=filter)
grep SeccompFilters /proc/$$/status
# SeccompFilters: 1   (yüklü filtre sayısı)

strace ile syscall yakalama

bash
# Engellenen syscall'ları bul (EPERM/EACCES dönen çağrılar)
strace -e trace=all 2>&1 ./myapp | grep -E "EPERM|EACCES|= -1"

# Yalnızca başarısız syscall'ları göster
strace -Z ./myapp 2>&1 | head -50   # -Z: başarısız syscall'ları vurgula

# Hangi syscall'ların kullanıldığını say (filtre yazmadan önce)
strace -c ./myapp 2>&1 | sort -k4 -rn | head -20

# Thread ve child süreçleri de takip et
strace -f -e trace=all ./myapp 2>strace.log
grep "SIGSYS\|SECCOMP\|EPERM" strace.log

auditd ile SIGSYS yakalama

bash
# seccomp ihlallerini audit log'a yaz
# (SECCOMP_RET_KILL ile öldürülen süreçler audit log'a düşer)
ausearch -m SECCOMP -ts recent

# Örnek audit log çıktısı:
# type=SECCOMP msg=audit(1234567890.123:456):
#   auid=1000 uid=1000 gid=1000
#   ses=3 subj=unconfined_u:... pid=12345 comm="myapp"
#   exe="/usr/bin/myapp" sig=31 arch=c000003e syscall=56
#   compat=0 ip=0x7f... code=0x80000000

# syscall=56 nedir?
ausyscall x86_64 56   # clone

# SECCOMP_RET_LOG ile loglayan filtre (engelleme yok)
# Geliştirme sırasında kullanışlı: SCMP_ACT_LOG
journalctl -k | grep "seccomp"

Capability sorunları

bash
# Bir ikiliyi hangi capability ile çalıştırılması gerektiğini test et
# capsh ile geçici capability değişimi
capsh --caps="cap_net_raw=eip" -- -c "./myapp"

# setpriv ile belirli capability seti ile çalıştır (util-linux)
setpriv --bounding-set -cap_sys_admin --inh-caps -all ./myapp

# Capability gerektiren erişim hatası: EPERM + errno=1
# strace ile yakalama:
strace -e trace=socket,bind,open ./myapp 2>&1 | grep EPERM

# Dosya capability'lerini listele (tüm /usr/bin)
find /usr/bin -exec getcap {} + 2>/dev/null

Yaygın sorunlar tablosu

BelirtiNedenÇözüm
Program anında ölüyor (signal 31)seccomp KILL_PROCESS: yasak syscallstrace -c ile syscall'ı bul, filtreye ekle
socket() EPERM döndürüyorseccomp ERRNO veya CAP_NET_RAW eksikfiltre veya capability kontrolü
bind() port 80 için EACCESCAP_NET_BIND_SERVICE yoksetcap veya AmbientCapabilities ekle
ptrace attach başarısızCAP_SYS_PTRACE yok veya Yama Yatchescapability ekle veya /proc/sys/kernel/yama/ptrace_scope=0
mount() EPERMCAP_SYS_ADMIN yok + seccompuser namespace + unshare -m veya capability
exec() sonrası capability kaybıinheritable/ambient ayarlanmamışPR_CAP_AMBIENT_RAISE veya dosya capability

Bu bölümde

  • /proc/PID/status: CapPrm/CapEff/CapBnd/CapAmb hex değerleri + capsh --decode
  • strace -c: syscall envanteri; strace -Z: başarısız çağrıları vurgula
  • ausearch -m SECCOMP: audit log'da yakalanan seccomp ihlalleri
  • SECCOMP_RET_LOG / SCMP_ACT_LOG: geliştirme sırasında engelsiz loglama