Nftables

From Frack - Hackerspace Friesland
Jump to navigationJump to search
Onderwerp: nftables
Firewall.png
Deskundigen
Afbeelding Anoniem.png Fludizz
Fludizz Rol: deelnemer Deskundig met: GL-Inet, Glasvezel lassen, Iptables, Linux, Netwerken, Nftables, OpenVPN, Python, RaspberryPi, Software Beginnend met: ENC28J60 Werkt aan: Geen projecten :(
Onderdeel van Netwerken, Linux, Beveiliging

iptables verdwijnt langzaam naar de legacy achtergrond. De standaard firewall voor bijv. Debian 10 is tegenwoordig op basis van nftables.

Een firewall is een mechanisme om toegang tot een computer of een heel netwerk te beperken op basis van regels. In een firewall kan je configureren wat wel doorgegeven mag worden en wat niet. Dit kan bijvoorbeeld het blokkeren van poorten zijn, het toestaan van bepaalde IP-adressen of bepaalde protocollen filteren. Voorheen was standaard iptables aanwezig op linux distributies, dit is echter veroudert en word langzaam aan vervangen met nftables (bijv. Debian 10).

Een (gekoppeld) netwerk kan niet zonder firewalls. Waar internet vroeger nog heel erg open en onbeveiligd was kan je tegenwoordig niet meer zonder firewalls, wegens kwaadwillende mensen en tegen malafide programma's die ongevraagd toegang willen tot de machines. Op deze pagina word een basic nftables firewall uitgelegd met NAT en IPv6 routing erin, deze configuratie gaat er vanuit dat alle machines op het interne netwerk "trusted" zijn en filtert alleen verkeer dat vanaf de WAN naar het LAN gerouteerd word.

Setup

De setup is relatief simpel, de server is voorzien van twee netwerkkaarten en een drietal virtuele interfaces:

  • enp2s0 (WAN, untrusted)
  • enp1s0 (LAN, trusted)
  • tun0 (VPN, virtuele adapter)
  • 6in4 (IPv6 in IPv4 tunnel, virtuele adapter, untrusted)
  • lo (Loopback adapter, virtuele adapter, altijd aanwezig)

Kernel modules

Voor nftables worden kernel modules geladen die beginnen met "nft_". In tegenstelling tot iptables, lijkt het erop dat nft automatisch de benodigde kernel modules laad.

nftables

/usr/sbin/nft is de megeleverde binary (executable) waarmee de nftables firewall gemaakt kan worden. Er is geen apart commando meer voor IPv6 omdat nftables dit allemaal in één bevat. Voor het filteren van IPv4 en IPv6 kan je gebruik maken van "ip", "ip6" of de "inet" family, waarbij de laatste het grootste voordeel heeft dat dit zowel IPv4 als IPv6 in dezelfde filter-set te behandelen. Effectief maakt het gebruik van de "inet" family je firewall rules korter/simpeler.

De daadwerkelijke firewall rules kunnen heel vlot geladen worden in nftables door nft -f /path/naar/ruleset te gebruiken. Je ruleset valideren voordat je hem inlaad is erg handig, hiervoor kan je de -c vlag gebruiken, deze controleerd de file syntax zonder hem toe te passen.

Port en IP adres syntax

Voor het schrijven van nftables rules is het handig om te weten hoe poort(en) en ipadressen/subnets erin gezet moeten worden. Het is van belang om deze basis syntax te kennen omdat de regelset verderop in de configuratie deze syntax gebruikt. De uitleg hieronder gaat uit dat dit allemaal voorbereid word in een text file die geladen word met nft -f commando.

Een groot voordeel van nftables over iptables is dat er gebruik gemaakt kan worden van variabelen en verzamelingen (sets en maps). Hierdoor kan je de te laden regels nog verder versimpelen. Als voorbeeld, de je wil de TCP poorten 22, 80 en 443 en UPD poorten 53 en 1194 open zetten, dit kan je dan vooraf definiëren in twee variabelen als volgt:

define TCPPORTS = { 22,80,443}
define UDPPORTS = { 53,1194}

Deze poorten kunnen vervolgens open gezet worden voor zowel IPv4 als IPv6 tegelijk door hem in de inet family te zetten als volgt:

add rule inet filter INPUT meta l4proto tcp tcp dport $TCPPORTS counter accept
add rule inet filter INPUT meta l4proto udp udp dport $UDPPORTS counter accept

Bovenstaande regels kijken naar meta-informatie van de packets, in dit geval naar Layer4 protocols (UDP en TCP). Het "counter" keyword is optioneel, dit geeft packet en byte counters als je de actieve regels bekijkt middels nft list ruleset commando.

Voor filteren/routing kan het handig zijn om bepaalde IP-adressen expliciet toegang te ontzeggen. Dit kan gedaan worden door simpelweg een enkel IP-adres of subnet in te tikken. Wil je echter meerdere subnets en/of hosts toevoegen, dan is het veel handiger om met een verzameling te werken, een 'set', die aan een enkele regel gehangen kan worden. Dit bied de mogelijkheid om IP addressen en subnets toe te voegen (danwel te verwijderen), zonder aan de daadwerkelijke firewall regels (en bijbehorende volgorde) te komen. deze "set" maak je als volgt aan:

add set inet filter MyIPv4 { type ipv4_addr ; flags interval,timeout ; }
add set inet filter MyIPv6 { type ipv6_addr ; flags interval,timeout ; }

In bovenstaande commando's word vastgesteld wat vor type set het is (in dit geval één voor IPv4 en één voor IPv6) en worden er een aantal vlaggen aan toegekend. De vlag "interval" is nodig om subnets te kunnen definiëren. Zonder deze vlag kunnen alleen losse host-adressen toegevoegd worden. De vlag "timeout" geeft je de mogelijkheid om een optionele tijd mee te geven aan een entry in deze set. Na het verlopen van de timeout verdwijnt de specefieke host/subnet automatisch weer uit de lijst. Dit is heel er handig in combinatie met tools voor dynamisch black-/whitelisting van hosts. Indien er bij het toevoegen van een host/subnet geen timeout veld meegegeven word, blijft deze waarde "permanent" in de set zitten.

Een belangrijke voetnoot hierbij: de timeout vlag werkt alleen als deze set al actief in gebruik is in de firewall. Als de timeout vlag gezet word bij het "initieel" laden van de regels, doet deze niets en blijft de host entry alsnog permanent in de tabel staan.

Om vooraf addressen toe te voegen (de vaste adressen die niet veranderen), kan je vooraf in de regels de volgende commando's toevoegen:

add element inet filter MyIPv4 { 192.168.1.0/24 }
add element inet filter MyIPv4 { 10.100.100.0/24 }
add element inet filter MyIPv6 { fe80::/64 }

Dit kan toegepast worden in een firewall regel om bovenstaande IP verzamelingen toegang tot de machine geheel te verbieden:

add rule inet filter INPUT ip saddr @MyIPv4 counter drop
add rule inet filter INPUT ip6 saddr @MyIPv6 counter drop

In dit geval worden de packets gedropt zonder ICMP antwoord terug te sturen. Is een expliciete ICMP status code nodig, dan kan dit middels "reject" gedefinieerd worden.

Mocht je na het laden van de firewall regels hosts of subnets permanent of tijdelijk toe willen voegen danwel verwijderen, dan kan dat middels het volgende CLI commando's

nft add element inet filter MyIPv6 { ff00::/8 }
nft add element inet filter MyIPv4 {10.11.11.0/24; timeout 300s }
nft delete element inet filter MyIPv6 { ff00::/8 }

Ruleset

Onderstaande ruleset kan gebruikt worden met de eerder genoemde #Setup. Zoals je ziet worden alleen de vertrouwde interfaces opgegeven en de interface waarop NAT routing toegepast moet worden. Alle interfaces die NIET in de lijst van vertrouwde interfaces staat moet door alle firewall regels heen. Vandaar dat de 6in4 interface niet zichtbaar terug komt in deze ruleset.

Onderstaand script kan aangeroepen worden vanuit /etc/network/interfaces, in bijv. met pre-up /usr/sbin/nft -f /path/to/nftables.sh zodat dit automatisch (en "on boot") toegepast worden.

nftables.sh

#!/urs/sbin/nft -f
 
###Define variables
 
# Interfaces
define LANIF = { lo, tun0, enp1s0 }
define WANIF = enp2s0
 
# Open Ports on WANIF
define TCPPORTS = { 22,80,443}
define UDPPORTS = { 53,1194}
 
# flush all previous (including iptables) rulesets to start with clean slate.
flush ruleset
 
#Create the required tables so Maps/sets can be created
add table inet filter
add table ip nat
 
# Blocklist Sets
add set inet filter BLOCKIPV4 { type ipv4_addr ; flags interval,timeout ; }
add set inet filter BLOCKIPV6 { type ipv6_addr ; flags interval,timeout ; }
 
# Block deprecated 6to4 addresses inbound - we are dualstack ffs!
add element inet filter BLOCKIPV6 { 2002::/16 }
 
#Define chains
add chain inet filter INPUT { type filter hook input priority 0; policy drop; }
add chain inet filter FORWARD { type filter hook forward priority 0; policy drop; }
add chain inet filter OUTPUT { type filter hook output priority 0; policy accept; }
 
# Permit LAN interface
add rule inet filter INPUT iifname $LANIF counter accept
 
#Block blacklisted IP's with ICMP Reject
add rule inet filter INPUT ip saddr @BLOCKIPV4 counter reject with icmp type host-prohibited
add rule inet filter INPUT ip6 saddr @BLOCKIPV6 counter reject with icmpv6 type admin-prohibited
 
#Allow ICMP and Established connections
add rule inet filter INPUT ct state related,established counter accept
add rule inet filter INPUT meta l4proto { icmp, ipv6-icmp } counter accept
 
#Permit explicitly opened ports
add rule inet filter INPUT meta l4proto tcp tcp dport $TCPPORTS counter accept
add rule inet filter INPUT meta l4proto udp udp dport $UDPPORTS counter accept
 
#Optional: Permit DHCP (needed in some corner cases - uncomment if DHCP on WAN does not work)
#add rule inet filter INPUT meta protocol ip udp sport 68 udp dport 67 counter accept
#add rule inet filter INPUT meta protocol ip tcp sport 68 tcp dport 67 counter accept
 
#Permit traceroute/pmtud
add rule inet filter INPUT meta l4proto udp udp dport 33434-33523 ct state new counter accept
add rule inet filter INPUT meta l4proto udp udp dport 33434-33523 ct state established counter drop
add rule inet filter INPUT meta l4proto udp udp dport 44444-44644 ct state new counter accept
add rule inet filter INPUT meta l4proto udp udp dport 44444-44644 ct state established counter drop
 
#Forwarding rules - Allow all dowarding from LAN side.
add rule inet filter FORWARD iifname $LANIF counter accept
 
#Block blacklisted IP's from forwarding
add rule inet filter FORWARD ip saddr @BLOCKIPV4 counter reject with icmp type admin-prohibited
add rule inet filter FORWARD ip6 saddr @BLOCKIPV6 counter reject with icmpv6 type admin-prohibited
 
#Allow established connections and ICMP in both directions
add rule inet filter FORWARD ct state related,established counter accept
add rule inet filter FORWARD meta l4proto { icmp, ipv6-icmp } counter accept
 
#Allow traceroute/pmtud
add rule inet filter FORWARD meta l4proto udp udp dport 33434-33523 ct state new counter accept
add rule inet filter FORWARD meta l4proto udp udp dport 33434-33523 ct state established counter drop
add rule inet filter FORWARD meta l4proto udp udp dport 44444-44644 ct state new counter accept
add rule inet filter FORWARD meta l4proto udp udp dport 44444-44644 ct state established counter drop
 
#Open Ports to specific IPv6 LAN Hosts
add rule inet filter FORWARD meta l4proto tcp ip6 daddr 2a00:1111:2222:3333:c0:ff:feee:aaaa tcp dport { 22, 8080} counter accept
 
# Allow only internally originated connections (ie: block IPv6 SYN packets WAN-to-LAN).
add rule inet filter FORWARD iifname $WANIF meta protocol ip6 tcp flags & (fin|syn|rst|ack) != syn counter accept
 
# NAT Routing
add chain ip nat PREROUTING { type nat hook prerouting priority -100; policy accept; }
add chain ip nat INPUT { type nat hook input priority 100; policy accept; }
add chain ip nat POSTROUTING { type nat hook postrouting priority 100; policy accept; }
add chain ip nat OUTPUT { type nat hook output priority -100; policy accept; }
add rule ip nat POSTROUTING oifname $WANIF counter masquerade
 
# NAT Port Forwards - each forward needs an explicit rule in both PREROUTING and FORWARD chains
add rule ip nat PREROUTING iifname $WANIF ip protocol tcp tcp dport 8000 counter dnat to 192.168.128.12:80
add rule inet filter FORWARD ip daddr 192.168.128.12 tcp dport 80 counter accept
 
add rule ip nat PREROUTING iifname $WANIF ip protocol tcp tcp dport 54321 counter dnat to 192.168.128.7:54321
add rule inet filter FORWARD ip daddr 192.168.128.7 tcp dport 54321 counter accept

Dynamic Blacklisting met fail2ban

Bovenstaande setup kan erg goed geintegreerd worden in bijvoobeeld de fail2ban policy daemon. Deze tool kan logfiles scannen en op basis van regex matches automatisch IP adressen vinden welke bijvoorbeeld brute-force attacks uitvoeren tegen een service die open staat.

Action Script

fail2ban gebruikt action scripts on de gedetecteerde IP adressen toe te voegen aan een firewall. Helaas zijn de default action scripts van fail2ban niet geschikt voor bovenstaande nftables ruleset. Echter was dit erg simpel op te lossen door een nieuw action script te schrijven specefiek voor de regels zoals bovenstaand gedefinieerd is.

/etc/fail2ban/action.d/custom_nft-with-set.conf

# Fail2Ban configuration file
#
# Custom script to interact with nftables.
# This makes f2b only add an IP to a defined set with a timeout setting based on bantime.
# example in nft:
#
# add set inet filter BLOCKIPV4 { type ipv4_addr ; flags interval,timeout ; }
# add rule inet filter INPUT ip saddr @BLOCKIPV4 counter reject with icmp type host-prohibited
#
# add set inet filter BLOCKIPV6 { type ipv6_addr ; flags interval,timeout ; }
# add rule inet filter INPUT ip6 saddr @BLOCKIPV6 counter reject with icmpv6 type admin-prohibited
# 
 
[Definition]
 
actionstart =
 
actionstop =
 
actioncheck =
 
actionban = <nftables> add element inet filter <nft_block> \{ <ip> timeout <bantime>s \}
 
# Optionally, if you do not want to rely on bantime alone for removal:
#actionunban = <nftables> delete element inet filter <nft_block> \{ <ip> \}
actionunban = 
 
[Init]
 
nftables = nft
nft_block = BLOCKIPV4
 
[Init?family=inet6]
 
nftables = nft
nft_block = BLOCKIPV6
 
#EOF

Dit action script moet vervolgens ingesteld worden om gebruikt te worden als de standaard acties. Dit kan door in /etc/fail2ban/jail.local het volgende toe te voegen:

[DEFAULT]
banaction = custom_nft-with-set
banaction-allports = custom_nft-with-set

Note: als je in een jail een custom action gedefinieerd hebt moet deze uiteraard ook aangepast worden.

En hiermee heb je volledige integratie van fail2ban in deze nftables setup, voor zowel IPv4 als IPv6!