SSH Knowledge Base β Hardening, Keys, Jump-Hosts, sshd_config
Quellen
- RFC 4251 β SSH Protocol Architecture
- RFC 4252 β SSH Authentication Protocol
- RFC 4253 β SSH Transport Layer Protocol
- OpenSSH Manual Pages
- CIS Benchmark SSH
Teil 1: Wie SSH funktioniert (bevor man sich aussperrt)
SSH (Secure Shell) ist ein verschlΓΌsseltes Netzwerkprotokoll.
Port 22/TCP. Zwei Phasen: Transport β Authentifizierung.
Client Server
β β
βββββ TCP SYN βββββββββββββββββββΊ β Port 22
βββββ TCP SYN-ACK ββββββββββββββ β
β β
β ββ TRANSPORT LAYER βββββββββββββββββββββββββββββ
βββββ Server-Banner (SSH-2.0-OpenSSH_9.x) ββββββ β
βββββ Client-Banner βββββββββββββΊ β β
β β β
β Key Exchange (ECDH/DH) β Kein β
β Server schickt Host-Key β Klartext β
β Client prΓΌft Host-Key β ab hier β
β Session-Keys werden abgeleitet β β
β β β
β ββ AUTH LAYER ββββββββββββββββββββββββββββββββββ
β β
βββββ Auth-Request ββββββββββββββΊ β
β (publickey / password) β
βββββ Auth-Response ββββββββββββ β
β β
β ββ SESSION βββββββββββββββββββββ
β Shell / SFTP / Port-Forward β
Host-Keys β das Fundament des Vertrauens
# Server hat mehrere Host-Keys (ein pro Algorithmus):
ls /etc/ssh/ssh_host_*
# ssh_host_ecdsa_key ssh_host_ecdsa_key.pub
# ssh_host_ed25519_key ssh_host_ed25519_key.pub
# ssh_host_rsa_key ssh_host_rsa_key.pub
# Client speichert bekannte Server in:
~/.ssh/known_hosts
# Warnung bei geΓ€ndertem Host-Key:
# "REMOTE HOST IDENTIFICATION HAS CHANGED"
# β Server neu aufgesetzt? MITM? Immer prΓΌfen!
# Host-Key Fingerprint prΓΌfen (auf dem Server):
ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
# Ausgabe: 256 SHA256:ABC123... root@server01 (ED25519)
# Known-Hosts-Eintrag entfernen (nach Server-Neuaufbau):
ssh-keygen -R hostname
ssh-keygen -R 10.0.1.50
Teil 2: SSH-Keys β Authentifizierung ohne Passwort
Key-Typen im Vergleich
RSA-4096 β Alt, weit kompatibel, groΓ (langsam)
Minimum: 4096 Bit! 2048 nicht mehr empfohlen.
ECDSA-256 β Elliptische Kurven, schneller als RSA
Achtung: NIST-Kurven, theoretische NSA-Bedenken
ED25519 β Modern, schnell, klein, sicher
Empfohlen fΓΌr alle neuen Keys! β
Nicht auf sehr alten Systemen (OpenSSH < 6.5)
Keys erstellen
# ED25519 β empfohlen
ssh-keygen -t ed25519 -C "admin@rz-server01" -f ~/.ssh/id_ed25519
# RSA fΓΌr alte Systeme (z.B. FreeBSD 10, alte IPMI-Konsolen)
ssh-keygen -t rsa -b 4096 -C "admin@legacy-server" -f ~/.ssh/id_rsa_legacy
# Ohne Passphrase (fΓΌr Automatisierung, Cronjobs)
ssh-keygen -t ed25519 -N "" -f /etc/ssh/deploy_key
# Passphrase Γ€ndern (ohne neuen Key!)
ssh-keygen -p -f ~/.ssh/id_ed25519
Public Key auf Server deployen
# Methode 1: ssh-copy-id (einfach)
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@10.0.1.50
# Methode 2: manuell (wenn ssh-copy-id nicht verfΓΌgbar)
cat ~/.ssh/id_ed25519.pub | ssh user@10.0.1.50 \
"mkdir -p ~/.ssh && chmod 700 ~/.ssh && \
cat >> ~/.ssh/authorized_keys && \
chmod 600 ~/.ssh/authorized_keys"
# Methode 3: direkt auf Server
echo "ssh-ed25519 AAAA...KEY... admin@client" \
>> /home/adminuser/.ssh/authorized_keys
chmod 600 /home/adminuser/.ssh/authorized_keys
chmod 700 /home/adminuser/.ssh
Berechtigungen β die hΓ€ufigste Fehlerquelle
# KRITISCH: falsche Berechtigungen = Key funktioniert nicht!
# SSH ist absichtlich paranoid bei Dateiberechtigungen.
chmod 700 ~/.ssh # nur Owner darf rein
chmod 600 ~/.ssh/authorized_keys # nur Owner lesen/schreiben
chmod 600 ~/.ssh/id_ed25519 # Private Key: KEIN Gruppenlesezugriff!
chmod 644 ~/.ssh/id_ed25519.pub # Public Key: darf gelesen werden
chmod 600 ~/.ssh/config # Client-Config
# Diagnose wenn Key-Auth nicht funktioniert:
ssh -vvv user@server 2>&1 | grep -E "permission|auth|key"
# Auf dem Server prΓΌfen:
ls -la ~/.ssh/
ls -la ~/.ssh/authorized_keys
# Auch Elternverzeichnis prΓΌfen!
ls -la ~/ # Home-Dir darf NICHT world-writable sein!
namei -l ~/.ssh/authorized_keys # komplette Pfad-Berechtigungen
Teil 3: sshd_config β Hardening
Datei-Pfade
/etc/ssh/sshd_config β Haupt-Config (alle Distros)
/etc/ssh/sshd_config.d/*.conf β Drop-in Configs (neuere Systeme)
~/.ssh/config β Client-Config (pro User)
/etc/ssh/ssh_config β Globale Client-Config
GehΓ€rtete sshd_config fΓΌr RZ
# /etc/ssh/sshd_config β RZ-HΓ€rtung
# Nach Γnderungen: sshd neu laden (NICHT neustarten solange Verbindung offen!)
# ββ Protokoll und Port ββββββββββββββββββββββββββββββββββββββββ
Port 22 # Im RZ oft belassen β Security by Obscurity hilft wenig
# Port 2222 # Alternative β reduziert Brute-Force-Rauschen
# ββ Authentifizierung βββββββββββββββββββββββββββββββββββββββββ
PasswordAuthentication no # β WICHTIGSTE EINSTELLUNG! Keys only!
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
PermitRootLogin no # Root-Login verbieten!
# PermitRootLogin prohibit-password # Kompromiss: Root nur mit Key
# Kein leeres Passwort
PermitEmptyPasswords no
# Challenge-Response deaktivieren (PAM-Bypass-Vektor)
ChallengeResponseAuthentication no
KbdInteractiveAuthentication no # Neuerer Name fΓΌr dasselbe
# ββ Verbindungs-Timeouts ββββββββββββββββββββββββββββββββββββββ
LoginGraceTime 30 # 30s fΓΌr Login, dann Disconnect
MaxAuthTries 3 # Max 3 Versuche pro Verbindung
MaxSessions 10 # Max parallele Sessions pro Verbindung
ClientAliveInterval 300 # Alle 300s keepalive senden
ClientAliveCountMax 2 # Nach 2 Misses: Disconnect
# β hΓ€ngende Verbindungen werden nach 10min getrennt
# ββ Verbindungs-EinschrΓ€nkungen βββββββββββββββββββββββββββββββ
MaxStartups 10:30:60 # Brute-Force-Schutz:
# 10 gleichzeitig, ab 30% random drop,
# ab 60 komplett blockieren
# Nur bestimmte User erlauben (whitelist)
# AllowUsers adminuser deployuser
# AllowGroups sshusers admins
# IP-basiert einschrΓ€nken (Alternative zu iptables)
# ListenAddress 10.0.0.53 # Nur auf diesem Interface lauschen
# ββ Crypto-Algorithmen (moderne Auswahl) βββββββββββββββββββββ
# Schwache Algorithmen deaktivieren:
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512
HostKeyAlgorithms ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-512,rsa-sha2-256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
# ββ Features deaktivieren die man nicht braucht βββββββββββββββ
X11Forwarding no # GUI-Forwarding aus
AllowTcpForwarding no # Port-Forwarding aus (wenn nicht gebraucht)
AllowAgentForwarding no # SSH-Agent-Forwarding aus
PermitTunnel no # VPN-Tunnel aus
# ββ Banner und MOTD βββββββββββββββββββββββββββββββββββββββββββ
Banner /etc/ssh/banner # Rechtlicher Hinweis vor Login
PrintMotd no # MOTD ΓΌber PAM steuern
# ββ Logging βββββββββββββββββββββββββββββββββββββββββββββββββββ
LogLevel VERBOSE # Mehr als INFO, aber kein DEBUG
# LogLevel DEBUG3 # Nur fΓΌr ProblemlΓΆsung!
SyslogFacility AUTH
sshd nach Config-Γnderung neu laden
# β οΈ NIEMALS sshd neustarten wΓ€hrend man verbunden ist!
# Bei Fehler in Config β neue Verbindung schlΓ€gt fehl β ausgesperrt!
# SICHERE Vorgehensweise:
# 1. Config prΓΌfen BEVOR reload:
sshd -t # Test-Modus, prΓΌft Syntax
sshd -T | grep -i password # Effektive Config anzeigen
# 2. In ZWEITER SSH-Session testen (alte Session OFFEN lassen!)
ssh -o "StrictHostKeyChecking=no" adminuser@10.0.1.50 "echo OK"
# 3. Wenn neue Session funktioniert: reload
systemctl reload sshd # Debian/Ubuntu/RHEL
# oder
kill -HUP $(cat /var/run/sshd.pid)
# β οΈ NICHT: systemctl restart sshd (trennt bestehende Verbindungen)
Teil 4: Das Aussperr-Kapitel
Die hΓ€ufigsten Wege sich selbst auszusperren β und wie man rauskommt.
Szenario 1: PasswordAuthentication no β und kein Key hinterlegt
# PrΓ€vention:
# IMMER Key hinterlegen BEVOR PasswordAuthentication deaktiviert wird!
# Reparatur via Konsole (IPMI, iDRAC, iLO, KVM):
# 1. Konsole ΓΆffnen
# 2. Datei editieren:
vi /etc/ssh/sshd_config
# PasswordAuthentication yes β temporΓ€r
systemctl reload sshd
# 3. Key hinterlegen
# 4. Wieder auf no setzen
Szenario 2: Falsche Berechtigungen auf ~/.ssh
# Symptom: Key-Auth schlΓ€gt fehl obwohl Key stimmt
# Server-Log:
journalctl -u sshd | tail -20
# Authentication refused: bad ownership or modes for directory /home/user/.ssh
# Reparatur:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R $USER:$USER ~/.ssh
Szenario 3: sshd startet nicht nach Config-Fehler
# Symptom: systemctl reload sshd schlΓ€gt fehl
# Diagnose:
sshd -t # zeigt den genauen Fehler
journalctl -u sshd -n 20
# HΓ€ufige Fehler:
# "Bad configuration option" β Tippfehler in sshd_config
# "Permissions 0644 for '/etc/ssh/ssh_host_ed25519_key' are too open"
chmod 600 /etc/ssh/ssh_host_*_key
# Reparatur: alte Config aus Backup wiederherstellen
cp /etc/ssh/sshd_config.bak /etc/ssh/sshd_config
systemctl start sshd
Szenario 4: fail2ban hat eigene IP gesperrt
# Diagnose:
fail2ban-client status sshd
iptables -L f2b-sshd -n
# Entsperren:
fail2ban-client set sshd unbanip 10.0.1.100
# Eigene Management-IPs fΓΌr immer whitelisten:
# /etc/fail2ban/jail.local:
[DEFAULT]
ignoreip = 127.0.0.1/8 10.0.0.0/19
Szenario 5: AllowUsers/AllowGroups zu restriktiv
# Symptom: "Permission denied" obwohl Key stimmt
# Server-Log:
journalctl -u sshd | grep "User .* not allowed"
# PrΓΌfen wer erlaubt ist:
sshd -T | grep -i "allowusers\|allowgroups\|denyusers"
# User zur Gruppe hinzufΓΌgen:
usermod -aG sshusers adminuser
# Dann: NEUE Session testen, BEVOR alte geschlossen wird!
Notfallzugang: IPMI / Serial Console
# Wenn SSH komplett versperrt: IPMI nutzen
# IPMI ist der Zugang zum Server unabhΓ€ngig vom OS
# iDRAC (Dell):
racadm -r 10.0.0.100 -u root -p password getconfig
# iLO (HP):
# Browser: https://10.0.0.100
# IPMI Serial-over-LAN:
ipmitool -I lanplus -H 10.0.0.100 -U admin -P password sol activate
# Nach Reparatur:
ipmitool -I lanplus -H 10.0.0.100 -U admin -P password sol deactivate
Teil 5: SSH Client-Config (~/.ssh/config)
# ~/.ssh/config β spart tΓ€glich viele Tippfehler
# ββ Globale Defaults ββββββββββββββββββββββββββββββββββββββββββ
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
IdentityFile ~/.ssh/id_ed25519
StrictHostKeyChecking ask # ask | yes | no | accept-new
# ββ RZ-Server βββββββββββββββββββββββββββββββββββββββββββββββββ
Host rz-server01
HostName 10.0.1.50
User adminuser
Port 22
IdentityFile ~/.ssh/id_ed25519_rz
Host rz-*
User adminuser
IdentityFile ~/.ssh/id_ed25519_rz
StrictHostKeyChecking yes # Im RZ: kein auto-accept!
# ββ Jump-Host (Bastion) βββββββββββββββββββββββββββββββββββββββ
Host bastion
HostName 203.0.113.10
User jumpuser
Port 22
IdentityFile ~/.ssh/id_ed25519_bastion
Host internal-*
ProxyJump bastion # automatisch ΓΌber bastion
User adminuser
IdentityFile ~/.ssh/id_ed25519_internal
# ββ IPMI-Management direkt ββββββββββββββββββββββββββββββββββββ
Host ipmi-*
User admin
IdentityFile ~/.ssh/id_rsa_ipmi # IPMI oft noch RSA
KexAlgorithms +diffie-hellman-group14-sha1 # alte IPMI-Firmware
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
# Mit Config: kurze Befehle
ssh rz-server01 # statt: ssh -i ~/.ssh/id_ed25519_rz adminuser@10.0.1.50
ssh internal-server02 # automatisch ΓΌber bastion
Teil 6: Jump-Hosts und Port-Forwarding
Jump-Host / Bastion
# Direkt via ProxyJump (OpenSSH >= 7.3)
ssh -J jumpuser@bastion.example.com adminuser@10.0.1.50
# Mehrere Hops:
ssh -J user@hop1,user@hop2 user@final-target
# In ~/.ssh/config:
Host internal-server
HostName 10.0.1.50
ProxyJump bastion
# Γltere Methode (ProxyCommand, immer noch nΓΌtzlich):
Host internal-server
HostName 10.0.1.50
ProxyCommand ssh -W %h:%p bastion
Port-Forwarding
# Local Port Forward: lokaler Port β remote
# "Ich will auf 10.0.1.50:5432 (PostgreSQL) zugreifen"
ssh -L 15432:10.0.1.50:5432 adminuser@bastion
# β localhost:15432 verbindet zu 10.0.1.50:5432
# Remote Port Forward: remote Port β lokal
# "Server soll auf meinen lokalen Dienst zugreifen"
ssh -R 8080:localhost:80 adminuser@server
# β server:8080 verbindet zu meinem localhost:80
# Dynamic Port Forward (SOCKS Proxy)
ssh -D 1080 adminuser@bastion
# β localhost:1080 als SOCKS5-Proxy fΓΌr das remote Netz
# Persistent (kein Terminal, nur Tunnel):
ssh -fNT -L 15432:10.0.1.50:5432 adminuser@bastion
# -f = in Hintergrund
# -N = kein Befehl ausfΓΌhren
# -T = kein PTY
SSH-Agent und Agent-Forwarding
# SSH-Agent starten und Key hinzufΓΌgen
eval $(ssh-agent)
ssh-add ~/.ssh/id_ed25519
ssh-add -l # geladene Keys anzeigen
ssh-add -t 3600 ~/.ssh/id_ed25519 # Key mit Timeout (1h)
# Agent-Forwarding (mit Vorsicht verwenden!)
ssh -A adminuser@bastion # Agent wird weitergeleitet
# β οΈ Auf Bastion kann Root den Agent-Socket nutzen!
# Nur auf vertrauenswΓΌrdigen Servern!
# Sicherer: ProxyJump statt Agent-Forwarding
# ProxyJump nutzt Client-Keys direkt, kein Forwarding nΓΆtig
Teil 7: fail2ban β SSH Brute-Force-Schutz
# Installation
apt install fail2ban # Debian/Ubuntu
dnf install fail2ban # RHEL/Rocky
# /etc/fail2ban/jail.local β lokale Anpassungen:
[DEFAULT]
bantime = 1h # Sperrzeit
findtime = 10m # Zeitfenster fΓΌr maxretry
maxretry = 5 # Versuche bis Sperre
ignoreip = 127.0.0.1/8 10.0.0.0/19 # Eigenes Netz nie sperren!
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
backend = %(syslog_backend)s
maxretry = 3
# Service
systemctl enable fail2ban
systemctl start fail2ban
# Status
fail2ban-client status
fail2ban-client status sshd
# Gesperrte IPs anzeigen
fail2ban-client status sshd | grep "Banned IP"
# IP entsperren
fail2ban-client set sshd unbanip 1.2.3.4
# Log beobachten
tail -f /var/log/fail2ban.log
journalctl -u fail2ban -f
Teil 8: Distro-Unterschiede
sshd_config Standardwerte
| Einstellung | Debian 12 | Ubuntu 24 | RHEL 9 | FreeBSD |
|---|---|---|---|---|
| PasswordAuthentication | yes | yes | yes | yes |
| PermitRootLogin | no | prohibit-password | no (RHEL9) | no |
| Default-Port | 22 | 22 | 22 | 22 |
| Config-Pfad | /etc/ssh/sshd_config |
/etc/ssh/sshd_config |
/etc/ssh/sshd_config |
/etc/ssh/sshd_config |
| Drop-in-Dir | /etc/ssh/sshd_config.d/ |
/etc/ssh/sshd_config.d/ |
/etc/ssh/sshd_config.d/ |
β |
| Service-Reload | systemctl reload sshd |
systemctl reload ssh |
systemctl reload sshd |
service sshd reload |
Ubuntu: Service heiΓt ssh nicht sshd!
# Ubuntu:
systemctl status ssh # β ssh, nicht sshd!
systemctl reload ssh
journalctl -u ssh
# Alle anderen (Debian, RHEL, Arch, FreeBSD):
systemctl status sshd
systemctl reload sshd
journalctl -u sshd
RHEL 9: sshd_config.d Drop-ins
# RHEL 9 hat crypto-policies die sshd_config ΓΌberschreiben kΓΆnnen!
# Effektive Config anzeigen:
sshd -T | head -40
# Crypto-Policy setzen:
update-crypto-policies --set DEFAULT
update-crypto-policies --set FUTURE # strenger
update-crypto-policies --show
# Drop-in fΓΌr eigene Regeln:
# /etc/ssh/sshd_config.d/99-rz-hardening.conf
FreeBSD: rc.conf und sshd
# /etc/rc.conf:
sshd_enable="YES"
# Service:
service sshd start
service sshd reload
service sshd status
# Kein systemd! Kein journalctl!
# Logs:
tail -f /var/log/auth.log
grep sshd /var/log/auth.log
# authorized_keys fΓΌr root:
/root/.ssh/authorized_keys
# Alte IPMI-Firmware auf FreeBSD-Servern:
# oft braucht man legacy KexAlgorithms in Client-Config:
# KexAlgorithms +diffie-hellman-group1-sha1
Teil 9: SSH-Diagnose β Einzeiler
# Verbindung mit maximalem Debug-Output
ssh -vvv user@server 2>&1 | less
# Welche Authentifizierungsmethoden bietet der Server?
ssh -v user@server 2>&1 | grep "Authentications that can continue"
# Welchen Key nutzt der Client?
ssh -v user@server 2>&1 | grep "Trying\|Offering\|Server accepts"
# Server-Config live anzeigen (ohne Restart)
sshd -T
sshd -T | grep -i "passwordauth\|pubkeyauth\|permitroot"
# Alle fehlgeschlagenen Logins heute
journalctl -u sshd --since today | grep "Failed\|Invalid"
# Bruteforce-Versuch erkennen
journalctl -u sshd | grep "Failed password" | \
awk '{print $11}' | sort | uniq -c | sort -rn | head -20
# Wer ist gerade eingeloggt?
who
w
ss -tlnp | grep :22
last | head -20 # letzte Logins
# SSH-Verbindungen im System
ss -tnp | grep :22
netstat -tnp | grep :22
# Host-Key Fingerprint des Servers anzeigen
ssh-keyscan -t ed25519 10.0.1.50 2>/dev/null | ssh-keygen -lf -
# Known-Hosts aufrΓ€umen
ssh-keygen -R 10.0.1.50 # einzelnen Eintrag entfernen
Teil 10: SSH-HΓ€rtung Checkliste RZ
[ ] PasswordAuthentication no β Keys only
[ ] PermitRootLogin no β kein direkter Root-Login
[ ] MaxAuthTries 3 β Brute-Force-Erschwernis
[ ] LoginGraceTime 30 β Timeout fΓΌr Login
[ ] ClientAliveInterval 300 β hΓ€ngende Sessions aufrΓ€umen
[ ] X11Forwarding no β GUI aus
[ ] AllowTcpForwarding no β Port-Forward aus (wenn nicht gebraucht)
[ ] Banner gesetzt β rechtlicher Hinweis
[ ] fail2ban aktiv β automatische IP-Sperre
[ ] eigene IP in fail2ban ignoreip β eigenes Netz nie sperren!
[ ] sshd -t vor jedem reload β Syntax immer prΓΌfen
[ ] zweite Session testen β vor SchlieΓen der alten
[ ] IPMI-Zugang bekannt β Notfallzugang dokumentiert
[ ] Host-Keys gesichert β /etc/ssh/ssh_host_* im Backup
[ ] authorized_keys gepflegt β alte Keys regelmΓ€Γig entfernen
Wichtige Dateipfade
| Datei | Inhalt |
|---|---|
/etc/ssh/sshd_config |
Server-Config |
/etc/ssh/sshd_config.d/ |
Drop-in Configs |
/etc/ssh/ssh_host_* |
Server Host-Keys (privat + ΓΆffentlich) |
~/.ssh/config |
Client-Config (pro User) |
~/.ssh/authorized_keys |
Erlaubte Public Keys |
~/.ssh/known_hosts |
Bekannte Server-Keys |
~/.ssh/id_ed25519 |
Privater Key (chmod 600!) |
~/.ssh/id_ed25519.pub |
Γffentlicher Key |
/var/log/auth.log |
SSH-Logs (Debian/Ubuntu) |
/var/log/secure |
SSH-Logs (RHEL/Rocky) |
/var/log/auth.log |
SSH-Logs (FreeBSD) |
journalctl -u sshd |
SSH-Logs (systemd) |
journalctl -u ssh |
SSH-Logs Ubuntu (!) |
Quellen / WeiterfΓΌhrend
man sshd_config,man ssh_config,man ssh-keygen- RFC 4251/4252/4253 β SSH Protokoll
- OpenSSH: https://www.openssh.com
- CIS Benchmark: https://www.cisecurity.org
- Arch Wiki: https://wiki.archlinux.org/title/OpenSSH
- Mozilla SSH Guidelines: https://infosec.mozilla.org/guidelines/openssh