Absicherung eines DNS Servers mit nftables
Einleitung
Ziel war es, den in Konfiguration eines Secondary DNS mit Plex erstellten DNS Server über nftables sauber abzusichern, so dass Dienste nur von entsprechenden Quellen erreicht werden können.
Firewall konfigurieren
Erforderliche Ports:
Dienst | Quelle | Freigabe |
|---|---|---|
DNS | Internet | UDP/53 |
DNS | Internet | TCP/53 |
SSH | definierte Admin-IP-Adressen | TCP/22 |
RNDC | ausschließlich Plesk-Primary | TCP/953 |
alles andere eingehend | beliebig | blockiert |
ausgehender Verkehr | Server | erlaubt |
DNS muss öffentlich sowohl über UDP als auch über TCP erreichbar sein. RFC 9210 verlangt für DNS-Server die Unterstützung beider Transportprotokolle. TCP/53 darf daher nicht ausschließlich auf den Primary eingeschränkt werden.
Platzhalter festlegen
Beispielwerte:
Plesk-Primary: 203.0.113.10
Admin-IP 1: 198.51.100.25
Admin-Netz: 198.51.100.64/28
SSH-Port: 22
Secondary-DNS-IP: 192.0.2.20
Die Adressen 192.0.2.0/24, 198.51.100.0/24 und 203.0.113.0/24 sind Beispiel Adressen. Sie müssen zwingend ersetzt werden.
Beim Plesk-Primary muss die tatsächlich am Secondary sichtbare Quelladresse eingetragen werden. Befindet sich Plesk hinter NAT, ist das die öffentliche NAT-Adresse, nicht die interne Serveradresse.
nftables installieren
sudo apt update
sudo apt install -y nftables
Prüfen, ob bereits ein anderes Firewall-Frontend läuft:
systemctl is-active ufw
systemctl is-active firewalld
systemctl is-active nftables
Nicht gleichzeitig ufw, firewalld und eine manuell verwaltete nftables.conf verwenden. Das führt zu schwer nachvollziehbaren beziehungsweise überschriebenen Regeln.
Firewall-Konfiguration
Vorhandene Konfiguration sichern:
sudo cp -a /etc/nftables.conf "/etc/nftables.conf.backup-$(date +%F-%H%M%S)"
Datei bearbeiten:
sudo nano /etc/nftables.conf
Vollständiger Inhalt:
#!/usr/sbin/nft -f
#
# Dedizierter Secondary-DNS-Server
# Debian 13 / nftables
#
# WARNUNG:
# Alle Beispiel-IP-Adressen vor Aktivierung ersetzen
#
flush ruleset
define SSH_PORT = 22
table inet firewall {
#
# IPv4-Adressen und Netze, von denen SSH erlaubt wird
#
set ssh_admin_ipv4 {
type ipv4_addr
flags interval
elements = {
198.51.100.25,
198.51.100.64/28
}
}
#
# IPv6-Adressen und Netze, von denen SSH erlaubt wird
#
set ssh_admin_ipv6 {
type ipv6_addr
flags interval
elements = {
2001:db8:100::25,
2001:db8:200::/64
}
}
#
# Öffentliche Quelladresse des Plesk-Primary
# Nur diese Adresse darf RNDC/TCP 953 verwenden
#
set plesk_primary_ipv4 {
type ipv4_addr
flags interval
elements = {
203.0.113.10
}
}
chain input {
type filter hook input priority filter;
policy drop;
#
# Ungültige Pakete verwerfen.
#
ct state invalid counter drop
#
# Antworten und zugehörige Verbindungen zulassen
# Dadurch bleibt auch die bestehende SSH-Sitzung beim
# Aktivieren des Rulesets zunächst bestehen
#
ct state established,related counter accept
#
# Loopback-Kommunikation zulassen.
#
iifname "lo" counter accept
#
# Gefälschte Loopback-Quelladressen auf externen Interfaces
# verwerfen.
#
iifname != "lo" ip saddr 127.0.0.0/8 counter drop
iifname != "lo" ip6 saddr ::1 counter drop
#
# ICMP für Fehlerbehandlung
#
ip protocol icmp counter accept
#
# ICMPv6 ist unter IPv6 unter anderem für Neighbor Discovery
# und Path-MTU-Discovery erforderlich
#
meta l4proto ipv6-icmp counter accept
#
# Öffentlicher autoritativer DNS-Dienst
# Gilt durch die inet-Tabelle für IPv4 und IPv6
#
udp dport 53 counter accept
tcp dport 53 counter accept
#
# SSH ausschließlich von definierten IPv4-Quellen
#
ip saddr @ssh_admin_ipv4 \
tcp dport $SSH_PORT \
ct state new \
counter accept
ip6 saddr @ssh_admin_ipv6 \
tcp dport $SSH_PORT \
ct state new \
counter accept
#
# BIND-RNDC ausschließlich vom Plesk-Primary
#
ip saddr @plesk_primary_ipv4 \
tcp dport 953 \
ct state new \
counter accept
#
# Rate Limit auf Management-Ports
# protokollieren und anschließend verwerfen
#
tcp dport { $SSH_PORT, 953 } \
ct state new \
limit rate 6/minute burst 10 packets \
log prefix "nft-denied-mgmt: " level info \
counter drop
#
# OPTIONAL: Sollte der Server seine IP per DHCP bekommen, muss DHCP
# in der Firewall freigegeben werden.
#
udp sport 67 udp dport 68 counter accept
udp sport 547 udp dport 546 counter accept
#
# Alles Weitere wird durch policy drop verworfen
#
}
chain forward {
type filter hook forward priority filter;
policy drop;
}
chain output {
type filter hook output priority filter;
policy accept;
}
}
Konfiguration prüfen
nft -c prüft die Gültigkeit, ohne die Regeln anzuwenden.
sudo nft -c -f /etc/nftables.conf
Bei erfolgreicher Prüfung gibt der Befehl normalerweise nichts aus.
Zusätzlich kann noch ein Check ausgeführt werden:
sudo nft --check --file /etc/nftables.conf
Sichere Aktivierung bei Remote-Zugriff
Vor dem Anwenden eine automatische Rücknahme planen:
sudo systemd-run --unit=nft-rollback --on-active=5m /usr/sbin/nft flush ruleset
Nimmt nach 5min automatisch mit einem flush ruleset das Regelwerk zurück.
Danach Regeln anwenden:
sudo nft -f /etc/nftables.conf
Aktives Ruleset anzeigen:
sudo nft list ruleset
Nun in einem zweiten Terminalfenster eine neue SSH-Verbindung aufbauen.
Wenn die neue Verbindung funktioniert, Rollback stoppen:
sudo systemctl stop nft-rollback.timer
sudo systemctl reset-failed nft-rollback.service 2>/dev/null || true
Firewall dauerhaft aktivieren:
sudo systemctl enable nftables --now
Status prüfen:
sudo systemctl status nftables
Da die Regeln bereits manuell geladen wurden, ist an dieser Stelle kein Neustart notwendig.
Neustartfestigkeit prüfen
sudo systemctl restart nftables
sudo nft list ruleset
Danach erneut eine neue SSH-Sitzung öffnen.
Abschließend sollte ein kontrollierter Neustart getestet werden:
sudo reboot
Nach dem Neustart:
systemctl status nftables
sudo nft list ruleset
Ports und Listener kontrollieren
sudo ss -lntup
Erwartet werden insbesondere:
UDP 0.0.0.0:53
TCP 0.0.0.0:53
UDP [::]:53
TCP [::]:53
TCP <Secondary-IP>:953
TCP 0.0.0.0:22
TCP [::]:22
Dass ss SSH auf allen Adressen anzeigt, ist nicht automatisch ein Problem. Die Erreichbarkeit wird durch nftables eingeschränkt. RNDC sollte idealerweise nicht auf 0.0.0.0:953 lauschen, sondern nur auf der Secondary-IP und gegebenenfalls Loopback.
Funktionstests
DNS über UDP
Von einem externen System:
dig @192.0.2.20 example.org SOA +norecurse
DNS über TCP
dig @192.0.2.20 example.org SOA +tcp +norecurse
Beide Abfragen müssen eine autoritative Antwort liefern. Das flags-Feld sollte aa enthalten.
SSH von erlaubter Adresse
nc -vz 192.0.2.20 22
Oder direkt:
ssh admin@192.0.2.20
SSH von nicht erlaubter Adresse
Von einem anderen Internetanschluss:
nc -vz -w 5 192.0.2.20 22
Der Zugriff sollte in einen Timeout laufen.
RNDC vom Plesk-Primary
Auf dem Plesk-Server:
nc -vz 192.0.2.20 953
Der Port muss erreichbar sein.
Firewall-Logs prüfen
Abgewiesene SSH- und RNDC-Verbindungen werden mit dem Präfix nft-denied-mgmt protokolliert:
sudo journalctl -k -g 'nft-denied-mgmt'
Live-Ansicht:
sudo journalctl -k -f
Oder:
sudo dmesg --follow
Die Protokollierung ist bewusst limitiert, damit Portscanner nicht unkontrolliert das Journal füllen.
No comments to display
No comments to display