18.5. Firewall-Setup mit nftables

Mit nftables wurde die Unterstützung einer Tabelle names ”inet” eingeführt in welcher Regeln für IPv4/IPv6 gleichzeitig gelten

18.5.1. Präparation zur Nutzung von nftables

Installieren einer Linux-Distribution, welche die Unterstützung für nftables bereits eingebaut hat. Beim Schreiben dieses Absatzes (Mai 2014) war mindestens Fedora Rawhide (Vorläufer der Version 21) mit entsprechendem Support und nftables version 0.2.0 versehen.

18.5.2. Basis-nftables Konfiguration

Laden der Kernel-Module:

# modprobe nf_tables
# modprobe nf_tables_ipv4
# modprobe nf_tables_ipv6
# modprobe nf_tables_inet 

Löschen der Regeln in iptables and ip6tables um Interferenzen zu vermeiden:

# iptables -F
# ip6tables -F

Erzeugen der Filter-Tabelle:

# nft add table inet filter 

Erzeugen einer input chain in der Filter-Tabelle:

# nft add chain inet filter input { type filter hook input priority 0 \; }

18.5.3. Einfache Filter-Policy mit nftables

18.5.3.1. Konfiguration

Erlauben von Paketen, die zu existierenden Einträgen in der Connection-Tracking-Tabelle gehören

# nft add rule inet filter input ct state established,related counter accept 

Erlauben von IPv4 und IPv6 ICMP echo-request (aka ping)

# nft add rule inet filter input meta nfproto ipv4 icmp type { echo-request } counter accept
# nft add rule inet filter input meta nfproto ipv6 icmpv6 type echo-request counter accept 

Erlauben einiger wichtiger IPv6 ICMP Pakete, ohne Zähler, dafür mit Hop-Limit-Prüfung (erhöht die Sicherheit)

# nft add rule inet filter input meta nfproto ipv6
¬  icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} ip6 hoplimit 1 accept
# nft add rule inet filter input meta nfproto ipv6
¬  icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} ip6 hoplimit 255 counter accept

Erlauben von eingehenden SSH-Verbindungen für IPv4 und IPv6

# nft add rule inet filter input tcp dport 22 ct state new tcp flags \& \(syn \| ack\) == syn counter accept

Reject/drop anderer Pakete

# nft add rule inet filter input tcp dport 0-65535 reject
# nft add rule inet filter input udp dport 0-65535 counter drop
# nft add rule inet filter input counter drop

18.5.3.2. Ergebnis

Tabelle für IP unabhängigen Filter

table inet filter {
	chain input {
		 type filter hook input priority 0;
		 ct state established,related counter packets 0 bytes 0 accept
		 ip protocol icmp icmp type { echo-request} counter packets 0 bytes 0 accept
		 ip6 nexthdr ipv6-icmp icmpv6 type echo-request counter packets 0 bytes 0 accept
		 ip6 nexthdr ipv6-icmp ip6 hoplimit 1 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} accept
		 ip6 nexthdr ipv6-icmp ip6 hoplimit 255 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} accept
		 tcp dport ssh ct state new tcp flags & (syn | ack) == syn counter packets 0 bytes 0 accept
		 tcp dport >= 0 tcp dport <= 65535 counter packets 0 bytes 0 reject
		 udp dport >= 0 udp dport <= 65535 counter packets 0 bytes 0 drop
		 log prefix counter packets 0 bytes 0 drop
	}
} 

18.5.3.3. Tipps für's Loggen

Für Logging wird ein zusätzliches Kernelmodul benötigt:

# modprobe xt_LOG

ACHTUNG, MOMENTAN KANN DER LOG-LEVEL NICHT ANGEGEBEN WERDEN, dadurch werden nftables-Ereignisse mit Log-Level kern.emerg ausgegeben - ES BESTEHT DIE GEFAHR, DASS DIE KONSOLE DADURCH ÜBERFLUTET WIRD!

Für erste Tests mit der Log-Option kann es nützlich sein, das Loggens für emergency-Ereignisse in z.B. /etc/rsyslog.conf zu deaktivieren mit Hilfe eines ”#” am Anfang der Zeile und Neustart des logging-Daemons

#*.emerg    :omusrmsg:* 

Regel von oben, welche SSH auf Port 22 erlaubt, nun mit Logging:

# nft add rule inet filter input tcp dport 22 ct state new tcp flags \& \(syn \| ack\) == syn log prefix \"inet/input/accept: \" counter accept

18.5.4. Filter-Policy mit nftables unter Benutzung der Tablellen ”ip”, ”ip6” und ”inet”

Wie oben schon beschrieben, wenn die Regeln in den einzelnen Tabellen konfiguriert werden, muss gesichert sein, dass frühere ”accepts” nicht aufgehoben werden. Eine einfache Lösung ist die Benutzung von Markierungen. Regeln, die Pakete erlauben, setzen die Marke mit ”meta mark set xxxx”. Eine generische Regel erlaubt Pakete mit gesetzter Marke ”mark xxxx”. Beispiel für ein resultierendes Filter-Regelwerk:

# for table in ip ip6 inet; do nft list table $table filter; done
table ip filter {
	chain input {
		 type filter hook input priority 0;
		 ct state established,related counter packets 241 bytes 25193 accept
		 counter packets 2 bytes 120 mark 0x00000100 accept
		 icmp type { echo-request} counter packets 0 bytes 0 meta mark set 0x00000100 accept
	}
}
table ip6 filter {
	chain input {
		 type filter hook input priority 0;
		 ct state established,related counter packets 14 bytes 4077 accept
		 counter packets 4 bytes 408 mark 0x00000100 accept
		 icmpv6 type echo-request counter packets 1 bytes 104 meta mark set 0x00000100
		 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} counter packets 2 bytes 224 meta mark set 0x00000100 accept
	}
}
table inet filter {
	chain input {
		 type filter hook input priority 0;
		 ct state established,related counter packets 307 bytes 31974 accept
		 counter packets 6 bytes 528 mark 0x00000100 accept
		 tcp dport ssh ct state new tcp flags & (syn | ack) == syn log prefix "inet/input/accept: " meta mark set 0x00000100 counter packets 3 bytes 200 accept
		 log prefix "inet/input/reject: " counter packets 0 bytes 0 reject
	}
}