Zum Inhalt springen
Serverküche
Suche

Die Suche wird geladen … (nur in der veröffentlichten Seite verfügbar).

Sicherheit Schwierigkeit: Fortgeschritten

Wie sicher ist mein Server? Selbst-Audit mit Lynis

Lynis 3.1.7 auf Debian 13: Server auditieren, den Hardening-Index verstehen, Findings abarbeiten – und wissen, welche Vorschläge den Docker-Host zerlegen.

· 16 Min. Lesezeit ·Dauer: ca. 60 Minuten
Inhaltsverzeichnis

Du hast SSH abgesichert, eine Firewall gesetzt, fail2ban laufen – und weißt trotzdem nicht, was du übersehen hast. Lynis beantwortet genau diese Frage: Es geht deinen Server von innen durch und sagt dir, was noch offen ist. Der schwierigere Teil kommt danach, nämlich zu entscheiden, welche der Vorschläge du wirklich umsetzt.

Was bauen wir?

Wir installieren Lynis 3.1.7 auf einem Debian 13 und lassen ein vollständiges Audit laufen. Aus dem Ergebnis – auf einem frischen netcup-Server mit Docker zunächst Hardening-Index 64, eine Warnung und 44 Vorschläge – arbeiten wir die Punkte ab, die tatsächlich etwas bringen, und lassen die anderen bewusst stehen. Am Ende steht ein reproduzierbarer Audit-Lauf per systemd-Timer, eine dokumentierte Ausnahmeliste und ein Index von 77.

Genauso wichtig ist, was wir nicht tun: Einer der Vorschläge nimmt jedem Container auf der Stelle das Netz, ein zweiter legt beim nächsten neuen Stack eine Zeitbombe, ein dritter beanstandet eine Einstellung, die bereits strenger ist als gefordert. Lynis kennt deinen Server nicht – du schon.

Voraussetzungen

  • Ein abgesicherter Server mit Debian 13 und root- bzw. sudo-Zugang
  • Grundverständnis für systemd-Units und Timer – für die Automatisierung am Ende
  • Etwa eine Stunde Zeit; das erste Audit selbst dauert nur 1–2 Minuten
  • Optional, aber praktisch: unattended-upgrades ist schon eingerichtet – dann fällt eines der Findings von selbst weg
🍳 Empfehlung Anzeige

VPS 1000 G12.5

4 vCore · 8 GB RAM · 128 GB SSD

ab 14,50 €/Monat

Lynis läuft auf jedem noch so kleinen Server – ein VPS 1000 reicht für Audit und Docker-Stack locker.

Zu netcup →

💶 1 Monat gratis:

Einmal einlösbar, gilt für VPS 1000 G12.5. Im Warenkorb einlösen →

💶 5,95 € Gutschein für netcup-Neukunden: immer gültig · nicht für Domains und VPS Lite

Alle netcup-Gutscheine →

Schritt für Schritt

Schritt 1: Lynis aus dem CISOfy-Repository installieren

Debian bringt Lynis mit, aber in einer älteren Fassung:

Terminal
apt-cache policy lynis
Ausgabe
lynis:
  Installed: (none)
  Candidate: 3.1.4-1

Das ist kein Schönheitsfehler. Lynis besteht fast vollständig aus Tests, und die veralten schneller als das Programm drumherum – neue Kernel-Parameter, neue Dienste, neue Standardwerte. Ein Audit mit einer alten Testsammlung übersieht genau die Dinge, die neu dazugekommen sind. Deshalb nehmen wir das Repository von CISOfy, den Lynis-Entwicklern.

Den Signaturschlüssel legen wir als ASCII-armored Datei (.asc) ab. Das ist der Trick, der dir die Installation von gnupg erspart – apt kann Klartext-Schlüssel direkt lesen, wenn die Datei auf .asc endet:

Terminal
install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://packages.cisofy.com/keys/cisofy-software-public.key \
  -o /etc/apt/keyrings/cisofy.asc

