Linux-Server absichern: SSH-Schlüssel, ufw, fail2ban
Neuen Linux-Server absichern, ohne Dich auszusperren: SSH-Schlüssel, Passwort-Login aus, ufw, die Docker-Falle, fail2ban und automatische Updates.
Von Nathan Base
Ein frischer VPS oder ein Raspberry Pi mit Portfreigabe steht nach wenigen Minuten im Visier automatischer Scanner. Schau nach einem Tag einmal mit sudo journalctl -u ssh ins Protokoll: Dort stehen Hunderte fehlgeschlagene Anmeldeversuche mit Benutzernamen wie root, admin und test. Das ist kein gezielter Angriff, sondern Grundrauschen. Mit einem Passwort-Login und einem schwachen Passwort ist es trotzdem nur eine Frage der Zeit.
Diese Anleitung bringt Deinen Server in einer guten Stunde auf eine solide Grundabsicherung: eigener Benutzer, SSH nur noch mit Schlüssel, eine Firewall, fail2ban gegen Dauerversuche und automatische Sicherheitsupdates. Der wichtigste Teil ist dabei die Reihenfolge, denn jeder Schritt ist so gebaut, dass Du Dich nicht aussperrst. Die Befehle gelten für Ubuntu Server 24.04 und 26.04 LTS, Debian 13 und Raspberry Pi OS auf Basis von Debian 13. Wo sich die Systeme unterscheiden, steht es dabei.
Die Lebensversicherung: eine zweite Sitzung
Bevor Du irgendetwas an SSH oder der Firewall änderst, öffne zwei Terminalfenster und melde Dich in beiden am Server an. Im ersten arbeitest Du, das zweite lässt Du einfach offen. Eine bestehende SSH-Verbindung bleibt auch dann bestehen, wenn Du die SSH-Konfiguration änderst oder die Firewall einschaltest. Geht etwas schief, machst Du die Änderung im zweiten Fenster wieder rückgängig.
Erst wenn eine neue Anmeldung in einem dritten Fenster klappt, schließt Du die alten. Diese Regel gilt für jeden Schritt in dieser Anleitung, der SSH oder ufw betrifft. Als zweite Rückfallebene merkst Du Dir, wo Dein Anbieter eine Web-Konsole hat, bei DigitalOcean etwa unter Access > Recovery Console im Droplet. Beim Raspberry Pi sind es Bildschirm und Tastatur.
Schritt 1: Ein eigener Benutzer mit sudo
Viele VPS-Anbieter geben Dir nur den Benutzer root. Arbeite nicht dauerhaft damit, sondern leg einen eigenen Benutzer an. Ersetze deinname in allen Befehlen durch den Namen, den Du nutzen willst:
adduser deinname
usermod -aG sudo deinnameadduser fragt nach einem Passwort und ein paar optionalen Angaben, die Du mit Enter überspringst. Das Passwort brauchst Du später nur noch für sudo, nicht mehr zum Anmelden, wähle trotzdem ein gutes. Der zweite Befehl nimmt den Benutzer in die Gruppe sudo auf und gibt ihm damit Administratorrechte. Auf Ubuntu-Images bei vielen Anbietern und auf Raspberry Pi OS existiert schon ein normaler Benutzer mit sudo-Rechten, dann überspringst Du diesen Schritt.
Schritt 2: SSH-Schlüssel statt Passwort
Ein SSH-Schlüssel ist ein Schlüsselpaar aus einer privaten Datei auf Deinem Rechner und einer öffentlichen Datei auf dem Server. Anders als ein Passwort lässt er sich nicht erraten. Hast Du noch keinen Schlüssel, erzeugst Du ihn auf Deinem Mac oder PC, nicht auf dem Server:
ssh-keygen -t ed25519 -C "deinname@laptop"Bestätige den vorgeschlagenen Speicherort mit Enter und vergib eine Passphrase, die den Schlüssel schützt, falls jemand Deine Festplatte in die Hände bekommt. Danach kopierst Du den öffentlichen Schlüssel auf den Server:
ssh-copy-id deinname@SERVER_IPErsetze SERVER_IP durch die IP-Adresse oder den Hostnamen des Servers. Der Befehl fragt ein letztes Mal nach dem Passwort und trägt Deinen Schlüssel in die Datei ~/.ssh/authorized_keys des Benutzers ein. Melde Dich danach testweise an:
ssh deinname@SERVER_IPFragt SSH jetzt nach der Passphrase Deines Schlüssels statt nach dem Passwort des Servers, funktioniert die Anmeldung per Schlüssel. Wirst Du stattdessen wieder nach dem Passwort des Servers gefragt, ist der Schlüssel nicht angekommen. Mach dann auf keinen Fall mit Schritt 3 weiter, sondern prüf zuerst, ob die Datei ~/.ssh/authorized_keys auf dem Server Deinen Schlüssel enthält.
Schritt 3: sshd_config über eine eigene Datei härten
Statt die große Datei /etc/ssh/sshd_config zu bearbeiten, legst Du Deine Einstellungen in einer eigenen kleinen Datei im Ordner /etc/ssh/sshd_config.d/ ab. Das hat zwei Vorteile: Updates von OpenSSH überschreiben Deine Änderungen nicht, und Du siehst auf einen Blick, was Du geändert hast.
Dabei musst Du eine Eigenheit von OpenSSH kennen: Bei jeder Einstellung gilt der erste Wert, den der Dienst liest, und die Dateien im Ordner liest er in alphabetischer Reihenfolge. Manche Cloud-Images legen dort eine Datei 50-cloud-init.conf mit PasswordAuthentication yes ab. Deine Datei bekommt deshalb einen Namen, der davor sortiert:
sudo nano /etc/ssh/sshd_config.d/01-haertung.confTrag diese Zeilen ein und ersetze deinname wieder durch Deinen Benutzernamen:
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
AllowUsers deinnameDamit ist die direkte Anmeldung als root gesperrt, Passwörter werden nicht mehr akzeptiert, und nur noch Dein Benutzer darf sich überhaupt anmelden. Speichere mit Strg + O, bestätige mit Enter und schließe den Editor mit Strg + X.
Prüf die Konfiguration, bevor Du sie aktivierst:
sudo sshd -tGibt der Befehl nichts aus, ist die Syntax in Ordnung. Ob Deine Werte tatsächlich gelten, zeigt die fertige Konfiguration, die der Dienst verwenden würde:
sudo sshd -T | grep -Ei "passwordauthentication|permitrootlogin|allowusers"In der Ausgabe muss passwordauthentication no, permitrootlogin no und allowusers deinname stehen. Steht bei passwordauthentication noch yes, gewinnt eine andere Datei im Ordner. Schau Dir dann mit ls /etc/ssh/sshd_config.d/ an, welche Dateien dort liegen. Jetzt startest Du den Dienst neu:
sudo systemctl restart sshDer Dienst heißt auf Ubuntu, Debian und Raspberry Pi OS ssh. Deine offenen Sitzungen bleiben dabei bestehen. Öffne jetzt ein neues Terminalfenster und melde Dich an. Klappt das, versuch zur Probe eine Anmeldung als root mit ssh root@SERVER_IP. Sie muss mit Permission denied abgelehnt werden.
Den SSH-Port musst Du übrigens nicht ändern. Ein anderer Port reduziert zwar das Rauschen im Protokoll, schützt aber nicht vor einem Angreifer, der alle Ports durchsucht. Auf Ubuntu ab 24.04 ist ein Portwechsel wegen der Socket-Aktivierung von SSH außerdem umständlicher, als viele Anleitungen zeigen.
Schritt 4: Die Firewall ufw
ufw ist eine einfache Oberfläche für die Firewall des Linux-Kernels. Auf Ubuntu ist sie vorinstalliert, auf Debian und Raspberry Pi OS installierst Du sie zuerst:
sudo apt update
sudo apt install ufwJetzt legst Du die Grundregeln fest: Alles Eingehende ist verboten, alles Ausgehende erlaubt, und SSH bekommt eine Ausnahme. Diese Reihenfolge ist wichtig, denn ohne die SSH-Regel sperrst Du Dich mit dem Einschalten aus:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcpLäuft auf dem Server ein Webserver oder Reverse Proxy, gibst Du zusätzlich HTTP und HTTPS frei:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcpDanach schaltest Du die Firewall ein. ufw warnt, dass bestehende SSH-Verbindungen unterbrochen werden könnten, bestätige mit y:
sudo ufw enableMit sudo ufw status verbose siehst Du die aktiven Regeln. Dort stehen jetzt deny (incoming) als Standard und Deine freigegebenen Ports mit ALLOW IN. Öffne zur Kontrolle wieder eine neue SSH-Sitzung. Die Firewall startet ab jetzt bei jedem Neustart automatisch.
Die Docker-Falle: veröffentlichte Ports umgehen ufw
Wenn auf dem Server Docker läuft, gibt es einen Fallstrick, über den fast jeder stolpert: Ports, die Du mit Docker veröffentlichst, sind aus dem Internet erreichbar, auch wenn ufw sie nicht freigibt. Docker schreibt seine eigenen Regeln direkt in iptables, und diese greifen, bevor ufw die Pakete überhaupt zu sehen bekommt. Die Docker-Doku zu Paketfiltern und Firewalls beschreibt dieses Verhalten ausdrücklich.
Ein Container mit ports: - "8080:8080" in der Compose-Datei ist also weltweit auf Port 8080 erreichbar, obwohl ufw status diesen Port gar nicht kennt. Die einfachste Lösung: Binde veröffentlichte Ports an 127.0.0.1, also "127.0.0.1:8080:8080", und lass einen Reverse Proxy auf den Ports 80 und 443 den Zugriff von außen übernehmen. Dann ist der Container nur noch vom Server selbst aus erreichbar. Warum das so ist und wie Du Compose-Dateien entsprechend aufbaust, erklärt der Artikel Docker Compose verstehen: Volumes, Netzwerke und .env.
Schalte auf keinen Fall die iptables-Verwaltung von Docker ab, wie es manche alten Anleitungen empfehlen. Laut Docker-Doku bricht das die Netzwerkfunktionen der Container.
Schritt 5: fail2ban gegen Dauerversuche
Mit Schlüssel-Login kann niemand mehr Dein Passwort erraten, die Anmeldeversuche laufen aber weiter und füllen das Protokoll. fail2ban liest das Protokoll mit und sperrt IP-Adressen, die sich zu oft vergeblich anmelden, für eine Weile in der Firewall. Installiere es zusammen mit dem Modul, das es zum Lesen des Systemprotokolls braucht:
sudo apt install fail2ban python3-systemdDie Einstellungen kommen in eine eigene Datei jail.local, die Updates nicht überschreiben:
sudo nano /etc/fail2ban/jail.localTrag diese Zeilen ein:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
backend = systemdEine IP-Adresse, die innerhalb von 10 Minuten fünfmal scheitert, wird damit für eine Stunde gesperrt. Die Zeile backend = systemd ist wichtig: Debian 13 und Raspberry Pi OS schreiben Anmeldeversuche nicht mehr in die klassische Datei /var/log/auth.log, sondern nur noch ins Systemprotokoll von systemd. Ohne diese Zeile startet fail2ban dort nicht. Auf Ubuntu funktioniert sie genauso.
Hast Du eine feste IP-Adresse zu Hause oder im Büro, ergänze im Abschnitt [DEFAULT] die Zeile ignoreip = 127.0.0.1/8 ::1 DEINE_IP und ersetze DEINE_IP durch Deine Adresse. Dann sperrt Dich fail2ban nie selbst aus. Speichere die Datei, starte fail2ban neu und prüf den Status:
sudo systemctl restart fail2ban
sudo fail2ban-client status sshdDie Ausgabe zeigt unter Filter die bisher erkannten Fehlversuche und unter Actions die gesperrten Adressen. Läuft der Server schon länger im Internet, stehen dort meist nach wenigen Minuten die ersten Einträge.
Schritt 6: Automatische Sicherheitsupdates
Die meisten erfolgreichen Angriffe nutzen bekannte Lücken, für die es längst Updates gibt. Das Paket unattended-upgrades spielt Sicherheitsupdates deshalb täglich automatisch ein. Auf Ubuntu ist es vorinstalliert und meist schon aktiv, auf Debian und Raspberry Pi OS installierst Du es:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgradesDer zweite Befehl fragt, ob stabile Updates automatisch installiert werden sollen. Bestätige mit Ja. Ob die automatischen Updates aktiv sind, siehst Du in der Datei /etc/apt/apt.conf.d/20auto-upgrades:
cat /etc/apt/apt.conf.d/20auto-upgradesDort müssen die Zeilen APT::Periodic::Update-Package-Lists "1"; und APT::Periodic::Unattended-Upgrade "1"; stehen. Einen Probelauf ohne echte Änderungen startest Du mit sudo unattended-upgrade --dry-run --debug.
Updates für den Kernel werden erst nach einem Neustart wirksam. Ob einer nötig ist, zeigt die Datei /var/run/reboot-required, die nach solchen Updates angelegt wird. Wenn Du den Server automatisch nachts neu starten lassen willst, öffne /etc/apt/apt.conf.d/50unattended-upgrades, entferne die Kommentarzeichen // vor den Zeilen Unattended-Upgrade::Automatic-Reboot und Unattended-Upgrade::Automatic-Reboot-Time und setze die Werte auf "true" und "04:00". Für die übrigen Updates, die nicht automatisch kommen, hilft ein kleines Skript wie im Artikel Linux-Updates per Shell-Skript: update-system für Ubuntu und Debian.
Prüfen, was offen ist
Zum Abschluss kontrollierst Du, welche Dienste auf dem Server überhaupt auf Verbindungen warten:
sudo ss -tulpnDie Spalte Local Address:Port zeigt, auf welcher Adresse und welchem Port ein Dienst lauscht, die letzte Spalte, welches Programm es ist. Interessant sind die Einträge mit 0.0.0.0 oder [::] davor, denn diese sind aus dem Netz erreichbar. Einträge mit 127.0.0.1 oder [::1] sind nur lokal erreichbar und unkritisch. Für jeden offenen Dienst solltest Du wissen, wozu er da ist. Einen Dienst, den Du nicht brauchst, schaltest Du mit sudo systemctl disable --now NAME ab.
Den echten Blick von außen bekommst Du nur von einem anderen Rechner aus. Auf dem Mac installierst Du dafür nmap mit brew install nmap und prüfst mit nmap SERVER_IP, welche Ports von außen antworten. Dort dürfen nur die Ports stehen, die Du bewusst freigegeben hast, plus eventuell Docker-Ports, die Du noch an 127.0.0.1 binden musst.
Wenn Du Dich doch ausgesperrt hast
Permission denied (publickey): Der Server nimmt keine Passwörter mehr an, und Dein Schlüssel passt nicht. Prüf mitssh -v deinname@SERVER_IP, welchen Schlüssel Dein Rechner anbietet. Über die Web-Konsole des Anbieters trägst Du den richtigen Schlüssel in~/.ssh/authorized_keysein.- Die Verbindung hängt und läuft in einen Timeout: Das ist fast immer die Firewall ohne SSH-Regel. Melde Dich über die Web-Konsole an und führe
sudo ufw allow 22/tcpaus. - Sofort
Connection refused, obwohl alles stimmte: Eventuell hat fail2ban Deine eigene Adresse gesperrt. Gib sie in der Web-Konsole mitsudo fail2ban-client set sshd unbanip DEINE_IPwieder frei und trag sie unterignoreipein.
Wenn Du noch einen Server für Deine Projekte suchst, bekommst Du bei DigitalOcean (Affiliate Link) einen Droplet mit Ubuntu LTS in wenigen Minuten und mit Recovery Console für genau die Notfälle oben. Als nächsten Schritt lohnt sich ein eigenes VPN, über das Du Dienste erreichst, ohne sie überhaupt ins Internet zu stellen. Wie das geht, zeigt der Artikel WireGuard-VPN auf einem Ubuntu-Droplet einrichten.
Weiterlesen.
-
Web & CodeDocker Compose verstehen: Volumes, Netzwerke und .env
Docker Compose verständlich erklärt: Named Volume oder Bind Mount, Dienstnamen als Hostnamen, .env-Variablen, Healthchecks, sichere Updates und Backups.
Nathan Base -
Raspberry Pi & LinuxLinux-Updates per Shell-Skript: update-system für Ubuntu und Debian
Dein erstes nützliches Shell-Skript: update-system aktualisiert Ubuntu, Debian oder den Raspberry Pi mit einem Befehl, protokolliert und meldet Neustarts.
Nathan Base -
Raspberry Pi & LinuxWireGuard-VPN auf einem Ubuntu-Droplet einrichten
Eigenes VPN mit WireGuard: Server auf einem Ubuntu-Droplet einrichten und Mac oder iPhone verbinden. Schlüssel, Konfiguration, Firewall und Test.
Nathan Base -
Raspberry Pi & LinuxUbuntu 26.04 LTS vom Mac steuern: So klappt es mit RDP
Die Mac-Bildschirmfreigabe spricht VNC, Ubuntu 26.04 nur RDP. Warum alte Anleitungen scheitern und wie Du Ubuntu mit der Windows App vom Mac steuerst.
Nathan Base -
Raspberry Pi & LinuxMit Raspberry Pi und Server Geld verdienen: Was 2026 geht
Lässt sich mit einem Raspberry Pi oder Server 2026 noch Geld verdienen? Mining, Solo-Lotterie und Bandbreite vermieten im ehrlichen Vergleich.
Nathan Base -
Raspberry Pi & LinuxSSH-Abbrüche vom Mac zum Server verhindern
SSH-Verbindungen vom Mac zum Server brechen ab? ServerAliveInterval verhindert Timeouts, tmux hält Sitzungen auch nach Abbrüchen am Leben.
Nathan Base