Netfilter

Submitted by admin on So, 11.12.2016 - 12:30

1466329163232-netfilter-logo.png

Wieder einmal ändert sich die Firewall Plattform für Linux - und es war überfällig. Wer den Packetfilter von FreeBSD oder OpenBSD kennt, hat iptables oder damals ipchains bei Linux eher mit spitzen Fingern angefasst. Auch mit iptables kommt man zum Ziel, nur eben umständlicher und mit einem schwerer lesbaren Regelwerk.

Stand der Dinge

Eine kleine Übersicht, wo die wichtigsten Linux Distributionen derzeit stehen (Stand 19.6.2016):

  • Fedora 23: Hier ist alles State of the Art, wer Selinux und RPM mag, kommt mit den aktuellen Fedora Versionen auf seine Kosten.
  • Ubuntu 16.04: Ebenfalls alles aktuell, da es sich um eine LTS Version handelt, werden auch noch neuere Kernel kommen, so dass neue Features hier mit Langzeitsupport zu haben sind.
  • Debian: Der Kernel 3.16 kann zwar schon Netfilter, allerdings sind in den 4er Versionen des Kernels schon neue Funktionen gekommen
  • Arch Linux: Alles auf aktuellem Stand
  • RedHat/CentOS: Ab der Version 7 kann der Kernel zwar Netfilter, aber das Tool nft fehlt.

Der beste Kompromiss bei Netfilter zwischen Stabilität und Funktionsumfang dürfte derzeit also Ubuntu 16.04 sein. Bei RedHat/CentOS ist selber compilieren notwendig.

Systemd

Der Trend geht eindeutig zu Systemd, daher hier ein kleines Skript, das den Systemd dazu veranlasst die Netfilter Regeln an der richtigen Stelle zu laden. Der Systemd ist hier gegenüber SysV Init tatsächlich deutlich pflegeleichter.

[Unit]
Description=nft
Documentation=man:nft(8)
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/nft -f /etc/iptables/nft.ipv4 ; /usr/sbin/nft -f /etc/iptables/nft.ipv6
ExecStop=/usr/sbin/nft flush ruleset

[Install]
WantedBy=multi-user.target

Es wird erwartet, dass die Files /etc/iptables/nft.ipv6 und /etc/iptables/nft.ipv4 vorhanden sind. Der Pfadname /etc/iptables hat hier historische Gründe.

nft.ipv4

Es wird hier ein Beispiel verwendet, das in einem Homerouter (Intel Atom Board mit 2 Ethernets) steckt und eine SIP Telefonanlage bedienen soll. IP-NAT wird bei IPv4 leider nun einmal gebraucht; hoffentlich verschwindet IPv4 irgendwann einmal! Von NAT ausgenommen sollen natürlich alle RFC1918 Adressen sein. Ein Chain kümmert sich um die SIP Verbindungen, die in diesem Beispiel über Sipgate gehen. Parameter innerhalb {} können durch Komma getrennt, mehrfach vorkommen.

Weiterhin berücksichtigt dieses Ruleset auch IPsec Verbindungen, die einen GRE Tunnel enthalten.

table ip nat {
	chain prerouting {
		type nat hook prerouting priority 0; policy accept;
		iif enp2s0 udp dport { sip, 16384-32767} dnat 192.168.26.1 
		iif enp2s0 tcp dport { sip-tls, sip} dnat 192.168.26.1 
	}

	chain postrouting {
		type nat hook postrouting priority 100; policy accept;
		ip saddr 10.0.0.0/8 jump local
		ip saddr 172.16.0.0/12 jump local
		ip saddr 192.168.0.0/16 jump local
		masquerade 
	}

	chain local {
		ip daddr 10.0.0.0/8 accept
		ip daddr 172.16.0.0/12 accept
		ip daddr 192.168.0.0/16 counter
		ip daddr 192.168.0.0/16 accept
	}
}

