Zum Inhalt springen
myvpsguide

SSH härten: Schlüssel, Konfiguration und der Weg zurück

Ed25519-Schlüssel richtig verwalten, sshd_config ohne Selbstaussperrung, Match-Blöcke, Zwei-Faktor per TOTP und ein Notfallplan für den Ernstfall.

aktualisiert 12.08.2026
5 min lesezeit
fortgeschritten

SSH ist der einzige Dienst, über den praktisch jede Verwaltungsaufgabe läuft. Wer ihn übernimmt, hat alles. Gleichzeitig ist er der Dienst, bei dem eine falsche Zeile in der Konfiguration dich selbst aussperrt und zwar sofort und vollständig.

Dieser Beitrag geht deshalb in zwei Richtungen: was du abschalten solltest, und wie du sicherstellst, dass du danach noch reinkommst.

Schlüssel: welcher Typ, und warum#

Der private Schlüssel bleibt auf dem eigenen Rechner, der öffentliche wandert in authorized_keys auf dem Server. Bei der Anmeldung schickt der Server eine Challenge, die der Client signiert.
Zwischen Client und Server wandert nie der Schlüssel selbst, nur eine Signatur über eine Zufallsaufgabe.
ssh-keygen -t ed25519 -C "mauri@laptop-2026"

Ed25519 ist der Standardfall: kurze Schlüssel, schnelle Prüfung, keine Parameterwahl, bei der man etwas falsch machen kann. RSA brauchst du nur, wenn eine Gegenstelle wirklich nichts anderes kann, dann mit mindestens 4096 Bit.

Drei Gewohnheiten, die sich lohnen:

  • Ein Schlüssel pro Gerät, nicht einer für alles. Geht ein Laptop verloren, entfernst du eine Zeile aus authorized_keys statt überall neue Schlüssel zu verteilen.
  • Der Kommentar am Ende ist Dokumentation. mauri@laptop-2026 verrät in zwei Jahren, welche Zeile weg kann. user@host verrät nichts.
  • Passphrase setzen. Der Komfortverlust ist dank ssh-agent genau einmal pro Sitzung spürbar.
# Agent starten und Schlüssel einmalig entsperren
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519          # unter macOS: ssh-add --apple-use-keychain

Die Client-Konfiguration, die vieles einfacher macht#

Bevor es an den Server geht: ~/.ssh/config auf deinem Rechner spart jeden Tag Tipparbeit und verhindert, dass du bei zehn Maschinen den falschen Schlüssel anbietest.

Host web01
    HostName 203.0.113.10
    User mauri
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

Host *
    ServerAliveInterval 30
    ServerAliveCountMax 3
    HashKnownHosts yes

IdentitiesOnly yes ist wichtiger, als es aussieht: ohne diese Zeile bietet der Client dem Server der Reihe nach alle geladenen Schlüssel an. Bei MaxAuthTries 3 auf der Gegenseite ist die Verbindung dann abgelehnt, bevor der richtige Schlüssel dran war.

ServerAliveInterval hält die Sitzung offen, wenn die Leitung kurz stockt und ist der Unterschied zwischen „hängt“ und „ist weg“ bei einer eingefrorenen Verbindung.

Die Server-Konfiguration#

