Linux sunucularda çalışan bir servis, varsayılan olarak çalıştığı kullanıcının erişebildiği her şeye ulaşabilir: dosya sisteminin büyük bölümüne, ağ soketlerine, /proc altındaki süreç bilgilerine ve kimi zaman çekirdek ayarlarına. Bir web uygulaması, API servisi ya da arka plan işi ele geçirildiğinde bu geniş erişim, saldırganın sunucunun geri kalanına yayılmasını kolaylaştırır. systemd, konteyner ya da sanal makine kurmadan, servis birim dosyasına eklenen birkaç satırla bu erişimi daraltan yerleşik bir sandbox mekanizması sunar. Bu rehberde en etkili direktifleri, bunları güvenle uygulama yöntemini ve systemd-analyze security ile ölçümü ele alıyoruz.

Direktifler nereye yazılır?

Sandbox ayarları birim dosyasının [Service] bölümüne yazılır ve systemd.exec(5) belgesinde tanımlıdır. Paket yöneticisinin kurduğu birim dosyasını doğrudan düzenlemek yerine bir "drop-in" dosyası oluşturmak, güncellemelerde değişikliklerin kaybolmasını önler:

CODE TERMINAL
systemctl edit uygulama.service


Bu komut /etc/systemd/system/uygulama.service.d/override.conf dosyasını açar. Kaydettikten sonra servisi yeniden başlatmak yeterlidir:

CODE TERMINAL
systemctl daemon-reload
systemctl restart uygulama.service


Dosya sistemi izolasyonu: ProtectSystem ve ProtectHome

ProtectSystem= servisin işletim sistemi dizinlerine yazmasını engeller. Alabileceği değerler:

DeğerEtkisi
falseKısıtlama yok (varsayılan)
true/usr ve önyükleme dizinleri salt okunur
fulltrue'ya ek olarak /etc de salt okunur
strict/dev, /proc ve /sys hariç tüm dosya sistemi salt okunur


systemd belgeleri, işletim sistemini değiştirmesi gerekmeyen uzun süre çalışan servisler için strict kullanılmasını önerir. Servisin gerçekten yazması gereken dizinler ReadWritePaths= ile istisna olarak açılır; ek salt okunur dizinler için ReadOnlyPaths=, tamamen gizlenecek dizinler için InaccessiblePaths= kullanılır.

ProtectHome= ise /home, /root ve /run/user dizinlerini hedefler. true bu dizinleri erişilemez yapar, read-only salt okunur hâle getirir, tmpfs ise yerlerine boş, geçici bir dosya sistemi bağlar.

CODE TERMINAL
[Service]
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/uygulama


Veri, önbellek ve log dizinleri için StateDirectory=, CacheDirectory= ve LogsDirectory= kullanmak da iyi bir alternatiftir: systemd bu dizinleri /var/lib, /var/cache ve /var/log altında oluşturur, sahipliğini servise verir ve ProtectSystem=strict altında bile yazılabilir tutar.

Yetki yükseltmeyi engellemek: NoNewPrivileges ve yetenekler

NoNewPrivileges=true, sürecin ve alt süreçlerinin execve() çağrısıyla, örneğin setuid/setgid bitleri ya da dosya yetenekleri (file capabilities) üzerinden yeni ayrıcalık kazanamamasını garanti eder. Neredeyse her servis için güvenle açılabilecek en etkili tek satırlardan biridir.

Servisin root olarak başlaması gerekiyorsa CapabilityBoundingSet= ile sahip olabileceği Linux yetenekleri sınırlandırılır. Örneğin yalnızca 1024 altındaki bir portu dinlemesi gereken bir servis için:

CODE TERMINAL
[Service]
NoNewPrivileges=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
User=uygulama


Ayrı bir sistem kullanıcısı oluşturmak istemiyorsanız DynamicUser=yes servis her başladığında geçici bir kullanıcı ve grup atar; bu seçenek bazı dosya sistemi korumalarını da otomatik olarak etkinleştirir.

Geçici dosyalar, aygıtlar ve /proc görünürlüğü

PrivateTmp=true, servise sistemin geri kalanından ayrı /tmp ve /var/tmp dizinleri verir; böylece başka süreçlerin geçici dosyaları okunamaz ve /tmp üzerinden yapılan sembolik bağlantı saldırıları zorlaşır. Yeni systemd sürümlerinde disconnected değeri, host ile hiçbir bağı olmayan ayrı bir tmpfs kullanır.

PrivateDevices=true ayarlandığında servis yalnızca /dev/null, /dev/zero ve /dev/random gibi sözde aygıtları görür; disk ve donanım aygıtlarına erişim kaldırılır.

ProtectProc=invisible, başka kullanıcılara ait süreçleri /proc altında görünmez yapar. ProcSubset=pid ise /proc içinde yalnızca süreçlerle ilgili girdileri bırakır, çekirdek ve sistem bilgisi içeren diğer dosyaları gizler.

CODE TERMINAL
[Service]
PrivateTmp=true
PrivateDevices=true
ProtectProc=invisible
ProcSubset=pid


Çekirdek ve kontrol grubu koruması

