define NAT_LAN_IFACES = { "br-lan" } define NAT_WAN_IFACES = { "wg-upstream" } define PHYSICAL_WAN = "enp1s0" define MASQUERADE_IFACES = { $NAT_WAN_IFACES, $PHYSICAL_WAN } define VUEKO_V4 = 168.119.176.53 define VUEKO_V6 = 2a01:4f8:c012:2f4::1 define VUEKO_PORT = 51820 define PLASTIC_ROUTER_V4 = 192.168.0.1 table inet filter { chain forward { type filter hook forward priority filter; policy drop # Use MSS clamping # to avoid too large packets from client on the lan # not going through the tunnel. iifname wg-upstream tcp flags syn / syn,rst tcp option maxseg size set rt mtu oifname wg-upstream tcp flags syn / syn,rst tcp option maxseg size set rt mtu # allow traffic between lan and wan iifname $NAT_LAN_IFACES oifname $NAT_WAN_IFACES counter accept iifname $NAT_WAN_IFACES oifname $NAT_LAN_IFACES ct state established,related counter accept # accept responses on physical wan iifname $PHYSICAL_WAN oifname $NAT_LAN_IFACES ct state established,related counter accept # allow selected destinations via physical wan # plastic router iifname $NAT_LAN_IFACES oifname $PHYSICAL_WAN ip daddr $PLASTIC_ROUTER_V4 counter accept # all destinations configured via policy based routing oifname $PHYSICAL_WAN mark $VPN_BYPASS_MARK counter accept } } table inet nat { chain postrouting { type nat hook postrouting priority filter; policy accept oifname $MASQUERADE_IFACES masquerade } } # Bypass VPN by setting 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. table inet vpn-bypass { # 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 { ip daddr $VUEKO_V4 udp dport $VUEKO_PORT mark set $VPN_BYPASS_MARK counter ip6 daddr $VUEKO_V6 udp dport $VUEKO_PORT mark set $VPN_BYPASS_MARK counter } } # 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 to plastic router oifname $PHYSICAL_WAN ip daddr $PLASTIC_ROUTER_V4 counter accept # accept icmpv6 oifname $PHYSICAL_WAN icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept # accept connections to selected endpoints # VPN (wg-upstream) oifname $PHYSICAL_WAN ip daddr $WG_UPSTREAM_ENDPOINT counter accept # only this is used # destinations configured in VPN bypass oifname $PHYSICAL_WAN mark $VPN_BYPASS_MARK counter accept # drop all other packets oifname $PHYSICAL_WAN counter drop } } # 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 } }