Linux-Server-Hardening: CIS Benchmark Anwenden Ohne die Produktion zu Zerlegen
Wie man den CIS Benchmark auf Debian und Ubuntu in Produktion anwendet, jede Kontrolle validiert, Impact misst und das SLA halt, ohne die Nacht im Rollback zu verbringen.

Den kompletten CIS Benchmark auf einen Schlag auf einem Debian 12 auszurollen, der 40.000 Requests pro Minute bedient, ist der schnellste Weg, einen Freitag in einen Sev-1-Vorfall zu verwandeln. Im Basilisk-Team haben wir mehrfach erlebt, wie Firmen das ansible-lockdown-Playbook roh gegen Produktion laufen liessen und um drei Uhr morgens feststellten, dass Kontrolle 5.2.16 den Account abgeschaltet hat, der das Postgres-Backup orchestriert. Ernsthaftes Hardening heisst nicht, 380 Kontrollen aus einem PDF zu kopieren, sondern die 60 auszuwahlen, die das Risiko wert sind, sie in Staging unter realer Last zu replizieren und Telemetrie zu haben, um zu wissen, wie viele Minuten bis zum Rollback bleiben. Dieser Beitrag ist das Runbook, das wir bei echten Engagements benutzen.
Warum <em>alles auf einmal</em> scheitert
Ein CIS-Benchmark-PDF listet Level 1 (konservativ) und Level 2 (aggressiv) getrennt auf, aber die meisten Teams behandeln beide Ebenen als eine flache To-do-Liste und wenden sie per Konfigurationsmanagement in einem einzigen Lauf an. Das Problem ist nicht die einzelne Kontrolle, sondern die Kombinatorik: 380 gleichzeitige Anderungen bedeuten, dass man bei einer Regression keine Bisektion mehr fahren kann. Man weiss, dass etwas kaputt ist, aber nicht welche der 380 Zeilen. Genau deshalb landet man um drei Uhr morgens beim git revert des gesamten Playbooks statt bei einer chirurgischen Korrektur.
Die zweite Falle ist der Idempotenz-Irrtum. Viele CIS-Remediations sind nicht wirklich idempotent, wenn die Ausgangslage vom Erwarteten abweicht. Ein Task, der /etc/pam.d/common-auth umschreibt, geht von einem bestimmten Standard-Stack aus. Hat der Host bereits ein SSSD- oder Kerberos-Modul, hangt die neue Zeile die falsche Reihenfolge an und du sperrst jeden foderierten Login aus. Der Fix ist immer derselbe: kleine Batches, Validierung zwischen jedem Batch, und ein dokumentierter Rollback-Pfad, bevor die erste Zeile geschrieben wird.
Die vier Buckets
Der richtige Einstieg ist, die Kontrollen in vier Buckets zu splitten, bevor man irgendeinen Server anfasst. Bucket 1: Kernel und Boot (sysctl, GRUB, Module), geringes App-Risiko, hoher Gewinn. Bucket 2: Netzwerk und Firewall (nftables, IPv6, ICMP), mittleres Risiko falls nicht alle Ports kartiert sind. Bucket 3: Authentifizierung, PAM und SSH, wo die meisten Post-Hardening-Vorfalle leben. Bucket 4: auditd, syslog und AIDE-Integritat, praktisch null Betriebsrisiko.
Die Reihenfolge ist bewusst kontraintuitiv: erst Bucket 4, dann 1, dann 2, SSH und PAM zuletzt. Man beginnt dort, wo nichts kaputtgehen kann (Auditing), sammelt Telemetrie uber das Normalverhalten, hartet dann den Kernel, dann das Netzwerk, und ruhrt die Auth-Ebene erst an, wenn man ein zweites Zugangsband (Konsole, Out-of-Band-Management, ein zweiter SSH-Key auf einem separaten Port) verifiziert hat. Falls SSH ohnehin angefasst wird, vorher SSH-Hardening 2026: Algorithmen, Zertifikate und Bastion-Hosts lesen, weil sich die Algorithmen 2026 verschoben haben.
Ist-Zustand mit OpenSCAP inventarisieren
Zur Inventarisierung des Ist-Zustands hilft openscap-scanner mit dem Profil xccdf_org.ssgproject.content_profile_cis_level2_server aus dem ComplianceAsCode-Projekt. Ein realer Aufruf sieht so aus: oscap xccdf eval --profile cis_level2_server --results scan.xml --report scan.html /usr/share/xml/scap/ssg/content/ssg-ubuntu2404-ds.xml. Auf einem frischen Ubuntu 24.04 sieht man 110 bis 140 Kontrollen im Fail-Zustand, das ist normal und kein Grund zur Panik.
HTML-Report exportieren, in Jira als eine Epic pro Bucket importieren und Aufwand in Risikopunkten statt Stunden schatzen. Der Trick ist, nie eine Remediation ohne den Rationale zu lesen anzuwenden, denn die Halfte der CIS-Level-2-Empfehlungen zerlegt moderne Workloads. usb-storage zu deaktivieren ergibt auf einem Bastion Sinn, nicht auf einem Host, der verschlusselte Dumps auf einem HSM-Stick fur eine Air-Gap-Ubergabe ablegt. Behandle den Scan als lebendes Dokument: nach jedem Batch neu laufen lassen und den Fortschritt als Kurve, nicht als Momentaufnahme verfolgen.
Kernel- und sysctl-Layer
Die Kernel-Schicht zahlt sich enorm aus, bei geringem Risiko, wenn man weiss, was man tunt. kernel.kptr_restrict=2, kernel.dmesg_restrict=1, kernel.yama.ptrace_scope=2 und fs.protected_hardlinks=1 sind Gratis-Wins gegen lokale Informationslecks und Symlink-Rennen. Erganze net.ipv4.conf.all.rp_filter=1, net.ipv4.tcp_syncookies=1 und kernel.randomize_va_space=2 und lege alles in eine versionierte /etc/sysctl.d/60-cis.conf statt in die Haupt-sysctl.conf, damit ein Paket-Upgrade deine Anderungen nicht uberschreibt.
kernel.unprivileged_userns_clone=0 dagegen killt rootless Docker, Podman, Bubblewrap und jede Anwendungssandbox, also wer Container fahrt oder die Techniken aus Linux Anwendungs Sandbox mit Bubblewrap, Firejail und Flatpak nutzt, lasst es auf 1 und dokumentiert die Abweichung formal. Beim Boot gehoren ein passwortgeschutzter GRUB (CIS 1.4.x), Secure Boot mit signierten Modulen und das Blacklisten ungenutzter Dateisystem-Module (cramfs, udf, usb-storage) dazu. Fur stark exponierte Services SELinux in enforcing mit eigener targeted-Policy fahren, exakt wie wir in SELinux ohne Angst: Eigene Policies fur kritische Dienste am Beispiel eines internetexponierten Nginx durchspielen.
Netzwerk und Firewall mit nftables
Bevor du eine einzige Firewall-Regel schreibst, kartiere jeden lauschenden Port mit ss -tulpen und gleiche ihn gegen den erwarteten Service ab. Ein CIS-konformes nftables-Ruleset arbeitet mit default drop auf Input und Forward und einer expliziten Allowlist. Die haufigste Selbstsperre passiert, wenn man den Loopback-Verkehr vergisst: ohne iif lo accept brechen lokale Sockets, Health-Checks und der Datenbank-Loopback zusammen, und die App wirft undurchsichtige Timeouts.
IPv6 ist die stille Falle. Viele Teams harten IPv4 sauber und lassen den kompletten IPv6-Stack offen, weil sie annehmen, er sei nicht geroutet. Entweder du deaktivierst IPv6 konsequent (net.ipv6.conf.all.disable_ipv6=1 und im Bootloader) oder du spiegelst jede IPv4-Regel auf ip6tables/nftables inet-Family. Halbe Deckung ist schlimmer als keine, weil sie ein falsches Sicherheitsgefuhl erzeugt. Teste jede Regel-Anderung mit einer zweiten offenen Session als Rettungsleine.
Auditd ohne Log-Flut
Auditd wird fast immer zum Bottleneck, wenn man das CIS-Ruleset gedankenlos kopiert. Default-Regeln erzeugen 8 bis 15 Tausend Events pro Minute auf einem durchschnittlichen Host, fullen /var/log in sechs Stunden und zwingen journald zum Verwerfen. Das Basilisk-Rezept: execve-Regeln fur bekannte Service-User (postgres, nginx, app) zuruckschneiden und aggressives Monitoring nur fur uid 0, sudo und interaktive Shells behalten.
Setze -b 8192 fur den Backlog, --backlog_wait_time 0 gegen Kernel-Stalls und leite per audisp-remote oder einem auditd-Plugin in eine Sigma-Pipeline wie in Threat Hunting mit Sigma und Elastic: Vom Indikator zur Detektionsregel beschrieben. Sonst produziert man teuren Larm ohne jede umsetzbare Detection auf der Konsumentenseite. Setze -e 2 (immutable rules) erst ganz am Ende, weil es bis zum nachsten Reboot jede Regel-Anderung sperrt.
Ein oft ubersehener Punkt: auditd und ein voll aktiviertes execve-Logging kosten messbar CPU auf syscall-lastigen Diensten. Auf einem Reverse-Proxy, der pro Request Dutzende connect- und openat-Aufrufe macht, kann ein zu breites Ruleset 5 bis 10 Prozent p95-Latenz addieren. Miss das explizit im Staging-Lasttest und behandle die Audit-Regeln als Teil des Performance-Budgets, nicht als kostenlose Beigabe. Die richtige Metrik ist nicht die Anzahl der Regeln, sondern die Events pro Sekunde im Normalbetrieb.
Validierung unter realer Last
Validierung ist der Teil, den niemand sauber macht. Eine Spiegelumgebung in LXD oder Proxmox aufbauen mit identischem Kernel, identischer glibc und identischen Serviceversionen, dann 30 Minuten echten Traffic per tcpdump aufzeichnen und mit k6 oder wrk replayen. Kontrollen in Zehnerbatches anwenden, Test wiederholen, p95-Latenz und Fehlerrate vergleichen. Liegt die Regression uber 3%, isoliert man per Bisektion den schuldigen Controller.
Zusatzlich atomic-red-team mit auf MITRE ATT&CK gemappten Techniken laufen lassen, um zu bestatigen, dass das Hardening die Angriffsflache wirklich verkleinert: gleiche Logik wie in Adversary Emulation mit Caldera und MITRE ATT&CK im Unternehmenslab, nur fokussiert auf Post-Exploitation auf dem geharteten Host. Wenn eine Technik nach dem Hardening immer noch erfolgreich ist, hast du eine messbare Lucke statt einer Vermutung.
Disk-Krypto und physischer Zugriff
Fur Server mit sensiblen Daten oder ohne physischen Zugriff erganzt man das Hardening mit zwangsresistenter Plattenverschlusselung und verifizierten Backups, gemass dem Vorgehen aus Disk-Krypto und Backups: VeraCrypt, LUKS und eine Belastbare 3-2-1-Strategie. LUKS2 mit Argon2id, im TPM2 versiegelter Key gebunden an PCR0+PCR7 und verschlusseltes Offsite-Snapshot losen den physischen Diebstahl im Rechenzentrum.
Kombiniert mit Secure Boot, signierten Modulen und passwortgeschutztem GRUB (CIS 1.4.x) hebt man die Kosten eines Hands-on-Angriffs auf ein Niveau, das nur gegen sehr spezifische Ziele lohnt. Wichtig: Ein TPM-gebundener Key ohne getestete Recovery-Passphrase ist eine Zeitbombe. Am Tag, an dem ein Firmware-Update die PCR-Werte andert, bootet die Maschine nicht mehr, und ohne hinterlegte Passphrase ist die Platte Datenmull.
Praktische Checkliste
Bevor du einen geharteten Host als fertig deklarierst: (1) OpenSCAP-Score vorher und nachher dokumentiert; (2) zweiter Zugangspfad (Konsole/OOB) verifiziert, bevor SSH angefasst wurde; (3) alle sysctl-Anderungen in /etc/sysctl.d/ versioniert; (4) nftables mit iif lo accept und getestetem IPv6-Verhalten; (5) auditd unter 3000 Events/Minute im Ruhezustand; (6) Staging-Lasttest mit unter 3% p95-Regression; (7) atomic-red-team-Lauf, der die geschlossenen Techniken belegt; (8) Rollback-Pfad dokumentiert und einmal geprobt.
FAQ: Level 1 oder Level 2?
Fur internetexponierte Server ohne strikte Compliance-Vorgabe ist Level 1 vollstandig plus ausgewahlte Level-2-Kontrollen aus Bucket 1 und 4 der beste Kosten-Nutzen-Punkt. Level 2 komplett ergibt Sinn bei regulierten Workloads (PCI-DSS, Behordenauflagen), aber nur mit einem dokumentierten Ausnahmeregister fur die Kontrollen, die deinen konkreten Workload brechen. Blindes Level 2 auf einem Container-Host ist ein Ausfall mit Zertifikat.
FAQ: Wie oft neu scannen? Bei jedem grossen OS-Release (Debian Point Release, LTS Upgrade) und mindestens vierteljahrlich als automatischer Cron-Scan, dessen Ergebnis in dieselbe Dashboards fliesst wie deine Metriken. Drift ist unvermeidlich, weil Paket-Upgrades sysctl-Defaults zurucksetzen und neue Services neue Ports offnen.
FAQ: Ansible, Bash-Skript oder Image-Baking? Fur eine Flotte ist ein gehartetes Golden Image (Packer plus die geprufte Kontroll-Liste, dann unveranderlich ausgerollt) robuster als ein Ansible-Lauf gegen laufende Hosts, weil Drift praktisch verschwindet. Ansible bleibt gut fur den ersten iterativen Zyklus und fur Hosts, die man nicht neu bauen kann. Ein reines Bash-Skript ohne Idempotenz ist die schlechteste Option: es lasst sich weder sauber wiederholen noch verlasslich zurucknehmen. Was auch immer du wahlst, die Kontroll-Liste selbst muss versioniert und reviewbar sein, nicht in einem Runbook-Wiki begraben.
Praktisches Takeaway: eine Tabelle pflegen mit jeder angewandten CIS-Kontrolle, der Paketversion zum Zeitpunkt der Anwendung, dem openscap-Ergebnis vorher und nachher und dem in Staging gemessenen Latenz-Delta. Diese Tabelle bei jedem grossen OS-Release erneut durchziehen, weil sich sysctl-Defaults verschieben und auditd neue Felder ergibt. Hardening ist kein Projekt, sondern ein Prozess: wer in sechs Monaten nicht per automatischem Scan beweisen kann, dass die 60 Kontrollen weiterhin aktiv sind, hat kein Hardening, sondern Glauben.


