Úvod: Proč jsou vaše systemd služby bezpečnostní riziko
Spusťte na svém serveru tenhle jeden příkaz a výsledek vás nejspíš nepotěší:
systemd-analyze security
Výstup bude vypadat přibližně takhle:
UNIT EXPOSURE PREDICATE
nginx.service 9.2 UNSAFE 😨
postgresql.service 9.6 UNSAFE 😨
sshd.service 9.6 UNSAFE 😨
docker.service 9.6 UNSAFE 😨
redis-server.service 9.6 UNSAFE 😨
cron.service 9.6 UNSAFE 😨
Skóre 9.6 z 10. Prakticky každá služba na serveru běží s hodnocením UNSAFE — včetně těch, co jsou vystavené do internetu. Co to v praxi znamená? Pokud útočník kompromituje jedinou z těchto služeb, dostane se k celému souborovému systému, všem síťovým rozhraním, systémovým zařízením a potenciálně i k eskalaci práv na roota.
Upřímně, čísla jsou dost děsivá. Podle CrowdStrike 2025 Global Threat Report bylo 79 % kyberútoků zcela bez malwaru — útočníci zneužívali kompromitované služby a legitimní systémové nástroje. Podle dat CIQ dosáhl počet CVE v jádře Linuxu v roce 2025 celkem 5 530 zranitelností, což je nárůst o 28 % oproti předchozímu roku. Každá z nich může být vstupním bodem — a pokud vaše služby nemají žádný sandboxing, je to jako nechat dokořán otevřené dveře.
Řešení je přitom překvapivě jednoduché. Systemd — init systém, který už běží na prakticky každém moderním linuxovém serveru — nabízí desítky bezpečnostních direktiv pro sandboxing služeb. Žádný další nástroj, žádná instalace nového softwaru. Stačí přidat správné direktivy do konfigurace služby a skóre 9.6 můžete srazit na 2.0 nebo méně.
V tomhle průvodci vám ukážu, jak systematicky zabezpečit systemd služby — od auditu přes klíčové direktivy až po praktické příklady hardeningu nginx, PostgreSQL a vlastních aplikací. Probereme i automatizovaný nástroj SHH a integraci s dalšími bezpečnostními vrstvami. Tak pojďme na to.
Jak funguje systemd-analyze security
Než se pustíme do samotného hardeningu, potřebujeme nástroj pro měření. A tím nástrojem je systemd-analyze security — vestavěný auditní příkaz, který je součástí systemd od verze 239.
Auditní skóre a jeho interpretace
Příkaz systemd-analyze security projde bezpečnostní nastavení každé systemd služby a přiřadí jí číselné expozice skóre v rozsahu 0.0 až 10.0. Čím nižší číslo, tím lépe:
- 0.0 – 2.0 (OK) — služba je dobře zabezpečená
- 2.1 – 4.9 (MEDIUM) — přijatelná úroveň, ale prostor pro zlepšení tu je
- 5.0 – 7.4 (EXPOSED) — služba je vystavena řadě rizik
- 7.5 – 10.0 (UNSAFE) — prakticky žádný sandboxing
Pro detailní pohled na konkrétní službu:
# Detailní audit konkrétní služby
systemd-analyze security nginx.service
# Výstup ukazuje každou bezpečnostní direktivu a její stav:
# ✓ PrivateNetwork= Service has access to the host's network 0.5 EXPOSED
# ✗ PrivateTmp= Service has access to other software's ... 0.1 UNSAFE
# ✓ ProtectSystem= Service has full access to the OS file... 0.2 UNSAFE
# ...
Důležitá poznámka: tohle skóre je heuristika, ne absolutní měřítko bezpečnosti. Nezohledňuje bezpečnostní funkce zabudované v samotné aplikaci ani vynucení přes SELinux nebo AppArmor. Berte to jako startovní bod pro identifikaci služeb, které potřebují pozornost — ne jako konečný verdikt.
Uložení baseline pro sledování pokroku
Než začnete s hardeningem, uložte si výchozí stav. Budete se k němu chtít vracet pro porovnání (a taky je fajn vidět ten pokrok černé na bílém):
# Vytvoření adresáře pro auditní záznamy
sudo mkdir -p /var/log/systemd-hardening
# Uložení baseline pro všechny služby
systemd-analyze security --no-pager | sudo tee /var/log/systemd-hardening/baseline-$(date +%Y%m%d).txt
# Baseline pro konkrétní službu
systemd-analyze security --no-pager nginx.service | sudo tee /var/log/systemd-hardening/nginx.before.txt
Klíčové bezpečnostní direktivy systemd
Systemd nabízí desítky bezpečnostních direktiv. Nemusíte je znát všechny nazpaměť — důležité je pochopit hlavní kategorie a vědět, které direktivy mají největší dopad na snížení expozičního skóre.
Ochrana souborového systému
Tyhle direktivy omezují přístup služby k souborovému systému:
[Service]
# Zamezí zápisu do /usr, /boot, /efi a /etc
ProtectSystem=strict
# Zpřístupní /home, /root a /run/user jako nedostupné
ProtectHome=true
# Privátní /tmp pro službu (izolovaný od ostatních procesů)
PrivateTmp=true
# Explicitní povolení zápisu do konkrétních adresářů
ReadWritePaths=/var/lib/myapp /var/log/myapp
# Znepřístupnění citlivých umístění
InaccessiblePaths=/etc/shadow /etc/gshadow /root/.ssh
ProtectSystem=strict je nejpřísnější varianta — připojí celý kořenový souborový systém jako read-only. Když služba potřebuje zapisovat do specifických adresářů, definujete je přes ReadWritePaths=. Princip nejmenších oprávnění v praxi: služba smí zapisovat jenom tam, kam skutečně potřebuje.
Izolace procesů a oprávnění
[Service]
# Zákaz eskalace oprávnění (setuid, setgid)
NoNewPrivileges=true
# Omezení Linux capabilities na minimum
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
# Odstranění všech ambient capabilities
AmbientCapabilities=
# Zamezení přepínání personalit jádra
LockPersonality=true
# Zákaz vytváření zapisovatelné a zároveň spustitelné paměti
MemoryDenyWriteExecute=true
NoNewPrivileges=true je podle mě jedna z nejdůležitějších direktiv vůbec. Zabrání procesu i jeho potomkům v získání nových oprávnění — ani přes binárky s nastaveným setuid bitem. To efektivně zablokuje většinu technik eskalace práv. Pokud byste měli přidat jen jednu direktivu, tahle by to měla být.
Ochrana jádra a systémových zdrojů
[Service]
# Ochrana /proc/sys, /sys a dalších tunables jádra
ProtectKernelTunables=true
# Zákaz načítání kernel modulů
ProtectKernelModules=true
# Ochrana kernel logů
ProtectKernelLogs=true
# Zamezení zápisu do cgroups
ProtectControlGroups=true
# Omezení přístupu k /proc
ProtectProc=invisible
ProcSubset=pid
# Zákaz přístupu k hardwarovým zařízením
PrivateDevices=true
# Skrytí hodin systému
ProtectClock=true
# Zákaz přístupu k hostname jádra
ProtectHostname=true
Filtrování systémových volání (seccomp)
Systemd umí přes seccomp filtrovat systémová volání, čímž výrazně zmenšuje útočnou plochu jádra. Tohle je vlastně jedna z nejúčinnějších vrstev ochrany:
[Service]
# Povolení pouze systémových volání běžných pro služby
SystemCallFilter=@system-service
# Explicitní zakázání nebezpečných skupin syscalls
SystemCallFilter=~@privileged @resources @mount @clock @debug @reboot @swap @raw-io @obsolete
# Nastavení architektury syscalls (prevence exploitů přes x86 kompatibilitu)
SystemCallArchitectures=native
# Chování při nepovolemém syscall — ukončení procesu
SystemCallErrorNumber=EPERM
Direktiva SystemCallFilter=@system-service je dobrý výchozí bod — povolí skupinu syscalls potřebných pro většinu standardních služeb a zakáže nebezpečné operace jako reboot, kexec_load nebo mount.
Síťová izolace
[Service]
# Kompletní izolace od sítě (pouze pro služby bez síťového přístupu)
PrivateNetwork=true
# Omezení typů síťových soketů
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
# Omezení jmenných prostorů
RestrictNamespaces=true
# Zákaz real-time plánování
RestrictRealtime=true
# Zákaz SUID/SGID
RestrictSUIDSGID=true
Pozor: PrivateNetwork=true nikdy nepoužívejte pro služby, které potřebují síťový přístup (webové servery, databáze komunikující přes TCP atd.). Tahle direktiva kompletně izoluje službu od všech síťových rozhraní. Párkrát jsem viděl, jak to někomu sestřelilo produkci — tak radši zdůrazňuji.
Praktický příklad: Hardening nginx
Dost teorie, pojďme to převést do praxe. Nginx je jeden z nejrozšířenějších webových serverů a často je prvním kontaktním bodem pro příchozí požadavky z internetu. Zabezpečit ho na úrovni systemd je proto zásadní.
Výchozí stav
# Zjistíme výchozí skóre
systemd-analyze security nginx.service
# → OVERALL EXPOSURE LEVEL: 9.2 UNSAFE 😨
Vytvoření drop-in override
Nikdy neupravujte originální soubory služby v /lib/systemd/system/ — budou přepsány při aktualizaci balíčku. Místo toho použijte drop-in override:
# Otevře editor pro vytvoření přepisovacího souboru
sudo systemctl edit nginx.service
Do otevřeného editoru vložte tuhle konfiguraci:
[Service]
# === Ochrana souborového systému ===
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/log/nginx /var/cache/nginx /run/nginx
InaccessiblePaths=/etc/shadow /etc/gshadow
# === Izolace procesů ===
NoNewPrivileges=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETUID CAP_SETGID CAP_DAC_OVERRIDE
AmbientCapabilities=
# === Ochrana jádra ===
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectClock=true
ProtectHostname=true
# === Omezení zařízení a paměti ===
PrivateDevices=true
MemoryDenyWriteExecute=true
LockPersonality=true
# === Filtrování syscalls ===
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources @mount @clock @debug @reboot @swap @raw-io @obsolete
SystemCallArchitectures=native
# === Síťová omezení ===
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
# === Viditelnost procesů ===
ProtectProc=invisible
ProcSubset=pid
Aplikace a ověření
# Načtení nové konfigurace
sudo systemctl daemon-reload
# Restart nginx s novým sandboxingem
sudo systemctl restart nginx.service
# Ověření, že nginx funguje
curl -I http://localhost
# HTTP/1.1 200 OK
# Kontrola nového bezpečnostního skóre
systemd-analyze security nginx.service
# → OVERALL EXPOSURE LEVEL: 2.8 OK 🙂
Z 9.2 na 2.8 — to je docela dramatický skok. Nginx teď běží v izolovaném prostředí, kde nemůže číst citlivé systémové soubory, načítat kernel moduly, eskalovat oprávnění ani přistupovat k hardwarovým zařízením. A pořád plně funkčně obsluhuje webové požadavky. To je přesně ten výsledek, který chcete.
Praktický příklad: Hardening PostgreSQL
Databázové servery uchovávají nejcennější data organizace. Zabezpečit PostgreSQL na úrovni systemd je proto ještě důležitější než u webového serveru.
# Výchozí stav
systemd-analyze security postgresql.service
# → OVERALL EXPOSURE LEVEL: 9.6 UNSAFE 😨
# Vytvoření drop-in override
sudo systemctl edit postgresql.service
[Service]
# === Souborový systém ===
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=/var/lib/postgresql /var/log/postgresql /var/run/postgresql /etc/postgresql
# === Izolace procesů ===
NoNewPrivileges=true
CapabilityBoundingSet=CAP_DAC_OVERRIDE CAP_CHOWN CAP_FOWNER CAP_SETUID CAP_SETGID
AmbientCapabilities=
# === Ochrana jádra ===
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectClock=true
ProtectHostname=true
# === Zařízení a paměť ===
PrivateDevices=true
LockPersonality=true
# === Síť ===
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
# === Syscalls ===
SystemCallFilter=@system-service @io-event
SystemCallFilter=~@mount @clock @debug @reboot @swap @raw-io @obsolete
SystemCallArchitectures=native
# === Viditelnost procesů ===
ProtectProc=invisible
ProcSubset=pid
# Aplikace změn
sudo systemctl daemon-reload
sudo systemctl restart postgresql.service
# Ověření funkčnosti
sudo -u postgres psql -c "SELECT version();"
# Kontrola skóre
systemd-analyze security postgresql.service
# → OVERALL EXPOSURE LEVEL: 3.2 OK 🙂
Všimněte si jedné věci — u PostgreSQL nepoužíváme MemoryDenyWriteExecute=true. Proč? PostgreSQL interně využívá JIT kompilaci dotazů (přes LLVM), která potřebuje zapisovatelnou a zároveň spustitelnou paměť. Tohle je typický příklad situace, kdy musíte hardening přizpůsobit konkrétním potřebám aplikace. Žádné universální řešení neexistuje.
Praktický příklad: Hardening vlastní aplikace
Pro vlastní aplikace — třeba Node.js API server nebo Go mikroslužbu — můžete jít s hardeningem ještě dál, protože přesně víte, co vaše aplikace potřebuje:
# /etc/systemd/system/myapi.service
[Unit]
Description=My API Server
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=myapi
Group=myapi
ExecStart=/opt/myapi/bin/server --config /etc/myapi/config.yaml
# === Maximální sandboxing ===
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
PrivateDevices=true
ReadWritePaths=/var/log/myapi
ReadOnlyPaths=/etc/myapi
# === Izolace procesů ===
NoNewPrivileges=true
CapabilityBoundingSet=
AmbientCapabilities=
LockPersonality=true
MemoryDenyWriteExecute=true
# === Ochrana jádra ===
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectControlGroups=true
ProtectClock=true
ProtectHostname=true
ProtectProc=invisible
ProcSubset=pid
# === Filtrování syscalls ===
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources @mount @clock @debug @reboot @swap @raw-io @obsolete
SystemCallArchitectures=native
# === Síťová omezení ===
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
# === Omezení zdrojů ===
MemoryMax=512M
MemoryHigh=384M
CPUQuota=200%
TasksMax=128
# === Dynamický uživatel (alternativa k manuálnímu vytváření uživatele) ===
# DynamicUser=true
# StateDirectory=myapi
[Install]
WantedBy=multi-user.target
# Kontrola skóre
systemd-analyze security myapi.service
# → OVERALL EXPOSURE LEVEL: 1.4 OK 🙂
Skóre 1.4 — téměř maximální zabezpečení. Služba běží s prázdným CapabilityBoundingSet=, takže nemá vůbec žádné superuser oprávnění. Nemůže přistupovat k zařízením, chráněným souborům ani eskalovat práva. Pro vlastní aplikaci je tohle ideální stav.
Automatizace hardeningu s nástrojem SHH
Ruční hardening funguje skvěle, ale vyžaduje znalost toho, které syscalls a schopnosti aplikace skutečně využívá. A tady přichází na scénu SHH (Systemd Hardening Helper) — nástroj od bezpečnostní firmy Synacktiv, který celý proces automatizuje prostřednictvím runtime profilování.
Jak SHH funguje
SHH je napsaný v Rustu a používá strace k zachycení všech systémových volání, které služba za běhu provede. Na základě tohoto profilu pak automaticky vygeneruje sadu hardeningových direktiv, které povolí pouze pozorované operace — vše ostatní zakáže. Docela elegantní řešení.
Instalace SHH
# Prerekvizity: strace >= 6.4 a Rust toolchain
sudo apt install strace
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Instalace SHH z crates.io
cargo install systemd-hardening-helper
# Nebo na Arch Linux přes AUR
yay -S shh
Praktický workflow s SHH
# Krok 1: Spuštění profilování služby
sudo shh service start-profile nginx.service
# Služba se restartuje pod strace a začne zaznamenávat syscalls.
# Nyní provádějte běžné operace — HTTP požadavky, reload konfigurace atd.
# Snažte se pokrýt co nejvíce scénářů běžného provozu.
# Krok 2: Ukončení profilování a aplikace hardeningu
sudo shh service finish-profile nginx.service -a
# SHH automaticky:
# 1. Analyzuje zachycené syscalls
# 2. Vygeneruje optimální sadu hardeningových direktiv
# 3. Vytvoří drop-in override soubor
# 4. Restartuje službu s novým hardeningem
# Krok 3: Ověření výsledku
systemd-analyze security nginx.service
Důležité upozornění: Hardening profily generované SHH nejsou přenositelné mezi systémy. Závisí na konkrétní verzi OS, knihoven a konfiguraci služby. Profil vygenerovaný na Ubuntu 24.04 klidně může způsobit problémy na RHEL 9. Vždy profilujte přímo na cílovém systému — tohle je opravdu důležité si zapamatovat.
Správný postup hardeningu: krok za krokem
Hardening systemd služeb není operace typu „nastavit a zapomenout". Potřebujete metodický přístup, jinak riskujete, že si službu rozbijete.
1. Prioritizace služeb
Začněte tím nejpalčivějším — službami, které jsou vystavené do internetu nebo zpracovávají nedůvěryhodná data:
# Seřazení služeb podle expozičního skóre (od nejhoršího)
systemd-analyze security --no-pager 2>/dev/null | sort -k2 -rn | head -20
2. Inkrementální přístup
Tohle je asi nejdůležitější rada v celém článku: nikdy nepřidávejte všechny direktivy najednou. Postupujte po skupinách a po každé změně ověřte funkčnost:
# Fáze 1: Základní ochrana souborového systému
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
ReadWritePaths=... # Přidejte cesty, kam služba potřebuje zapisovat
# → Restart a test funkčnosti
# Fáze 2: Izolace procesů
NoNewPrivileges=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
# → Restart a test funkčnosti
# Fáze 3: Filtrování syscalls (nejagresivnější)
SystemCallFilter=@system-service
SystemCallArchitectures=native
# → Restart a důkladný test včetně edge cases
3. Monitoring po nasazení
Po aplikaci hardeningu sledujte logy. Příliš restriktivní konfigurace se projeví chybami v journalu:
# Sledování logů služby v reálném čase
journalctl -u nginx.service -f
# Hledání chyb souvisejících se seccomp nebo oprávněními
journalctl -u nginx.service --since "1 hour ago" | grep -iE "denied|seccomp|permission|EPERM"
4. Využití drop-in overridů správně
Drop-in override soubory se ukládají do /etc/systemd/system/<služba>.service.d/. Klidně je můžete rozdělit do víc souborů pro lepší přehlednost:
# Hardening direktivy
/etc/systemd/system/nginx.service.d/security.conf
# Omezení zdrojů
/etc/systemd/system/nginx.service.d/resources.conf
# Přepsání konkrétních parametrů
/etc/systemd/system/nginx.service.d/override.conf
Soubory se načítají v abecedním pořadí, tak s tím počítejte při pojmenovávání.
Integrace s dalšími bezpečnostními vrstvami
Hardening systemd služeb je mocný nástroj, ale nejsilnější je v kombinaci s dalšími bezpečnostními mechanismy. Jde o princip obrany do hloubky (defense in depth) — a ten funguje.
Systemd + SELinux/AppArmor
Systemd sandboxing a MAC systémy jako SELinux nebo AppArmor se vzájemně doplňují:
- Systemd omezuje schopnosti služby na úrovni jmenných prostorů, capabilities a seccomp
- SELinux/AppArmor vynucuje přístupové politiky na úrovni souborových kontextů a síťových operací
Obě vrstvy fungují nezávisle. Pokud útočník obejde jednu, druhá stále chrání systém. V praxi to znamená, že služba musí projít oběma vrstvami kontroly pro jakoukoliv operaci. Tohle je přesně ta redundance, kterou v bezpečnosti chcete.
Systemd + Landlock
Landlock funguje na úrovni samotné aplikace a je stackable se systemd hardeningem. Aplikace se může „zamknout zevnitř" přes Landlock API, zatímco systemd ji omezuje „zvnějšku". Výsledkem je dvojitá ochrana, kde ani kompromitovaná aplikace, ani kompromitovaná systemd konfigurace nestačí k úspěšnému útoku.
Systemd + eBPF runtime monitoring
Nástroje jako Falco a Tetragon doplňují systemd hardening o vrstvu detekce. Systemd zabraňuje nebezpečným operacím, zatímco eBPF monitoring detekuje pokusy o jejich provedení. Tato kombinace vám dává jak prevenci, tak viditelnost — a oboje potřebujete.
Novinky v systemd 257–259 pro bezpečnost
Systemd se neustále vyvíjí a každá verze přináší bezpečnostní vylepšení. V roce 2025 vyšly verze 257 až 259 s několika zajímavými změnami:
PrivateUsers=managed (systemd 257)
Direktiva PrivateUsers= získala novou hodnotu managed, která automaticky přiřadí službe dynamický a dočasný rozsah 65 536 UID/GID prostřednictvím systemd-nsresourced. To umožňuje bezpečnější izolaci uživatelských jmenných prostorů bez manuální konfigurace. Hodně užitečná novinka.
Deprecation cgroup v1 (systemd 257–259)
Podpora pro cgroup v1 (legacy a hybrid hierarchie) je oficiálně zastaralá a bude kompletně odstraněna v systemd 260. Pokud vaše služby stále závisí na cgroup v1, teď je ten správný čas na migraci na cgroup v2 — nabízí lepší bezpečnostní kontroly včetně jednotného stromu řízení zdrojů.
Pouze nftables pro NAT (systemd 257+)
Systemd-networkd a systemd-nspawn už nepodporují vytváření NAT pravidel přes iptables/libiptc API — podporován je výhradně nftables. Tohle je v souladu s širším trendem odchodu od iptables v celém linuxovém ekosystému, takže to asi nikoho nepřekvapí.
Vylepšené Varlink API (systemd 259)
Systemd 259 rozšiřuje Varlink rozhraní o nová volání pro správu služeb včetně přístupu k bezpečnostním nastavením. To otevírá cestu pro sofistikovanější automatizaci auditu a hardeningu přes programatické API.
Běžné problémy a jejich řešení
Při hardeningu systemd služeb narazíte na typické problémy. Tady jsou ty nejčastější (a jak se s nimi vypořádat):
Služba se nespustí po přidání ProtectSystem=strict
Služba potřebuje zapisovat do adresářů, které jsou teď read-only. Řešení je jednoduché — přidejte ReadWritePaths= s konkrétními cestami:
# Zjištění, kam služba zapisuje
strace -f -e trace=open,openat,write -p $(pgrep -f nginx) 2>&1 | grep -E "O_WRONLY|O_RDWR"
# Alternativně přes audit log
journalctl -u nginx.service | grep -i "read-only"
Pád služby po přidání SystemCallFilter
Služba volá syscall, který není v povolené skupině. Identifikujte chybějící syscall a přidejte ho:
# Zjištění, který syscall byl zablokován
journalctl -u myservice.service | grep -i seccomp
# audit: type=1326 ... syscall=314 ...
# Převod čísla syscall na název
ausyscall 314
# → io_uring_setup
# Přidání do filtru
SystemCallFilter=@system-service io_uring_setup
PostgreSQL nefunguje s MemoryDenyWriteExecute
PostgreSQL od verze 12 používá JIT kompilaci přes LLVM, která vyžaduje zapisovatelnou a spustitelnou paměť. Máte dvě možnosti — buď MemoryDenyWriteExecute=true nepoužívejte, nebo JIT vypněte:
# V postgresql.conf
jit = off
# Poté můžete bezpečně přidat
MemoryDenyWriteExecute=true
Nginx nemůže bindovat port 80/443
Po odebrání capabilities nemůže nginx otevírat privilegované porty. Stačí přidat CAP_NET_BIND_SERVICE:
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETUID CAP_SETGID CAP_DAC_OVERRIDE
Skript pro hromadný audit a reporting
Pro automatizaci auditů napříč celou infrastrukturou se hodí tenhle skript:
#!/bin/bash
# systemd-security-audit.sh — Audit bezpečnosti systemd služeb
# Použití: sudo ./systemd-security-audit.sh [prahové_skóre]
THRESHOLD=${1:-5.0}
REPORT_DIR="/var/log/systemd-hardening"
REPORT_FILE="${REPORT_DIR}/audit-$(date +%Y%m%d-%H%M%S).txt"
mkdir -p "$REPORT_DIR"
echo "=== Bezpečnostní audit systemd služeb ===" | tee "$REPORT_FILE"
echo "Datum: $(date -u +'%Y-%m-%dT%H:%M:%SZ')" | tee -a "$REPORT_FILE"
echo "Prahové skóre: $THRESHOLD" | tee -a "$REPORT_FILE"
echo "===========================================" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
# Počítadla
TOTAL=0
UNSAFE=0
EXPOSED=0
OK=0
while IFS= read -r line; do
UNIT=$(echo "$line" | awk '{print $1}')
SCORE=$(echo "$line" | awk '{print $2}')
# Přeskočení hlavičky
[[ "$UNIT" == "UNIT" ]] && continue
[[ -z "$SCORE" ]] && continue
TOTAL=$((TOTAL + 1))
if (( $(echo "$SCORE >= 7.5" | bc -l) )); then
UNSAFE=$((UNSAFE + 1))
STATUS="UNSAFE"
elif (( $(echo "$SCORE >= $THRESHOLD" | bc -l) )); then
EXPOSED=$((EXPOSED + 1))
STATUS="EXPOSED"
else
OK=$((OK + 1))
STATUS="OK"
fi
if (( $(echo "$SCORE >= $THRESHOLD" | bc -l) )); then
echo "[!] $UNIT: skóre $SCORE ($STATUS) — vyžaduje hardening" | tee -a "$REPORT_FILE"
fi
done < <(systemd-analyze security --no-pager 2>/dev/null | tail -n +2)
echo "" | tee -a "$REPORT_FILE"
echo "=== Shrnutí ===" | tee -a "$REPORT_FILE"
echo "Celkem služeb: $TOTAL" | tee -a "$REPORT_FILE"
echo "OK (< $THRESHOLD): $OK" | tee -a "$REPORT_FILE"
echo "EXPOSED: $EXPOSED" | tee -a "$REPORT_FILE"
echo "UNSAFE: $UNSAFE" | tee -a "$REPORT_FILE"
echo "Report uložen: $REPORT_FILE" | tee -a "$REPORT_FILE"
Často kladené otázky (FAQ)
Ovlivní hardening systemd služeb výkon aplikace?
Ve většině případů ne. Direktivy jako ProtectSystem, PrivateTmp a ProtectHome využívají jmenné prostory jádra, které nemají měřitelný dopad na výkon za běhu — režie vzniká jenom při startu služby. Filtrování syscalls přes seccomp přidává minimální latenci v řádu nanosekund na každé systémové volání. V praxi to prostě nezpozorujete.
Mohu použít systemd hardening společně se SELinuxem nebo AppArmorem?
Rozhodně ano, a dokonce je to doporučený přístup. Systemd hardening a MAC systémy fungují na různých úrovních a vzájemně se doplňují. Systemd omezuje přes jmenné prostory, capabilities a seccomp, zatímco SELinux/AppArmor vynucuje kontextové přístupové politiky. Služba musí projít oběma vrstvami kontroly — a to je přesně to, co chcete.
Je bezpečné hardenit sshd.service?
Tady je potřeba opatrnost. SSH je navržený tak, aby umožňoval spouštění libovolných příkazů, takže příliš restriktivní sandboxing může narušit jeho funkčnost. Můžete bezpečně aplikovat ProtectKernelTunables=true, ProtectKernelModules=true a ProtectKernelLogs=true, ale vyhněte se ProtectSystem=strict nebo přísnému SystemCallFilter — mohlo by to zabránit uživatelům v běžné práci na serveru. A to nechcete.
Jak zjistím, které syscalls moje aplikace potřebuje?
Existují tři hlavní způsoby: 1) Použijte strace pro zachycení všech syscalls za běhu: strace -c -f -p $(pgrep myapp). 2) Využijte nástroj SHH, který celý proces automatizuje. 3) Začněte s permisivní skupinou @system-service a postupně zpřísňujte na základě chyb v journalu. Osobně nejčastěji volím kombinaci prvního a třetího přístupu.
Přežijí mé hardening úpravy aktualizace systému?
Ano — pokud používáte drop-in override soubory přes systemctl edit. Ty se ukládají do /etc/systemd/system/, který má přednost před /lib/systemd/system/, a aktualizace balíčků je nepřepíší. Proto nikdy neupravujte originální soubory služeb přímo.