Linux’ta servis hardening için AppArmor/SELinux gibi güçlü araçlar var. Ama sahada en hızlı “etki/çaba” oranını çoğu zaman systemd unit sandboxing verir: uygulamayı değiştirmeden izin alanını daraltırsınız.
Bu yazıda “minimum risk” ile başlayıp kademeli sıkılaştırılan bir model anlatıyorum.
Başlangıç: önce gözlemle
Sertleştirmeye başlamadan önce iki şeyi netleştirin:
- Servis hangi dosyalara yazıyor? (log, tmp, cache, socket)
- Hangi portlara çıkıyor / dinliyor?
Bu gözlem olmadan ProtectSystem veya ReadOnlyPaths gibi ayarlar “kır ve yak” olur.
Faz 1 — Düşük riskli üçlü
Bu üç ayar çoğu serviste az sürpriz çıkarır:
NoNewPrivileges=truePrivateTmp=trueProtectHome=true(servis home erişimi gerekmiyorsa)
Faz 2 — Dosya sistemi: ProtectSystem
En etkili ama en çok kıran yer burasıdır:
ProtectSystem=full(bazı yollar read-only)ProtectSystem=strict(neredeyse her yer read-only)
Bu noktada servisinizin yazması gereken yerleri açıkça tanımlarsınız:
ReadWritePaths=/var/lib/myapp /var/log/myappStateDirectory=myapp(systemd’nin yönetmesini sağlar)CacheDirectory=myapp
Örnek unit parçası
[Service]
DynamicUser=true
StateDirectory=myapp
CacheDirectory=myapp
LogsDirectory=myapp
NoNewPrivileges=true
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ReadWritePaths=/var/lib/myapp /var/log/myapp
Faz 3 — Kimlik: DynamicUser
DynamicUser=true ile servis, sistemde kalıcı kullanıcı açmadan izole olur. Sahadaki kazanımlar:
- “yanlışlıkla ortak user” kullanımı biter
- izin seti daralır
- servis kaldırıldığında iz bırakmaz
Ama dikkat:
- Servis kalıcı dosya yazacaksa
StateDirectory=gibi systemd-managed dizin kullanın - Bazı uygulamalar UID/GID’yi “kalıcı” bekler; burada istisna yönetimi gerekir
Faz 4 — Linux capabilities ve syscall filtreleri
En kritik prensip: capability listesini minimal tut.
Örnek:
- Web servisi genelde
CAP_NET_BIND_SERVICEdışında hiçbir şeye ihtiyaç duymaz (80/443 dinleyecekse).
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
Syscall filtreleri daha ileri bir seviye:
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
Faz 5 — Ağ kısıtları (gerekiyorsa)
Uygulama sadece belirli aileleri kullanıyorsa:
RestrictAddressFamilies=AF_INET AF_INET6
Eğer servis “dışarı çıkmamalı” ise daha sert kısıtlar düşünülür, ama burada “kırma” riski büyür. Önce egress gözlemlemesi öneririm.
Rollout ve geri dönüş
Ben bu tarz hardening değişikliklerini de “release ring” ile yayarım:
- Canary: 1 host
- Pilot: 5–10%
- Prod: kalan
Rollback planı basit olmalı:
- Unit drop-in dosyasını geri al
systemctl daemon-reload && systemctl restart
Kapanış: Sertleştirme bir ürün gibi yönetilmeli
systemd sandboxing “tek seferlik güvenlik işi” değil, servis yaşam döngüsünün parçası olmalı. En iyi sonuç, küçük adımlarla ve ölçümle gelir: hangi ayar, hangi riski düşürdü ve hangi operasyonel maliyetle?