Debian und Ubuntu lesen seit einigen Versionen /etc/ssh/sshd_config.d/*.conf mit ein. Eigene Änderungen gehören dorthin, nicht in die Hauptdatei, dann fragt kein Paket-Update, ob du deine Konfiguration behalten willst.

# /etc/ssh/sshd_config.d/10-haertung.conf

# Kein Root-Login, auch nicht mit Schlüssel: alles läuft über sudo,
# damit in den Protokollen steht, wer etwas getan hat.
PermitRootLogin no

# Passwörter aus. Beide Zeilen sind nötig — die zweite deckt den
# Tastatur-interaktiven Weg ab, über den sich sonst doch ein
# Passwortdialog öffnet.
PasswordAuthentication no
KbdInteractiveAuthentication no

PubkeyAuthentication yes
AuthenticationMethods publickey

# Nur diese Konten dürfen sich anmelden. Neue Benutzer müssen bewusst
# hinzugefügt werden — das ist der Sinn der Zeile.
AllowUsers mauri deploy

MaxAuthTries 3
LoginGraceTime 20
X11Forwarding no
AllowAgentForwarding no

Prüfen und laden, in dieser Reihenfolge, nie andersherum:

sudo sshd -t                       # Syntaxprüfung, gibt bei Fehlern die Zeile aus
sudo systemctl reload ssh          # bestehende Sitzungen bleiben offen
Der Rettungsanker

Halte während der ganzen Änderung eine zweite, bereits angemeldete SSH-Sitzung offen. reload wirft bestehende Verbindungen nicht raus. Teste den neuen Zugang in einem dritten Fenster, erst wenn der funktioniert, schließt du die alte Sitzung. Ohne diesen Ablauf ist eine vertippte AllowUsers-Zeile eine Neuinstallation.

Match-Blöcke: unterschiedliche Regeln für unterschiedliche Konten#

Ein Deploy-Konto braucht andere Rechte als ein Mensch. Match erlaubt das, ohne einen zweiten SSH-Dienst zu betreiben:

Match User deploy
    # Deploy-Schlüssel dürfen nur ein Kommando ausführen, kein interaktives Shell-Login
    ForceCommand /usr/local/bin/deploy-hook
    PermitTTY no
    AllowTcpForwarding no

Match-Blöcke stehen immer am Ende der Datei. Alles nach einem Match gilt nur für diesen Fall, bis der nächste Match kommt, eine allgemeine Einstellung, die versehentlich darunter rutscht, wirkt plötzlich nur noch für einen einzigen Benutzer.

Der gleiche Effekt lässt sich auch pro Schlüssel erreichen, direkt in authorized_keys:

restrict,command="/usr/local/bin/backup-pull" ssh-ed25519 AAAAC3Nz... backup@nas

restrict schaltet alle Weiterleitungen ab und ist damit die sinnvolle Grundhaltung für jeden Schlüssel, der nur einen Job hat.

Port ändern: ja oder nein?#

Ehrliche Antwort: Es hilft gegen Statistik, nicht gegen Angreifer.

Ein anderer Port als 22 reduziert die Menge automatisierter Login-Versuche in den Protokollen um Größenordnungen. Das macht die Protokolle lesbar, ein echter Vorteil, wenn du sie tatsächlich liest. Gegen jemanden, der es auf deine Maschine abgesehen hat, hilft er nicht: ein Portscan findet den Dienst in Sekunden.

Wenn du ihn änderst, dann sauber:

Port 2222
sudo ufw allow 2222/tcp comment 'SSH'
sudo sshd -t && sudo systemctl restart ssh
# Erst testen, dann die alte Regel entfernen:
sudo ufw delete allow 22/tcp
SELinux und AppArmor

Auf Systemen mit SELinux (RHEL, Rocky, Alma) muss der neue Port zusätzlich freigegeben werden: semanage port -a -t ssh_port_t -p tcp 2222. Ohne das startet der Dienst zwar, bindet aber nicht.

Fail2ban: nützlich, aber nicht der Hauptschutz#

Sobald Passwort-Logins aus sind, laufen Brute-Force-Versuche ins Leere, sie kosten nur noch CPU und Protokollzeilen. Fail2ban räumt beides weg:

sudo apt install -y fail2ban
# /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
backend = systemd
maxretry = 4
findtime = 10m
bantime = 1h
# Eskalierend sperren: Wiederholungstäter länger.
bantime.increment = true
bantime.factor = 4
sudo systemctl restart fail2ban
sudo fail2ban-client status sshd

Wichtig ist die Reihenfolge im Kopf: Fail2ban ist Lärmschutz. Der Schutz ist der abgeschaltete Passwort-Login.

Zwei-Faktor, wenn es mehr sein soll#

Für Maschinen, auf denen wirklich etwas liegt, lässt sich ein zweiter Faktor ergänzen, Schlüssel und Einmalcode:

sudo apt install -y libpam-google-authenticator
google-authenticator -t -d -f -r 3 -R 30 -w 3   # als der Benutzer, nicht als root
# /etc/ssh/sshd_config.d/20-mfa.conf
KbdInteractiveAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
# /etc/pam.d/sshd — ganz oben ergänzen
auth required pam_google_authenticator.so nullok

Das nullok erlaubt Konten ohne eingerichteten zweiten Faktor weiterhin die Anmeldung. Das ist während der Einführung praktisch und danach ein Loch, sobald alle eingerichtet sind, gehört es entfernt.

Eine bequemere Alternative für Menschen mit Hardware-Token: ssh-keygen -t ed25519-sk. Der private Schlüssel liegt dann auf dem Stick, und ohne Berührung des Tokens passiert nichts. Voraussetzung ist OpenSSH 8.2 oder neuer auf beiden Seiten, bei aktuellen Distributionen kein Thema mehr.

Wenn es doch schiefgeht#

Der Ablauf, wenn du dich ausgesperrt hast, in der Reihenfolge der Erfolgswahrscheinlichkeit:

  1. Serielle Konsole oder VNC im Kundenpanel. Fast jeder KVM-Anbieter hat das. Damit kommst du an einen Login, auch wenn das Netz der Maschine dicht ist.
  2. Rettungssystem (Rescue). Bootet ein Live-Linux, hängt deine Platte ein. Dann /mnt/etc/ssh/sshd_config.d/ aufräumen und neu starten.
  3. Snapshot zurückrollen, falls du vor der Änderung einen gemacht hast. Genau dafür sind Snapshots gut.

Punkt 1 solltest du einmal ausprobiert haben, bevor du ihn brauchst. Die Entdeckung, dass die Konsole ein Java-Applet von 2012 will, macht man besser an einem entspannten Dienstagnachmittag.

Zum Abhaken#

PrüfungErwartetes Ergebnis
ssh -o PubkeyAuthentication=no user@hostZugriff verweigert
ssh root@hostZugriff verweigert
sudo sshd -T | grep -E 'permitrootlogin|passwordauth'beides no
sudo fail2ban-client status sshdJail aktiv
Konsole im Kundenpaneleinmal getestet, Zugang bekannt

Der nächste Baustein ist die Firewall und dort besonders die Richtung, die fast alle vergessen: Firewall auf dem VPS mit nftables.

MRMedia

Geschrieben aus dem laufenden Betrieb: MRMedia betreibt eigene Proxmox- Hosts, einen Backup-Server und Kundendienste in der EU. Fehler in einer Anleitung sind Fehler im eigenen Betrieb. Korrekturen bitte über Discord.