# SPDX-FileCopyrightText: 2023-2024 Simon Bruder # # SPDX-License-Identifier: AGPL-3.0-or-later define NAT_LAN_IFACES = { "br-lan", "br-guest" } define PHYSICAL_WAN = "enp1s0" # only includes interfaces that use NAT define NAT_WAN_IFACES = { $PHYSICAL_WAN } # also includes interfaces that do not use NAT define WAN_IFACES = { $NAT_WAN_IFACES, "wg-he" } table inet filter { chain forward { type filter hook forward priority filter; policy drop # Use MSS clamping to avoid too large packets not going through the tunnel. tcp flags syn / syn,rst tcp option maxseg size set rt mtu # plastic router, might be vulnerable (FIXME v6 is still reachable) iifname "br-guest" ip daddr "192.168.0.1" drop # allow traffic between selected VLANs and wan iifname $NAT_LAN_IFACES oifname $WAN_IFACES counter accept iifname $WAN_IFACES oifname $NAT_LAN_IFACES ct state established,related counter accept # allow lan clients to be publicly reachable iifname "wg-he" oifname "br-lan" counter accept # traffic from lan to all other vlans is allowed iifname "br-lan" oifname $VLAN_BRIDGES counter accept; iifname $VLAN_BRIDGES oifname "br-lan" ct state established,related counter accept iifname $WAN_IFACES oifname "br-iot" ct state established,related counter accept iifname "br-printer" oifname "br-lan" ip daddr $STATIC_HOST_fuuko_address4 tcp dport { 21, 30000-30009 } counter accept iifname "br-printer" oifname "br-lan" ip6 daddr $STATIC_HOST_fuuko_address6 tcp dport { 21, 30000-30009 } counter accept } } table ip nat { chain postrouting { type nat hook postrouting priority filter; policy accept oifname $NAT_WAN_IFACES masquerade } } # Bypass HE tunnel by setting a firewall mark. # This acts in two places that are handled separatly by nftables: # Packets from the local host (output hook) and forwared packets (prerouting hook). # To simplify the handling, # there is a single chain that handles both, # which is jumped to from the specific chains. # Additionally, masquerading is allowed for all packages with a destination of the dynamic set. table ip6 he-bypass { # Dynamically managed by dnsmasq (based on resolved addresses). set addresses { type ipv6_addr comment "IPv6 addresses for which the HE tunnel should be bypassed by using NAT on wan instead" } # This must be of type route, otherwise no route lookup will be performed chain output { type route hook output priority mangle jump common } # This does not need to be of type route chain prerouting { type filter hook prerouting priority mangle jump common } chain common { ip6 daddr @addresses mark set 0x7974 counter } chain postrouting { type nat hook postrouting priority filter; policy accept oifname $NAT_WAN_IFACES ip6 daddr @addresses masquerade } } table ip6 public-access { chain input { type filter hook input priority filter; policy accept iifname "wg-he" oifname "br-lan" counter accept } } # Only allow select connections from and to (physical) wan, # overriding NixOS firewall in some cases. table inet restrict-wan { # Priorities must be higher than filter (0), # which the NixOS firewall uses. chain input { type filter hook input priority -50; policy accept # accept responses iifname $PHYSICAL_WAN ct state established,related counter accept # accept icmpv6 iifname $PHYSICAL_WAN icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept # drop everything else iifname $PHYSICAL_WAN counter drop } # This handles all packets (local and forwarded) chain postrouting { type filter hook postrouting priority 0; policy accept # accept connections over physical wan oifname $PHYSICAL_WAN counter accept } } # Traffic control # Needs output and prerouting to match packets from localhost and lan table inet tc { chain output { type route hook output priority mangle # hardcoded, but unlikely to change ip daddr { "9.9.9.9", "149.112.112.112" } meta priority set 1:3 counter return comment "DNS (4)" ip6 daddr { "2620:fe::9", "2620:fe::fe" } meta priority set 1:3 counter return comment "DNS (6)" jump common } chain forward { type filter hook forward priority mangle jump common } chain common { iifname "br-guest" meta priority set 1:a counter return comment "guest network" meta l4proto tcp meta length 1-64 meta priority set 1:3 counter return comment "small tcp packets" tcp dport 22 ip dscp af21 meta priority set 1:4 counter return comment "interactive SSH (4)" tcp dport 22 ip6 dscp af21 meta priority set 1:4 counter return comment "interactive SSH (6)" meta l4proto udp ip dscp af13 meta priority set 1:5 ip dscp set cs0 counter return comment "fuuko torrent" ip daddr 168.119.176.53 tcp dport 443 ip dscp af12 meta priority set 1:9 counter return comment "restic (4)" ip6 daddr 2a01:4f8:c012:2f4::1 tcp dport 443 ip6 dscp af12 meta priority set 1:9 counter return comment "restic (6)" meta l4proto { tcp, udp } th dport 443 meta priority set 1:6 counter return comment "HTTPS" ip daddr 168.119.176.53 udp dport 51820 meta priority set 1:7 counter return comment "wg-home" meta l4proto { tcp, udp } ip dscp ef meta priority set 1:8 counter return comment "VoIP (4)" meta l4proto { tcp, udp } ip6 dscp ef meta priority set 1:8 counter return comment "VoIP (6)" meta l4proto { tcp, udp } th dport 64738 meta priority set 1:8 counter return comment "Mumble" } } # Tracing infrastructure, can be used for debugging (nft monitor trace) table inet trace { chain prerouting { type filter hook prerouting priority raw - 1 jump common } chain output { type filter hook output priority raw - 1 jump common } chain common { # Add tracing rule here # … meta nftrace set 1 # DO NOT COMMIT ANY TRACING RULES } }