nftables auf Dedicated Servern mit zeitgesteuertem Rückrollen ändern

nftables auf Dedicated Servern mit zeitgesteuertem Rückrollen ändern

nftables auf Dedicated Servern mit zeitgesteuertem Rückrollen ändern

Bei der Remote-Verwaltung eines Dedicated Servers kommt es nach Firewall-Änderungen häufig zu einer Fehleinschätzung: Weil sich in der laufenden SSH-Sitzung noch Befehle ausführen lassen, gelten die neuen Regeln als erfolgreich geprüft. Tatsächlich kann das Gegenteil zutreffen: Die Verbindungsverfolgung hält die bestehende Sitzung offen, während neue Verbindungen bereits blockiert werden. Ein zuverlässiger Ablauf besteht deshalb darin, zunächst den aktuellen Zustand zu sichern und die Wiederherstellung vorzubereiten, dann die Regeln zu laden und anschließend den Verwaltungszugang über eine unabhängige Anmeldung zu prüfen.

Dieser Artikel zeigt mit Debian 12, nftables und systemd eine gezielte Änderung, die ausschließlich die zulässigen SSH-Quelladressen einschränkt. Die Verbindungsadresse und Zugangsdaten für AMDSSH-Server finden Sie nach der Zahlung in der Konsole. Bei mehreren IPv4-Adressen verwendet SSH die erste Adresse. Das Beispiel setzt weder einen bestimmten Kontonamen noch einen bestimmten Port voraus und entwirft keine vollständige Regelung für die Ports der übrigen Dienste.

1. Zunächst die Zuständigkeit für die Regeln klären

Die folgenden Schritte setzen ein Konto mit sudo-Berechtigungen, ein installiertes nftables und eine einheitliche Regelverwaltung durch natives nftables voraus. Wenn Containernetzwerke, Virtualisierungsbrücken oder andere Firewall-Werkzeuge dynamisch Regeln erzeugen, dürfen Sie die Wiederherstellung eines vollständigen Snapshots nicht unverändert übernehmen: Beim Rückrollen könnten auch Einträge überschrieben werden, die diese Werkzeuge während der Änderung hinzugefügt haben. Klären Sie in diesem Fall zuerst, welches Werkzeug die Regeln verwaltet, und verwenden Sie dessen Änderungsverfahren.

Prüfen Sie zunächst den aktuellen Zustand:

sudo nft list ruleset
sudo systemctl cat nftables.service
command -v nft

Ermitteln Sie die vorhandenen Regeln, die beim Start geladene Datei und den tatsächlichen Pfad zu nft. Dieser Artikel verwendet eine separate Tabelle namens inet ops_guard. Falls bereits eine Tabelle mit diesem Namen existiert, müssen Sie deren Zweck prüfen, damit keine Regeln anderer Aufgaben überschrieben werden.

Der Rückrolltimer behandelt ausschließlich diese Änderung der Firewall-Regeln. Er kann keine Störungen beheben, die durch gleichzeitige Änderungen am Routing, am SSH-Dienst oder an der Quelle des Verwaltungszugangs entstehen. Ändern Sie pro Vorgang nur eine Variable.

Die tatsächlichen SSH-Verbindungsparameter auslesen

Führen Sie die folgenden Befehle in der Bash der aktuellen Remote-Sitzung aus. Wechseln Sie vorher nicht in eine andere Sitzung, in der die Umgebungsvariablen der Verbindung fehlen:

test -n "$SSH_CONNECTION" || exit 1
read -r ADMIN_IP CLIENT_PORT SERVER_IP SSH_PORT <<< "$SSH_CONNECTION"
case "$ADMIN_IP" in
  *:*) printf '%s\n' 'IPv4 SSH session required'; exit 1 ;;
esac
printf 'source=%s destination=%s port=%s\n' \
  "$ADMIN_IP" "$SERVER_IP" "$SSH_PORT"

Die ermittelte Adresse ist die Verbindungsquelle aus Sicht des Servers. Das kann die externe Adresse des Büronetzwerks oder die Adresse eines Sprungservers sein, nicht die interne Adresse Ihres Arbeitsplatzrechners. Falls sich die externe Adresse ändern kann, richten Sie zunächst einen stabilen Verwaltungszugang ein. Dieses Beispiel setzt voraus, dass die aktuelle Sitzung IPv4 verwendet. Erzeugen Sie die Regeln nicht aus einer IPv6-Sitzung heraus, um dann trotz dieser Voraussetzung fortzufahren.

2. Einen Snapshot sichern und die automatische Wiederherstellung planen

Der Snapshot muss einen Befehl zum Löschen der alten Regeln enthalten. Andernfalls können bei der Wiederherstellung Konflikte mit bereits vorhandenen Objekten auftreten. Das Verzeichnis darf nur für Administratoren zugänglich sein, damit gewöhnliche Konten die in den Regeln enthaltene Adressplanung nicht einsehen können.