Normal bir uygulama servisinin çekirdek ayarlarını değiştirmesine, modül yüklemesine ya da cgroup hiyerarşisine yazmasına gerek yoktur. Aşağıdaki direktifler bu alanları kapatır:


  • ProtectKernelTunables=true: /proc/sys, /sys ve benzeri çekirdek ayar arayüzlerini salt okunur yapar.
  • ProtectKernelModules=true: Çekirdek modülü yükleme ve kaldırmayı engeller.
  • ProtectKernelLogs=true: Çekirdek log arabelleğine erişimi kapatır.
  • ProtectControlGroups=true: cgroup hiyerarşisini salt okunur yapar.
  • ProtectClock=true ve ProtectHostname=true: Sistem saatinin ve sunucu adının değiştirilmesini engeller.



Ağ, ad alanı ve sistem çağrısı kısıtlaması

RestrictAddressFamilies= servisin açabileceği soket ailelerini sınırlar; çoğu ağ servisi için AF_INET, AF_INET6 ve AF_UNIX yeterlidir. RestrictNamespaces=true yeni ad alanı (namespace) oluşturmayı, RestrictSUIDSGID=true setuid/setgid bitli dosya oluşturmayı engeller.

SystemCallFilter= izin verilen sistem çağrılarını tanımlar. Tek tek liste yazmak yerine systemd'nin hazır gruplarını kullanmak daha güvenlidir: @system-service tipik bir servisin ihtiyaç duyduğu çağrıları kapsar; başına ~ konan gruplar ise reddedilir.

CODE TERMINAL
[Service]
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
RestrictSUIDSGID=true
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources


Hangi grupların hangi çağrıları içerdiğini görmek için systemd-analyze syscall-filter komutu kullanılabilir.

Bellek koruması

MemoryDenyWriteExecute=true, bir bellek bölgesinin aynı anda hem yazılabilir hem çalıştırılabilir olmasını engeller; enjekte edilen kodun çalıştırılmasına dayanan pek çok istismar tekniğini zorlaştırır. Ancak JIT derleyici kullanan çalışma ortamlarında (örneğin bazı Java, Node.js ya da PHP JIT yapılandırmaları) servisin çalışmasını bozabilir, bu yüzden test edilerek açılmalıdır. LockPersonality=true süreç kişiliğinin değiştirilmesini engeller; UMask=0077 servisin oluşturduğu dosyaların yalnızca sahibi tarafından okunmasını sağlar.

Bir araya getirilmiş örnek

Aşağıdaki drop-in, /var/lib/uygulama altına yazan ve 8080 portunu dinleyen bir servis için başlangıç noktasıdır:

CODE TERMINAL
[Service]
User=uygulama
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
StateDirectory=uygulama
PrivateTmp=true
PrivateDevices=true
ProtectProc=invisible
ProcSubset=pid
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectClock=true
ProtectHostname=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
RestrictSUIDSGID=true
LockPersonality=true
MemoryDenyWriteExecute=true
SystemCallArchitectures=native
SystemCallFilter=@system-service
CapabilityBoundingSet=
UMask=0077


Boş bırakılan CapabilityBoundingSet= satırı servisin tüm Linux yeteneklerini bırakmasını sağlar.

systemd-analyze security ile ölçüm

systemd, sandbox düzeyini değerlendirmek için bir analiz aracı içerir. Argümansız çalıştırıldığında yüklü tüm uzun süre çalışan servisleri kısa bir tabloyla listeler; bir birim adı verildiğinde ayrıntılı analiz yapar:

CODE TERMINAL
systemd-analyze security
systemd-analyze security uygulama.service


Araç her güvenlik ayarına önemine göre bir "maruziyet" değeri atar ve birimin tamamı için 0.0 ile 10.0 arasında genel bir maruziyet düzeyi hesaplar. Yüksek değer az sandbox uygulandığını, düşük değer sıkı kısıtlamaları gösterir. Hiç sertleştirilmemiş servisler genellikle UNSAFE olarak işaretlenir; hedef, puanı adım adım düşürmektir.

Faydalı seçenekler:


  • --threshold=: Belirlenen eşiğin üzerindeki birimler için hata dönerek CI süreçlerinde kullanım sağlar.
  • --offline=yes: Birim dosyasını çalışan sisteme dayanmadan inceler; dağıtımdan önce denetim için uygundur.
  • --json=pretty: Sonucu JSON olarak verir.
  • --security-policy=: JSON biçiminde kendi güvenlik gereksinimlerinizi tanımlamanızı sağlar.



Önemli bir sınır: araç yalnızca systemd'nin kendi uyguladığı servis başına güvenlik özelliklerini değerlendirir. Uygulamanın kendi kodunda uyguladığı güvenlik mekanizmaları puana yansımaz, bu yüzden puan bir karşılaştırma aracı olarak görülmelidir.

Güvenli uygulama için öneriler


  • Direktifleri tek seferde değil, küçük gruplar hâlinde ekleyin; her adımdan sonra servisi yeniden başlatıp journalctl -u uygulama.service ile hataları kontrol edin.
  • "Permission denied", "Read-only file system" ya da "Operation not permitted" hataları genellikle eksik bir ReadWritePaths= ya da fazla dar bir SystemCallFilter= işaretidir.
  • Değişiklikleri önce test ortamında, --offline analizle birlikte doğrulayın.
  • Sandbox; güncelleme, güvenlik duvarı ve en az yetkili kullanıcı gibi diğer önlemlerin yerine geçmez, onları tamamlar.



Kaynaklar

  • systemd.exec(5): https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html
  • systemd-analyze(1): https://man7.org/linux/man-pages/man1/systemd-analyze.1.html
  • systemd.exec(5), Arch Linux kılavuz aynası: https://man.archlinux.org/man/systemd.exec.5.en

TR Siber Ekibi Yazar · TRSiber
← Ana sayfaya dön