Intro
This is part two of another post I wrote: Should I enable ICMP?
The goals of this firewall ruleset are as follows:
- Enforce sane limits over the number of automated ICMP responses that can be elicited by sources on the internet; mitigating ICMP amplification while still allowing some symbolance of networking mechanisms that rely on ICMP to function.
- Establish the basis for a "zero trust" model by ensuring the use of end-to-end encrypted protocols for necessary egress traffic (primarily NTPSEC and DoTLS.) This is necessary in an environment where protocols like
NDP-RAare in use because of the possibility that other users on a network can advertise routes themselves which can potentially be used to hijack another user's traffic. Unauthorized route advertisements can certainly be mitigated with multicast filtering/snooping however it is not necessarily consistent from one network to the next. - Providing an abstract ruleset such that it can be the basis for application virtually anywhere while minimizing the amount of overhead that can be potentially introduced by abstraction as much as possible (YMMV)
The simplest way to go about this is to start at the filter state / configuration format section.
Preparation
This demo uses Debian on Parallels, updated from bullseye to bookworm:
sed -i 's/bullseye/bookworm/g' apt/sources.list
apt update && apt -y dist-upgrade
apt -y install nftables chrony systemd-resolved && reboot
Chrony (NTPSEC)
Unencrypted NTP (UDP 123) destinations will only be permitted in the NFTables ruleset if they are local networks, if you need internet time setup chrony
Disable systemd-timesyncd if it is enabled:
sudo systemctl stop systemd-timesyncd.service
replace /etc/chrony.conf with:
confdir /etc/chrony/conf.d
server time.cloudflare.com nts iburst
sourcedir /etc/chrony/sources.d
keyfile /etc/chrony/chrony.keys
driftfile /var/lib/chrony/chrony.drift
ntsdumpdir /var/lib/chrony
logdir /var/log/chrony
maxupdateskew 100.0
rtcsync
makestep 1 3
leapsectz right/UTC
Enable the Chrony service:
systemctl enable chrony
systemctl start chrony
(a list of other NTS-enabled NTP servers is available.)
DNSoTLS
Unencrypted DNS (UDP 53) destinations will only be permitted in the NFTables ruleset if they are local networks, if you need internet DNS setup systemd-resolved with DoTLS then
replace the contents of /etc/systemd/resolved.conf with:
[Resolve]
DNS=1.1.1.1,1.0.0.1,2606:4700:4700::1111,2606:4700:4700::1001
DNSOverTLS=yes
start systemd-resolved:
systemctl enable systemd-resolved
systemctl start systemd-resolved
Creating the default table
nft flush ruleset
nft add table inet filter
Chains
nft add chain inet filter input '{ type filter hook input priority filter; policy accept; }'
nft add chain inet filter forward '{ type filter hook forward priority filter; policy accept; }'
nft add chain inet filter output '{ type filter hook output priority filter; policy accept; }'
nft add chain inet filter prerouting '{ type nat hook prerouting priority 100; policy accept; }'
nft add chain inet filter postrouting '{ type nat hook postrouting priority 100; policy accept; }'
nft add chain inet filter masq
nft add chain inet filter ether_in
nft add chain inet filter ether_out
nft add chain inet filter ether_forward
nft add chain inet filter icmp_in
nft add chain inet filter icmp_out
nft add chain inet filter icmp_forward
nft add chain inet filter icmp_echo_reply_rate_limit
nft add chain inet filter reject_with_icmp_port_unreachable_metered
nft add chain inet filter reject_with_icmp_port_unreachable
nft add chain inet filter reject_with_icmp_host_unreachable_metered
nft add chain inet filter reject_with_icmp_host_unreachable
nft add chain inet filter reject_with_icmp_no_route_metered
nft add chain inet filter reject_with_icmp_no_route
nft add chain inet filter reject_with_icmp_admin_prohibited_metered
nft add chain inet filter reject_with_icmp_admin_prohibited
nft add chain inet filter tcp_in
nft add chain inet filter tcp_out
nft add chain inet filter tcp_forward
nft add chain inet filter udp_in
nft add chain inet filter udp_out
nft add chain inet filter udp_forward
nft add chain inet filter bogon
nft add rule inet filter bogon log prefix "bogon" group 1
nft add rule inet filter bogon counter drop
nft add chain inet filter wont_forward
nft add rule inet filter wont_forward log prefix "wont_forward" group 1
nft add rule inet filter wont_forward counter drop
Meter sets
nft add set inet filter icmp_egress_meter4 '{ type ipv4_addr; size 8; flags timeout, dynamic; }'
nft add set inet filter icmp_egress_meter6 '{ type ipv6_addr; size 8; flags timeout, dynamic; }'
Verdict maps and sets
IPv4 bogons
nft add map inet filter drop_bogons4 '{ type ipv4_addr : verdict; flags interval; }'
nft add element inet filter drop_bogons4 '{ 224.0.0.0/4 : continue }'
nft add element inet filter drop_bogons4 '{ 192.168.0.0/16 : continue }'
nft add element inet filter drop_bogons4 '{ 10.0.0.0/8 : continue }'
nft add element inet filter drop_bogons4 '{ 172.16.0.0/12 : continue }'
nft add element inet filter drop_bogons4 '{ 169.254.0.0/16 : continue }'
nft add element inet filter drop_bogons4 '{ 100.64.0.0/10 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 0.0.0.0/8 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 127.0.0.0/8 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 192.0.0.0/24 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 192.0.2.0/24 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 198.18.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 198.51.100.0/24 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 203.0.113.0/24 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 240.0.0.0/4 : jump bogon }'
Local networks
nft add set inet filter local_networks '{ type ipv4_addr; flags interval; }'
nft add element inet filter local_networks '{ 169.254.0.0/16, 10.0.0.0/8, 172.17.0.0/12, 192.168.0.0/16 }'
TODO
- Class E / limited broadcast (240.0.0.0/4)
IPv6 bogons
nft add map inet filter drop_bogons6 '{ type ipv6_addr : verdict; flags interval; }'
nft add element inet filter drop_bogons6 '{ fe80::/10 : continue }'
nft add element inet filter drop_bogons6 '{ fc00::/7 : continue }'
nft add element inet filter drop_bogons6 '{ ff00::/8 : continue }'
nft add element inet filter drop_bogons6 '{ ::ffff:0:0/96 : jump bogon }'
nft add element inet filter drop_bogons6 '{ ::/96 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 100::/64 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:10::/28 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:db8::/32 : jump bogon }'
nft add element inet filter drop_bogons6 '{ fec0::/10 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002::/24 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:a00::/24 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:7f00::/24 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:a9fe::/32 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:ac10::/28 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:c000::/40 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:c000:200::/40 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:c0a8::/32 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:c612::/31 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:c633:6400::/40 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:cb00:7100::/40 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:e000::/20 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2002:f000::/20 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001::/40 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:a00::/40 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:7f00::/40 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:a9fe::/48 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:ac10::/44 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:c000::/56 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:c000:200::/56 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:c0a8::/48 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:c612::/47 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:c633:6400::/56 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:cb00:7100::/56 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:e000::/36 : jump bogon }'
nft add element inet filter drop_bogons6 '{ 2001:0:f000::/36 : jump bogon }'
TODO
- Multicast scopes; node-local
ff01::vs. link-localff02::vs. site-localff05:: - variable scope
- more info: https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml
IPv4 reject or drop
nft add map inet filter reject_or_drop_port4 '{ typeof ip saddr . ip daddr : verdict; flags interval; }'
nft add element inet filter reject_or_drop_port4 '{ 10.0.0.0/8 . 10.0.0.0/8 : jump reject_with_icmp_port_unreachable }'
nft add element inet filter reject_or_drop_port4 '{ 172.16.0.0/12 . 172.16.0.0/12 : jump reject_with_icmp_port_unreachable }'
nft add element inet filter reject_or_drop_port4 '{ 192.168.0.0/16 . 192.168.0.0/16 : jump reject_with_icmp_port_unreachable }'
nft add element inet filter reject_or_drop_port4 '{ 169.254.0.0/16 . 169.254.0.0/16 : jump reject_with_icmp_port_unreachable }'
nft add element inet filter reject_or_drop_port4 '{ 0.0.0.0/0 . 0.0.0.0/0 : jump reject_with_icmp_port_unreachable_metered }'
IPv6 reject or drop
nft add map inet filter reject_or_drop_port6 '{ typeof ip6 saddr . ip6 daddr : verdict; flags interval; }'
nft add element inet filter reject_or_drop_port6 '{ fe80::/10 . fe80::/10 : jump reject_with_icmp_port_unreachable }'
nft add element inet filter reject_or_drop_port6 '{ fc00::/7 . fc00::/7 : jump reject_with_icmp_port_unreachable }'
nft add element inet filter reject_or_drop_port6 '{ ::/0 . ::/0 : jump reject_with_icmp_port_unreachable_metered }'
IPv4 ingress ICMP types
nft add map inet filter icmp_types_in4 '{ typeof ip saddr . ip daddr . icmp type : verdict; flags interval; }'
nft add element inet filter icmp_types_in4 '{ 0.0.0.0/0 . 0.0.0.0/0 . echo-request : accept }'
nft add element inet filter icmp_types_in4 '{ 0.0.0.0/0 . 0.0.0.0/0 . echo-reply : accept }'
nft add element inet filter icmp_types_in4 '{ 0.0.0.0/0 . 0.0.0.0/0 . destination-unreachable : accept }'
IPv6 ingress ICMP types
nft add map inet filter icmp_types_in6 '{ typeof ip6 saddr . ip6 daddr . icmpv6 type : verdict; flags interval; }'
nft add element inet filter icmp_types_in6 '{ fe80::/10 . fe80::/10 . echo-request : accept }'
nft add element inet filter icmp_types_in6 '{ fe80::/10 . ff00::/8 . echo-request : accept }'
nft add element inet filter icmp_types_in6 '{ fc00::/7 . fc00::/7 . echo-request : accept }'
nft add element inet filter icmp_types_in6 '{ fe80::/10 . fe80::/10 . echo-reply : accept }'
nft add element inet filter icmp_types_in6 '{ fe80::/10 . ff00::/8 . echo-reply : accept }'
nft add element inet filter icmp_types_in6 '{ fc00::/7 . fc00::/7 . echo-reply : accept }'
nft add element inet filter icmp_types_in6 '{ fe80::/10 . fe80::/10 . nd-neighbor-solicit : accept }'
nft add element inet filter icmp_types_in6 '{ fc00::/7 . ff00::/8 . nd-neighbor-solicit : accept }'
nft add element inet filter icmp_types_in6 '{ fc00::/7 . fc00::/7 . nd-neighbor-solicit : accept }'
nft add element inet filter icmp_types_in6 '{ fc00::/7 . fc00::/7 . nd-neighbor-advert : accept }'
nft add element inet filter icmp_types_in6 '{ fe80::/10 . fe80::/10 . nd-neighbor-advert : accept }'
nft add element inet filter icmp_types_in6 '{ fe80::/10 . ff00::/8 . nd-router-advert : accept }'
nft add element inet filter icmp_types_in6 '{ fe80::/10 . fe80::/10 . nd-router-advert : accept }'
TODO
- NDP for GUA
IPv4 ingress TCP ports
nft add map inet filter tcp_ports_in4 '{ typeof ip saddr . ip daddr . tcp dport : verdict; flags interval; }'
nft add element inet filter tcp_ports_in4 '{ 0.0.0.0/0 . 0.0.0.0/0 . 22 : accept }'
IPv6 ingress TCP ports
nft add map inet filter tcp_ports_in6 '{ typeof ip6 saddr . ip6 daddr . tcp dport : verdict; flags interval; }'
nft add element inet filter tcp_ports_in6 '{ ::/0 . ::/0 . 22 : accept }'
IPv4 ingress UDP ports
nft add map inet filter udp_ports_in4 '{ typeof ip saddr . ip daddr . udp dport : verdict; flags interval; }'
nft add element inet filter udp_ports_in4 '{ 169.254.0.0/16 . 169.254.0.0/16 . 68 : accept }'
nft add element inet filter udp_ports_in4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 68 : accept }'
nft add element inet filter udp_ports_in4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 68 : accept }'
nft add element inet filter udp_ports_in4 '{ 192.168.0.0/12 . 192.168.0.0/16 . 68 : accept }'
nft add element inet filter udp_ports_in4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 137 : accept }'
nft add element inet filter udp_ports_in4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 137 : accept }'
nft add element inet filter udp_ports_in4 '{ 192.168.0.0/12 . 192.168.0.0/16 . 137 : accept }'
nft add element inet filter udp_ports_in4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 5353 : accept }'
nft add element inet filter udp_ports_in4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 5353 : accept }'
nft add element inet filter udp_ports_in4 '{ 192.168.0.0/12 . 192.168.0.0/16 . 5353 : accept }'
nft add element inet filter udp_ports_in4 '{ 10.0.0.0/8 . 224.0.0.0/4 . 5353 : accept }'
nft add element inet filter udp_ports_in4 '{ 172.16.0.0/12 . 224.0.0.0/4 . 5353 : accept }'
nft add element inet filter udp_ports_in4 '{ 192.168.0.0/12 . 224.0.0.0/4 . 5353 : accept }'
IPv6 ingress UDP ports
nft add map inet filter udp_ports_in6 '{ typeof ip6 saddr . ip6 daddr . udp dport : verdict; flags interval; }'
nft add element inet filter udp_ports_in6 '{ fe80::/10 . ff00::/8 . 546 : accept }'
nft add element inet filter udp_ports_in6 '{ fe80::/10 . ff00::/8 . 5353 : accept }'
nft add element inet filter udp_ports_in6 '{ fc00::/7 . ff00::/8 . 5353 : accept }'
IPv4 default forward networks
nft add map inet filter default_forward4 '{ typeof ip saddr . ip daddr . ct state : verdict; flags interval; }'
nft add element inet filter default_forward4 '{ 169.254.0.0/16 . 0.0.0.0/0 . new : jump wont_forward }'
nft add element inet filter default_forward4 '{ 0.0.0.0/0 . 169.254.0.0/16 . new : jump wont_forward }'
nft add element inet filter default_forward4 '{ 10.0.0.0/8 . 172.16.0.0/12 . new : jump reject_with_icmp_no_route }'
nft add element inet filter default_forward4 '{ 10.0.0.0/8 . 192.168.0.0/16 . new : jump reject_with_icmp_no_route }'
nft add element inet filter default_forward4 '{ 172.16.0.0/12 . 10.0.0.0/8 . new : jump reject_with_icmp_no_route }'
nft add element inet filter default_forward4 '{ 172.16.0.0/12 . 192.168.0.0/16 . new : jump reject_with_icmp_no_route }'
nft add element inet filter default_forward4 '{ 192.168.0.0/16 . 10.0.0.0/8 . new : jump reject_with_icmp_no_route }'
nft add element inet filter default_forward4 '{ 192.168.0.0/16 . 172.16.0.0/12 . new : jump reject_with_icmp_no_route }'
nft add element inet filter default_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . new : continue }'
nft add element inet filter default_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . established : accept }'
nft add element inet filter default_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . new : continue }'
nft add element inet filter default_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . established : accept }'
nft add element inet filter default_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . new : continue }'
nft add element inet filter default_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . established : accept }'
nft add element inet filter default_forward4 '{ 10.0.0.0/8 . 0.0.0.0/0 . new : continue }'
nft add element inet filter default_forward4 '{ 172.16.0.0/12 . 0.0.0.0/0 . new : continue }'
nft add element inet filter default_forward4 '{ 192.168.0.0/16 . 0.0.0.0/0 . new : continue }'
nft add element inet filter default_forward4 '{ 0.0.0.0/0 . 10.0.0.0/8 . established : accept }'
nft add element inet filter default_forward4 '{ 0.0.0.0/0 . 172.16.0.0/12 . established : accept }'
nft add element inet filter default_forward4 '{ 0.0.0.0/0 . 192.168.0.0/16 . established : accept }'
IPv6 default forward networks
nft add map inet filter default_forward6 '{ typeof ip6 saddr . ip6 daddr . ct state : verdict; flags interval; }'
nft add element inet filter default_forward6 '{ fe80::/10 . ::/0 . new : jump wont_forward }'
nft add element inet filter default_forward6 '{ ::/0 . fe80::/10 . new : jump wont_forward }'
nft add element inet filter default_forward6 '{ fc00::/7 . fc00::/7 . new : continue }'
nft add element inet filter default_forward6 '{ fc00::/7 . fc00::/7 . established : accept }'
IPv4 egress ICMP types
nft add map inet filter icmp_types_out4 '{ typeof ip saddr . ip daddr . icmp type : verdict; flags interval; }'
nft add element inet filter icmp_types_out4 '{ 10.0.0.0/8 . 0.0.0.0/0 . echo-request : accept }'
nft add element inet filter icmp_types_out4 '{ 172.16.0.0/12 . 0.0.0.0/0 . echo-request : accept }'
nft add element inet filter icmp_types_out4 '{ 192.168.0.0/16 . 0.0.0.0/0 . echo-request : accept }'
nft add element inet filter icmp_types_out4 '{ 10.0.0.0/8 . 10.0.0.0/8 . echo-reply : accept }'
nft add element inet filter icmp_types_out4 '{ 172.16.0.0/12 . 172.16.0.0/12 . echo-reply : accept }'
nft add element inet filter icmp_types_out4 '{ 192.168.0.0/16 . 192.168.0.0/16 . echo-reply : accept }'
nft add element inet filter icmp_types_out4 '{ 10.0.0.0/8 . 10.0.0.0/8 . destination-unreachable : accept }'
nft add element inet filter icmp_types_out4 '{ 172.16.0.0/12 . 172.16.0.0/12 . destination-unreachable : accept }'
nft add element inet filter icmp_types_out4 '{ 192.168.0.0/16 . 192.168.0.0/16 . destination-unreachable : accept }'
nft add element inet filter icmp_types_out4 '{ 0.0.0.0/0 . 0.0.0.0/0 . echo-reply : jump icmp_echo_reply_rate_limit }'
IPv6 egress ICMP types
nft add map inet filter icmp_types_out6 '{ typeof ip6 saddr . ip6 daddr . icmpv6 type : verdict; flags interval; }'
nft add element inet filter icmp_types_out6 '{ fe80::/10 . ff00::/8 . echo-request : accept }'
nft add element inet filter icmp_types_out6 '{ fc00::/7 . fc00::/7 . echo-reply : accept }'
nft add element inet filter icmp_types_out6 '{ 2000::/3 . ::/0 . echo-reply : jump icmp_echo_reply_rate_limit }'
nft add element inet filter icmp_types_out6 '{ fc00::/7 . fc00::/7 . nd-neighbor-advert : accept }'
nft add element inet filter icmp_types_out6 '{ fe80::/10 . fe80::/10 . nd-neighbor-advert : accept }'
nft add element inet filter icmp_types_out6 '{ fc00::/7 . ff00::/8 . nd-neighbor-solicit : accept }'
nft add element inet filter icmp_types_out6 '{ fe80::/10 . fc00::/7 . nd-neighbor-solicit : accept }'
nft add element inet filter icmp_types_out6 '{ fe80::/10 . fe80::/10 . nd-neighbor-solicit : accept }'
nft add element inet filter icmp_types_out6 '{ fe80::/10 . ff00::/8 . nd-router-solicit : accept }'
GUA prefixes
The same rules apply, but it's less than ideal to use an over-reaching prefix like 2000::/3 because 2000::/3 should be metered.
That is, anything except local GUA prefixes should be metered.
IPv4 egress TCP ports
Ports 21, 23, 25, 53, and 80 can be omitted if the point is to ensure that no egress traffic will ever be destined for unencrypted protocols. With this particular ruleset they are limited to the following destinations:
169.254.0.0/1610.0.0.0/8172.16.0.0/12192.168.0.0/16
This is covered later in the hardening section.
nft add map inet filter tcp_ports_out4 '{ typeof ip saddr . ip daddr . tcp dport : verdict; flags interval; }'
nft add element inet filter tcp_ports_out4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 22 : accept }'
nft add element inet filter tcp_ports_out4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 22 : accept }'
nft add element inet filter tcp_ports_out4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 22 : accept }'
nft add element inet filter tcp_ports_out4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter tcp_ports_out4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter tcp_ports_out4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter tcp_ports_out4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 853 : accept }'
nft add element inet filter tcp_ports_out4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 853 : accept }'
nft add element inet filter tcp_ports_out4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 853 : accept }'
nft add element inet filter tcp_ports_out4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 4460 : accept }'
nft add element inet filter tcp_ports_out4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 4460 : accept }'
nft add element inet filter tcp_ports_out4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 4460 : accept }'
nft add element inet filter tcp_ports_out4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 5349 : accept }'
nft add element inet filter tcp_ports_out4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 5349 : accept }'
nft add element inet filter tcp_ports_out4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 5349 : accept }'
IPv6 egress TCP ports
Ports 21, 23, 25, 53, and 80 can be omitted if the point is to ensure that no egress traffic will ever be destined for unencrypted protocols. With this particular ruleset they are limited to the following destinations:
fc00::/7(ULA)
This is covered later in the hardening section.
nft add map inet filter tcp_ports_out6 '{ typeof ip6 saddr . ip6 daddr . tcp dport : verdict; flags interval; }'
nft add element inet filter tcp_ports_out6 '{ fc00::/7 . fc00::/7 . 443 : accept }'
nft add element inet filter tcp_ports_out6 '{ fc00::/7 . fc00::/7 . 853 : accept }'
nft add element inet filter tcp_ports_out6 '{ fc00::/7 . fc00::/7 . 4460 : accept }'
nft add element inet filter tcp_ports_out6 '{ fc00::/7 . fc00::/7 . 5349 : accept }'
nft add element inet filter tcp_ports_out6 '{ 2000::/3 . 2000::/3 . 443 : accept }'
nft add element inet filter tcp_ports_out6 '{ 2000::/3 . 2000::/3 . 853 : accept }'
nft add element inet filter tcp_ports_out6 '{ 2000::/3 . 2000::/3 . 4460 : accept }'
nft add element inet filter tcp_ports_out6 '{ 2000::/3 . 2000::/3 . 5349 : accept }'
IPv4 egress UDP ports
Ports 53, 67, 137 can be omitted if the point is to ensure that no egress traffic will ever be destined for unencrypted protocols. With this particular ruleset they are limited to the following destinations:
169.254.0.0/1610.0.0.0/8172.16.0.0/12192.168.0.0/16
Port 5353 is used for mDNS. It can be safely omitted, but it is useful for enumeration of hostnames on local networks (zeroconf). Currently mDNS doesn't use any encryption as of 1-26-2023, and I would say it should be omitted in environments that use NDP-RA. For more information on mDNS, refer to: https://datatracker.ietf.org/doc/html/draft-rafiee-dnssd-mdns-threatmodel-01
This is covered later in the hardening section.
nft add map inet filter udp_ports_out4 '{ typeof ip saddr . ip daddr . udp dport : verdict; flags interval; }'
nft add element inet filter udp_ports_out4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 67 : accept }'
nft add element inet filter udp_ports_out4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 67 : accept }'
nft add element inet filter udp_ports_out4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 67 : accept }'
nft add element inet filter udp_ports_out4 '{ 169.254.0.0/16 . 169.254.0.0/16 . 67 : accept }'
nft add element inet filter udp_ports_out4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 53 : accept }'
nft add element inet filter udp_ports_out4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 53 : accept }'
nft add element inet filter udp_ports_out4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 53 : accept }'
nft add element inet filter udp_ports_out4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 137 : accept }'
nft add element inet filter udp_ports_out4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 137 : accept }'
nft add element inet filter udp_ports_out4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 137 : accept }'
nft add element inet filter udp_ports_out4 '{ 10.0.0.0/8 . 224.0.0.0/4 . 5353 : accept }'
nft add element inet filter udp_ports_out4 '{ 172.16.0.0/12 . 224.0.0.0/4 . 5353 : accept }'
nft add element inet filter udp_ports_out4 '{ 192.168.0.0/16 . 224.0.0.0/4 . 5353 : accept }'
nft add element inet filter udp_ports_out4 '{ 169.254.0.0/16 . 224.0.0.0/4 . 5353 : accept }'
nft add element inet filter udp_ports_out4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter udp_ports_out4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter udp_ports_out4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter udp_ports_out4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 1194 : accept }'
nft add element inet filter udp_ports_out4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 1194 : accept }'
nft add element inet filter udp_ports_out4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 1194 : accept }'
IPv6 egress UDP ports
nft add map inet filter udp_ports_out6 '{ typeof ip6 saddr . ip6 daddr . udp dport : verdict; flags interval; }'
nft add element inet filter udp_ports_out6 '{ fe80::/10 . ff00::/8 . 547 : accept }'
nft add element inet filter udp_ports_out6 '{ 2000::/3 . ::/0 . 443 : accept }'
nft add element inet filter udp_ports_out6 '{ fc00::/7 . ff00::/8 . 5353 : accept }'
IPv4 forward ICMP types
nft add map inet filter icmp_types_forward4 '{ typeof ip saddr . ip daddr . icmp type : verdict; flags interval; }'
nft add element inet filter icmp_types_forward4 '{ 10.0.0.0/8 . 0.0.0.0/0 . echo-request : accept }'
nft add element inet filter icmp_types_forward4 '{ 172.16.0.0/12 . 0.0.0.0/0 . echo-request : accept }'
nft add element inet filter icmp_types_forward4 '{ 192.168.0.0/16 . 0.0.0.0/0 . echo-request : accept }'
nft add element inet filter icmp_types_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . echo-reply : accept }'
nft add element inet filter icmp_types_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . echo-reply : accept }'
nft add element inet filter icmp_types_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . echo-reply : accept }'
nft add element inet filter icmp_types_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . destination-unreachable : accept }'
nft add element inet filter icmp_types_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . destination-unreachable : accept }'
nft add element inet filter icmp_types_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . destination-unreachable : accept }'
nft add element inet filter icmp_types_forward4 '{ 0.0.0.0/0 . 0.0.0.0/0 . echo-reply : jump icmp_echo_reply_rate_limit }'
IPv6 forward ICMP types
nft add map inet filter icmp_types_forward6 '{ typeof ip6 saddr . ip6 daddr . icmpv6 type : verdict; flags interval; }'
nft add element inet filter icmp_types_forward6 '{ fe80::/10 . ff00::/8 . echo-request : accept }'
nft add element inet filter icmp_types_forward6 '{ fc00::/7 . fc00::/7 . echo-reply : accept }'
nft add element inet filter icmp_types_forward6 '{ 2000::/3 . ::/0 . echo-reply : jump icmp_echo_reply_rate_limit }'
IPv4 forward TCP ports
nft add map inet filter tcp_ports_forward4 '{ typeof ip saddr . ip daddr . tcp dport : verdict; flags interval; }'
nft add element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 21 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 21 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 21 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 23 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 23 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 23 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 25 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 25 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 25 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 53 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 53 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 53 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 80 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 80 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 80 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 22 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 22 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 22 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 853 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 853 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 853 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 4460 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 4460 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 4460 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 5349 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 5349 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 5349 : accept }'
IPv6 forward TCP ports
nft add map inet filter tcp_ports_forward6 '{ typeof ip6 saddr . ip6 daddr . tcp dport : verdict; flags interval; }'
nft add element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 21 : accept }'
nft add element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 23 : accept }'
nft add element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 25 : accept }'
nft add element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 53 : accept }'
nft add element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 80 : accept }'
nft add element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 443 : accept }'
nft add element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 853 : accept }'
nft add element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 4460 : accept }'
nft add element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 5349 : accept }'
nft add element inet filter tcp_ports_forward6 '{ 2000::/3 . 2000::/3 . 443 : accept }'
nft add element inet filter tcp_ports_forward6 '{ 2000::/3 . 2000::/3 . 853 : accept }'
nft add element inet filter tcp_ports_forward6 '{ 2000::/3 . 2000::/3 . 4460 : accept }'
nft add element inet filter tcp_ports_forward6 '{ 2000::/3 . 2000::/3 . 5349 : accept }'
IPv4 forward UDP ports
nft add map inet filter udp_ports_forward4 '{ typeof ip saddr . ip daddr . udp dport : verdict; flags interval; }'
nft add element inet filter udp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 53 : accept }'
nft add element inet filter udp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 53 : accept }'
nft add element inet filter udp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 53 : accept }'
nft add element inet filter udp_ports_forward4 '{ 10.0.0.0/8 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter udp_ports_forward4 '{ 172.16.0.0/12 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter udp_ports_forward4 '{ 192.168.0.0/16 . 0.0.0.0/0 . 443 : accept }'
IPv6 forward UDP ports
nft add map inet filter udp_ports_forward6 '{ typeof ip6 saddr . ip6 daddr . udp dport : verdict; flags interval; }'
nft add element inet filter udp_ports_forward6 '{ 2000::/3 . ::/0 . 443 : accept }'
NAT Forwarding ports for IPv4
nft add map inet filter tcp_ports_nat_forward4 '{ type inet_service : ipv4_addr; }'
nft add map inet filter udp_ports_nat_forward4 '{ type inet_service : ipv4_addr; }'
Masquerading
nft add map inet filter masquerade_networks4 '{ typeof oif . ip saddr : verdict; flags interval; }'
Rules
Metered ICMP port unreachable
nft add rule inet filter reject_with_icmp_port_unreachable_metered add @icmp_egress_meter4 '{ ip daddr timeout 4s limit rate 3/second }' counter reject with icmpx type port-unreachable
nft add rule inet filter reject_with_icmp_port_unreachable_metered add @icmp_egress_meter6 '{ ip6 daddr timeout 4s limit rate 3/second }' counter reject with icmpx type port-unreachable
Un-metered ICMP port unreachable
nft add rule inet filter reject_with_icmp_port_unreachable counter reject with icmpx type port-unreachable
Metered ICMP host unreachable
nft add rule inet filter reject_with_icmp_host_unreachable_metered add @icmp_egress_meter4 '{ ip daddr timeout 4s limit rate 3/second }' counter reject with icmpx type host-unreachable
nft add rule inet filter reject_with_icmp_host_unreachable_metered add @icmp_egress_meter6 '{ ip6 daddr timeout 4s limit rate 3/second }' counter reject with icmpx type host-unreachable
Un-Metered ICMP host unreachable
nft add rule inet filter reject_with_icmp_host_unreachable counter reject with icmpx type host-unreachable
Metered ICMP no route
nft add rule inet filter reject_with_icmp_no_route_metered add @icmp_egress_meter4 '{ ip daddr timeout 4s limit rate 3/second }' counter reject with icmpx type no-route
nft add rule inet filter reject_with_icmp_no_route_metered add @icmp_egress_meter6 '{ ip6 daddr timeout 4s limit rate 3/second }' counter reject with icmpx type no-route
Un-Metered ICMP no route
nft add rule inet filter reject_with_icmp_no_route counter reject with icmpx type no-route
Metered ICMP admin prohibited
nft add rule inet filter reject_with_icmp_admin_prohibited add @icmp_egress_meter4 '{ ip daddr timeout 4s limit rate 3/second }' counter reject with icmpx type admin-prohibited
nft add rule inet filter reject_with_icmp_admin_prohibited_metered add @icmp_egress_meter6 '{ ip6 daddr timeout 4s limit rate 3/second }' counter reject with icmpx type admin-prohibited
Un-Metered ICMP admin-prohibited
nft add rule inet filter reject_with_icmp_admin_prohibited counter reject with icmpx type admin-prohibited
icmp_in
nft add rule inet filter icmp_in ip saddr . ip daddr . icmp type vmap @icmp_types_in4
nft add rule inet filter icmp_in ip6 saddr . ip6 daddr . icmpv6 type vmap @icmp_types_in6
tcp_in
nft add rule inet filter tcp_in ct state established accept
nft add rule inet filter tcp_in ip saddr . ip daddr . tcp dport vmap @tcp_ports_in4
nft add rule inet filter tcp_in ip6 saddr . ip6 daddr . tcp dport vmap @tcp_ports_in6
udp_in
nft add rule inet filter udp_in ct state established accept
nft add rule inet filter udp_in ip saddr . ip daddr . udp dport vmap @udp_ports_in4
nft add rule inet filter udp_in ip6 saddr . ip6 daddr . udp dport vmap @udp_ports_in6
ether_in
nft add rule inet filter ether_in ip protocol vmap '{ tcp : jump tcp_in, udp : jump udp_in , icmp : jump icmp_in }'
nft add rule inet filter ether_in ip6 nexthdr vmap '{ tcp : jump tcp_in, udp : jump udp_in , icmpv6 : jump icmp_in, ipv6-icmp: jump icmp_in }'
input
nft add rule inet filter input meta iiftype vmap '{ loopback: accept }'
nft add rule inet filter input ip saddr vmap @drop_bogons4
nft add rule inet filter input ip6 saddr vmap @drop_bogons6
nft add rule inet filter input meta iiftype vmap '{ ether: jump ether_in }'
icmp_forward
nft add rule inet filter icmp_forward ip saddr . ip daddr . icmp type vmap @icmp_types_forward4
nft add rule inet filter icmp_forward ip6 saddr . ip6 daddr . icmpv6 type vmap @icmp_types_forward6
tcp_forward
nft add rule inet filter tcp_forward ct state established accept
nft add rule inet filter tcp_forward ip saddr . ip daddr . tcp dport vmap @tcp_ports_forward4
nft add rule inet filter tcp_forward ip6 saddr . ip6 daddr . tcp dport vmap @tcp_ports_forward6
udp_forward
nft add rule inet filter udp_forward ct state established accept
nft add rule inet filter udp_forward ip saddr . ip daddr . udp dport vmap @udp_ports_forward4
nft add rule inet filter udp_forward ip6 saddr . ip6 daddr . udp dport vmap @udp_ports_forward6
ether_forward
nft add rule inet filter ether_forward ip saddr . ip daddr . ct state vmap @default_forward4
nft add rule inet filter ether_forward ip6 saddr . ip6 daddr . ct state vmap @default_forward6
nft add rule inet filter ether_forward ip protocol vmap '{ tcp : jump tcp_forward, udp : jump udp_forward , icmp : jump icmp_forward }'
forward
nft add rule inet filter forward ip saddr vmap @drop_bogons4
nft add rule inet filter forward ip6 saddr vmap @drop_bogons6
nft add rule inet filter forward meta oiftype vmap '{ ether: jump ether_forward }'
ICMP echo-reply rate limit
nft add rule inet filter icmp_echo_reply_rate_limit add @icmp_egress_meter4 '{ ip saddr timeout 4s limit rate 3/second }' accept
nft add rule inet filter icmp_echo_reply_rate_limit add @icmp_egress_meter6 '{ ip6 saddr timeout 4s limit rate 3/second }' accept
icmp_out
nft add rule inet filter icmp_out ip saddr . ip daddr . icmp type vmap @icmp_types_out4
nft add rule inet filter icmp_out ip6 saddr . ip6 daddr . icmpv6 type vmap @icmp_types_out6
tcp_out
nft add rule inet filter tcp_out ct state established accept
nft add rule inet filter tcp_out ip saddr . ip daddr . tcp dport vmap @tcp_ports_out4
nft add rule inet filter tcp_out ip6 saddr . ip6 daddr . tcp dport vmap @tcp_ports_out6
udp_out
nft add rule inet filter udp_out ct state established accept
nft add rule inet filter udp_out ip saddr . ip daddr . udp dport vmap @udp_ports_out4
nft add rule inet filter udp_out ip6 saddr . ip6 daddr . udp dport vmap @udp_ports_out6
ether_out
nft add rule inet filter ether_out ip protocol vmap '{ tcp : jump tcp_out, udp : jump udp_out, icmp : jump icmp_out }'
nft add rule inet filter ether_out ip6 nexthdr vmap '{ tcp : jump tcp_out, udp : jump udp_out, icmpv6 : jump icmp_out, ipv6-icmp: jump icmp_out }'
masq
nft add rule inet filter masq ip daddr vmap @drop_bogons4
nft add rule inet filter masq ip daddr @local_networks return
nft add rule inet filter masq masquerade
output
nft add rule inet filter output meta oiftype vmap '{ loopback: accept }'
nft add rule inet filter output ip daddr vmap @drop_bogons4
nft add rule inet filter output ip6 daddr vmap @drop_bogons6
nft add rule inet filter output meta oiftype vmap '{ ether: jump ether_out }'
prerouting
nft add rule inet filter prerouting ip protocol tcp dnat to tcp dport map @tcp_ports_nat_forward4
nft add rule inet filter prerouting ip protocol udp dnat to udp dport map @udp_ports_nat_forward4
postrouting
nft add rule inet filter postrouting oif . ip saddr vmap @masquerade_networks4
Default chain policies
nft add chain inet filter input '{ policy drop; }'
nft add chain inet filter forward '{ policy drop; }'
nft add chain inet filter output '{ policy drop; }'
Logging
nft add rule inet filter input log prefix "input" group 1
nft add rule inet filter input counter
nft add rule inet filter forward log prefix "forward" group 1
nft add rule inet filter forward counter
nft add rule inet filter output log prefix "output" group 1
nft add rule inet filter output counter
Viewing logged packets
Dropped packets are logged in pcap format: tcpdump -vvv -n -e -ttt -i nflog:1 -XX
00:00:00.000207 version 0, resource ID 1, family IPv4 (2), length 128: (tos 0x10, ttl 64, id 39694, offset 0, flags [DF], proto UDP (17), length 76)
10.211.55.11.50429 > 108.59.2.24.123: [bad udp cksum 0xb07a -> 0x5c51!] NTPv4, Client, length 48
Root Delay: 0.000000, Root dispersion: 0.000000, Reference-ID: (unspec)
Reference Timestamp: 0.000000000
Originator Timestamp: 0.000000000
Receive Timestamp: 0.000000000
Transmit Timestamp: 3883429715.031809542 (2023-01-23T02:28:35Z)
Originator - Receive Timestamp: 0.000000000
Originator - Transmit Timestamp: 3883429715.031809542 (2023-01-23T02:28:35Z)
0x0000: 0200 0001 0800 0100 0800 0300 0b00 0a00 ................
0x0010: 6f75 7470 7574 0000 0800 0500 0000 0002 output..........
0x0020: 0800 0b00 0000 0069 0800 0e00 0000 006f .......i.......o
0x0030: 5000 0900 4510 004c 9b0e 4000 4011 ef51 P...E..L..@.@..Q
0x0040: 0ad3 370b 6c3b 0218 c4fd 007b 0038 b07a ..7.l;.....{.8.z
0x0050: 2300 0000 0000 0000 0000 0000 0000 0000 #...............
0x0060: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0x0070: 0000 0000 0000 0000 e778 6f53 0824 ab92 .........xoS.$..
Per-chain logging
Metered ICMP port unreachable
nft add rule inet filter reject_with_icmp_port_unreachable_metered log prefix "reject_with_icmp_port_unreachable_metered" group 1
nft add rule inet filter reject_with_icmp_port_unreachable_metered counter drop
Metered ICMP host unreachable
nft add rule inet filter reject_with_icmp_host_unreachable_metered log prefix "reject_with_icmp_host_unreachable_metered" group 1
nft add rule inet filter reject_with_icmp_host_unreachable_metered counter drop
Metered ICMP no route
nft add rule inet filter reject_with_icmp_no_route_metered log prefix "reject_with_icmp_no_route_metered" group 1
nft add rule inet filter reject_with_icmp_no_route_metered counter drop
Metered ICMP admin prohibited
nft add rule inet filter reject_with_icmp_admin_prohibited_metered log prefix "reject_with_icmp_admin_prohibited_metered" group 1
nft add rule inet filter reject_with_icmp_admin_prohibited_metered counter drop
Ingress ICMP
nft add rule inet filter icmp_in log prefix "icmp_in" group 1
nft add rule inet filter icmp_in counter drop
Ingress TCP
nft add rule inet filter tcp_in log prefix "tcp_in" group 1
nft add rule inet filter tcp_in counter drop
Ingress UDP
nft add rule inet filter udp_in log prefix "udp_in" group 1
nft add rule inet filter udp_in counter drop
Ingress Ether
nft add rule inet filter ether_in log prefix "ether_in" group 1
nft add rule inet filter ether_in counter drop
Forward ICMP
nft add rule inet filter icmp_forward log prefix "icmp_forward" group 1
nft add rule inet filter icmp_forward counter drop
Forward TCP
nft add rule inet filter tcp_forward log prefix "tcp_forward" group 1
nft add rule inet filter tcp_forward counter drop
Forward UDP
nft add rule inet filter udp_forward log prefix "udp_forward" group 1
nft add rule inet filter udp_forward counter drop
Forward Ether
nft add rule inet filter ether_forward log prefix "ether_forward" group 1
nft add rule inet filter ether_forward counter drop
Egress ICMP echo replies
nft add rule inet filter icmp_echo_reply_rate_limit log prefix "icmp_echo_reply_rate_limit" group 1
nft add rule inet filter icmp_echo_reply_rate_limit counter drop
Egress ICMP
nft add rule inet filter icmp_out log prefix "icmp_out" group 1
nft add rule inet filter icmp_out counter drop
Egress TCP
nft add rule inet filter tcp_out log prefix "tcp_out" group 1
nft add rule inet filter tcp_out counter drop
Egress UDP
nft add rule inet filter udp_out log prefix "udp_out" group 1
nft add rule inet filter udp_out counter drop
Egress Ether
nft add rule inet filter ether_out log prefix "ether_out" group 1
nft add rule inet filter ether_out counter drop
Filter state persistence
Configuration format
If you are starting from here you can copy / paste this section to /etc/nftables.conf. Otherwise the current filter state can be made to persist with:
nft -a -s list ruleset | tee /etc/nftables.conf
table inet filter { # handle 60
set icmp_egress_meter4 { # handle 34
type ipv4_addr
size 8
flags dynamic,timeout
}
set icmp_egress_meter6 { # handle 35
type ipv6_addr
size 8
flags dynamic,timeout
}
map drop_bogons4 { # handle 36
type ipv4_addr : verdict
flags interval
elements = { 0.0.0.0/8 : jump bogon, 10.0.0.0/8 : continue,
100.64.0.0/10 : jump bogon, 127.0.0.0/8 : jump bogon,
169.254.0.0/16 : continue, 172.16.0.0/12 : continue,
192.0.0.0/24 : jump bogon, 192.0.2.0/24 : jump bogon,
192.168.0.0/16 : continue, 198.18.0.0/15 : jump bogon,
198.51.100.0/24 : jump bogon, 203.0.113.0/24 : jump bogon,
224.0.0.0/4 : continue, 240.0.0.0/4 : jump bogon }
}
set local_networks { # handle 37
type ipv4_addr
flags interval
elements = { 10.0.0.0/8, 169.254.0.0/16,
172.16.0.0/12, 192.168.0.0/16 }
}
map drop_bogons6 { # handle 38
type ipv6_addr : verdict
flags interval
elements = { ::/96 : jump bogon,
::ffff:0.0.0.0/96 : jump bogon,
100::/64 : jump bogon,
2001::/40 : jump bogon,
2001:0:a00::/40 : jump bogon,
2001:0:7f00::/40 : jump bogon,
2001:0:a9fe::/48 : jump bogon,
2001:0:ac10::/44 : jump bogon,
2001:0:c000::/56 : jump bogon,
2001:0:c000:200::/56 : jump bogon,
2001:0:c0a8::/48 : jump bogon,
2001:0:c612::/47 : jump bogon,
2001:0:c633:6400::/56 : jump bogon,
2001:0:cb00:7100::/56 : jump bogon,
2001:0:e000::/36 : jump bogon,
2001:0:f000::/36 : jump bogon,
2001:10::/28 : jump bogon,
2001:db8::/32 : jump bogon,
2002::/24 : jump bogon,
2002:a00::/24 : jump bogon,
2002:7f00::/24 : jump bogon,
2002:a9fe::/32 : jump bogon,
2002:ac10::/28 : jump bogon,
2002:c000::/40 : jump bogon,
2002:c000:200::/40 : jump bogon,
2002:c0a8::/32 : jump bogon,
2002:c612::/31 : jump bogon,
2002:c633:6400::/40 : jump bogon,
2002:cb00:7100::/40 : jump bogon,
2002:e000::/20 : jump bogon,
2002:f000::/20 : jump bogon,
fc00::/7 : continue,
fe80::/10 : continue,
fec0::/10 : jump bogon,
ff00::/8 : continue }
}
map reject_or_drop_port4 { # handle 39
typeof ip saddr . ip daddr : verdict
flags interval
elements = { 10.0.0.0/8 . 10.0.0.0/8 : jump reject_with_icmp_port_unreachable,
172.16.0.0/12 . 172.16.0.0/12 : jump reject_with_icmp_port_unreachable,
192.168.0.0/16 . 192.168.0.0/16 : jump reject_with_icmp_port_unreachable,
169.254.0.0/16 . 169.254.0.0/16 : jump reject_with_icmp_port_unreachable,
0.0.0.0/0 . 0.0.0.0/0 : jump reject_with_icmp_port_unreachable_metered }
}
map reject_or_drop_port6 { # handle 40
typeof ip6 saddr . ip6 daddr : verdict
flags interval
elements = { fe80::/10 . fe80::/10 : jump reject_with_icmp_port_unreachable,
fc00::/7 . fc00::/7 : jump reject_with_icmp_port_unreachable,
::/0 . ::/0 : jump reject_with_icmp_port_unreachable_metered }
}
map icmp_types_in4 { # handle 41
typeof ip saddr . ip daddr . icmp type : verdict
flags interval
elements = { 0.0.0.0/0 . 0.0.0.0/0 . echo-request : accept,
0.0.0.0/0 . 0.0.0.0/0 . echo-reply : accept,
0.0.0.0/0 . 0.0.0.0/0 . destination-unreachable : accept }
}
map icmp_types_in6 { # handle 42
typeof ip6 saddr . ip6 daddr . icmpv6 type : verdict
flags interval
elements = { fe80::/10 . fe80::/10 . echo-request : accept,
fe80::/10 . ff00::/8 . echo-request : accept,
fc00::/7 . fc00::/7 . echo-request : accept,
fe80::/10 . fe80::/10 . echo-reply : accept,
fe80::/10 . ff00::/8 . echo-reply : accept,
fc00::/7 . fc00::/7 . echo-reply : accept,
fe80::/10 . fe80::/10 . nd-neighbor-solicit : accept,
fc00::/7 . ff00::/8 . nd-neighbor-solicit : accept,
fc00::/7 . fc00::/7 . nd-neighbor-solicit : accept,
fc00::/7 . fc00::/7 . nd-neighbor-advert : accept,
fe80::/10 . fe80::/10 . nd-neighbor-advert : accept,
fe80::/10 . ff00::/8 . nd-router-advert : accept,
fe80::/10 . fe80::/10 . nd-router-advert : accept }
}
map tcp_ports_in4 { # handle 43
typeof ip saddr . ip daddr . tcp dport : verdict
flags interval
elements = { 0.0.0.0/0 . 0.0.0.0/0 . 22 : accept }
}
map tcp_ports_in6 { # handle 44
typeof ip6 saddr . ip6 daddr . tcp dport : verdict
flags interval
elements = { ::/0 . ::/0 . 22 : accept }
}
map udp_ports_in4 { # handle 45
typeof ip saddr . ip daddr . udp dport : verdict
flags interval
elements = { 169.254.0.0/16 . 169.254.0.0/16 . 68 : accept,
10.0.0.0/8 . 10.0.0.0/8 . 68 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 68 : accept,
192.160.0.0/12 . 192.168.0.0/16 . 68 : accept,
10.0.0.0/8 . 10.0.0.0/8 . 137 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 137 : accept,
192.160.0.0/12 . 192.168.0.0/16 . 137 : accept,
10.0.0.0/8 . 10.0.0.0/8 . 5353 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 5353 : accept,
192.160.0.0/12 . 192.168.0.0/16 . 5353 : accept,
10.0.0.0/8 . 224.0.0.0/4 . 5353 : accept,
172.16.0.0/12 . 224.0.0.0/4 . 5353 : accept,
192.160.0.0/12 . 224.0.0.0/4 . 5353 : accept }
}
map udp_ports_in6 { # handle 46
typeof ip6 saddr . ip6 daddr . udp dport : verdict
flags interval
elements = { fe80::/10 . ff00::/8 . 546 : accept,
fe80::/10 . ff00::/8 . 5353 : accept,
fc00::/7 . ff00::/8 . 5353 : accept }
}
map default_forward4 { # handle 47
typeof ip saddr . ip daddr . ct state : verdict
flags interval
elements = { 169.254.0.0/16 . 0.0.0.0/0 . new : jump wont_forward,
0.0.0.0/0 . 169.254.0.0/16 . new : jump wont_forward,
10.0.0.0/8 . 172.16.0.0/12 . new : jump reject_with_icmp_no_route,
10.0.0.0/8 . 192.168.0.0/16 . new : jump reject_with_icmp_no_route,
172.16.0.0/12 . 10.0.0.0/8 . new : jump reject_with_icmp_no_route,
172.16.0.0/12 . 192.168.0.0/16 . new : jump reject_with_icmp_no_route,
192.168.0.0/16 . 10.0.0.0/8 . new : jump reject_with_icmp_no_route,
192.168.0.0/16 . 172.16.0.0/12 . new : jump reject_with_icmp_no_route,
10.0.0.0/8 . 10.0.0.0/8 . new : continue,
10.0.0.0/8 . 10.0.0.0/8 . established : accept,
172.16.0.0/12 . 172.16.0.0/12 . new : continue,
172.16.0.0/12 . 172.16.0.0/12 . established : accept,
192.168.0.0/16 . 192.168.0.0/16 . new : continue,
192.168.0.0/16 . 192.168.0.0/16 . established : accept,
10.0.0.0/8 . 0.0.0.0/0 . new : continue,
172.16.0.0/12 . 0.0.0.0/0 . new : continue,
192.168.0.0/16 . 0.0.0.0/0 . new : continue,
0.0.0.0/0 . 10.0.0.0/8 . established : accept,
0.0.0.0/0 . 172.16.0.0/12 . established : accept,
0.0.0.0/0 . 192.168.0.0/16 . established : accept }
}
map default_forward6 { # handle 48
typeof ip6 saddr . ip6 daddr . ct state : verdict
flags interval
elements = { fe80::/10 . ::/0 . new : jump wont_forward,
::/0 . fe80::/10 . new : jump wont_forward,
fc00::/7 . fc00::/7 . new : continue,
fc00::/7 . fc00::/7 . established : accept }
}
map icmp_types_out4 { # handle 49
typeof ip saddr . ip daddr . icmp type : verdict
flags interval
elements = { 10.0.0.0/8 . 0.0.0.0/0 . echo-request : accept,
172.16.0.0/12 . 0.0.0.0/0 . echo-request : accept,
192.168.0.0/16 . 0.0.0.0/0 . echo-request : accept,
10.0.0.0/8 . 10.0.0.0/8 . echo-reply : accept,
172.16.0.0/12 . 172.16.0.0/12 . echo-reply : accept,
192.168.0.0/16 . 192.168.0.0/16 . echo-reply : accept,
10.0.0.0/8 . 10.0.0.0/8 . destination-unreachable : accept,
172.16.0.0/12 . 172.16.0.0/12 . destination-unreachable : accept,
192.168.0.0/16 . 192.168.0.0/16 . destination-unreachable : accept,
0.0.0.0/0 . 0.0.0.0/0 . echo-reply : jump icmp_echo_reply_rate_limit }
}
map icmp_types_out6 { # handle 50
typeof ip6 saddr . ip6 daddr . icmpv6 type : verdict
flags interval
elements = { fe80::/10 . ff00::/8 . echo-request : accept,
fc00::/7 . fc00::/7 . echo-reply : accept,
2000::/3 . ::/0 . echo-reply : jump icmp_echo_reply_rate_limit,
fc00::/7 . fc00::/7 . nd-neighbor-advert : accept,
fe80::/10 . fe80::/10 . nd-neighbor-advert : accept,
fc00::/7 . ff00::/8 . nd-neighbor-solicit : accept,
fe80::/10 . fc00::/7 . nd-neighbor-solicit : accept,
fe80::/10 . fe80::/10 . nd-neighbor-solicit : accept,
fe80::/10 . ff00::/8 . nd-router-solicit : accept }
}
map tcp_ports_out4 { # handle 51
typeof ip saddr . ip daddr . tcp dport : verdict
flags interval
elements = { 10.0.0.0/8 . 0.0.0.0/0 . 22 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 22 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 22 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 443 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 443 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 443 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 853 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 853 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 853 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 4460 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 4460 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 4460 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 5349 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 5349 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 5349 : accept }
}
map tcp_ports_out6 { # handle 52
typeof ip6 saddr . ip6 daddr . tcp dport : verdict
flags interval
elements = { fc00::/7 . fc00::/7 . 443 : accept,
fc00::/7 . fc00::/7 . 853 : accept,
fc00::/7 . fc00::/7 . 4460 : accept,
fc00::/7 . fc00::/7 . 5349 : accept,
2000::/3 . 2000::/3 . 443 : accept,
2000::/3 . 2000::/3 . 853 : accept,
2000::/3 . 2000::/3 . 4460 : accept,
2000::/3 . 2000::/3 . 5349 : accept }
}
map udp_ports_out4 { # handle 53
typeof ip saddr . ip daddr . udp dport : verdict
flags interval
elements = { 10.0.0.0/8 . 10.0.0.0/8 . 67 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 67 : accept,
192.168.0.0/16 . 192.168.0.0/16 . 67 : accept,
169.254.0.0/16 . 169.254.0.0/16 . 67 : accept,
10.0.0.0/8 . 10.0.0.0/8 . 53 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 53 : accept,
192.168.0.0/16 . 192.168.0.0/16 . 53 : accept,
10.0.0.0/8 . 10.0.0.0/8 . 137 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 137 : accept,
192.168.0.0/16 . 192.168.0.0/16 . 137 : accept,
10.0.0.0/8 . 224.0.0.0/4 . 5353 : accept,
172.16.0.0/12 . 224.0.0.0/4 . 5353 : accept,
192.168.0.0/16 . 224.0.0.0/4 . 5353 : accept,
169.254.0.0/16 . 224.0.0.0/4 . 5353 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 443 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 443 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 443 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 1194 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 1194 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 1194 : accept }
}
map udp_ports_out6 { # handle 54
typeof ip6 saddr . ip6 daddr . udp dport : verdict
flags interval
elements = { fe80::/10 . ff00::/8 . 547 : accept,
2000::/3 . ::/0 . 443 : accept,
fc00::/7 . ff00::/8 . 5353 : accept }
}
map icmp_types_forward4 { # handle 55
typeof ip saddr . ip daddr . icmp type : verdict
flags interval
elements = { 10.0.0.0/8 . 0.0.0.0/0 . echo-request : accept,
172.16.0.0/12 . 0.0.0.0/0 . echo-request : accept,
192.168.0.0/16 . 0.0.0.0/0 . echo-request : accept,
10.0.0.0/8 . 10.0.0.0/8 . echo-reply : accept,
172.16.0.0/12 . 172.16.0.0/12 . echo-reply : accept,
192.168.0.0/16 . 192.168.0.0/16 . echo-reply : accept,
10.0.0.0/8 . 10.0.0.0/8 . destination-unreachable : accept,
172.16.0.0/12 . 172.16.0.0/12 . destination-unreachable : accept,
192.168.0.0/16 . 192.168.0.0/16 . destination-unreachable : accept,
0.0.0.0/0 . 0.0.0.0/0 . echo-reply : jump icmp_echo_reply_rate_limit }
}
map icmp_types_forward6 { # handle 56
typeof ip6 saddr . ip6 daddr . icmpv6 type : verdict
flags interval
elements = { fe80::/10 . ff00::/8 . echo-request : accept,
fc00::/7 . fc00::/7 . echo-reply : accept,
2000::/3 . ::/0 . echo-reply : jump icmp_echo_reply_rate_limit }
}
map tcp_ports_forward4 { # handle 57
typeof ip saddr . ip daddr . tcp dport : verdict
flags interval
elements = { 10.0.0.0/8 . 10.0.0.0/8 . 21 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 21 : accept,
192.168.0.0/16 . 192.168.0.0/16 . 21 : accept,
10.0.0.0/8 . 10.0.0.0/8 . 23 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 23 : accept,
192.168.0.0/16 . 192.168.0.0/16 . 23 : accept,
10.0.0.0/8 . 10.0.0.0/8 . 25 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 25 : accept,
192.168.0.0/16 . 192.168.0.0/16 . 25 : accept,
10.0.0.0/8 . 10.0.0.0/8 . 53 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 53 : accept,
192.168.0.0/16 . 192.168.0.0/16 . 53 : accept,
10.0.0.0/8 . 10.0.0.0/8 . 80 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 80 : accept,
192.168.0.0/16 . 192.168.0.0/16 . 80 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 22 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 22 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 22 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 443 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 443 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 443 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 853 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 853 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 853 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 4460 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 4460 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 4460 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 5349 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 5349 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 5349 : accept }
}
map tcp_ports_forward6 { # handle 58
typeof ip6 saddr . ip6 daddr . tcp dport : verdict
flags interval
elements = { fc00::/7 . fc00::/7 . 21 : accept,
fc00::/7 . fc00::/7 . 23 : accept,
fc00::/7 . fc00::/7 . 25 : accept,
fc00::/7 . fc00::/7 . 53 : accept,
fc00::/7 . fc00::/7 . 80 : accept,
fc00::/7 . fc00::/7 . 443 : accept,
fc00::/7 . fc00::/7 . 853 : accept,
fc00::/7 . fc00::/7 . 4460 : accept,
fc00::/7 . fc00::/7 . 5349 : accept,
2000::/3 . 2000::/3 . 443 : accept,
2000::/3 . 2000::/3 . 853 : accept,
2000::/3 . 2000::/3 . 4460 : accept,
2000::/3 . 2000::/3 . 5349 : accept }
}
map udp_ports_forward4 { # handle 59
typeof ip saddr . ip daddr . udp dport : verdict
flags interval
elements = { 10.0.0.0/8 . 10.0.0.0/8 . 53 : accept,
172.16.0.0/12 . 172.16.0.0/12 . 53 : accept,
192.168.0.0/16 . 192.168.0.0/16 . 53 : accept,
10.0.0.0/8 . 0.0.0.0/0 . 443 : accept,
172.16.0.0/12 . 0.0.0.0/0 . 443 : accept,
192.168.0.0/16 . 0.0.0.0/0 . 443 : accept }
}
map udp_ports_forward6 { # handle 60
typeof ip6 saddr . ip6 daddr . udp dport : verdict
flags interval
elements = { 2000::/3 . ::/0 . 443 : accept }
}
map tcp_ports_nat_forward4 { # handle 61
type inet_service : ipv4_addr
}
map udp_ports_nat_forward4 { # handle 62
type inet_service : ipv4_addr
}
map masquerade_networks4 { # handle 63
typeof oif . ip saddr : verdict
flags interval
}
chain input { # handle 1
type filter hook input priority filter; policy drop;
meta iiftype vmap { loopback : accept } # handle 89
ip saddr vmap @drop_bogons4 # handle 90
ip6 saddr vmap @drop_bogons6 # handle 91
meta iiftype vmap { ether : jump ether_in } # handle 93
log prefix "input" group 1 # handle 136
counter # handle 137
}
chain forward { # handle 2
type filter hook forward priority filter; policy drop;
ip saddr vmap @drop_bogons4 # handle 106
ip6 saddr vmap @drop_bogons6 # handle 107
meta oiftype vmap { ether : jump ether_forward } # handle 109
log prefix "forward" group 1 # handle 138
counter # handle 139
}
chain output { # handle 3
type filter hook output priority filter; policy drop;
meta oiftype vmap { loopback : accept } # handle 128
ip daddr vmap @drop_bogons4 # handle 129
ip6 daddr vmap @drop_bogons6 # handle 130
meta oiftype vmap { ether : jump ether_out } # handle 132
log prefix "output" group 1 # handle 140
counter # handle 141
}
chain prerouting { # handle 4
type nat hook prerouting priority 100; policy accept;
ip protocol tcp dnat ip to tcp dport map @tcp_ports_nat_forward4 # handle 133
ip protocol udp dnat ip to udp dport map @udp_ports_nat_forward4 # handle 134
}
chain postrouting { # handle 5
type nat hook postrouting priority srcnat; policy accept;
oif . ip saddr vmap @masquerade_networks4 # handle 135
}
chain masq { # handle 6
ip daddr vmap @drop_bogons4 # handle 124
ip daddr @local_networks return # handle 125
masquerade # handle 126
}
chain ether_in { # handle 7
ip protocol vmap { icmp : jump icmp_in, tcp : jump tcp_in, udp : jump udp_in } # handle 85
ip6 nexthdr vmap { tcp : jump tcp_in, udp : jump udp_in, ipv6-icmp : jump icmp_in } # handle 87
log prefix "ether_in" group 1 # handle 156
counter drop # handle 157
}
chain ether_out { # handle 8
ip protocol vmap { icmp : jump icmp_out, tcp : jump tcp_out, udp : jump udp_out } # handle 121
ip6 nexthdr vmap { tcp : jump tcp_out, udp : jump udp_out, ipv6-icmp : jump icmp_out } # handle 123
log prefix "ether_out" group 1 # handle 174
counter drop # handle 175
}
chain ether_forward { # handle 9
ip saddr . ip daddr . ct state vmap @default_forward4 # handle 102
ip6 saddr . ip6 daddr . ct state vmap @default_forward6 # handle 103
ip protocol vmap { icmp : jump icmp_forward, tcp : jump tcp_forward, udp : jump udp_forward } # handle 105
log prefix "ether_forward" group 1 # handle 164
counter drop # handle 165
}
chain icmp_in { # handle 10
ip saddr . ip daddr . icmp type vmap @icmp_types_in4 # handle 76
ip6 saddr . ip6 daddr . icmpv6 type vmap @icmp_types_in6 # handle 77
log prefix "icmp_in" group 1 # handle 150
counter drop # handle 151
}
chain icmp_out { # handle 11
ip saddr . ip daddr . icmp type vmap @icmp_types_out4 # handle 112
ip6 saddr . ip6 daddr . icmpv6 type vmap @icmp_types_out6 # handle 113
log prefix "icmp_out" group 1 # handle 168
counter drop # handle 169
}
chain icmp_forward { # handle 12
ip saddr . ip daddr . icmp type vmap @icmp_types_forward4 # handle 94
ip6 saddr . ip6 daddr . icmpv6 type vmap @icmp_types_forward6 # handle 95
log prefix "icmp_forward" group 1 # handle 158
counter drop # handle 159
}
chain icmp_echo_reply_rate_limit { # handle 13
add @icmp_egress_meter4 { ip saddr timeout 4s limit rate 3/second } accept # handle 110
add @icmp_egress_meter6 { ip6 saddr timeout 4s limit rate 3/second } accept # handle 111
log prefix "icmp_echo_reply_rate_limit" group 1 # handle 166
counter drop # handle 167
}
chain reject_with_icmp_port_unreachable_metered { # handle 14
add @icmp_egress_meter4 { ip daddr timeout 4s limit rate 3/second } counter reject # handle 64
add @icmp_egress_meter6 { ip6 daddr timeout 4s limit rate 3/second } counter reject # handle 65
log prefix "reject_with_icmp_port_unreachable_metered" group 1 # handle 142
counter drop # handle 143
}
chain reject_with_icmp_port_unreachable { # handle 15
counter reject # handle 66
}
chain reject_with_icmp_host_unreachable_metered { # handle 16
add @icmp_egress_meter4 { ip daddr timeout 4s limit rate 3/second } counter reject with icmpx host-unreachable # handle 67
add @icmp_egress_meter6 { ip6 daddr timeout 4s limit rate 3/second } counter reject with icmpx host-unreachable # handle 68
log prefix "reject_with_icmp_host_unreachable_metered" group 1 # handle 144
counter drop # handle 145
}
chain reject_with_icmp_host_unreachable { # handle 17
counter reject with icmpx host-unreachable # handle 69
}
chain reject_with_icmp_no_route_metered { # handle 18
add @icmp_egress_meter4 { ip daddr timeout 4s limit rate 3/second } counter reject with icmpx no-route # handle 70
add @icmp_egress_meter6 { ip6 daddr timeout 4s limit rate 3/second } counter reject with icmpx no-route # handle 71
log prefix "reject_with_icmp_no_route_metered" group 1 # handle 146
counter drop # handle 147
}
chain reject_with_icmp_no_route { # handle 19
counter reject with icmpx no-route # handle 72
}
chain reject_with_icmp_admin_prohibited_metered { # handle 20
add @icmp_egress_meter6 { ip6 daddr timeout 4s limit rate 3/second } counter reject with icmpx admin-prohibited # handle 74
log prefix "reject_with_icmp_admin_prohibited_metered" group 1 # handle 148
counter drop # handle 149
}
chain reject_with_icmp_admin_prohibited { # handle 21
add @icmp_egress_meter4 { ip daddr timeout 4s limit rate 3/second } counter reject with icmpx admin-prohibited # handle 73
counter reject with icmpx admin-prohibited # handle 75
}
chain tcp_in { # handle 22
ct state established accept # handle 78
ip saddr . ip daddr . tcp dport vmap @tcp_ports_in4 # handle 79
ip6 saddr . ip6 daddr . tcp dport vmap @tcp_ports_in6 # handle 80
log prefix "tcp_in" group 1 # handle 152
counter drop # handle 153
}
chain tcp_out { # handle 23
ct state established accept # handle 114
ip saddr . ip daddr . tcp dport vmap @tcp_ports_out4 # handle 115
ip6 saddr . ip6 daddr . tcp dport vmap @tcp_ports_out6 # handle 116
log prefix "tcp_out" group 1 # handle 170
counter drop # handle 171
}
chain tcp_forward { # handle 24
ct state established accept # handle 96
ip saddr . ip daddr . tcp dport vmap @tcp_ports_forward4 # handle 97
ip6 saddr . ip6 daddr . tcp dport vmap @tcp_ports_forward6 # handle 98
log prefix "tcp_forward" group 1 # handle 160
counter drop # handle 161
}
chain udp_in { # handle 25
ct state established accept # handle 81
ip saddr . ip daddr . udp dport vmap @udp_ports_in4 # handle 82
ip6 saddr . ip6 daddr . udp dport vmap @udp_ports_in6 # handle 83
log prefix "udp_in" group 1 # handle 154
counter drop # handle 155
}
chain udp_out { # handle 26
ct state established accept # handle 117
ip saddr . ip daddr . udp dport vmap @udp_ports_out4 # handle 118
ip6 saddr . ip6 daddr . udp dport vmap @udp_ports_out6 # handle 119
log prefix "udp_out" group 1 # handle 172
counter drop # handle 173
}
chain udp_forward { # handle 27
ct state established accept # handle 99
ip saddr . ip daddr . udp dport vmap @udp_ports_forward4 # handle 100
ip6 saddr . ip6 daddr . udp dport vmap @udp_ports_forward6 # handle 101
log prefix "udp_forward" group 1 # handle 162
counter drop # handle 163
}
chain bogon { # handle 28
log prefix "bogon" group 1 # handle 29
counter drop # handle 30
}
chain wont_forward { # handle 31
log prefix "wont_forward" group 1 # handle 32
counter drop # handle 33
}
}
Flushing the ruleset
Something that helps me is adding flush ruleset to the beginning of the new /etc/nftables.conf file so that the existing state is flushed before loading the file.
Hardening
nft delete element inet filter udp_ports_in4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 137 : accept }' nft delete element inet filter udp_ports_in4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 137 : accept }' nft delete element inet filter udp_ports_in4 '{ 192.168.0.0/12 . 192.168.0.0/16 . 137 : accept }'
nft delete element inet filter udp_ports_in4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 5353 : accept }'
nft delete element inet filter udp_ports_in4 '{ 192.168.0.0/12 . 192.168.0.0/16 . 5353 : accept }'
nft delete element inet filter udp_ports_in4 '{ 10.0.0.0/8 . 224.0.0.0/4 . 5353 : accept }'
nft delete element inet filter udp_ports_in4 '{ 172.16.0.0/12 . 224.0.0.0/4 . 5353 : accept }'
nft delete element inet filter udp_ports_in4 '{ 192.168.0.0/12 . 224.0.0.0/4 . 5353 : accept }'
nft delete element inet filter udp_ports_in6 '{ fe80::/10 . ff00::/8 . 5353 : accept }'
nft delete element inet filter udp_ports_in6 '{ fc00::/7 . ff00::/8 . 5353 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 21 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 21 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 21 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 23 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 23 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 23 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 25 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 25 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 25 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 53 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 53 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 53 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 80 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 80 : accept }'
nft delete element inet filter tcp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 80 : accept }'
nft delete element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 21 : accept }'
nft delete element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 23 : accept }'
nft delete element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 25 : accept }'
nft delete element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 53 : accept }'
nft delete element inet filter tcp_ports_forward6 '{ fc00::/7 . fc00::/7 . 80 : accept }'
nft delete element inet filter udp_ports_forward4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 53 : accept }'
nft delete element inet filter udp_ports_forward4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 53 : accept }'
nft delete element inet filter udp_ports_forward4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 53 : accept }'
nft delete element inet filter udp_ports_out4 '{ 10.0.0.0/8 . 10.0.0.0/8 . 137 : accept }'
nft delete element inet filter udp_ports_out4 '{ 172.16.0.0/12 . 172.16.0.0/12 . 137 : accept }'
nft delete element inet filter udp_ports_out4 '{ 192.168.0.0/16 . 192.168.0.0/16 . 137 : accept }'
nft delete element inet filter udp_ports_out4 '{ 10.0.0.0/8 . 224.0.0.0/4 . 5353 : accept }'
nft delete element inet filter udp_ports_out4 '{ 172.16.0.0/12 . 224.0.0.0/4 . 5353 : accept }'
nft delete element inet filter udp_ports_out4 '{ 192.168.0.0/16 . 224.0.0.0/4 . 5353 : accept }'
nft delete element inet filter udp_ports_out4 '{ 169.254.0.0/16 . 224.0.0.0/4 . 5353 : accept }'
nft delete element inet filter udp_ports_out6 '{ fc00::/7 . ff00::/8 . 5353 : accept }'
Testing
Testing can be performed from another client using scapy:
from scapy.all import *; from ipaddress import IPv4Network as N4; [send(IP(src=str(x), dst='10.211.55.11')/ICMP(id=1, seq=1), loop=0) for x, y in zip(N4('65.146.55.0/24').hosts(), range(254))]
On the firewall: tcpdump -vvv -n -e -ttt -i nflog:1 -XX ... 00:00:00.000036 version 0, resource ID 1, family IPv4 (2), length 84: (tos 0x0, ttl 64, id 41167, offset 0, flags [none], proto ICMP (1), length 28) 10.211.55.11 > 65.146.55.254: ICMP echo reply, id 1, seq 1, length 8
0x0010: 6963 6d70 5f65 6368 6f5f 7265 706c 795f icmp_echo_reply_
0x0020: 7261 7465 5f6c 696d 6974 0000 0800 0500 rate_limit......
0x0030: 0000 0002 2000 0900 4500 001c a0cf 0000 ........E.......
0x0040: 4001 1ea4 0ad3 370b 4192 37fe 0000 fffd @.....7.A.7.....
0x0050: 0001 0001 ....
Custom Docker support
Preparation
apt -y install docker.io systemctl stop docker rm -rf /var/lib/docker/* rm /etc/docker/key.json echo DOCKER_OPTS="-H unix:///var/run/docker.sock --iptables=false --ip-masq=false --userns-remap=default --bip=100.64.80.1/20 --default-address-pool='base=100.64.96.0/20,size=28'" > /etc/default/docker
Custom prefix
This example will use the 100.64.0.0/17 prefix for docker. Earlier that prefix was marked to be discarded as a bogon, that
can be changed easily:
nft delete element inet filter drop_bogons4 '{ 100.64.0.0/10 : jump bogon }' nft add element inet filter drop_bogons4 '{ 100.64.0.0/17 : continue }' nft add element inet filter drop_bogons4 '{ 100.64.128.0/17 : jump bogon }' nft add element inet filter drop_bogons4 '{ 100.65.0.0/16 : jump bogon }' nft add element inet filter drop_bogons4 '{ 100.66.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.70.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.72.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.74.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.76.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.78.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.80.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.82.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.84.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.86.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.88.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.90.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.92.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.94.0.0/15 : jump bogon }'
nft add element inet filter drop_bogons4 '{ 100.96.0.0/11 : jump bogon }'
nft add element inet filter local_networks '{ 100.64.0.0/17 }'
Verify this with the nft list map inet filter drop_bogons4 command:
table inet filter { map drop_bogons4 { type ipv4_addr : verdict flags interval elements = { 0.0.0.0/8 : jump bogon, 10.0.0.0/8 : continue, 100.64.0.0/17 : continue, 100.64.128.0/17 : jump bogon,
100.68.0.0/15 : jump bogon, 100.70.0.0/15 : jump bogon,
100.72.0.0/15 : jump bogon, 100.74.0.0/15 : jump bogon,
100.76.0.0/15 : jump bogon, 100.78.0.0/15 : jump bogon,
100.80.0.0/15 : jump bogon, 100.82.0.0/15 : jump bogon,
100.84.0.0/15 : jump bogon, 100.86.0.0/15 : jump bogon,
100.88.0.0/15 : jump bogon, 100.90.0.0/15 : jump bogon,
100.92.0.0/15 : jump bogon, 100.94.0.0/15 : jump bogon,
100.96.0.0/11 : jump bogon, 127.0.0.0/8 : jump bogon,
169.254.0.0/16 : continue, 172.16.0.0/12 : continue,
192.0.0.0/24 : jump bogon, 192.0.2.0/24 : jump bogon,
192.168.0.0/16 : continue, 198.18.0.0/15 : jump bogon,
198.51.100.0/24 : jump bogon, 203.0.113.0/24 : jump bogon,
224.0.0.0/4 : continue, 240.0.0.0/4 : jump bogon }
}
}
Forward verdict map
nft add element inet filter default_forward4 '{ 100.64.0.0/20 . 0.0.0.0/0 . new : jump reject_with_icmp_no_route }'
nft add element inet filter default_forward4 '{ 100.64.16.0/20 . 100.64.32.0/20 . new : continue }'
nft add element inet filter default_forward4 '{ 100.64.32.0/20 . 100.64.16.0/20 . established : accept }'
nft add element inet filter default_forward4 '{ 100.64.48.0/20 . 100.64.0.0/17 . new : continue }'
nft add element inet filter default_forward4 '{ 100.64.80.0/20 . 100.64.0.0/17 . new : continue }'
nft add element inet filter default_forward4 '{ 100.64.48.0/20 . 0.0.0.0/0 . new : continue }'
nft add element inet filter default_forward4 '{ 100.64.80.0/20 . 0.0.0.0/0 . new : continue }'
nft add element inet filter default_forward4 '{ 100.64.48.0/20 . 0.0.0.0/0 . established : accept }'
nft add element inet filter default_forward4 '{ 100.64.80.0/20 . 0.0.0.0/0 . established : accept }'
nft add element inet filter default_forward4 '{ 0.0.0.0/0 . 100.64.48.0/20 . established : accept }'
nft add element inet filter default_forward4 '{ 0.0.0.0/0 . 100.64.80.0/20 . established : accept }'
nft add element inet filter default_forward4 '{ 100.64.64.0/20 . 100.64.64.0/20 . new : continue }'
nft add element inet filter default_forward4 '{ 100.64.96.0/20 . 100.64.96.0/20 . new : continue }'
nft add element inet filter default_forward4 '{ 100.64.64.0/20 . 100.64.64.0/20 . established : accept }'
nft add element inet filter default_forward4 '{ 100.64.96.0/20 . 100.64.96.0/20 . established : accept }'
NAT
nft add element inet filter masquerade_networks4 '{ enp0s5 . 100.64.80.0/20 : jump masq }'
nft add element inet filter masquerade_networks4 '{ enp0s5 . 100.64.48.0/20 : jump masq }'
Allow ICMP out to containers
nft add element inet filter icmp_types_out4 '{ 100.64.0.0/17 . 100.64.0.0/17 . destination-unreachable : accept }'
Allow forwarded ICMP by src/dst/type
nft add element inet filter icmp_types_forward4 '{ 100.64.80.0/20 . 100.64.0.0/17 . echo-request : jump reject_with_icmp_admin_prohibited }'
nft add element inet filter icmp_types_forward4 '{ 100.64.80.0/20 . 0.0.0.0/0 . echo-request : accept }'
nft add element inet filter icmp_types_forward4 '{ 100.64.48.0/20 . 100.64.0.0/17 . echo-request : jump reject_with_icmp_admin_prohibited }'
nft add element inet filter icmp_types_forward4 '{ 100.64.48.0/20 . 0.0.0.0/0 . echo-request : accept }'
Allow forwarded TCP by src/dst/dport
nft add element inet filter tcp_ports_forward4 '{ 100.64.80.0/20 . 100.64.0.0/17 . 80 : jump reject_with_icmp_admin_prohibited }'
nft add element inet filter tcp_ports_forward4 '{ 100.64.80.0/20 . 0.0.0.0/0 . 80 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 100.64.80.0/20 . 100.64.0.0/17 . 443 : jump reject_with_icmp_admin_prohibited }'
nft add element inet filter tcp_ports_forward4 '{ 100.64.80.0/20 . 0.0.0.0/0 . 443 : accept }'
nft add element inet filter tcp_ports_forward4 '{ 100.64.48.0/20 . 100.64.0.0/17 . 443 : jump reject_with_icmp_admin_prohibited }'
nft add element inet filter tcp_ports_forward4 '{ 100.64.48.0/20 . 0.0.0.0/0 . 443 : accept }'
Allow forwarded UDP by src/dst/dport
nft add element inet filter udp_ports_forward4 '{ 100.64.80.0/20 . 100.64.0.0/17 . 53 : jump reject_with_icmp_admin_prohibited }'
nft add element inet filter udp_ports_forward4 '{ 100.64.80.0/20 . 0.0.0.0/0 . 53 : accept }'
TODO
This networking scheme is nice for Docker, but it sucks having to specify every address in docker-compose. I have an IPAM driver for Docker that will allow tags to specified for networks as options to the IPAM driver in compose, but it is a work in progress.
IPAM Driver
Network Driver
- EVPN/VXLAN transports
- NFTables Flowtables; interface names must be known to the firewall state for offloading