sudo install -d -m 700 /root/firewall-change
sudo sh -c '
set -e
umask 077
{
  printf "flush ruleset\n"
  nft list ruleset
} > /root/firewall-change/before.nft
'
sudo nft -c -f /root/firewall-change/before.nft

Die Option -c prüft die Regeln, ohne sie zu laden. Damit lassen sich Syntaxfehler und fehlerhafte Verweise in den Regeln erkennen; die weitere Erreichbarkeit per Remote-Anmeldung ist dadurch jedoch nicht nachgewiesen. Schlägt die Prüfung fehl, müssen Sie die Änderung abbrechen. Umgehen Sie den Fehler nicht, um trotzdem fortzufahren.

Nachdem Sie sichergestellt haben, dass kein gleichnamiger Auftrag aus einem früheren Vorgang vorhanden ist, planen Sie die Wiederherstellung in drei Minuten:

sudo systemctl status ops-firewall-rollback.timer
sudo systemd-run \
  --unit=ops-firewall-rollback \
  --on-active=3m \
  "$(command -v nft)" \
  -f /root/firewall-change/before.nft
sudo systemctl list-timers ops-firewall-rollback.timer

Dass die erste Abfrage eine nicht vorhandene Unit meldet, ist normal. Existiert bereits ein Auftrag, klären Sie zunächst dessen Herkunft. Die drei Minuten sind die Wartezeit bis zum Rückrollen bei diesem Vorgang und keine Zusage zur Bereitstellung eines Dienstes. Bevor Sie die Regeln laden, müssen Sie sehen können, dass der Timer zur Ausführung eingeplant ist. Ändern Sie währenddessen keine anderen Netzwerkregeln, da die vollständige Wiederherstellung auch diese Änderungen rückgängig machen würde.

3. Die Verwaltungsquellen mit einer separaten Tabelle einschränken

Legen Sie bei der ersten Verwendung eine leere Tabelle an. Führen Sie den Befehl nur aus, nachdem Sie bestätigt haben, dass keine gleichnamige Tabelle existiert. Ist die für diesen Vorgang vorgesehene Tabelle bereits vorhanden, überspringen Sie diesen Schritt:

sudo nft add table inet ops_guard

Erstellen Sie anschließend die Datei mit dem vorgesehenen Regelsatz:

sudo tee /root/firewall-change/candidate.nft > /dev/null <<EOF
delete table inet ops_guard
table inet ops_guard {
  chain input {
    type filter hook input priority 10; policy accept;
    iifname "lo" accept
    ct state established,related accept
    ip saddr $ADMIN_IP tcp dport $SSH_PORT accept
    tcp dport $SSH_PORT counter drop
  }
}
EOF
sudo chmod 600 /root/firewall-change/candidate.nft
sudo nft -c -f /root/firewall-change/candidate.nft
sudo nft -f /root/firewall-change/candidate.nft
sudo nft -a list table inet ops_guard

Das Löschen und Neuerstellen aus dieser Datei erfolgt innerhalb einer einzigen nftables-Transaktion. Die Regeln werden also nicht erst geleert und dann einzeln hinzugefügt. Bei jedem Laden der Kandidatendatei muss die separate Tabelle bereits vorhanden sein. Die Datei kann deshalb nicht unmittelbar als Konfiguration für einen Kaltstart dienen.

Die Regeln erlauben Loopback-Verkehr und bereits bestehende Verbindungen. Danach gestatten sie der aktuellen IPv4-Verwaltungsquelle den Zugriff auf den tatsächlich verwendeten SSH-Port und blockieren diesen Port für andere Quellen. Die letzte Regel erfasst auch IPv6-Verkehr. Dieses Beispiel erlaubt daher keine neuen SSH-Verbindungen über IPv6. Wenn Sie einen Verwaltungszugang über IPv6 benötigen, müssen Sie vorher eine ausdrückliche Freigaberegel mit ip6 saddr ergänzen und diese gesondert testen.

Andere Dienstports werden von dieser Tabelle nicht eingeschränkt. Ein accept in dieser Tabelle kann außerdem kein drop in einer anderen Basiskette aufheben: Vorhandene Regeln können neue Verbindungen weiterhin blockieren. Verstehen Sie dieses Beispiel nicht als vollständige Sicherheitsbaseline und löschen Sie auf dieser Grundlage keinesfalls bestehende Regeln für Ihre Dienste.

4. Anhand einer unabhängigen neuen Verbindung über die Übernahme entscheiden

Lassen Sie die ursprüngliche Sitzung geöffnet und starten Sie von der erlaubten Quelle aus eine weitere Verbindung. Falls der Client Verbindungsmultiplexing verwendet, müssen Sie es deaktivieren. Andernfalls könnte ein neues Fenster weiterhin die alte TCP-Verbindung nutzen.

