Die meisten SSH-Hardening-Guides lesen sich gleich: Port ändern, Root-Login deaktivieren, Passwort-Authentifizierung abschalten. Das ist korrekt, aber es ist ungefähr so, als würde man Haustürsicherheit damit erklären, dass man abschließen soll. Wer seinen SSH-Dienst wirklich absichern will, muss tiefer gehen.
Warum der Standard-sshd unsicher ist
OpenSSH ist in der Grundkonfiguration bewusst kompatibel gehalten — nicht sicher. Das bedeutet: Standardmäßig werden veraltete Kryptographie-Algorithmen akzeptiert, die Verbindungen mit alten Clients ermöglichen, aber die Angriffsfläche unnötig vergrößern. Auf einem frischen Ubuntu-Server unterstützt sshd zum Beispiel noch Diffie-Hellman-Gruppen, die als schwach gelten, sowie ältere MAC-Algorithmen wie hmac-sha1.
Dazu kommt: Die meisten Admins konfigurieren SSH einmal und vergessen es. Dabei ist eine regelmäßige Überprüfung der Konfiguration mindestens so wichtig wie initiales Hardening.
Schlüsseltypen: Ed25519 statt RSA
Wer noch RSA-4096-Schlüssel verwendet, ist nicht falsch — aber Ed25519 ist die bessere Wahl. Warum? Ed25519 basiert auf elliptischer Kurven-Kryptographie (Edwards-Kurve 25519) und bietet bei deutlich kürzeren Schlüsseln eine vergleichbare oder höhere Sicherheit als RSA-4096. Außerdem ist die Signaturberechnung schneller und resistent gegen Timing-Seitenkanäle.
# Neuen Ed25519-Schlüssel generieren
ssh-keygen -t ed25519 -C "user@hostname-$(date +%Y)"
# Öffentlichen Schlüssel auf Zielserver übertragen
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server
Für den Server selbst sollte man in /etc/ssh/sshd_config explizit nur sichere Algorithmen erlauben:
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com
Das eliminiert veraltete Algorithmen wie diffie-hellman-group14-sha1, hmac-sha1 und schwache Chiffren wie RC4 oder 3DES, die von OpenSSH standardmäßig noch akzeptiert werden könnten.
sshd_config: Die wichtigsten Parameter
Über das Offensichtliche hinaus gibt es zahlreiche Parameter, die in den meisten Tutorials fehlen:
# Basiseinstellungen
Port 22 # Wechsel auf nicht-Standard-Port reduziert Noise
ListenAddress 0.0.0.0 # Nur benötigte Interfaces
Protocol 2 # SSHv1 ist tot — explizit verbieten
# Authentifizierung
PermitRootLogin no # Kein Root-Login, niemals
PasswordAuthentication no # Nur Keys
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3 # Nach 3 Fehlversuchen trennen
LoginGraceTime 30 # Verbindung trennen wenn kein Login in 30s
MaxSessions 5 # Maximale Multiplexing-Sessions
# Sicherheit
X11Forwarding no # X-Forwarding deaktivieren
AllowAgentForwarding no # Nur aktivieren wenn wirklich gebraucht
AllowTcpForwarding no # Ebenso
PermitTunnel no
GatewayPorts no
PermitUserEnvironment no # Keine Env-Variablen über authorized_keys
StrictModes yes # Berechtigungsprüfung auf .ssh-Verzeichnis
# Session-Keepalive
ClientAliveInterval 300 # Alle 5 Minuten keepalive
ClientAliveCountMax 2 # Max 2 Fehlversuche dann trennen
# Zugriffsbeschränkung
AllowUsers deploy admin greg # Whitelist — nur explizit erlaubte User
# oder
AllowGroups ssh-users # Gruppen-basiert
SSH-Zertifikate: Besser als authorized_keys
Das meistunterschätzte Feature von OpenSSH ist die CA-basierte Authentifizierung. Statt auf jedem Server die authorized_keys zu pflegen, signiert eine zentrale CA (eine einfache Schlüsseldatei) Benutzerschlüssel für bestimmte Hosts und Zeiträume. Das klingt komplex, ist aber erstaunlich einfach:
# 1. CA-Schlüsselpaar erstellen (einmalig, sicher verwahren)
ssh-keygen -t ed25519 -f /etc/ssh/ca_user_key -C "user-ca"
# 2. Öffentlichen CA-Schlüssel auf Server eintragen
echo "TrustedUserCAKeys /etc/ssh/ca_user_key.pub" >> /etc/ssh/sshd_config
# 3. Benutzerschlüssel signieren (zeitlich begrenzt, für bestimmte User)
ssh-keygen -s /etc/ssh/ca_user_key -I "greg@laptop-2025" -n greg,deploy -V +52w ~/.ssh/id_ed25519.pub
# Erzeugt: id_ed25519-cert.pub
Das Zertifikat ist 52 Wochen gültig, nur für die User greg und deploy. Nach Ablauf muss eine neue Signatur ausgestellt werden — perfekt für zeitlich begrenzte Zugänge oder rotierende Credentials. Kein Ändern von authorized_keys mehr nötig.
Fail2ban und Alternativen
Fail2ban ist der Klassiker für SSH-Brute-Force-Schutz, aber es hat Schwächen: Es liest Log-Dateien, reagiert also immer reaktiv, und bei hohem Angriffs-Traffic kann es selbst zum Bottleneck werden. Alternativen:
sshguard ist schlanker und reagiert schneller, da es direkt in die Logpipeline integriert ist. CrowdSec geht einen Schritt weiter: Es teilt blockierte IPs mit einer Community-Datenbank, sodass bekannte Angreifer-IPs proaktiv geblockt werden, bevor sie überhaupt einen Versuch gestartet haben.
# Fail2ban SSH-Konfiguration (bewährt)
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
maxretry = 3
bantime = 3600
findtime = 600
ignoreip = 127.0.0.1/8 192.168.0.0/16
Port Knocking: Sinnvoll oder Security Theater?
Port Knocking bedeutet: SSH ist nach außen gar nicht erreichbar. Erst nach einer definierten Sequenz von Verbindungsversuchen auf bestimmte Ports öffnet sich der SSH-Port temporär. Das eliminiert praktisch jeglichen Brute-Force-Traffic, weil Angreifer den Port gar nicht erst sehen.
Die Kehrseite: Komplexere Administration, Abhängigkeit von einem weiteren Dienst (knockd), und im Ernstfall — Netzwerkproblem, falsche Knock-Sequenz — sperrt man sich selbst aus. Für Server, auf die man regelmäßig zugreift, ist Port Knocking oft mehr Aufwand als Nutzen. Für Honeypots oder selten genutzte Server kann es sinnvoll sein.
Auditing: SSH-Zugriffe überwachen
Wer hat sich wann angemeldet? Unter systemd ist das mit journalctl trivial:
# Alle SSH-Logins heute
journalctl -u ssh --since today | grep "Accepted"
# Fehlgeschlagene Versuche
journalctl -u ssh --since "1 hour ago" | grep "Failed"
# Strukturiert mit lastlog
lastlog -u greg
Für ernsthafte Auditing-Anforderungen empfiehlt sich auditd mit entsprechenden Regeln, die SSH-bezogene Syscalls protokollieren — oder die Integration in ein zentrales Logging-System wie Graylog oder Elasticsearch.
Match-Blöcke für differenzierte Regeln
Ein oft übersehenes Feature: Match-Blöcke in der sshd_config ermöglichen unterschiedliche Regeln für verschiedene User, Gruppen oder Herkunfts-IPs:
# Deploy-User darf nur von internem Netz
Match User deploy Address 192.168.0.0/16
AllowTcpForwarding yes
X11Forwarding no
# Admin darf alles, aber nur mit Zertifikat
Match User admin
AuthenticationMethods publickey
AuthorizedKeysFile /etc/ssh/admin_keys
Konfiguration testen ohne Aussperren
Bevor man sshd neu startet, immer testen:
sshd -t # Konfiguration prüfen (kein Neustart)
sshd -T # Aktive Konfiguration ausgeben
# Sicher neu starten: zweite SSH-Verbindung offen lassen!
systemctl reload ssh # Nicht restart — reload reicht für Config-Änderungen
Fazit
SSH-Härtung ist kein einmaliges Projekt, sondern ein Prozess. Algorithmen, die heute als sicher gelten, können morgen kompromittiert sein. SSH-Zertifikate lösen das authorized_keys-Chaos auf elegante Weise. Und wer seine Logs nicht überwacht, erfährt von einem erfolgreichen Einbruch oft erst Wochen später. Die hier beschriebenen Maßnahmen zusammen ergeben eine Verteidigung in der Tiefe — jede Schicht macht einen erfolgreichen Angriff unwahrscheinlicher.
Zwei-Faktor-Authentifizierung für SSH
Auch mit Key-Authentifizierung lässt sich 2FA implementieren — für Szenarien, in denen maximale Sicherheit gefragt ist. Das Paket libpam-google-authenticator implementiert TOTP (Time-based One-Time Passwords) als zusätzlichen PAM-Faktor:
# Installation
sudo apt install libpam-google-authenticator
# Pro User konfigurieren (als jeweiliger User ausführen)
google-authenticator
# Beantworte die Fragen: Zeitbasiert (y), Tokens aktualisieren (y),
# Rate limiting (y), Extra-Fenster (n)
# PAM konfigurieren
# /etc/pam.d/sshd — ganz am Anfang hinzufügen:
auth required pam_google_authenticator.so
# sshd_config anpassen
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
Nach dieser Konfiguration ist sowohl ein gültiger SSH-Schlüssel als auch ein TOTP-Code erforderlich. Selbst ein gestohlener privater Schlüssel reicht nicht aus — der Angreifer bräuchte zusätzlich Zugang zum TOTP-Generator.
SSH-Verbindungen multiplexen
Wer häufig Verbindungen zu denselben Servern aufbaut, profitiert von SSH-Multiplexing: Eine einzelne physische TCP-Verbindung wird für mehrere SSH-Sessions genutzt. Das spart den TLS-Handshake-Overhead und macht aufeinanderfolgende Verbindungen nahezu instant.
# ~/.ssh/config
Host server.example.com
HostName server.example.com
User greg
IdentityFile ~/.ssh/id_ed25519
ControlMaster auto
ControlPath ~/.ssh/mux-%r@%h:%p
ControlPersist 10m # Verbindung 10 Minuten im Hintergrund halten
Nach der ersten Verbindung werden alle weiteren auf dieselbe zugrunde liegende TCP-Verbindung gemappt — ohne erneuten Handshake, ohne erneute Key-Exchange-Berechnung. Für Deployments, die dutzende SSH-Befehle hintereinander ausführen, ist das ein erheblicher Performancegewinn.
Jump Hosts: Sicher durch mehrere Netzwerkgrenzen
In sicherheitssensiblen Umgebungen sind Server nicht direkt aus dem Internet erreichbar, sondern nur über einen Bastion Host (Jump Host). SSH unterstützt das nativ:
# Direkte ProxyJump-Option
ssh -J bastion.example.com zielserver.intern
# Oder in ~/.ssh/config dauerhaft konfigurieren
Host intern.example.com
ProxyJump bastion.example.com
User greg
# Mehrere Sprünge
Host tief.beispiel.intern
ProxyJump bastion.extern.com,bastion.intern.com
Der Agent wird dabei nicht auf dem Jump Host exponiert — das ProxyJump nutzt direkte TCP-Weiterleitung statt AgentForwarding. Wichtig: ForwardAgent yes ist ein Sicherheitsrisiko und sollte nur temporär und bewusst verwendet werden.
Sshd-Socket-Activation unter systemd
Eine wenig bekannte Optimierung: SSH-Daemon als socket-aktivierten Dienst betreiben. Der Daemon startet nicht dauerhaft im Hintergrund, sondern nur bei eingehenden Verbindungen. Das spart Ressourcen auf idle-Servern und schränkt die Angriffsfläche ein.
# sshd.service deaktivieren, sshd.socket aktivieren
systemctl stop sshd.service
systemctl disable sshd.service
systemctl enable --now sshd.socket
# Status prüfen
systemctl status sshd.socket
# Lauscht auf Port 22, startet sshd nur bei Bedarf
Regelmäßiges Key-Rotation
SSH-Schlüssel sollten periodisch rotiert werden — mindestens jährlich, bei erhöhtem Risiko öfter. Der Prozess:
# Neues Schlüsselpaar generieren
ssh-keygen -t ed25519 -C "greg@laptop-$(date +%Y)" -f ~/.ssh/id_ed25519_new
# Neuen öffentlichen Schlüssel auf alle Server kopieren
# (alten Schlüssel noch nicht entfernen!)
ssh-copy-id -i ~/.ssh/id_ed25519_new.pub server1
ssh-copy-id -i ~/.ssh/id_ed25519_new.pub server2
# Verbindung mit neuem Schlüssel testen
ssh -i ~/.ssh/id_ed25519_new server1
# Alten Schlüssel aus authorized_keys entfernen
# (auf dem Server: in ~/.ssh/authorized_keys löschen)
# Alten privaten Schlüssel lokal löschen
shred -u ~/.ssh/id_ed25519
mv ~/.ssh/id_ed25519_new ~/.ssh/id_ed25519
Härtungs-Checkliste
- ☑ Ed25519-Schlüssel statt RSA
- ☑ PasswordAuthentication no
- ☑ PermitRootLogin no
- ☑ MaxAuthTries 3
- ☑ AllowUsers / AllowGroups gesetzt
- ☑ X11Forwarding no
- ☑ Algorithmen explizit auf sichere Varianten eingeschränkt
- ☑ Fail2ban oder Äquivalent aktiv
- ☑ SSH-Logs werden überwacht
- ☑ Schlüssel jährlich rotiert
- ☑ Konfiguration mit sshd -t getestet
SSH-Bannering und Honeypot-Erkennung
Der SSH-Banner (die erste Nachricht die der Server beim Verbindungsaufbau sendet) verrät standardmäßig die OpenSSH-Version — eine nützliche Information für Angreifer, die gezielt nach bekannten Schwachstellen suchen. Das Ausblenden oder Verfälschen des Banners ist eine einfache Maßnahme:
# sshd_config
Banner /etc/ssh/banner.txt # Eigene Willkommensnachricht
# oder Version ausblenden:
# In modernen OpenSSH-Versionen gibt es keine direkte Option,
# die Version zu verbergen — aber der Banner-Text kann ablenken
# /etc/ssh/banner.txt Beispiel
Authorized access only. All connections are logged and monitored.
Eine interessante Ergänzung: SSH-Honeypots wie Cowrie können auf einem nicht-Standard-Port lauschen und Angriffsversuche dokumentieren — sie simulieren eine verwundbare SSH-Umgebung und protokollieren alle Befehle des Angreifers. Das liefert wertvolle Einblicke in aktuelle Angriffsmuster und Toolsets.
SSH-Config-Management bei vielen Servern
Wer viele Server verwaltet, sollte die ~/.ssh/config strukturiert pflegen. Eine gut organisierte Config spart Zeit und verhindert Fehler:
# ~/.ssh/config — strukturierte Verwaltung
# Globale Defaults
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
ControlMaster auto
ControlPath ~/.ssh/mux-%r@%h:%p
ControlPersist 10m
# Produktionsserver
Host prod-*
User deploy
IdentityFile ~/.ssh/id_ed25519_prod
StrictHostKeyChecking yes
Host prod-web01
HostName 203.0.113.10
Port 22
Host prod-db01
HostName 203.0.113.20
ProxyJump prod-web01 # DB nur über Webserver erreichbar
# Entwicklungsumgebungen
Host dev-*
User greg
StrictHostKeyChecking accept-new # Flexibler für neue Dev-Hosts
Bei sehr vielen Servern empfiehlt sich ein Tool wie Ansible oder SSH-Inventory, das die Config dynamisch aus einem Inventar generiert. Statische Config-Dateien werden bei mehr als 20 Servern schnell unübersichtlich.
Known Hosts Management
Die ~/.ssh/known_hosts speichert Fingerabdrücke aller bekannten Server. Bei häufig wechselnden Servern (z.B. neue Cloud-Instanzen auf derselben IP) kann das zu "Host key changed"-Warnungen führen. Strategien:
# Spezifischen Eintrag aus known_hosts entfernen
ssh-keygen -R server.example.com
ssh-keygen -R 203.0.113.10
# Für bekannte wechselnde Hosts: Checking deaktivieren
# (nur für unkritische Hosts!)
Host dev.local
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
# Known Hosts hashing aktivieren (Datenschutz)
# Hostnamen werden als Hash gespeichert, nicht im Klartext
HashKnownHosts yes # in /etc/ssh/ssh_config oder ~/.ssh/config
Wireguard vs. SSH-Tunnel: Wann welches?
Ein häufig diskutierter Vergleich: Soll man Dienste über SSH-Tunnel oder über ein VPN wie Wireguard exponieren? SSH-Tunnel sind für ad-hoc-Zugriff ideal — man braucht keine zusätzliche Infrastruktur, nur eine SSH-Verbindung. ssh -L 8080:localhost:80 server leitet Port 80 des Servers auf den lokalen Port 8080 weiter. Einfach, keine Konfiguration, sofort verfügbar.
Wireguard ist für dauerhafte, strukturierte Zugänge besser: geringer Overhead, stabile Verbindungen auch bei Netzwechseln, bessere Performance. Für den dauerhaften Zugang zu einem Heimnetz oder einem internen Netzwerk ist Wireguard die überlegene Wahl. Für gelegentliche, schnelle Zugriffe ist SSH-Forwarding oft praktischer.
# SSH-Port-Forwarding Übersicht
# Local Forwarding: lokaler Port → remote Host
ssh -L 5432:db.intern:5432 jumphost
# Jetzt: psql -h localhost -p 5432 (verbindet zu db.intern)
# Remote Forwarding: remote Port → lokaler Dienst
ssh -R 8080:localhost:3000 server.public
# Jetzt: server.public:8080 zeigt deinen lokalen Port 3000
# Dynamic SOCKS-Proxy: SSH als SOCKS5-Proxy
ssh -D 1080 server.example.com
# Browser-Proxy auf localhost:1080 → surft über server
Für Selbsthoster empfiehlt sich die Kombination: Wireguard als permanentes VPN für die eigene Infrastruktur, SSH-Tunneling für gelegentliche ad-hoc-Weiterleitungen ohne VPN-Overhead.
Zusammenfassung: Der sichere SSH-Stack
Eine vollständig gehärtete SSH-Konfiguration ist kein einmaliges Projekt — sie ist ein lebender Sicherheitsstandard. Die Kombination aus modernen Algorithmen (Ed25519, ChaCha20-Poly1305), starken sshd_config-Einstellungen, SSH-Zertifikaten statt reiner Key-Verwaltung, aktivem Monitoring via Fail2ban oder CrowdSec, und regelmäßiger Schlüsselrotation ergibt eine Verteidigung in der Tiefe.
Wichtig ist auch das Bewusstsein für den Angriffs-Kontext: Die meisten SSH-Angriffe sind opportunistisch — automatisierte Scanner, die Standard-Passwörter ausprobieren. Gegen diese helfen die Basis-Maßnahmen (kein Passwort-Login, Fail2ban) vollständig. Gegen gezielte Angriffe auf einen spezifischen Server braucht es die erweiterten Maßnahmen: SSH-Zertifikate, Jump Hosts, 2FA, Auditing.
Die gute Nachricht: Wer alle beschriebenen Maßnahmen implementiert, hat einen SSH-Dienst der selbst einen gezielten Angriff mit erheblichem Aufwand unwahrscheinlich macht. Das ist das Ziel — keine absolute Sicherheit, sondern eine Kosten-Nutzen-Abwägung die zugunsten des Verteidigers ausgeht.