SSH-Hardening Deep-Dive: sshd_config, Match-Blöcke und ssh-audit für KMU-Server
Vollständig kommentierte sshd_config 2025/2026 für KMU-Server: post-quantum Key-Exchange, CIS-konforme Timeouts, Match-Blöcke für SFTP-only-User, Verifikation mit ssh-audit (Grade A) – Schritt für Schritt ohne Aussperr-Risiko.

Ein offen erreichbarer SSH-Dienst ist das Einfallstor Nummer eins auf KMU-Servern – und gleichzeitig das am häufigsten halbherzig gehärtete Protokoll. OpenSSH bringt von Haus aus moderate Standardeinstellungen mit, die jedoch keineswegs für den Produktionsbetrieb ausreichen: veraltete Diffie-Hellman-Gruppen, keine explizite Algorithmus-Whitelist, fehlende Timeouts. Diese Anleitung zeigt dir, wie du auf Ubuntu 22.04/24.04, Debian 12 oder RHEL 9/10 eine vollständig kommentierte, wartbare Drop-in-Konfiguration erstellst, Match-Blöcke für SFTP-only-User und Admin-Netze einrichtest und das Ergebnis mit ssh-audit auf Grade A verifizierst – ohne dich dabei auszusperren.
Voraussetzungen
- Server mit Ubuntu 22.04/24.04 LTS, Debian 12 oder RHEL 9/10 – OpenSSH >= 8.9 empfohlen, >= 9.0 für sntrup761x25519
- Root- oder sudo-Zugriff auf dem Zielserver
- Eine aktive, offene SSH-Sitzung während der gesamten Konfigurationsarbeit (Sicherheitsnetz gegen Aussperren)
- Konsolen-Zugang (KVM, IPMI oder Cloud-Konsole) als Notfall-Fallback
- Bestehende Ed25519-SSH-Schlüssel für alle Admin-Benutzer – nach dem Hardening ist Passwort-Auth deaktiviert
- Backup der aktuellen Konfiguration:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak - Python 3.9+ auf dem Audit-System (für ssh-audit via pip) oder direktes apt-Paket
- Für SFTP-Nutzer: geplante Benutzerkonten, Gruppen und Verzeichnisstruktur
Schritt 1: ssh-audit installieren und Ist-Zustand erfassen
Bevor du irgendetwas änderst, dokumentierst du den aktuellen Zustand. ssh-audit analysiert den SSH-Handshake und listet alle angebotenen Algorithmen mit Bewertung auf – ohne sich einzuloggen, rein passiv.
# Installation auf Debian/Ubuntu:
sudo apt install ssh-audit
# Alternativ via pip (immer aktuelle Version):
pip3 install ssh-audit
# Ist-Zustand des eigenen Servers erfassen:
ssh-audit localhost
# Nur Fehler und Grade anzeigen (schneller Überblick):
ssh-audit localhost 2>&1 | grep -E '\[fail\]|\[warn\]|Grade'
# Eingebaute Härtungsanleitungen auflisten:
ssh-audit --list-hardening-guides
# Härtungsanleitung für Ubuntu 24.04 LTS abrufen:
ssh-audit --get-hardening-guide ubuntu_2404_lts
Die Ausgabe enthält Bewertungen: [fail] = kritisch, [warn] = schwach, [info] = neutral, [ok] = bestanden. Der Grade (A–F) und der Score (0–100) zeigen den Gesamtstatus. Auf einem frischen Ubuntu-Server wirst du typischerweise Grade C oder D sehen – das ist normal und der Ausgangspunkt dieser Anleitung.
Schritt 2: Moduli-Datei filtern
Die Datei /etc/ssh/moduli enthält vordefinierte DH-Gruppen. Schwache Gruppen (unter 4096 Bit) solltest du entfernen, bevor du diffie-hellman-group-exchange-sha256 anbietest.
# Nur Einträge mit Bitlänge > 4095 behalten:
awk '$5 > 4095' /etc/ssh/moduli > /etc/ssh/moduli.safe
mv /etc/ssh/moduli.safe /etc/ssh/moduli
# Anzahl verbleibender Einträge prüfen (sollte mehrere hundert sein!):
wc -l /etc/ssh/moduli
Wichtig: Wenn nach dem Filter zu wenige Einträge übrig bleiben (unter ~50 Zeilen), schlägt diffie-hellman-group-exchange-sha256 zur Laufzeit fehl. Sollte das passieren, installiere das Paket openssh-server neu, um eine frische moduli-Datei zu erhalten, und wende den Filter erneut an.
Schritt 3: Host-Keys prüfen und ggf. neu generieren
Das Hardening beschränkt HostKeyAlgorithms auf Ed25519 und RSA/SHA-2. Falls noch kein Ed25519-Hostkey vorhanden ist, generierst du ihn jetzt.
# Vorhandene Host-Keys auflisten:
ls -la /etc/ssh/ssh_host_*
# Falls kein Ed25519-Key vorhanden:
sudo ssh-keygen -t ed25519 -q -N "" -f /etc/ssh/ssh_host_ed25519_key
# RSA 4096 Bit als Fallback für ältere Clients:
sudo ssh-keygen -t rsa -b 4096 -q -N "" -f /etc/ssh/ssh_host_rsa_key
Schritt 4: Drop-in-Härtungskonfiguration anlegen
Aktuelle Ubuntu-, Debian- und RHEL-9-Pakete laden automatisch alle /etc/ssh/sshd_config.d/*.conf ein. Prüfe zunächst, ob die Include-Direktive in der Hauptdatei vorhanden ist:
grep 'Include' /etc/ssh/sshd_config
# Erwartete Ausgabe: Include /etc/ssh/sshd_config.d/*.conf
# Falls die Zeile fehlt, füge sie ganz oben ein:
# sudo sed -i '1s/^/Include \/etc\/ssh\/sshd_config.d\/*.conf\n/' /etc/ssh/sshd_config
Nun legst du die eigentliche Härtungsdatei an. Die Nummerierung „10" stellt sicher, dass diese Datei vor eventuellen Ausnahme-Dateien geladen wird – denn bei mehreren Drop-ins gilt die erste Definition einer Direktive.
# Datei: /etc/ssh/sshd_config.d/10-hardening.conf
# SSH Hardening Drop-in – s-edv.com 2025/2026
# Kompatibel mit OpenSSH 8.x+ (Ubuntu 22.04+, Debian 12+, RHEL 9+)
# --- Post-quantum + klassische Key Exchange Algorithmen ---
# mlkem768x25519 = Standard ab OpenSSH 10.0 (ML-KEM + X25519, hybrid post-quantum)
# -> nur verfügbar ab OpenSSH 9.9 (Ubuntu 24.04+); auf Ubuntu 22.04 weglassen!
# sntrup761x25519 = Hybrid post-quantum (NTRU Prime + X25519), seit OpenSSH 9.0
# curve25519 = schnellster klassischer Algorithmus
# dh-group16/18 = RSA-DH 4096/8192 Bit mit SHA-512 als sicherer Fallback
KexAlgorithms mlkem768x25519-sha256,sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group-exchange-sha256
# --- Verschlüsselung: nur AEAD-Ciphers + ChaCha20 ---
# Reihenfolge: ChaCha20 (software-optimiert) > AES-GCM 256 > AES-GCM 128 > AES-CTR
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
# --- MACs: ausschließlich ETM (Encrypt-then-MAC) ---
# ETM-Varianten sind immun gegen Padding-Oracle-Angriffe
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
# --- Hostkey-Algorithmen: Ed25519 + RSA/SHA-2 only ---
# Kein ssh-rsa (SHA-1, ab OpenSSH 8.8 standardmäßig deaktiviert), kein ecdsa
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com
# --- Aktive Host-Keys ---
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
# --- Authentifizierung ---
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no
# --- Timeouts und Limits (CIS-Benchmark-orientiert) ---
LoginGraceTime 30
MaxAuthTries 3
MaxSessions 4
ClientAliveInterval 300
ClientAliveCountMax 2
# --- Sicherheitsverschärfte Defaults ---
PermitEmptyPasswords no
X11Forwarding no
AllowTcpForwarding no
PermitUserEnvironment no
AllowAgentForwarding no
UseDNS no
VersionAddendum none
LogLevel VERBOSE
PrintLastLog yes
Banner /etc/issue.net
# --- SFTP-Subsystem (OpenSSH-intern) ---
Subsystem sftp internal-sftp -f AUTHPRIV -l INFO
Lege die Datei mit sudo tee an und prüfe anschließend die Syntax:
sudo tee /etc/ssh/sshd_config.d/10-hardening.conf > /dev/null << 'EOF'
# (Inhalt wie oben einfügen)
EOF
# Syntaxprüfung – MUSS fehlerfrei sein:
sudo sshd -t
# Effektive Konfiguration nach Merge aller Drop-ins anzeigen:
sudo sshd -T | grep -E 'kexalgorithms|ciphers|macs|hostkeyalgorithms|maxauthtries'
Schritt 5: Match-Blöcke für Admin-Netze und SFTP-only-User
Match-Blöcke müssen zwingend am Ende der Konfiguration stehen. Durch eine separate Datei mit hoher Nummer (90) stellst du sicher, dass sie nach allen globalen Direktiven geladen wird.
# Datei: /etc/ssh/sshd_config.d/90-match-blocks.conf
# === Match-Block: Admin-Netz (Management-VLAN 192.168.10.0/24) ===
# Admins dürfen TCP-Forwarding und Agent-Forwarding nutzen
Match Address 192.168.10.0/24
AllowTcpForwarding yes
AllowAgentForwarding yes
X11Forwarding no
MaxAuthTries 5
# Optional: root-Login nur aus Admin-Netz erlauben
# PermitRootLogin prohibit-password
# === Match-Block: SFTP-only-Gruppe ===
# Alle Mitglieder der Gruppe 'sftponly' werden in ihr Verzeichnis gechrootet
# WICHTIG: ChrootDirectory muss root:root und chmod 755 sein!
Match Group sftponly
ForceCommand internal-sftp -l INFO
ChrootDirectory /srv/sftp/%u
AllowTcpForwarding no
AllowAgentForwarding no
X11Forwarding no
PasswordAuthentication yes
# === Match-Block: einzelner Backup-User ===
Match User backupuser
ForceCommand internal-sftp
ChrootDirectory /backup/sftp
AllowTcpForwarding no
AllowAgentForwarding no
X11Forwarding no
PasswordAuthentication no
SFTP-User anlegen und Verzeichnisstruktur vorbereiten – die ChrootDirectory-Rechte sind entscheidend:
# Gruppe und User anlegen:
sudo groupadd sftponly
sudo useradd -m -s /usr/sbin/nologin -G sftponly webkunde1
sudo passwd webkunde1
# ChrootDirectory anlegen – MUSS root:root / 755 sein:
sudo mkdir -p /srv/sftp/webkunde1
sudo chown root:root /srv/sftp/webkunde1
sudo chmod 755 /srv/sftp/webkunde1
# Upload-Unterverzeichnis für den User (hier darf er schreiben):
sudo mkdir -p /srv/sftp/webkunde1/upload
sudo chown webkunde1:sftponly /srv/sftp/webkunde1/upload
sudo chmod 775 /srv/sftp/webkunde1/upload
Schritt 6: Konfiguration prüfen und Dienst graceful neu laden
Dieser Schritt ist der kritischste. Schließe die aktuelle SSH-Sitzung NICHT – öffne stattdessen ein zweites Fenster mit einer weiteren Verbindung, bevor du den Dienst neu lädst.
# 1. Syntaxprüfung:
sudo sshd -t
# 2. Effektive Konfiguration kontrollieren:
sudo sshd -T | grep -E 'kexalgorithms|ciphers|macs|hostkeyalgorithms|maxauthtries|passwordauthentication'
# 3. Match-Block für spezifischen User testen:
sudo sshd -T -C user=webkunde1 | grep -E 'forcecommand|chrootdirectory|passwordauthentication'
# 4. Graceful Reload (laufende Verbindungen bleiben aktiv!):
sudo systemctl reload sshd
# 5. Status prüfen:
sudo systemctl status sshd
Verbinde dich jetzt mit dem zweiten Fenster neu. Erst wenn der Login erfolgreich ist, darfst du die alte Sitzung schließen.
Schritt 7: Verifikation mit ssh-audit (Ziel: Grade A)
Nach erfolgreichem Reload prüfst du das Ergebnis:
# Vollständiger Audit-Lauf:
ssh-audit localhost
# Erwartetes Ergebnis:
# (ok) kex: mlkem768x25519-sha256
# (ok) kex: sntrup761x25519-sha512@openssh.com
# (ok) enc: chacha20-poly1305@openssh.com
# (ok) mac: hmac-sha2-512-etm@openssh.com
# Grade: A Score: 100 / 100
# JSON-Ausgabe für Dokumentation:
ssh-audit -j localhost > /var/log/ssh-audit-$(date +%Y%m%d).json
# Baseline-Policy erstellen (für spätere Vergleiche nach Updates):
ssh-audit -M /etc/ssh/ssh-audit-policy.txt localhost
# Policy-Prüfung nach einem OpenSSH-Update:
ssh-audit -P /etc/ssh/ssh-audit-policy.txt localhost
Algorithmen-Übersicht 2025/2026
| Kategorie | Empfohlen ✓ | Abgelehnt ✗ |
|---|---|---|
| Key Exchange | mlkem768x25519-sha256, sntrup761x25519-sha512, curve25519-sha256, dh-group18-sha512, dh-group16-sha512 | diffie-hellman-group1-sha1, diffie-hellman-group14-sha1, ecdh-sha2-nistp256 |
| Ciphers | chacha20-poly1305, aes256-gcm, aes128-gcm, aes256-ctr | arcfour, 3des-cbc, aes128-cbc, aes192-cbc, aes256-cbc |
| MACs | hmac-sha2-512-etm, hmac-sha2-256-etm, umac-128-etm | hmac-md5, hmac-sha1, umac-64 (nicht ETM) |
| HostKey | ssh-ed25519, rsa-sha2-512, rsa-sha2-256 | ssh-rsa (SHA-1), ecdsa-sha2-nistp256/384/521 |
OpenSSH-Versionen und Feature-Verfügbarkeit
| Distribution | OpenSSH-Version | sntrup761x25519 | mlkem768x25519 |
|---|---|---|---|
| Ubuntu 22.04 LTS | 8.9 | nein (ab 9.0) | nein (ab 9.9) |
| Debian 12 | 9.2 | ja | nein (ab 9.9) |
| Ubuntu 24.04 LTS | 9.6 | ja | nein (ab 9.9) |
| RHEL 9 | 8.7 | nein | nein |
| OpenSSH 10.0+ (April 2025) | 10.0 | ja | ja (Standard) |
Versionscheck vor dem Setzen von mlkem768x25519: ssh -V. Auf Ubuntu 22.04 (OpenSSH 8.9) entfernst du mlkem768x25519-sha256 aus der KexAlgorithms-Zeile – sshd -t meldet sonst einen Fehler.
Troubleshooting / Typische Fehler
- „Unable to negotiate: no matching key exchange method found. Their offer: diffie-hellman-group1-sha1" – Ein alter Client (Cisco-Switches, WinSCP < 5.x, alte Java-Clients) unterstützt nur schwache KEX-Algorithmen. Diagnose:
ssh -vvv user@server. Einmalig:ssh -oKexAlgorithms=+diffie-hellman-group14-sha1 user@legacyhost. Dauerlösung: Match-Block mit erweitertem KexAlgorithms für die IP des Legacy-Systems anlegen. - „bad ownership or modes for chroot directory" – Das ChrootDirectory oder einer seiner Pfadanteile ist nicht root:root oder hat Schreibrechte für andere. Prüfen:
ls -la /srv/sftp/. Alle Verzeichnisse bis zum ChrootDirectory müssenroot:rootundchmod 755(oder 750) sein. - „no matching host key type found. Their offer: ecdsa-sha2-nistp256" – Der Client hat einen alten ECDSA-Eintrag im known_hosts. Lösung:
ssh-keyscan -H <server> >> ~/.ssh/known_hostsoder alten Eintrag aus~/.ssh/known_hostslöschen. - sshd -T zeigt andere Werte als erwartet – Eine andere Drop-in-Datei oder die Hauptkonfiguration hat dieselbe Direktive früher gesetzt. Ersten Treffer finden:
sudo sshd -T | grep kexalgorithms. Alle Drop-ins auf Konflikte prüfen:grep -r KexAlgorithms /etc/ssh/ - Passwort-Auth im SFTP-Match-Block greift nicht – SFTP-User erhält „Permission denied (publickey)" obwohl Passwort gesetzt. Diagnose:
sudo sshd -T -C user=webkunde1 | grep passwordauthentication– zeigt, ob der Match-Block tatsächlich aktiv ist. - Nach moduli-Filter kein DH-Group-Exchange mehr möglich – Zu aggressiver Filter hat alle Einträge entfernt. Prüfen:
wc -l /etc/ssh/moduli. Falls leer:sudo apt install --reinstall openssh-serverund Filter erneut anwenden. - mlkem768x25519-sha256 unbekannt / sshd -t Fehler – Algorithmus erst ab OpenSSH 9.9 verfügbar. Versionscheck:
ssh -V. Algorithmus aus KexAlgorithms-Zeile entfernen, falls OpenSSH < 9.9.
Häufige Fragen
Welche OpenSSH-Version brauche ich mindestens?
Für sntrup761x25519-sha512: OpenSSH >= 9.0 (Debian 12, Ubuntu 22.04 hat 8.9 – dort weglassen). Für mlkem768x25519-sha256: OpenSSH >= 9.9 (Ubuntu 24.04 hat 9.6 – dort ebenfalls weglassen). Versionscheck: ssh -V. sshd -t prüft Syntax, aber nicht ob Algorithmen zur Laufzeit verfügbar sind – ein Laufzeitfehler erscheint erst beim Verbindungsaufbau.
Muss ich die Hauptdatei /etc/ssh/sshd_config anfassen?
Nur prüfen, ob die Include-Direktive vorhanden ist: grep Include /etc/ssh/sshd_config. Aktuelle Ubuntu-, Debian- und RHEL-9-Pakete haben Include /etc/ssh/sshd_config.d/*.conf standardmäßig als erste nicht-kommentierte Zeile. Falls sie fehlt, muss sie ganz oben ergänzt werden.
Was prüft ssh-audit und was nicht?
ssh-audit prüft: unterstützte KEX/Cipher/MAC/HostKey-Algorithmen, OpenSSH-Version, Banner, bekannte CVEs, DH-Moduli-Qualität. Es prüft nicht: Firewall-Regeln, fail2ban-Konfiguration, ob PasswordAuthentication tatsächlich deaktiviert ist (nur was der Server im Handshake anbietet) oder Match-Block-Logik. Für ein vollständiges Audit müssen diese Punkte separat geprüft werden.
Warum reicht ein Port-Wechsel von 22 nicht aus?
Ein Port-Wechsel ist „Security by Obscurity" und reduziert lediglich Brute-Force-Noise in den Logs. Ein gezielter Angreifer findet offene Ports per nmap in Sekunden. Echter Schutz kommt durch kryptografische Härtung, Schlüssel-Authentifizierung, MaxAuthTries und ergänzend fail2ban und UFW.
Wie verhindere ich, dass mich ein Tippfehler aussperrt?
Drei Sicherheitsnetze: (1) sshd -t vor dem Reload ausführen (Syntaxfehler erkennen), (2) systemctl reload statt restart nutzen (laufende Verbindungen bleiben aktiv), (3) eine zweite SSH-Sitzung im selben Terminal offen lassen, bis der Login mit der neuen Konfiguration bestätigt ist. Bei Cloud-VMs: Konsolen-Zugang (KVM/VNC) als Notfall-Fallback prüfen.
Ist post-quantum Key-Exchange bereits FIPS-zertifiziert?
Noch nicht vollständig: sntrup761x25519-sha512 ist bisher nicht FIPS-zertifiziert. GitHub.com rollt den Algorithmus seit September 2025 weltweit aus, jedoch sind FIPS-Regionen (USA, Behörden) noch ausgenommen. Für FIPS-Compliance-Umgebungen verwendest du vorerst nur curve25519-sha256 und die DH-Group-16/18-Algorithmen.
Fazit
SSH-Hardening ist kein einmaliger Eingriff, sondern ein Prozess: Algorithmen-Whitelist setzen, Timeouts und Auth-Limits nach CIS-Benchmark konfigurieren, Match-Blöcke für spezifische Anwendungsfälle anlegen und das Ergebnis regelmäßig mit ssh-audit verifizieren. Die Drop-in-Struktur unter /etc/ssh/sshd_config.d/ macht die Konfiguration wartbar und sicher – Hauptdatei bleibt unangetastet, Ausnahmen kommen in nummerierte Dateien mit hoher Priorität. Nach einem OpenSSH-Update lohnt sich ein erneuter ssh-audit -P-Lauf gegen die gespeicherte Policy, um neu eingeführte Algorithmen zu evaluieren. Ergänze das SSH-Hardening mit UFW-Firewall und fail2ban sowie einer Zwei-Faktor-Authentifizierung für besonders exponierte Systeme.
Weiterführende Anleitungen und Quellen
- SSH-Key-Authentifizierung einrichten (Linux & Windows)
- Linux-Server absichern mit UFW und fail2ban
- Zwei-Faktor-Authentifizierung (2FA/MFA) einführen
- Linux-Benutzer, Gruppen, Rechte: chmod, chown und sudo
Quellen: OpenSSH Post-Quantum Cryptography · ssh-audit Hardening Guides · ssh-audit GitHub (jtesta) · Post-Quantum Security for SSH on GitHub · OpenSSH Legacy Options