Führen Sie die folgenden Befehle in der Bash auf dem Client aus und geben Sie die in der Konsole bereitgestellten Parameter interaktiv ein:

read -r -p 'Host: ' HOST
read -r -p 'User: ' LOGIN
read -r -p 'Port: ' PORT
ssh -o ControlMaster=no -o ControlPath=none \
  -p "$PORT" "$LOGIN@$HOST"

Die Abnahme muss mindestens drei Prüfungen umfassen:

Ein bloßer Fehler bei der Passwort- oder Schlüsselauthentifizierung beweist nicht, dass die Quellenbeschränkung versagt hat. Stellen Sie fest, auf welcher Ebene die Verbindung abgewiesen wurde. Lockern Sie die Regeln nicht vorübergehend für den Test.

Scheitert die unabhängige Anmeldung, lassen Sie den aktuellen Zustand unverändert und warten Sie auf das Rückrollen, statt fortlaufend weitere Freigaberegeln hinzuzufügen. Prüfen Sie nach der Wiederherstellung die Protokolle des zum Timer gehörenden Dienstes und untersuchen Sie anschließend Quelladresse, Port und den Einfluss anderer Ketten. Stoppen Sie den Timer, sobald alle Prüfungen erfolgreich waren:

sudo systemctl stop ops-firewall-rollback.timer
sudo systemctl is-active ops-firewall-rollback.timer

Vergewissern Sie sich außerdem, dass der Wiederherstellungsdienst noch nicht gestartet wurde. Ist der Timer gerade abgelaufen, müssen Sie die aktiven Regeln erneut auslesen. Ein erfolgreicher Stoppbefehl beweist nicht, dass die Kandidatenkonfiguration noch aktiv ist.

5. Persistente Speicherung und Startprüfung getrennt durchführen

Kümmern Sie sich erst um die Startdatei, nachdem die aktiven Regeln erfolgreich geprüft wurden. Ermitteln Sie anhand der zuvor untersuchten Dienstkonfiguration, welcher Pfad tatsächlich geladen wird. Unter Debian 12 ist /etc/nftables.conf üblich; ein angepasster Dienst kann jedoch einen anderen Pfad verwenden.

Wenn die Regeln stabil sind und kein anderer Regelmanager Änderungen schreibt, exportieren Sie den vollständigen aktiven Regelsatz in eine Datei mit flush ruleset, prüfen Sie diese mit nft -c -f und sichern Sie die ursprüngliche Startdatei, bevor Sie sie ersetzen. Speichern Sie nicht nur die separate Tabelle, da nach einem Neustart sonst bestehende Regeln für die Dienste fehlen könnten.

Die persistente Speicherung ist eine zweite Änderung. Planen Sie dafür erneut ein Rückrollen ein, laden Sie die vollständige Startdatei und wiederholen Sie die Tests mit einer unabhängigen Anmeldung. Verketten Sie das Aktivieren des Dienstes, das Ersetzen der Konfiguration und den Neustart des Servers nicht zu einem einzigen Befehl. Planen Sie die Abnahme nach einem Neustart gesondert und prüfen Sie dabei die Regeln, die Anmeldung von der erlaubten Quelle und die Erreichbarkeit der Dienste.

Dokumentieren Sie abschließend die tatsächliche Quelle, den SSH-Port, die Kandidatendatei, den Snapshot und die Prüfergebnisse. Als belastbarer Nachweis genügt nicht „Der Befehl war erfolgreich“. Drei Bedingungen müssen gleichzeitig erfüllt sein: Eine neue Verbindung funktioniert, die vorgesehene Blockierung greift wie erwartet und die Startkonfiguration kann die Regeln reproduzieren.

Häufig gestellte Fragen

Reicht eine weiterhin offene SSH-Sitzung, um den Rückrolltimer zu stoppen?

Nein. Die Zustandsverfolgung kann bestehende Verbindungen erhalten. Vor dem Stoppen des Timers muss eine unabhängige neue Verbindung aus einer erlaubten Quelle einschließlich Anmeldung funktionieren.

Schließt das Beispiel auch die Anwendungsports?

Nein. Es begrenzt nur neue Verbindungen zum tatsächlich verwendeten SSH-Port. Andere Ports unterliegen weiterhin den vorhandenen Regeln; das Beispiel ist keine vollständige Default-Deny-Firewall.

Dedizierter Server mit EPYC 7543Pab $450.2/Monat

Von der Ressourcenplanung zur Serverkonfiguration

Modell und Optionen werden monatlich abgerechnet. Eine vollständige Kostenübersicht steht auf der Konfigurationsseite; die Zugangsdaten sind nach der Bereitstellung in der Konsole verfügbar.