table ip filter {
	chain input {
		type filter hook input priority 0; policy accept;
		ct state established,related counter
		ct state established,related accept
		jump lan
		iif enp2s0 ip saddr 192.168.116.1/32 accept;
		iif enp2s0 jump ipsec;
		iif enp2s0 ip saddr { 217.10.79.0/26 } jump sip;
		ct state invalid drop
		iif enp2s0 ip protocol icmp jump icmp_in
		iif velianet accept
		iif enp2s0 counter
		iif enp2s0 drop
	}

	chain output {
		type filter hook output priority 0; policy accept;
	}

	chain forward {
		type filter hook forward priority 0; policy accept;
		ct state invalid drop
		ct state established,related accept
		iif enp1s0 ip saddr { 192.168.0.0/24, 192.168.1.0/24 } accept
		iif enp1s0.2600 ip saddr { 192.168.26.0/24 } accept
		iif enp2s0 ip protocol icmp jump icmp_in
		iif enp2s0 counter
		iif enp2s0 reject
	}

	chain sip {
		udp dport { sip, 16384-32767 } accept;
		tcp dport { sip, sip-tls } accept;
	}

	chain lan {
		iif lo ip saddr 127.0.0.0/8 accept
		iif enp1s0.2600 ip saddr 192.168.26.0/24 accept
		iif enp1s0 ip saddr 192.168.1.0/24 accept
		iif enp1s0 ip saddr 192.168.0.0/24 accept
	}

	chain ipsec {
		udp dport { 500, 4500 } counter
		udp dport { 500, 4500 } accept
		ip protocol esp counter
		ip protocol esp accept
	}

	chain icmp_in {
		iif enp2s0 icmp type { echo-request } accept
		iif enp2s0 icmp type destination-unreachable icmp code 4 accept
	}

}

nft.ipv6

Selbstverständlich geht das auch alles mit IPv6. Die NAT Regel ist nur Pro-Forma gesetzt, bei IPv6 braucht man wirklich kein NAT, außer bei den OVH Kimsufi Servern, denn da bekommt man nur eine IPv6 Adresse. @OVH: Bitte noch einmal die IPv6 RFCs und RIPE Recommendations lesen!

table ip6 nat {
	chain prerouting {
		type nat hook prerouting priority 0; policy accept;
	}

	chain postrouting {
		type nat hook postrouting priority 100; policy accept;
	}
}

table ip6 filter {
	chain input {
		type filter hook input priority 0; policy accept;
		ct state established,related accept
		jump lan
		iif enp2s0 jump ipsec;
		ct state invalid drop
		iif enp2s0 ip6 nexthdr icmpv6 jump icmp_in
		iif enp2s0 drop
	}

	chain output {
		type filter hook output priority 0; policy accept;
	}

	chain forward {
		type filter hook forward priority 0; policy accept;
		ct state invalid drop
		ct state established,related accept
		iif enp2s0 jump lan
		iif enp2s0 ip6 nexthdr icmpv6 jump icmp_in
	}

	chain sip {
		udp dport { sip, 16384-32767 } accept;
		tcp dport { sip, sip-tls } accept;
	}

	chain lan {
		iif lo ip6 saddr ::1/8 accept
		iif enp1s0 ip6 saddr fd00:7a0:1:1::/64 accept
		iif enp1s0 ip6 saddr fd00:7a0:1:2::/64 accept
		iif enp1s0 ip6 saddr fd00:7a0:1:3::/64 accept
	}

	chain ipsec {
		udp dport { 500, 4500 } accept
		ip6 nexthdr esp accept
	}

	chain icmp_in {
		iif enp2s0 icmpv6 type { nd-neighbor-solicit, echo-request, nd-router-advert, nd-neighbor-advert } accept
		iif enp2s0 icmpv6 type { echo-request } accept
		iif enp2s0 icmpv6 type destination-unreachable icmpv6 code 4 accept
	}

}

Bugs

Wenn man in den IPv6 Rulesets statt ip6 irgendwo als Protokoll nur ip stehen hat, denn wirft nft einen Coredump.

ToDo

nft kann bereits seine Rulesets als XML oder JSON exportieren. Ein Import steht bei den Entwicklern bereits auf der ToDo Liste. Ein Python Skript, dass diese Schnittstelle Remote zugänglich macht kommt wohl auch noch. Mit libnftnl können eigene Lösungen anstatt nft implementiert werden.

Links

Netfilter Wiki

Netfilter Homepage

Systemd Doku

Wird fortgesetzt