#! /sbin/nft -f flush ruleset define wan_int = "wan" define wan_ip = 217.155.8.194 define allowed_icmp_types = { echo-request, echo-reply } define allowed_icmpv6_types = { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-request, echo-reply } define trusted_ips = { 10.0.10.0/24, 10.20.30.0/24, 10.55.55.0/24, 10.1.1.0/24, 10.65.65.0/24, 10.60.60.0/24, 10.50.50.0/24} define masq_to_internet = { 10.0.10.0/24, 10.20.30.0/24, 10.55.55.0/24, 10.1.1.0/24, 10.65.65.0/24, 10.60.60.0/24 } define bogons = { 0.0.0.0/8, 10.0.0.0/8, 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.168.0.0/16 } define internal_net = 10.0.10.0/24 define lan6_net = 2a02:8013:7f3d:10::/64 define my_prefix6 = 2a02:8013:7f3d::/48 define wg0_int = wg0 define wg0_ip = 10.50.50.1 define wg0_net = 10.50.50.0/24 define ekko_wg0 = 10.50.50.2 define epcc_wg0 = 10.50.50.3 define oneplus9_wg0 = 10.50.50.4 define dhcp_int = enp2s0 define heimer_incus = 10.20.30.0/24 define garen_incus = 10.55.55.0/24 define jellyfin_ip = 10.0.10.57 define jellyfin_port = 8096 define haproxy_ip = 10.0.10.37 define checkmk_server = 10.20.30.58 define oracle_wg0 = 10.50.50.6/32 define slskd_ip = 10.0.10.35 define slskd_port = 50300 define terraria_port = { 7777 } define ula_prefix = fdab:abab:abab::/48 define lan6_ula = fdab:abab:abab:10::/64 define dot_doh_ports = { 8443, 853 } define dot_doh_ip4 = { 10.0.10.5, 217.155.8.194 } define dot_doh_ip6 = { fdab:abab:abab:10::5, 2a02:8013:7f3d:10::5 } define jf_ipv6 = 2a02:8013:7f3d:10::57 table inet main { chain antispoof { type filter hook prerouting priority raw; policy accept; iifname $wan_int ip saddr $bogons counter drop comment "Private/bogus source addresses can't legitimately arrive from the internet" iifname $wan_int ip6 saddr $my_prefix6 counter drop comment "Our own delegated prefix can't legitimately arrive from the internet" } chain input { type filter hook input priority filter; policy drop; iifname lo accept ct state established,related accept ct state invalid drop iifname != $wan_int icmp type $allowed_icmp_types counter accept icmpv6 type $allowed_icmpv6_types counter accept icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } ip6 hoplimit 255 accept comment "IPv6 neighbour discovery" iifname $wan_int udp dport 546 ip6 saddr fe80::/10 accept comment "DHCPv6 replies from Zen" udp dport 51820 ct state new accept comment "Accept new incoming wireguard connections" ip saddr $wg0_net jump wg0_input udp dport 53 accept comment "Accept DNS Traffic UDP" tcp dport 53 accept comment "Accept DNS Traffic TCP" tcp dport $dot_doh_ports ip daddr $dot_doh_ip4 accept comment "Accept DoT and DoH Traffic" tcp dport $dot_doh_ports ip6 daddr $dot_doh_ip6 accept comment "Accept DoT and DoH Traffic" #ip saddr $trusted_ips udp dport 53 accept comment "Accept DNS Traffic UDP" #ip saddr $trusted_ips tcp dport 53 accept comment "Accept DNS Traffic TCP" ip saddr $trusted_ips tcp dport 8053 accept comment "Accept Jarl traffic TCP" ip saddr $trusted_ips tcp dport 8067 accept comment "Accept Steward traffic TCP" ip saddr $trusted_ips tcp dport 22 accept ip6 saddr $lan6_net udp dport 53 accept comment "Accept DNS Traffic UDP (IPv6)" ip6 saddr $lan6_net tcp dport 53 accept comment "Accept DNS Traffic TCP (IPv6)" ip6 saddr $lan6_net tcp dport 22 accept iifname $dhcp_int udp dport 67 accept comment "Accept DHCP" iifname $dhcp_int udp dport 547 ip6 saddr fe80::/10 accept comment "Accept DHCPv6" ip saddr $checkmk_server tcp dport 6556 accept comment "From Checkmk To Agent" } chain wg0_input { udp dport 53 accept tcp dport 53 accept ip saddr { $epcc_wg0, $oracle_wg0 } tcp dport 22 accept ip saddr $wg0_net ip daddr $wg0_ip drop comment "Block WireGuard clients from accessing the server itself" return } chain prerouting { type nat hook prerouting priority dstnat; policy accept; # Matching on the public IP (not the WAN interface) covers both internet traffic and hairpin traffic from inside ip daddr $wan_ip tcp dport $jellyfin_port dnat ip to $jellyfin_ip:443 comment "Port Forward Jellyfin" ip daddr $wan_ip tcp dport 443 dnat ip to $haproxy_ip:443 comment "Port Forward Haproxy" ip daddr $wan_ip tcp dport $slskd_port dnat ip to $slskd_ip:$slskd_port comment "Port Forward soulseek" ip daddr $wan_ip tcp dport $terraria_port dnat ip to 10.0.10.76 comment "Port Forward terraria" ip daddr $wan_ip tcp dport 25 dnat ip to 10.0.10.25 comment "Port Forward SMTP" } chain postrouting { type nat hook postrouting priority srcnat; policy accept; oifname $wan_int ip saddr $masq_to_internet masquerade comment "external masquerade" oifname != $wg0_int ip saddr $wg0_net masquerade # Same-subnet clients would otherwise get replies direct from the server, bypassing the router's NAT ip saddr $internal_net ip daddr { $haproxy_ip, $jellyfin_ip, $slskd_ip, 10.0.10.76, 10.0.10.25 } ct status dnat masquerade comment "Hairpin NAT same-subnet" } chain forward { type filter hook forward priority filter; policy drop; # PPPoE MTU is smaller than ethernet; must sit before the established rule so SYN-ACKs get clamped too oifname $wan_int tcp flags syn tcp option maxseg size set rt mtu comment "PPPoE MSS clamping" ct state established,related accept iifname != $wan_int ct state invalid ip daddr $trusted_ips accept comment "Needed due to asymmetric routing (internal only)" ct state invalid drop iifname $dhcp_int oifname $wan_int ip6 saddr $lan6_net accept comment "LAN IPv6 out" # From the internet, only allow traffic that was port forwarded iifname $wan_int ip daddr $jellyfin_ip tcp dport 443 ct status dnat accept comment "Forward jellyfin traffic" iifname $wan_int ip daddr $haproxy_ip tcp dport 443 ct status dnat accept comment "Forward Haproxy traffic" iifname $wan_int ip daddr $slskd_ip tcp dport $slskd_port ct status dnat accept comment "Forward slskd traffic" iifname $wan_int ip daddr 10.0.10.76 tcp dport $terraria_port ct status dnat accept comment "Forward terraria traffic" iifname $wan_int ip daddr 10.0.10.25 tcp dport 25 ct status dnat accept comment "Forward SMTP" ip6 daddr $jf_ipv6 tcp dport 443 accept iifname $wan_int counter drop comment "Nothing else gets in from the internet" ip daddr $wg0_net drop comment "Drop client-to-client traffic for wg0 network" icmp type $allowed_icmp_types accept ip saddr $wg0_net accept comment "Forward from wg0 network" ip saddr $trusted_ips accept comment "Forward from trusted IPs" } chain output { type filter hook output priority filter; policy accept; } }