Die moderne Paketfilterung mit nftables bietet unter Linux enorme Performance-Vorteile, bringt jedoch eine konzeptionelle Hürde mit sich: Sie kann, soweit ich weiß, keine dynamischen DNS-Namen direkt in Firewall-Regeln auflösen. Wer ausgehenden Datenverkehr auf bestimmte externe Domains beschränken will, steht vor einem Problem.
Die Lösung für dieses Problem ist ein Named Set. Ein Script löst die Domain regelmäßig per DNS auf und aktualisiert dieses Set automatisch via systemd-Timer. Wir nehmen hier als Beispiel Gitlab.com. Dadurch geben wir nicht die gesamte Cloudflare-IP-Range frei, sondern ausschließlich die IP-Adressen, die gitlab.com liefert.
1. nftables-Set definieren
Zuerst legen wir das Set gitlab_ipv4 und die dazugehörige Filter-Regel in der bestehenden nftables-Konfiguration (z. B. /etc/nft/Foobar.ruleset) an. Das Flag interval ist wichtig, damit das Set IP-Bereiche und einzelne Adressen effizient verarbeiten kann.
table ip filter {
set gitlab_ipv4 {
type ipv4_addr
flags interval
}
chain OUTPUT {
ip daddr @gitlab_ipv4 tcp dport { http, https } ct state new counter accept
}
}
2. Update-Skript erstellen
Das Skript holt sich die aktuellen IPs über getent und schreibt die Adressen direkt in das nftables-Set. Wir erstellen die Datei /usr/local/sbin/domainset.sh:
#!/bin/sh
set -e
DOMAIN="gitlab.com"
ips=(getent ahostsv4 "DOMAIN" | awk '{print \$1}' | sort -u)
# Abbrechen, falls keine IP gefunden wurde
[ -n "\$ips" ] || exit 1
# Set leeren und neu befüllen
nft flush set ip filter gitlab_ipv4
for ip in \$ips; do
nft add element ip filter gitlab_ipv4 "{ \$ip }"
done
Damit das System den Code ausführen darf, machen wir das Skript ausführbar:
chmod 755 /usr/local/sbin/domainset.sh
3. Automatisierung mit systemd
Die zeitgesteuerte Ausführung wird nun über zwei modulare systemd-Komponenten realisiert.
Service
Die Service-Unit unter /etc/systemd/system/domainset.service steuert den eigentlichen Aufruf des Skripts. Sie dient als Bindeglied zwischen dem Timer und der Systemumgebung.
[Unit]
Description=Update external domain nftables set
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/domainset.sh
Abhängigkeiten über After= und Wants=: Da das Skript eine DNS-Abfrage im Internet durchführt, darf es erst starten, wenn die Netzwerkverbindung vollständig aufgebaut ist. network-online.target stellt sicher, dass der DNS-Resolver des Servers bereit ist.
Der [Service]-Block: Definiert die Art der Ausführung. Type=oneshot ist ideal für Skripte, die eine kurze Aufgabe erledigen und sich danach sofort wieder beenden. Erst wenn ExecStart= erfolgreich durchgelaufen ist, gilt die Unit für systemd als erledigt.
Timer
Der Timer unter /etc/systemd/system/domainset.timer übernimmt ausschließlich die Zeitplanung und stößt den oben definierten Service in festen Intervallen an.
[Unit]
Description=Update external domain nftables set every 15 minutes
[Timer]
OnBootSec=1min
OnUnitActiveSec=15min
Unit=domainset.service
[Install]
WantedBy=timers.target
Der [Timer]-Block: Bestimmt das “Wann”. Im Gegensatz zu festen Uhrzeiten nutzen wir hier relative Intervalle. OnBootSec=1min startet das Skript das erste Mal genau eine Minute nach dem Systemstart. OnUnitActiveSec=15min sorgt dafür, dass der Service danach alle 15 Minuten erneut getriggert wird. Unit= verknüpft diesen Timer explizit mit unserem Update-Service.
Der [Install]-Block: Das ist der Autostart-Eintrag für den Timer. WantedBy=timers.target sorgt dafür, dass die Zeitschaltuhr beim Systemstart automatisch geladen und aktiviert wird. Ohne diesen Block würde der Timer nach einem Reboot inaktiv bleiben.
Timer aktivieren und testen
Damit systemd die neuen Konfigurationen einliest und den Timer startet, führen wir folgende Befehle aus:
systemctl daemon-reload
systemctl enable --now domainset.timer
Administration & Fehlersuche
Ein großer Vorteil gegenüber Cronjobs ist die einfache Überwachung mit Standard-Befehlen:
# Status des Timers und die nächste geplante Ausführung anzeigen
systemctl status domainset.timer
systemctl list-timers domainset.timer
# Den Update-Service manuell triggern (bequemer Test)
systemctl start domainset.service
# Inhalt des nftables-Sets live überprüfen
nft list set ip filter gitlab_ipv4
# Logs der Skriptausführung auslesen
journalctl -u domainset.service
Dateiübersicht
Nach der Einrichtung sind folgende Dateien auf unserem System aktiv:
/etc/nft/Foobar.ruleset(nftables-Konfiguration)/usr/local/sbin/domainset.sh(Update-Script)/etc/systemd/system/domainset.service(systemd-Dienst)/etc/systemd/system/domainset.timer(systemd-Zeitsteuerung)
Wichtige Hinweise für die Praxis
⚠️ Reines IPv4-Szenario: Diese Konfiguration ist auf
type ipv4_addrausgelegt. Falls Verbindungen zu GitLab auch über IPv6 laufen sollen, muss ein analoges Set mittype ipv6_addrin einerip6- oderinet-Tabelle angelegt werden. Das Skript muss in diesem Fall umahostsv6erweitert werden.