[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/nftables-mode f00cf640fb 15/41: nftables - glob gotcha;
From: |
Stefan Monnier |
Subject: |
[elpa] externals/nftables-mode f00cf640fb 15/41: nftables - glob gotcha; HOW to rename ifaces; gateway (-i/-o) policy; mail reputation protection |
Date: |
Mon, 23 May 2022 09:27:23 -0400 (EDT) |
branch: externals/nftables-mode
commit f00cf640fb7adeebc1b20dce80acf0bc5b85bd1e
Author: Trent W. Buck <trentbuck@gmail.com>
Commit: Trent W. Buck <trentbuck@gmail.com>
nftables - glob gotcha; HOW to rename ifaces; gateway (-i/-o) policy; mail
reputation protection
---
nftables-router.nft | 126 +++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 114 insertions(+), 12 deletions(-)
diff --git a/nftables-router.nft b/nftables-router.nft
index dcf3c283c8..8a478043ef 100644
--- a/nftables-router.nft
+++ b/nftables-router.nft
@@ -48,6 +48,16 @@
#### meaning comes from the "type filter hook input priority
#### filter" line.
####
+#### GOTCHA: you can't use globs inside a set or map/vmap:
+####
+#### iifname "ppp*" # works
+#### iifname {"ppp*", "en*"} # fails
+#### iifname vmap {"ppp*": drop} # fails
+####
+#### GOTCHA: Service names resolve via nss (/etc/services) only in nft 0.9.1+!
+#### For example, "imap" is in /etc/services, but not in nft 0.9.0.
+#### In nft 0.9.0, "nft describe tcp dport" will print the internal
list.
+####
#### GOTCHA: Using variable ("define x = y") is annoying:
####
#### * definition variable names aren't limited to C-style
identifiers;
@@ -109,6 +119,37 @@
#### what happens?
####
#### * Rule of thumb: always use "iifname" (not "iif").
+####
+#### NOTE: instead of using "define dmz = enp12s0f3",
+#### give your interface a logical name directly.
+#### Then you use the name EVERYWHERE, e.g. "ip -s l show dmz".
+####
+#### OLD METHOD:
+#### $ cat /etc/udev/rules.d/70-persistent-net.rules
+#### SUBSYSTEM=="net", DRIVERS=="?*",
ATTR{address}=="de:ad:be:ef:ba:be", NAME="internet"
+#### SUBSYSTEM=="net", DRIVERS=="?*",
ATTR{address}=="de:af:be:ad:de:ed", NAME="dmz"
+#### SUBSYSTEM=="net", DRIVERS=="?*",
ATTR{address}=="da:bb:ed:fa:ca:de", NAME="byod"
+####
+#### NEW METHOD (DID NOT WORK WHEN I TRIED IT):
+#### $ cat /etc/systemd/network/dmz.link
+#### [Match]
+#### MACAddress=dead.beef.babe
+#### [Link]
+#### Name=dmz
+#### # Is this line needed?
+#### #NamePolicy=
+####
+#### NOTE: a single rule can match "allow 53/tcp and 53/udp", but
+#### it is ugly, so don't do it:
+####
+#### meta l4proto {tcp, udp} @th,16,16 53 accept comment "accept
DNS on UDP/TCP"
+####
+#### The @th,16,16 goes to the transport header, skips 16 bits, then
reads 16 bits.
+#### If those bits are equal to 53, the rule matches.
+####
+#### This relies on (abuses) the fact that TCP dport and UDP dport have
the same offset and length.
+#### You cannot use "domain", because nft doesn't know we're matching a
service number.
+
# NOTE: this will remove *ALL* tables --- including tables set up by
@@ -133,15 +174,8 @@ table inet my_filter {
# 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
jump my_epilogue
}
@@ -153,9 +187,67 @@ table inet my_filter {
jump my_prologue comment "deal with boring
conntrack/loopback/ICMP/ICMPv6"
# YOUR RULES HERE.
- # NOTE: service names resolve via nss (/etc/hosts) only in nft 0.9.1+!
- # NOTE: a single rule CAN match "allow 53/tcp and 53/udp", but it's
UGLY, so we don't.
- # NOTE: I assume you used systemd (networkd or udev) to rename
"enp0s0f0" to "lan".
+
+ # If a pwned devices spams the internet,
+ # your entire network will be blacklisted!
+ # To avoid this, blacklist outbound SMTP (25/tcp) from non-MTA hosts.
+ # MSAs (e.g. Outlook) are not affected, because they use submission
(587/tcp).
+ #
+ # NOTE: this must appear BEFORE "allow all to internet", obviously.
+ # NOTE: the LANs can still spam the DMZ.
+ # NOTE: the DMZ can still spam the internet, because
+ # occasionally someone adds an MTA to the DMZ without telling me.
+ iifname != dmz oifname internet reject comment "MSAs MUST use
submission (not smtp)"
+
+ # Example of a router between four networks:
+ # internet (the internet),
+ # dmz (internet-facing servers),
+ # lan (internal servers & managed laptops).
+ # byod (BYOD phones and laptops)
+ #
+ # Due to prologue, we're only handling NEW FLOWS.
+ # The return traffic is implicitly allowed.
+ # We want to say something like:
+ #
+ # * full access to internet (except port 25?)
+ # * limited access to dmz ("typical" server ports)
+ # * everything else implicitly blocked
+ #
+ # Ignoring hairpin traffic (iifname = oifname),
+ # we have 4 P 2 = 12 permutations.
+ #
+ # We can write every combination out longhand:
+ #
+ # * BONUS: very clear
+ # * BONUS: can't get rules in "wrong" order (efficiency)
+ # * BONUS: can't accidentally have overlapping rules (correctness)
+ # * BONUS: if a new iface appears, it will default deny (correctness)
+ # * MALUS: too verbose with many networks (5P2 = 20; 6P2 = 30, 7P2 =
42)
+ # * MALUS: can't use globs for shorthand (e.g. "*" . dmz).
+ #
+ # NOTE: the "continue" lines could be omitted, but are harmless.
+ iifname . oifname vmap {
+ lan . internet : accept, # all can attack the internet
+ byod . internet : accept, # all can attack the internet
+ dmz . internet : accept, # all can attack the internet
+ lan . dmz : jump my_dmz, # all can attack DMZ (only
specific ports)
+ byod . dmz : jump my_dmz, # all can attack DMZ (only
specific ports)
+ internet . dmz : jump my_dmz, # all can attack DMZ (only
specific ports)
+ lan . byod : accept, # only LAN can attack phones
+ dmz . byod : continue, # only LAN can attack phones
+ internet . byod : continue, # only LAN can attack phones
+ byod . lan : continue, # nobody can attack LAN
+ dmz . lan : continue, # nobody can attack LAN
+ internet . lan : continue, # nobody can attack LAN
+ }
+ # OR, we can try to be clever, and write individual rules.
+ # This is shorter, but harder to reason about!
+ # oifname internet accept
+ # oifname dmz jump my_dmz
+ # iifname lan accept
+
+ ### NOTE: a single rule CAN match "allow 53/tcp and 53/udp", but it's
UGLY, so we don't.
+ ### NOTE: I assume you used systemd (networkd or udev) to rename
"enp0s0f0" to "lan".
tcp dport ssh accept
tcp dport { http, https } accept
iifname lan tcp dport domain accept
@@ -171,6 +263,17 @@ table inet my_filter {
# policy accept
#}
+ # In theory DMZ hosts must fend for themselves;
+ # in practice their competence is suspect.
+ # Thus, limit DMZ access to "typical" ports (plus some per-host
exceptions).
+ # Within those typical ports, DMZ hosts still fend for themselves.
+ chain my_dmz {
+ tcp dport {domain,ssh,http,https,smtp,submission,imaps} accept
+ udp dport {domain} accept
+ # Allow additional special ports, but only to the server that serves
them.
+ define russm.example.com = 127.254.254.254
+ ip daddr $russm.example.com udp dport 60000-61000 accept comment
"mosh (FIXME: write nf_conntrack_mosh.ko!)"
+ }
chain my_prologue {
# Typically 99%+ of packets are part of an already-established flow.
@@ -203,7 +306,6 @@ table inet my_filter {
# 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
-
}
chain my_epilogue {
@@ -303,7 +405,7 @@ table inet my_filter {
# NOTE: I assume "the internet" iface is ADSL PPPoE/PPPoA (iiftype/oiftype
ppp), and
# that's the ONLY ppp iface you have (cf. bullshit ppptp VPN for iPhone
users).
# If you have decent internet, you will probably want to give the iface
a logical name,
-# then match by that name (iifname/oifname "upstream").
+# then match by that name (iifname/oifname "internet").
#
table ip my_nat {
chain my_postrouting {
- [elpa] externals/nftables-mode 4974259919 30/41: typo fixes (thanks mattcen), (continued)
- [elpa] externals/nftables-mode 4974259919 30/41: typo fixes (thanks mattcen), Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 3e9c8cf907 32/41: fixup! typo fixes (thanks mattcen), Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 70910dbc2a 35/41: Merge remote-tracking branch 'KB/master', Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 109dfa382a 33/41: Remove "list ruleset" due to https://bugs.debian.org/982576, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 7f924acbac 37/41: basic README for github, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode a207b02bd6 40/41: Lightly edited, adding some of the normal conventions for .el files, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 1817c43fb9 02/41: Initial example nftables ruleset, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 242fae1e71 11/41: limit ICMP by type, tweak notes, expand on iif vs iifname, document "flush table" gotcha, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 794a6e6774 10/41: limit ICMP by type, tweak notes, expand on iif vs iifname, document "flush table" gotcha, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 8fcd04379c 08/41: bugfix and tweak, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode f00cf640fb 15/41: nftables - glob gotcha; HOW to rename ifaces; gateway (-i/-o) policy; mail reputation protection,
Stefan Monnier <=
- [elpa] externals/nftables-mode 6e908b1d67 17/41: Got the IPS working at last (inc IPv6), mua ha ha!, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 16adfabcec 21/41: add reminder re IPv6 ranges for SSH IPS, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 34ffd618ac 19/41: fixup! Got the IPS working at last (inc IPv6), mua ha ha!, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 166b789260 22/41: old comments, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 9bc4a6f589 25/41: Don't do "flush ruleset" (i.e. expect auxiliary tables w/ race conditions), Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 94f54f52ec 28/41: reference nftables ruleset, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 3fd8b3f79e 26/41: comment tweaks, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 760486c219 27/41: update note from sshguard, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 70b0e577a6 31/41: Debian doesn't have "pptp" in /etc/services, Stefan Monnier, 2022/05/23
- [elpa] externals/nftables-mode 20fa3d3a55 38/41: Oops, this was never under version control before., Stefan Monnier, 2022/05/23