branch: externals/nftables-mode commit 1817c43fb92bcc677955c93156e2564140876d5c Author: Trent W. Buck <trentb...@gmail.com> Commit: Trent W. Buck <trentb...@gmail.com>
Initial example nftables ruleset Later I will add "nftables-router.nft" (replaces our "iptab" and "iptab.nat") and "nftables-router-ips.nft" (replaces out "iptab.ips"). --- nftables-host.nft | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/nftables-host.nft b/nftables-host.nft new file mode 100644 index 0000000000..e944676225 --- /dev/null +++ b/nftables-host.nft @@ -0,0 +1,126 @@ +#!/usr/sbin/nft --file +# -*- mode: conf; mode: nftables; conf-space-keywords: "table\\|chain\\|type\\|policy\\|accept\\|drop\\|counter\\|jump"; -*- + +#### This -*-nftables-*- ruleset is my /etc/nftables.conf for new hosts. +#### Ref. http://jengelh.medozas.de/documents/Perfect_Ruleset.pdf +#### Ref. https://wiki.nftables.org/ +#### +#### GOTCHA: This is written for nft 0.9.1. +#### Several options have changed, even since 0.9.0! +#### +#### GOTCHA: "nft --file" does not flush by default. +#### That is, it acts like "iptables-restore --noflush". +#### +#### GOTCHA: If your ruleset does "flush; add; flush; add", +#### it will actually do "flush; flush; add; add". +#### That is, all the flushes move to the top. +#### +#### This makes it impossible to do my old trick of +#### having the ruleset start with a "deny all" policy, +#### which was useful to make sure that if the firewall failed, +#### the OS would lock down the whole system. +#### (See "iptab" for notes about that.) +#### +#### To achieve that under nft, you instead need to patch +#### nftables.service to have +#### OnFailure=nftables-denyall.service, and then write that +#### unit to load a SEPARATE, DIFFERENT nftables file that +#### blocks everything. And even then, it won't behave quite +#### the same. +#### +#### This also means it is no longer safe to refer to +#### hostnames in this ruleset, safe in the knowledge that +#### they can only be resolved via local /etc/hosts. Because +#### the "deny all" ruleset can't be prepended here, we CANNOT +#### be sure the real ruleset will only resolve hostnames via +#### local sources -- so you might add ones that are only in +#### DNS, and then have the firewall fail to load during early +#### boot -- leaving you with a "working" host, with no +#### firewall! +#### +#### GOTCHA: "list ruleset" here will print the ruleset BEFORE it goes through the kernel; +#### "nft list ruleset" later will print the ruleset AFTER it goes through the kernel. +#### The difference is usually like "iptables -p tcp" getting an implied "-m tcp". +#### However, differences MIGHT indicate a bug! Watch out! +#### +#### GOTCHA: "table filter" and "chain INPUT" or "chain input" just +#### conventions and have NO MEANING WHATSOEVER. The actual +#### meaning comes from the "type filter hook input priority +#### filter" line. +#### +#### NOTE: Only create a chain if you use it. +#### An empty chain is slightly slower than no chain at all. +#### e.g. most hosts don't need an output chain. +#### +#### NOTE: iptables ALWAYS counts how many packets/bytes hit every chain and rule. +#### nftables makes this OPT IN, e.g. change "accept" to "counter accept". +#### iptables-save -c would print "[pkts:bytes] -A ...". +#### nftables list rulset will print "... counter packgets 12 bytes 34 ...". +#### +#### Since counters are useful during debugging but not production, +#### I have left them out of this example. +#### +#### NOTE: "table x" is implicitly "table ip x", which is IPv4 only. +#### If you want dual-stack, say "table inet x". + +# FIXME: add ICMPv6 then change "table ip" to "table inet" (i.e. dual-stack). +# FIXME: rate-limit ICMPv4 by source IP? + +flush ruleset + +table inet my_filter { + chain my_input { + type filter hook input priority filter + policy drop + + # Typically 95%+ 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). + ct state established,related accept + ct state 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 not "ifftype loopback"? Is it just inertia? + iif lo accept + # Allow arbitrary IPv4/ICMP and IPv6/ICMPv6. + # FIXME: this is too broad -- narrow this! + ip protocol icmp accept + ip6 nexthdr icmpv6 accept + + # YOUR RULES HERE. + # NOTE: service names resolve via nss (/etc/hosts) only in nft 0.9.1+! + ##FOR "router" EXAMPLE### NOTE: a single rule CAN match "allow 53/tcp and 53/udp", but it's UGLY, so we don't. + ##FOR "router" EXAMPLE### NOTE: I assume you used systemd (networkd or udev) to rename "enp0s0f0" to "lan". + ##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 + ##FOR "router" EXAMPLE##iif enp11s0 tcp dport domain accept + ##FOR "router" EXAMPLE##iif enp11s0 udp dport { domain, ntp, bootps } accept + + # Finally, politely reject all other attempts. + # Omit to use the default policy ("policy drop", above) instead. + # FIXME: can we simply do "policy reject" nowadays? + reject + } + + # A host can't route unless you explicitly enable it, e.g.: + # + # sysctl net/ipv4/ip_forward=1 + # sysctl net/ipv6/conf/all/forwarding=1 + # + # We create a "deny all" inet filter forward chain anyway, as + # defense-in-depth against someone enabling routing ACCIDENTALLY. + chain my_forward { + type filter hook forward priority filter + policy drop + } + + # We want output to be "allow all", so we don't even create a chain. + +} + +list ruleset + +