Prüfe kurz, dass wirklich ein Schlüssel angekommen ist und nicht eine Fehlerseite:

Terminal
head -1 /etc/apt/keyrings/cisofy.asc
Ausgabe
-----BEGIN PGP PUBLIC KEY BLOCK-----

Jetzt die Paketquelle eintragen. Das signed-by bindet die Quelle fest an genau diesen Schlüssel – CISOfy darf damit nur Lynis-Pakete signieren, nicht dein halbes System ersetzen:

Terminal
echo "deb [signed-by=/etc/apt/keyrings/cisofy.asc] https://packages.cisofy.com/community/lynis/deb/ stable main" \
  > /etc/apt/sources.list.d/cisofy-lynis.list
apt-get update
apt-get install -y lynis
Terminal
lynis --version
Ausgabe
3.1.7

Hinweis

Wenn apt-cache policy lynis weiter nur 3.1.4 anbietet, hat apt-get update die neue Quelle nicht eingelesen – meist wegen eines Tippfehlers im Dateinamen. Die Liste muss auf .list enden und in /etc/apt/sources.list.d/ liegen.

Schritt 2: Der erste Audit-Lauf

Lynis braucht keinen Dienst und keine Konfiguration. Einmal aufrufen genügt:

Terminal
lynis audit system --quick --no-colors | tee /root/lynis-run1.txt

--quick überspringt die Pausen, in denen Lynis sonst auf einen Tastendruck wartet – sonst identisch zum normalen Lauf. --no-colors macht die Ausgabe kopierbar, tee hebt sie auf, damit du später vergleichen kannst.

Nach ein bis zwei Minuten steht am Ende die Zusammenfassung:

