branch: externals/nftables-mode commit 794a6e67745a92cae2c104872c6902e3f6364edd Author: Trent W. Buck <trentb...@gmail.com> Commit: Trent W. Buck <trentb...@gmail.com>
limit ICMP by type, tweak notes, expand on iif vs iifname, document "flush table" gotcha --- nftables-host.nft | 83 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/nftables-host.nft b/nftables-host.nft index 38fdef5cdc..d218446a4f 100644 --- a/nftables-host.nft +++ b/nftables-host.nft @@ -62,31 +62,56 @@ #### #### NOTE: "table x" is implicitly "table ip x", which is IPv4 only. #### If you want dual-stack, say "table inet x". +#### +#### NOTE: "iif lo" is resolved at ruleset load time into an interface +#### NUMBER inside the kernel; whereas "iifname lo" remains a +#### string. This means that: +#### +#### * "iifname" is ~10 times slower than "iif" for every +#### packet considered (strcmp versus ==). +#### +#### * If you load a ruleset with "iif foo" before foo exists, +#### the load will fail, LEAVING YOU UNPROTECTED! +#### +#### * If you load a ruleset with "iif foo" and then foo is +#### removed and readded (e.g. ppp0 for a flaky ADSL link), +#### what happens? +#### +#### * Rule of thumb: always use "iifname" (not "iif"). +# NOTE: this will remove *ALL* tables --- including tables set up by +# other things (e.g. sshguard)! +# +# In theory you can flush just your own rules, e.g. +# +# flush table inet my_filter +# +# FIXME: I tried that, and I got locked out of SSH! +# What it did was remove all the rules, but NOT the chains, so +# the default-deny policy dropped EVERYTHING!!! flush ruleset + table inet my_filter { chain my_input { type filter hook input priority filter policy drop # Typically 99%+ of packets are part of an already-established flow. # Allow those first, so we're a fast, stateful firewall. - # The rest SHOULD be "ct state new" (or untracked). + # After this only "ct state new" (or "ct state untracked") will remain. # FIXME: is a vmap here better (more efficient) than two separate rules? # FIXME: {established or related: accept} does not match correctly! ct state vmap { established: accept, related: accept, invalid: drop } + # Loopback traffic is needed for e.g. NFS RPC, and for debugging. - # NOTE: assumes exactly one loopback interface named "lo" that already exists. - # FIXME: why "iif lo" not "ifftype loopback"? Is it just inertia? - iiftype loopback accept + # FIXME: is iiftype here better than iif/iifname? + iiftype loopback accept - # Allow arbitrary IPv4/ICMP and IPv6/ICMPv6. - # FIXME: this is too broad -- narrow this! - # FIXME: rate-limit (some) ICMPv4 by source IP? - ip protocol icmp accept - # FIXME: should we limit to "ip6 nexthdr icmpv6"? - icmpv6 type vmap @ICMPv6_RFC4890_policy + # Allow *some* kinds of IPv4/ICMP and IPv6/ICMPv6. + # FIXME: are "ip protocol icmp" and "ip6 nexthdr icmpv6" needed? + ip protocol icmp icmp type vmap @ICMP_policy + ip6 nexthdr icmpv6 icmpv6 type vmap @ICMPv6_RFC4890_policy # YOUR RULES HERE. # NOTE: service names resolve via nss (/etc/hosts) only in nft 0.9.1+! @@ -95,8 +120,8 @@ table inet my_filter { ##FOR "router" EXAMPLE### NOTE: "iif foo" must exist at ruleset load time. ##FOR "router" EXAMPLE### If your ruleset starts BEFORE udev and/or systemd-networkd are READY=1, ##FOR "router" EXAMPLE### consider using 'iifname lan' instead of "iif lan". - tcp dport ssh accept - tcp dport { http, https } accept + tcp dport ssh accept + tcp dport { http, https } accept ##FOR "router" EXAMPLE##iif enp11s0 tcp dport domain accept ##FOR "router" EXAMPLE##iif enp11s0 udp dport { domain, ntp, bootps } accept @@ -118,6 +143,10 @@ table inet my_filter { } # We want output to be "allow all", so we don't even create a chain. + #chain my_output { + # type filter hook output priority filter + # policy accept + #} # Allow all ICMPv6 is wrong (insecure); @@ -164,8 +193,34 @@ table inet my_filter { 255: drop, # RFC 4890 4.3.5 & 4.4.5 extension type numbers } } -} -list ruleset + # NOTE: I couldn't find an RFC for ICMPv4 firewall, so + # I am adopting the following heuristic: + # + # 1. if there is an ICMPv6 equivalent, follow RFC4890. + # 2. if deprecated or experimental or reserved or unallocated, drop. + # 3. NOT rate-limiting ping for now, because ICBF. + # 4. NOT filtering by type.code (only type) for now, because ICBF. + map ICMP_policy { + type icmp_type : verdict + flags interval + elements = { #FIXME: icmp type 5 12 13 14 40 + destination-unreachable: accept, # RFC 4890 4.3.1 essential errors + time-exceeded: accept, # RFC 4890 4.3.1 essential errors + parameter-problem: accept, # RFC 4890 4.3.1 essential errors + echo-request: accept, # RFC 4890 4.3.1 echo (ping) + echo-reply: accept, # RFC 4890 4.3.1 echo (ping) + source-quench: drop, # deprecated + 1 - 2: drop, # unassigned + 6 - 7: drop, # deprecated / unassigned + 9 - 10: accept, # RFC 4890 4.3.3 & 4.4.1 (IRDP - alternative to DHCPv4??) + 15 - 255: drop, # deprecated / unassigned / reserved / experimental + } + } +} +# This is here to aid debugging. +# Note that its output WILL NOT MATCH a later "nft list rulset". +# Also, it is buggy, e.g. the ICMPv6_RFC4890_policy it prints has gibberish in v0.9.1. +list ruleset