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
Tehdit modeli: privilege escalation
| Saldırı vektörü | DAC | Capabilities | seccomp |
|---|---|---|---|
| Dosya okuma yetkisi yok | Engeller | Kısmen | Etkisiz |
| setuid binary istismarı | Etkisiz | Engeller (drop) | Kısmen |
| Kernel exploit via syscall | Etkisiz | Etkisiz | Engeller |
| ptrace ile süreç enjeksiyonu | Kısmen | CAP_SYS_PTRACE drop ile | ptrace syscall filtrele |
| Network raw socket | Etkisiz | CAP_NET_RAW drop ile | socket() 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üme | Açı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
Capability'leri okuma ve yönetme
# 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
#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
#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.
#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)
#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
# 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ı
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ı
#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
| Eylem | Değer | Davranış |
|---|---|---|
| SECCOMP_RET_ALLOW | 0x7fff0000 | Syscall'a izin ver |
| SECCOMP_RET_ERRNO | 0x00050000 | errno | Syscall'ı reddet, errno döndür |
| SECCOMP_RET_KILL_THREAD | 0x00000000 | Çağıran thread'i SIGSYS ile öldür |
| SECCOMP_RET_KILL_PROCESS | 0x80000000 | Tüm süreci öldür (kernel 4.14+) |
| SECCOMP_RET_TRAP | 0x00030000 | SIGSYS gönder (signal handler tetiklenir) |
| SECCOMP_RET_TRACE | 0x7ff00000 | ptrace ile izleyen sürece bildir |
| SECCOMP_RET_LOG | 0x7ffc0000 | Audit 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.
/* 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
#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
/* 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)
# 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ı
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
[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-io | read, write, close, lseek, dup, pipe, … |
| @network-io | socket, connect, bind, accept, send, recv, … |
| @system-service | Daemon için gerekli temel syscall kümesi (geniş) |
| @privileged | mount, chown, mknod, ptrace, … — genellikle engellenmeli |
| @resources | setrlimit, nice, ioprio_set, … — performans ayarları |
| @raw-io | iopl, ioperm, pciconfig_read — donanım erişimi |
| @reboot | reboot, kexec_load — sistemi yeniden başlatma |
| @clock | settimeofday, clock_settime, adjtimex |
Güvenlik skorunu kontrol etme
# 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.
{
"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"
}
]
}
# Ö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
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ı
| Runtime | Varsayı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ı
# 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
# 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
# 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ı
# 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
| Belirti | Neden | Çözüm |
|---|---|---|
| Program anında ölüyor (signal 31) | seccomp KILL_PROCESS: yasak syscall | strace -c ile syscall'ı bul, filtreye ekle |
| socket() EPERM döndürüyor | seccomp ERRNO veya CAP_NET_RAW eksik | filtre veya capability kontrolü |
| bind() port 80 için EACCES | CAP_NET_BIND_SERVICE yok | setcap veya AmbientCapabilities ekle |
| ptrace attach başarısız | CAP_SYS_PTRACE yok veya Yama Yatches | capability ekle veya /proc/sys/kernel/yama/ptrace_scope=0 |
| mount() EPERM | CAP_SYS_ADMIN yok + seccomp | user 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