Ausgabe
  Details:
  Hardening index : 64 [############        ]
  Tests performed : 251
  Plugins enabled : 0

Darüber stehen die eigentlichen Ergebnisse, getrennt in zwei Kategorien:

Ausgabe
  Warnings (1):
  ----------------------------
  ! Found one or more vulnerable packages. [PKGS-7392]

  Suggestions (44):

Der Unterschied ist wichtig. Eine Warning ist für Lynis ein Zustand, der jetzt falsch ist – ein ungepatchtes Paket, ein Dienst, der nicht tut, was er soll. Eine Suggestion ist eine Empfehlung, die je nach Einsatzzweck sinnvoll sein kann oder eben nicht. Warnungen schaust du dir immer an; Vorschläge sortierst du.

Der Hardening-Index ist dabei die am häufigsten missverstandene Zahl. Er ist kein Prozentwert für „Sicherheit", sondern ein simpler Punktequotient: Jeder Test vergibt erreichte und mögliche Punkte, am Ende wird geteilt. Ein Server ohne Webserver bekommt für nicht durchgeführte Webserver-Tests weder Punkte noch Maximum – ein Server mit Webserver kann also bei gleicher Sorgfalt schlechter dastehen. Vergleiche den Index nur mit dir selbst, nie mit anderen.

Schritt 3: Den Bericht lesen, nicht die Bildschirmausgabe

Die Konsolenausgabe ist die schlechteste der drei Informationsquellen. Zehn Zeilen „Consider hardening SSH configuration [SSH-7408]" untereinander sagen dir nicht, welche zehn Optionen gemeint sind. Die stehen im maschinenlesbaren Bericht:

Terminal
grep "^suggestion\[\]=SSH-7408" /var/log/lynis-report.dat
Ausgabe
suggestion[]=SSH-7408|Consider hardening SSH configuration|AllowTcpForwarding (set YES to NO)|-|
suggestion[]=SSH-7408|Consider hardening SSH configuration|ClientAliveCountMax (set 3 to 2)|-|
suggestion[]=SSH-7408|Consider hardening SSH configuration|LogLevel (set INFO to VERBOSE)|-|
suggestion[]=SSH-7408|Consider hardening SSH configuration|MaxAuthTries (set 6 to 3)|-|
suggestion[]=SSH-7408|Consider hardening SSH configuration|MaxSessions (set 10 to 2)|-|
suggestion[]=SSH-7408|Consider hardening SSH configuration|PermitRootLogin (set YES to (FORCED-COMMANDS-ONLY|NO|PROHIBIT-PASSWORD|WITHOUT-PASSWORD))|-|
suggestion[]=SSH-7408|Consider hardening SSH configuration|Port (set 22 to )|-|
suggestion[]=SSH-7408|Consider hardening SSH configuration|TCPKeepAlive (set YES to NO)|-|
suggestion[]=SSH-7408|Consider hardening SSH configuration|X11Forwarding (set YES to NO)|-|
suggestion[]=SSH-7408|Consider hardening SSH configuration|AllowAgentForwarding (set YES to NO)|-|

Das dritte Feld ist die konkrete Handlungsanweisung. Merke dir die drei Dateien:

  • /var/log/lynis-report.dat – Ergebnis als Schlüssel-Wert-Liste, ideal zum Greppen
  • /var/log/lynis.log – das vollständige Protokoll inklusive der Begründung jedes Tests
  • die Bildschirmausgabe – nur zum Überfliegen

Was Lynis bei einem Test genau erwartet, steht im Log. Beispiel Kernel-Parameter:

Terminal
grep "different value than expected" /var/log/lynis.log | sed "s/.*sysctl key //"
Ausgabe
dev.tty.ldisc_autoload has a different value than expected in scan profile. Expected=0, Real=1
fs.protected_fifos has a different value than expected in scan profile. Expected=2, Real=1
kernel.kptr_restrict has a different value than expected in scan profile. Expected=2, Real=0
kernel.modules_disabled has a different value than expected in scan profile. Expected=1, Real=0
kernel.sysrq has a different value than expected in scan profile. Expected=0, Real=438
kernel.unprivileged_bpf_disabled has a different value than expected in scan profile. Expected=1, Real=2
kernel.yama.ptrace_scope has a different value than expected in scan profile. Expected=1 2 3, Real=0
net.core.bpf_jit_harden has a different value than expected in scan profile. Expected=2, Real=0
net.ipv4.conf.all.forwarding has a different value than expected in scan profile. Expected=0, Real=1
net.ipv4.conf.all.log_martians has a different value than expected in scan profile. Expected=1, Real=0
net.ipv4.conf.all.rp_filter has a different value than expected in scan profile. Expected=1, Real=0
net.ipv4.conf.all.send_redirects has a different value than expected in scan profile. Expected=0, Real=1
net.ipv4.conf.default.accept_redirects has a different value than expected in scan profile. Expected=0, Real=1
net.ipv4.conf.default.log_martians has a different value than expected in scan profile. Expected=1, Real=0
net.ipv6.conf.all.accept_redirects has a different value than expected in scan profile. Expected=0, Real=1
net.ipv6.conf.default.accept_redirects has a different value than expected in scan profile. Expected=0, Real=1

Sechzehn Abweichungen – und drei davon darfst du auf keinen Fall übernehmen. Dazu gleich mehr.

Schritt 4: Die Findings abarbeiten, die etwas bringen

Pakete aktualisieren (PKGS-7392)

Die erste Warnung ist die banalste und die wichtigste. Lynis vergleicht die installierten Pakete mit den verfügbaren Sicherheitsupdates:

Terminal
apt list --upgradable
apt-get -y upgrade

Wenn das bei dir regelmäßig auftaucht, ist nicht Lynis das Problem, sondern der fehlende Automatismus – dafür gibt es unattended-upgrades.

SSH nachziehen (SSH-7408)

Statt /etc/ssh/sshd_config anzufassen, legen wir eine Drop-in-Datei an. Debian 13 lädt alles aus /etc/ssh/sshd_config.d/, und bei einem Paketupdate bleibt deine Änderung so unangetastet:

Terminal
cat > /etc/ssh/sshd_config.d/99-hardening.conf <<'EOF'
LogLevel VERBOSE
MaxAuthTries 3
MaxSessions 2
TCPKeepAlive no
X11Forwarding no
AllowAgentForwarding no
EOF

LogLevel VERBOSE protokolliert zusätzlich den Fingerprint des verwendeten Schlüssels – genau die Information, die du bei einem Vorfall brauchst. MaxAuthTries 3 begrenzt Fehlversuche pro Verbindung, AllowAgentForwarding no verhindert, dass jemand mit Root auf dem Server deinen lokalen SSH-Agent mitbenutzt.

Achtung

Erst testen, dann neu laden. sshd -t prüft die Konfiguration, ohne den Dienst anzufassen. Ein Tippfehler plus blindes restart sperrt dich aus – und ein zweites Terminal aufzumachen hilft nur, solange es schon offen ist.
Terminal
sshd -t && systemctl reload ssh
sshd -T | grep -E "^(maxauthtries|maxsessions|x11forwarding|loglevel)"
Ausgabe
maxauthtries 3
maxsessions 2
x11forwarding no
loglevel VERBOSE

Die beiden schärfsten Punkte aus der Liste – PermitRootLogin und PasswordAuthentication – stehen hier bewusst nicht drin. Die setzt du erst, wenn ein funktionierender SSH-Schlüssel liegt, und genau das macht das Tutorial SSH absichern Schritt für Schritt. Wenn du es durchgearbeitet hast, ist SSH-7408 bei dir von vornherein kürzer.

Kernel-Parameter – hier lauert die Docker-Falle

Von den sechzehn abweichenden sysctl-Werten übernehmen wir dreizehn:

INI
# /etc/sysctl.d/99-lynis.conf
dev.tty.ldisc_autoload = 0
fs.protected_fifos = 2
kernel.kptr_restrict = 2
kernel.sysrq = 0
kernel.yama.ptrace_scope = 1
net.core.bpf_jit_harden = 2
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0

Die letzten vier betreffen ICMP-Redirects – Hinweise eines Routers, ein Paket doch besser über einen anderen Weg zu schicken. Ein Server mit fester Standardroute braucht sie weder zu senden noch anzunehmen, und Docker-Container kommen ohne sie über IPv4 wie IPv6 ins Netz.

Terminal
sysctl -p /etc/sysctl.d/99-lynis.conf

Drei Werte fehlen in der Datei, und das ist Absicht:

  • net.ipv4.conf.all.forwarding = 0 – Lynis will IP-Forwarding abschalten. Docker braucht es, um Pakete zwischen Containernetz und Außenwelt zu routen. Setzt du das auf 0, hat kein einziger Container mehr Netz. Wenn du wissen willst, warum, hilft Docker-Netzwerke verstehen.
  • kernel.modules_disabled = 1 – verbietet dem Kernel, weitere Module zu laden. Docker lädt je nach Stack overlay, br_netfilter oder nf_tables bei Bedarf nach. Auf einem Container-Host ist das eine Zeitbombe, die beim nächsten neuen Stack hochgeht.
  • kernel.unprivileged_bpf_disabled = 1 – hier ist Lynis schlicht zu eng. Debian 13 steht auf 2, und das ist die strengere Einstellung: BPF für unprivilegierte Nutzer ist aus und lässt sich zur Laufzeit nicht wieder einschalten. Lynis prüft auf exakte Gleichheit und meckert deswegen über eine Verbesserung.

Prüfe nach dem Anwenden, dass die Container noch reden können – rp_filter ist der Wert, der am ehesten Ärger macht:

Terminal
docker run --rm alpine:3.22 sh -c "wget -qO- -T5 https://example.com >/dev/null && echo NET-OK"
Ausgabe
NET-OK

Passwort-Richtlinien und umask (AUTH-9286, AUTH-9328)

Terminal
sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS\t365/; s/^PASS_MIN_DAYS.*/PASS_MIN_DAYS\t1/' /etc/login.defs
grep -q "^UMASK" /etc/login.defs || printf 'UMASK\t\t027\n' >> /etc/login.defs

UMASK 027 sorgt dafür, dass neu angelegte Dateien nicht mehr für alle lesbar sind. Das ist sinnvoll – hat aber eine Nebenwirkung, die dich beim nächsten Konfigurationsschritt einholt, siehe „Wenn es nicht funktioniert".

Nicht übernommen haben wir AUTH-9229 und AUTH-9230, die SHA_CRYPT_MIN_ROUNDS in /etc/login.defs verlangen. Debian 13 hasht Passwörter mit yescrypt:

Terminal
grep ^ENCRYPT_METHOD /etc/login.defs
Ausgabe
ENCRYPT_METHOD YESCRYPT

Die SHA_CRYPT_*-Werte gelten ausschließlich für SHA-512-Hashes. Sie hier einzutragen würde die Lynis-Meldung zum Verschwinden bringen, ohne irgendetwas zu ändern – die klassische Scheinverbesserung. Der Test schaut wirklich nur nach diesen beiden Zeilen und ignoriert ENCRYPT_METHOD komplett.

Ungenutzte Kernel-Module sperren (USB-1000, STRG-1846, NETW-3200)

Ein VPS braucht weder USB-Speicher noch FireWire noch die Netzwerkprotokolle DCCP, SCTP, RDS oder TIPC. Jedes geladene Modul ist Angriffsfläche:

Terminal
cat > /etc/modprobe.d/99-lynis-blacklist.conf <<'EOF'
install usb-storage /bin/true
install firewire-core /bin/true
install firewire_ohci /bin/true
install ohci1394 /bin/true
install dccp /bin/true
install sctp /bin/true
install rds /bin/true
install tipc /bin/true
EOF

install <modul> /bin/true ist stärker als blacklist: blacklist verhindert nur das automatische Laden, ein modprobe von Hand geht trotzdem. Prüfe das Ergebnis im Trockenlauf:

Terminal
modprobe -n -v sctp
Ausgabe
install /bin/true

Ein Banner, das Lynis zählen kann (BANN-7126, BANN-7130)

Lynis sucht in /etc/issue nach juristischen Reizwörtern und will mindestens fünf davon finden – aus einer fest eingebauten englischen Liste (authori, monitor, log, legal, prohibited, access, system …). Ein rein deutscher Text besteht den Test nicht, egal wie deutlich er ist. Deshalb zweisprachig:

Terminal
cat > /etc/issue <<'EOF'
Zugriff nur für autorisierte Nutzer. Alle Zugriffe werden protokolliert.
Authorized access only. All connections to this system are monitored and
logged. Unauthorized use is prohibited and may be prosecuted by law.
EOF
cp /etc/issue /etc/issue.net

/etc/issue sieht man an der lokalen Konsole, /etc/issue.net zeigt SSH vor dem Login an. Letzteres passiert allerdings nur, wenn du es sshd auch sagst – ab Werk steht das Banner auf none:

Terminal
echo "Banner /etc/issue.net" >> /etc/ssh/sshd_config.d/99-hardening.conf
sshd -t && systemctl reload ssh
sshd -T | grep -i "^banner"
Ausgabe
banner /etc/issue.net

Paketintegrität und Audit-Protokoll (PKGS-7370, ACCT-9622, ACCT-9628)

Terminal
apt-get install -y debsums apt-show-versions acct auditd

debsums vergleicht die Prüfsummen installierter Dateien mit denen aus dem Paket – damit fällt auf, wenn jemand /usr/bin/ssh ausgetauscht hat. Lynis will zusätzlich, dass die Prüfung regelmäßig läuft:

Terminal
sed -i 's/^CRON_CHECK=.*/CRON_CHECK=weekly/' /etc/default/debsums

auditd protokolliert Kernel-Ereignisse. Frisch installiert hat es allerdings kein einziges Regelwerk – und Lynis quittiert das prompt mit einem neuen Finding, das vorher nicht da war:

Ausgabe
* Audit daemon is enabled with an empty ruleset. Disable the daemon or define rules [ACCT-9630]

Das ist kein Fehler, sondern korrekt: ein Audit-Daemon ohne Regeln kostet Ressourcen und liefert nichts. Ein kleines, sinnvolles Regelwerk für einen Docker-Host:

Terminal
cat > /etc/audit/rules.d/99-serverkueche.rules <<'EOF'
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/sudoers -p wa -k privileged
-w /etc/sudoers.d/ -p wa -k privileged
-w /etc/ssh/sshd_config -p wa -k sshd
-w /etc/ssh/sshd_config.d/ -p wa -k sshd
-w /var/log/lastlog -p wa -k logins
-w /usr/bin/docker -p x -k docker
EOF
augenrules --load
auditctl -l

-p wa heißt „bei Schreib- und Attributänderung", -k setzt ein Stichwort, nach dem du später suchst: ausearch -k sshd. Die letzte Regel protokolliert jeden docker-Aufruf – auf einem Server, auf dem Docker-Zugriff faktisch Root-Zugriff bedeutet, ist das die interessanteste Zeile.

Die Zeit-Warnung, die früher oder später kommt (TIME-3185)

Eine zweite Warnung taucht auf fast jedem Server irgendwann auf und sieht nach einem echten Problem aus, ist aber keins:

Terminal
timedatectl timesync-status | grep -E "Poll|Offset"
Ausgabe
Poll interval: 34min 8s (min: 32s; max 34min 8s)
       Offset: +1.462ms

Die Uhr geht 1,5 Millisekunden falsch – sie ist perfekt synchron. Lynis misst aber das Alter der Datei /run/systemd/timesync/synchronized und warnt ab 2048 Sekunden. Genau 2048 Sekunden ist auch das Standard-Maximum, auf das systemd-timesyncd sein Abfrageintervall hochzieht, wenn die Uhr stabil läuft. Der Test schlägt also auf jedem gut laufenden System zwangsläufig irgendwann an.

Statt die Warnung zu ignorieren, drücken wir das Intervall knapp darunter. Dann bedeutet ein Anschlagen dieses Tests wieder etwas:

INI
# /etc/systemd/timesyncd.conf.d/99-lynis.conf
[Time]
PollIntervalMaxSec=1024
Terminal
chmod 0644 /etc/systemd/timesyncd.conf.d/99-lynis.conf
systemctl restart systemd-timesyncd
timedatectl timesync-status | grep Poll
Ausgabe
Poll interval: 1min 4s (min: 32s; max 17min 4s)

Das chmod ist kein Schreibfehler – warum es nötig ist, steht weiter unten.

Schritt 5: Nachmessen

Terminal
lynis audit system --quick --no-colors | tee /root/lynis-run2.txt
grep -E "Hardening index|Tests performed" /root/lynis-run2.txt
Ausgabe
  Hardening index : 77 [###############     ]
  Tests performed : 254

Von 64 auf 77, und der Abschnitt Warnings fehlt jetzt komplett – bei null Warnungen lässt Lynis ihn einfach weg. Die Vorschläge sind von 44 auf 22 geschrumpft.

Schritt 6: Bewusste Ausnahmen in custom.prf

Was übrig bleibt, sind Vorschläge, die du bei jedem Lauf wieder liest und jedes Mal wieder verwirfst. Genau dafür gibt es /etc/lynis/custom.prf. Wichtig: jede Zeile bekommt eine Begründung, sonst weißt du in einem halben Jahr nicht mehr, ob das eine Entscheidung war oder Bequemlichkeit.

INI
# /etc/lynis/custom.prf

# FILE-6310: eigene Partitionen für /home und /var. Ein VPS hat eine Disk;
# nachträglich partitionieren wäre hier mehr Risiko als Gewinn.
skip-test=FILE-6310

# AUTH-9229/9230: verlangen SHA_CRYPT_*_ROUNDS. Debian 13 nutzt yescrypt,
# die Werte hätten keinerlei Wirkung.
skip-test=AUTH-9229
skip-test=AUTH-9230

# BOOT-5122: GRUB-Passwort. Wer über die netcup-VNC-Konsole kommt, hat ohnehin
# Zugriff auf das Serverpanel und damit auf Snapshots und Rescue-System.
skip-test=BOOT-5122

Der nächste Lauf ist danach deutlich ruhiger – aber der Index bewegt sich nicht:

Ausgabe
  Hardening index : 77 [###############     ]
  Tests performed : 250
  Suggestions (17):

Das ist kein Fehler, sondern der Beweis, dass die Zahl sich nicht schönrechnen lässt: Ein übersprungener Test nimmt seine erreichten und seine möglichen Punkte aus der Rechnung. Von 22 auf 17 Vorschläge, vier Tests weniger – Index unverändert 77. custom.prf ist ein Werkzeug gegen Rauschen, kein Werkzeug für den Punktestand.

Achtung

skip-test schaltet den Test dauerhaft ab – auch dann noch, wenn sich die Lage ändert. Nimm die Liste bei jedem Quartals-Audit einmal durch und frag dich, ob die Begründung noch stimmt. Was du nur diesmal nicht sehen willst, gehört nicht hier hinein.

Zwei Vorschläge lassen wir bewusst sichtbar stehen, statt sie wegzuschalten: HRDN-7230 (Malware-Scanner) und LOGG-2154 (zentrales Logging). Beides ist auf einem einzelnen kleinen VPS schwer zu rechtfertigen – ClamAV allein zieht sich rund ein Gigabyte RAM für die Signaturdatenbank – aber sobald ein zweiter Server dazukommt, ändert sich die Rechnung. Ein Vorschlag, den du jedes Mal wieder liest und wieder ablehnst, ist eine Erinnerung; ein weggeschalteter ist vergessen.

Schritt 7: Das Audit automatisieren

Ein einmaliges Audit ist eine Momentaufnahme. Interessant wird es, wenn du den Verlauf siehst – besonders nach jedem neuen Stack, den du deployst. Lynis bringt dafür einen eigenen Modus mit:

INI
# /etc/systemd/system/lynis-audit.service
[Unit]
Description=Lynis Sicherheits-Audit

[Service]
Type=oneshot
ExecStart=/usr/sbin/lynis audit system --cronjob --quiet

--cronjob schaltet Farben und Rückfragen ab, --quiet unterdrückt alles außer Warnungen. Der Timer daneben:

INI
# /etc/systemd/system/lynis-audit.timer
[Unit]
Description=Monatliches Lynis-Audit

[Timer]
OnCalendar=monthly
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target

Persistent=true holt einen verpassten Lauf nach, wenn der Server zum Termin aus war.

Terminal
systemctl daemon-reload
systemctl enable --now lynis-audit.timer
systemctl list-timers lynis-audit.timer

Den Index eines Laufs holst du dir danach mit einer Zeile aus dem Bericht – das ist die Zahl, die du über die Monate beobachtest:

Terminal
grep ^hardening_index= /var/log/lynis-report.dat
Ausgabe
hardening_index=77

Wenn du dir das Ergebnis aufs Handy schicken lassen willst, hängst du eine ExecStartPost-Zeile mit ntfy an den Service.

Wenn es nicht funktioniert

Nach dem Setzen von UMASK 027 startet ein Dienst nicht mehr und meldet „Permission denied" auf seine eigene Konfigurationsdatei. Die neue umask gilt ab der nächsten Anmeldung, und alles, was du danach mit > oder cat > anlegst, bekommt Modus 0640 statt 0644. Dienste, die unter einem eigenen Benutzer laufen – systemd-timesyncd etwa nutzt DynamicUser –, dürfen die Datei dann nicht mehr lesen. Im Journal steht Failed to open ...: Permission denied. Setz die Rechte für Konfigurationsdateien, die nicht root gehören, explizit: chmod 0644 /etc/systemd/timesyncd.conf.d/99-lynis.conf.

Nach dem Anwenden der sysctl-Werte haben alle Container kein Netz mehr. Dann ist net.ipv4.conf.all.forwarding auf 0 gelandet. Lynis schlägt das vor, Docker kann ohne IP-Forwarding aber keine Pakete zwischen Containernetz und Außenwelt weiterreichen. Nimm die Zeile aus /etc/sysctl.d/99-lynis.conf heraus und setz den Wert mit sysctl -w net.ipv4.conf.all.forwarding=1 sofort zurück – ein Neustart von Docker allein hilft nicht.

systemd-timesyncd did not synchronized the time recently bleibt stehen, obwohl timedatectl NTPSynchronized=yes meldet. Lynis prüft nicht die Uhr, sondern das Alter von /run/systemd/timesync/synchronized, und warnt ab 2048 Sekunden. Da systemd sein Abfrageintervall bei stabiler Uhr genau auf 2048 Sekunden hochzieht, läuft der Test zwangsläufig in die Warnung. Entweder du senkst PollIntervalMaxSec wie in Schritt 4 oder du nimmst den Test in custom.prf auf – ignorieren solltest du ihn nicht, denn dann übersiehst du den Tag, an dem die Zeitsynchronisation wirklich hängt.

Lynis meldet Consider hardening SSH configuration zehnmal und sagt nicht, was gemeint ist. Die Bildschirmausgabe kürzt die Details weg. Sie stehen im dritten Feld von grep "^suggestion\[\]=SSH-7408" /var/log/lynis-report.dat – dort findest du für jede Zeile den Parameter und den Zielwert.

Nach der Installation von auditd ist ein Finding dazugekommen statt weg. ACCT-9630 meldet einen laufenden Audit-Daemon mit leerem Regelwerk. Das ist beabsichtigt: auditd ohne Regeln verbraucht Ressourcen, ohne etwas zu protokollieren. Leg Regeln unter /etc/audit/rules.d/ ab und lade sie mit augenrules --load – oder deinstalliere auditd wieder, wenn du das Protokoll nicht auswertest.

Dein Problem ist nicht dabei? Alle Fehlermeldungen durchsuchen →

Wartung & Backups

Lynis selbst aktualisiert sich über apt mit; durch das CISOfy-Repository bekommst du neue Testsammlungen zeitnah. Prüfe nach einem Major-Update kurz, ob plötzlich neue Findings auftauchen – das sind meist neue Tests, keine neuen Probleme auf deinem Server.

Realistischer Aufwand: Das erste Audit samt Abarbeiten kostet dich einen Abend. Danach sind es etwa 20 Minuten pro Quartal – Bericht überfliegen, neue Findings einsortieren, custom.prf gegenlesen. Der monatliche Timer läuft ohne dich; du schaust nur hin, wenn sich der Index bewegt.

Sichere die Ergebnisse deiner Arbeit mit. Die eigentlichen Werte stecken nicht in Lynis, sondern in den Dateien, die du angelegt hast. Nimm sie in dein Restic-Backup auf:

Ausgabe
/etc/lynis/custom.prf
/etc/sysctl.d/99-lynis.conf
/etc/modprobe.d/99-lynis-blacklist.conf
/etc/ssh/sshd_config.d/99-hardening.conf
/etc/audit/rules.d/
/etc/systemd/system/lynis-audit.{service,timer}

Nach jedem größeren Umbau – neuer Reverse-Proxy, neue Datenbank, neuer offener Port – lohnt ein Lauf von Hand. Genau dann entstehen die Lücken, die ein Monatstimer erst Wochen später findet.

Was Lynis nicht ist

Lynis auditiert von innen: Es liest Konfigurationsdateien und Systemzustände mit Root-Rechten. Es sagt dir nicht, was ein Angreifer von außen sieht, prüft keine Webanwendung auf Schwachstellen und ersetzt keinen Penetrationstest. Für die Außensicht brauchst du einen Portscan von einem anderen Host und – bei öffentlich erreichbaren Diensten – einen Blick auf Traefik-Middlewares.

Feedback per E-Mail: feedback@serverkueche.de

Das könnte dir auch schmecken