Nftables Netze absichern
Einleitung
- Nachdem wir ein Hostsystem abgesichert haben, kommen wir nun zum Absichern von Netzen.
- Die Firewall agiert als Vermittler zwischen verschiedenen Netzen.
- In unserem Beispiel haben wir 3 Netzbereiche.
WAN
- Wide Area Net steht für alles was nicht die anderen beiden Netze betrifft
LAN
- Local Area Net steht in der Regel für ein Netz das von aussen nicht erreichbar ist.
- Meist ist es über Network Address Translation (NAT) angebunden.
DMZ
- Demilitarized Zone ist ein Netz welches von aussen erreichbar ist.
- Die Zugriffe werden aber durch die Firewall abgesichert.
- Dort werden meistens Dienste wie Mail oder Web gehostet. Teilweise auch Proxy Server.
Der Plan
Das Grundgerüst
- Wir nutzen unsere Host Firewall als Ausgangsskript
- Wir wollen aber von vorneherein verstärkt mit Variablen arbeiten.
- Dies macht die Skripte universeller.
- vim /etc/nftables.conf
#!/usr/sbin/nft -f
define remote_tcp_ports = { 22,25,53,80,465,443 }
define remote_udp_ports = { 53 }
define local_tcp_ports = { 22,80,443 }
define wandev = ens18
define dmzdev = ens19
define landev = ens20
define lan = 192.168.4.0/24
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state new tcp dport $local_tcp_ports accept
iif "lo" accept
log prefix "--nftables-drop-input--"
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
log prefix "--nftables-drop-forward--"
}
chain output {
type filter hook output priority filter; policy drop;
ct state established,related accept
ct state new oifname "lo" accept
ct state new tcp dport $remote_tcp_ports accept
ct state new udp dport $remote_udp_ports accept
log prefix "--nftables-drop-output--"
}
}
Forwarding
- Damit Pakete weitergeleitet werden können, muss als erstes FORWARDING im Kernel aktiviert werden.
- Aktivierung
- echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
- sysctl -p
Weitere Tabellen
- Eine Skizze über die Reihenfolge der Hooks.
- Als erstes greift der Prerouting-Hook
- Je nachdem wie geroutet wird greift dann entweder Input- oder Fowrward-Hook
- Falls ein lokaler Prozess ein Paket sendet, dann greift der Output-Hook
- Als letztes kann man das Paket mit dem Postrouting-Hook beeinflußen
SNAT
- Rechner in einem LAN können nicht ohne weiteres mit dem WAN kommunizieren, da an die lokale IP-Adresse der Rechner im LAN nicht von außen geroutet werden kann.
- Um eine Internetverbindung aufzubauen, muss die Adresse aus dem LAN in eine öffentliche umgeschrieben werden.
#!/usr/sbin/nft -f
flush ruleset
define remote_tcp_ports = { 22,25,53,80,465,443 }
define remote_udp_ports = { 53 }
define local_tcp_ports = { 22,80,443 }
define wandev = ens18
define dmzdev = ens19
define landev = ens20
define wanip = 10.82.232.11
define lan = 192.168.4.0/24
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iifname "lo" ct state new accept
ct state new tcp dport 22 accept
ct state new icmp type echo-request accept
log prefix "--nftables-drop-input--"
}
chain output {
type filter hook output priority filter; policy drop;
ct state established,related,new accept
log prefix "--nftables-drop-output--"
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
log prefix "--nftables-drop-forward--"
}
}
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname $wandev ip saddr $lan snat ip to $wanip
}
}
Neu verwendete Syntax:
- Definieren einer Variable
define variable_name = value
- Interface des herausgehenden Paketes:
oifname device name
- IPv4-Adresse des Ursprungpaketes
ip saddr <source address>
Portforwarding
- Um auf bestimmte Funktionen eines Rechners hinter einer Firewall zugreifen zu können, müssen die dazugehörenden Ports entsprechend weitergeleitet werden.
- Hierbei kann es ein anderer, nicht-standard Port der Firewall sein.
#!/usr/sbin/nft -f
flush ruleset
flush ruleset
define remote_tcp_ports = { 22,25,53,80,465,443 }
define remote_udp_ports = { 53 }
define local_tcp_ports = { 22,80,443 }
define wandev = ens18
define dmzdev = ens19
define landev = ens20
define wanip = 10.82.232.11
define lan = 192.168.4.0/24
define webserver = 192.168.4.12
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iifname "lo" ct state new accept
ct state new tcp dport 22 accept
ct state new icmp type echo-request accept
log prefix "--nftables-drop-input--"
}
chain output {
type filter hook output priority filter; policy drop;
ct state established,related,new accept
log prefix "--nftables-drop-output--"
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname $wandev meta nfproto ipv4 ip daddr $webserver tcp dport 22 accept
iifname $wandev meta nfproto ipv4 ip daddr $webserver tcp dport 80 accept
log prefix "--nftables-drop-forward--"
}
}
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
meta nfproto ipv4 ip daddr $wanip tcp dport 9922 dnat ip to $webserver:22
meta nfproto ipv4 ip daddr $wanip tcp dport 80 dnat ip to $webserver:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname $wandev ip saddr $lan snat ip to $wanip
}
}
Nun kann man mit:
- ssh user@WEBSERVER -p 9922 auf den Rechner hinter der Firewall zugreifen
- curl WEBSERVER HTTP-Requests an den Rechner hinter der Firewall schicken
Absichern von Netzen
Momentan wird nichts vom LAN zum WAN weitergeleitet. Um nur bestimmte Anwendungen zu erlauben kann man die für diese designierten Ports freischalten.
#!/usr/sbin/nft -f
flush ruleset
define remote_tcp_ports = { 22,25,53,80,465,443 }
define remote_udp_ports = { 53 }
define local_tcp_ports = { 22,80,443 }
define wandev = ens18
define dmzdev = ens19
define landev = ens20
define lan = 192.168.4.0/24
define wanip = 10.82.232.11
define webserver = 192.168.4.12
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iifname "lo" ct state new accept
ct state new tcp dport 22 accept
ct state new icmp type echo-request accept
log prefix "--nftables-drop-input--"
}
chain output {
type filter hook output priority filter; policy drop;
ct state established,related,new accept
log prefix "--nftables-drop-output--"
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname $wandev meta nfproto ipv4 ip daddr $webserver tcp dport 22 accept
iifname $wandev meta nfproto ipv4 ip daddr $webserver tcp dport 80 accept
ct state new iifname $landev oifname $wandev ip saddr $lan icmp type echo-request accept
ct state new iifname $landev oifname $wandev ip saddr $lan udp dport 53 accept
ct state new iifname $landev oifname $wandev ip saddr $lan tcp dport { 25, 53, 80, 143, 443, 465, 993 } accept
log prefix "--nftables-drop-forward--"
}
}
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
meta nfproto ipv4 ip daddr $wanip tcp dport 9922 dnat ip to $webserver:22
meta nfproto ipv4 ip daddr $wanip tcp dport 80 dnat ip to $webserver:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname $wandev ip saddr $lan snat ip to $wanip
}
}
Neu verwendete Syntax:
Bestimmte Ziel-Ports angeben
transport_protocol dport { port number }
Eigene Ketten
Man kann auch Ketten ohne Default Policy oder Hooks erstellen, die mehrere Regeln zusammenfassen. In diese Ketten gelangt man durch die Basisketten.
#!/usr/sbin/nft -f
flush ruleset
define wandev = ens18
define dmzdev = ens19
define landev = ens20
define wanip = 10.82.232.11
define lan = 192.168.4.0/24
define webserver = 192.168.4.12
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state new iifname "lo" accept
ct state new tcp dport 22 accept
ct state new icmp type echo-request accept
log prefix "--nftables-drop-input--"
}
chain output {
type filter hook output priority filter; policy drop;
ct state established,related,new accept
log prefix "--nftables-drop-output--"
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
ct state new iifname $wandev ip daddr $webserver tcp dport 22 accept
ct state new iifname $wandev ip daddr $webserver tcp dport 80 accept
ct state new icmp type echo-request jump lan2wan
udp dport 53 jump lan2wan
tcp dport { 25, 53, 80, 143, 443, 465, 993 } jump lan2wan
log prefix "--nftables-drop-forward--"
}
chain lan2wan {
ct state new iifname $landev oifname $wandev ip saddr $lan accept
}
}
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
ip daddr $wanip tcp dport 9922 dnat ip to $webserver:22
ip daddr $wanip tcp dport 80 dnat ip to $webserver:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname $wandev ip saddr $lan snat ip to $wanip
}
}
Neu verwendete Syntax:
Springe in eine andere Kette
jump target
Limits setzten
- Man kann die Anzahl die eine Regel annimmt zeitlich begrenzen.
- Dafür fügt man ’‘’limit rate’’’ in die Regel ein.
- Falls nur 5 Pakete pro Minute geloggt werden sollen:
#!/usr/sbin/nft -f
flush ruleset
define remote_tcp_ports = { 22,25,53,80,465,443 }
define remote_udp_ports = { 53 }
define local_tcp_ports = { 22,80,443 }
define wandev = ens18
define dmzdev = ens19
define landev = ens20
define lan = 192.168.4.0/24
define wanip = 10.82.232.11
define webserver = 192.168.4.12
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iifname "lo" ct state new accept
ct state new tcp dport 22 accept
ct state new icmp type echo-request accept
limit rate 5/minute log prefix "--nftables-drop-input--"
}
chain output {
type filter hook output priority filter; policy drop;
ct state established,related,new accept
limit rate 5/minute log prefix "--nftables-drop-output--"
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname $wandev meta nfproto ipv4 ip daddr $webserver tcp dport 22 accept
iifname $wandev meta nfproto ipv4 ip daddr $webserver tcp dport 80 accept
icmp type echo-request jump lan2wan
udp dport 53 jump lan2wan
tcp dport { 25, 53, 80, 143, 443, 465, 993 } jump lan2wan
limit rate 5/minute log prefix "--nftables-drop-forward--"
}
chain lan2wan {
ct state new iifname $landev oifname $wandev ip saddr $lan accept
}
}
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
meta nfproto ipv4 ip daddr $wanip tcp dport 9922 dnat ip to $webserver:22
meta nfproto ipv4 ip daddr $wanip tcp dport 80 dnat ip to $webserver:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname $wandev ip saddr $lan snat ip to $wanip
}
}
VPNs ermöglichen
- Damit VPN-Verbindungen von außen aufgebaut werden können müssen die UDP-Ports 500 und 4500 für IPSec-Protokolle offen sein.
- Nachdem eine Secure Association hergestellt wurde, müssen ESP Pakete von der Firewall zugelassen und die entpackten Pakete weitergeleitet werden.
- vim fw.nft
#!/usr/sbin/nft -f
flush ruleset
define wandev = ens18
define landev = ens19
define wanip = 10.82.232.11
define lan = 192.168.4.0/24
define vpn = 192.168.178.0/24
define webserver = 192.168.4.12
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iifname "lo" ct state new accept
ct state new tcp dport 22 accept
ct state new icmp type echo-request accept
iifname $wandev ct state new udp dport 500 jump ipsec
iifname $wandev ct state new udp dport 4500 jump ipsec
iifname $wandev ct state new meta l4proto esp jump ipsec
limit rate 5/minute log prefix "--nftables-drop-input--"
}
chain output {
type filter hook output priority filter; policy drop;
ct state established,related,new accept
limit rate 5/minute log prefix "--nftables-drop-output--"
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname $wandev meta nfproto ipv4 ip daddr $webserver tcp dport 22 accept
iifname $wandev meta nfproto ipv4 ip daddr $webserver tcp dport 80 accept
icmp type echo-request jump lan2wan
udp dport 53 jump lan2wan
tcp dport { 25, 53, 80, 143, 443, 465, 993 } jump lan2wan
iifname $wandev ip saddr $vpn ct state new jump ipsec
limit rate 5/minute log prefix "--nftables-drop-forward--"
}
chain lan2wan {
ct state new iifname $landev oifname $wandev ip saddr $lan accept
}
chain ipsec {
accept
}
}
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
meta nfproto ipv4 ip daddr $wanip tcp dport 9922 dnat ip to $webserver:22
meta nfproto ipv4 ip daddr $wanip tcp dport 80 dnat ip to $webserver:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname $wandev ip saddr $lan snat ip to $wanip
}
}
Der Weg nach außen muss in dem Fall nicht speziell freigeschaltet werden, da unsere Firewall sowieso alle neuen Pakete nach außen durchlässt. Damit aber ESP-Pakete korrekt generiert werden, muss die postrouting-Regel für den Internetzugang der Clients angepasst werden. Ein Blick auf die Routing-Tabelle zeigt nämlich, …
- ip route show table 220
192.168.178.0/24 via 10.82.229.1 dev ens18 proto static src 192.168.4.1
… dass nur Pakete mit einer Ursprungs-IP von 192.168.4.1 an das lokale Netz der VPN-Verbindung geleitet wird. Die bisherige SNAT-Regel schreibt jedoch alle Pakete auf die IP des WAN-Interfaces um. Also müssen wir die Ziel-IPs der VPN ausschließen:
#!/usr/sbin/nft -f
flush ruleset
define wandev = ens18
define landev = ens19
define wanip = 10.82.232.11
define lan = 192.168.4.0/24
define vpn = 192.168.178.0/24
define webserver = 192.168.4.12
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
iifname "lo" ct state new accept
ct state new tcp dport 22 accept
ct state new icmp type echo-request accept
iifname $wandev ct state new udp dport 500 jump ipsec
iifname $wandev ct state new udp dport 4500 jump ipsec
iifname $wandev ct state new meta l4proto esp jump ipsec
limit rate 5/minute log prefix "--nftables-drop-input--"
}
chain output {
type filter hook output priority filter; policy drop;
ct state established,related,new accept
limit rate 5/minute log prefix "--nftables-drop-output--"
}
chain forward {
type filter hook forward priority filter; policy drop;
ct state established,related accept
iifname $wandev meta nfproto ipv4 ip daddr $webserver tcp dport 22 accept
iifname $wandev meta nfproto ipv4 ip daddr $webserver tcp dport 80 accept
icmp type echo-request jump lan2wan
udp dport 53 jump lan2wan
tcp dport { 25, 53, 80, 143, 443, 465, 993 } jump lan2wan
iifname $wandev ip saddr $vpn ct state new jump ipsec
limit rate 5/minute log prefix "--nftables-drop-forward--"
}
chain lan2wan {
ct state new iifname $landev oifname $wandev ip saddr $lan accept
}
chain ipsec {
accept
}
}
table inet nat {
chain prerouting {
type nat hook prerouting priority dstnat; policy accept;
meta nfproto ipv4 ip daddr $wanip tcp dport 9922 dnat ip to $webserver:22
meta nfproto ipv4 ip daddr $wanip tcp dport 80 dnat ip to $webserver:80
}
chain postrouting {
type nat hook postrouting priority srcnat; policy accept;
oifname $wandev ip saddr $lan ip daddr != $vpn snat ip to $wanip
}
}
Zum Vergleich die ‘’tcpdump’’-Analysen bei einem Ping auf einen der Rechner im VPN…
- …ohne die Ziel-IPs auszuschließen: tcpdump -i ens18 icmp or esp
15:30:07.481373 IP fw-linkai > 192.168.178.2: ICMP echo request, id 2845, seq 1, length 64 15:30:08.493319 IP fw-linkai > 192.168.178.2: ICMP echo request, id 2845, seq 2, length 64 15:30:09.517369 IP fw-linkai > 192.168.178.2: ICMP echo request, id 2845, seq 3, length 64
- …wenn die Ziel-IPs auszuschließen werden: tcpdump -i ens18 icmp or esp
15:27:31.091907 IP fw-linkai > 10.82.228.2: ESP(spi=0xc668795e,seq=0x4), length 136 15:27:31.093913 IP 10.82.228.2 > fw-linkai: ESP(spi=0xccf83bd8,seq=0x4), length 136 15:27:31.093913 IP 192.168.178.2 > 192.168.4.1: ICMP echo reply, id 32171, seq 1, length 64 15:27:32.093502 IP fw-linkai > 10.82.228.2: ESP(spi=0xc668795e,seq=0x5), length 136 15:27:32.095404 IP 10.82.228.2 > fw-linkai: ESP(spi=0xccf83bd8,seq=0x5), length 136 15:27:32.095404 IP 192.168.178.2 > 192.168.4.1: ICMP echo reply, id 32171, seq 2, length 64 15:27:33.095023 IP fw-linkai > 10.82.228.2: ESP(spi=0xc668795e,seq=0x6), length 136 15:27:33.096880 IP 10.82.228.2 > fw-linkai: ESP(spi=0xccf83bd8,seq=0x6), length 136 15:27:33.096880 IP 192.168.178.2 > 192.168.4.1: ICMP echo reply, id 32171, seq 3, length 64

