#!/sbin/nft -f # --------------------------------------------------------------------------------- # # References: # https://wiki.nftables.org/wiki-nftables/index.php/Main_Page # https://wiki.gentoo.org/wiki/Nftables/Examples # https://wiki.archlinux.org/title/Nftables # https://github.com/krabelize/nftables-firewall-config/blob/master/nftables.conf # https://github.com/atweiden/archvault/blob/master/resources/etc/nftables.conf # https://xdeb.org/post/2019/09/26/setting-up-a-server-firewall-with-nftables-that-support-wireguard-vpn/ # Libvirt: # https://libvirt.org/firewall.html # Notes: # - "limit" rules need to be put before "established" connections # - use sets for groups of things (eg. IP, ports, ...) # - explicitly allow IPv6 ICMP if do not have "policy accept" on the outgoing chain # - hook order: ingress -> prerouting -> input/output/forward -> postrouting # - "iif" should be use when possible (for persistent interfaces) since it is faster than "iifname" # --------------------------------------------------------------------------------- # flush ruleset # TCP ports to accept (both IPv4 and IPv6) #define ACCEPT_TCP_PORTS = { 80, 443, 1965 } # UDP ports to accept (both IPv4 and IPv6) #define ACCEPT_UDP_PORTS = { 51820 } # Wireguard (use for the 'server' only) define vpn = wg0 define wan = eth0 define vpn_net = 10.2.0.0/24 table inet filter { chain libvirt_input { iifname "virbr0" udp dport 53 counter accept iifname "virbr0" tcp dport 53 counter accept iifname "virbr0" udp dport 67 counter accept iifname "virbr0" tcp dport 67 counter accept } chain libvirt_forward { oifname "virbr0" ip daddr 192.168.122.0/24 ct state { established, related } counter accept iifname "virbr0" ip saddr 192.168.122.0/24 counter accept iifname "virbr0" oifname "virbr0" counter accept oifname "virbr0" counter reject with icmpx type port-unreachable iifname "virbr0" counter reject with icmpx type port-unreachable } chain wireguard_input { # Allow WireGuard clients to access DNS iifname $vpn udp dport 53 ct state new counter accept # Allow VPN clients to communicate with each other iifname $vpn oifname $vpn ct state new accept } chain wireguard_forward { iifname $vpn oifname $wan ct state new accept } # Default to drop all inbound traffic, unless they meet the criteria chain input { type filter hook input priority 0; policy drop; # Drop invalid packets early ct state invalid counter drop # Drop none SYN packets #tcp flags & (fin|syn|rst|ack) != syn ct state new counter drop # Reject AUTH to make it fail fast tcp dport 113 reject with icmpx type port-unreachable # Rate limit on SSH port #tcp dport ssh ct state new limit rate 6/minute accept # Mitigate ping floods ip protocol icmp icmp type { echo-reply, echo-request } limit rate over 1/second burst 4 packets drop ip6 nexthdr icmpv6 icmpv6 type { echo-reply, echo-request } limit rate over 1/second burst 4 packets drop ct state { established, related } counter accept ct status dnat accept # Allow loopback from host iif lo accept iif != lo ip daddr 127.0.0.1/8 counter drop iif != lo ip6 daddr ::1/128 counter drop counter jump libvirt_input counter jump wireguard_input # Accept user-defined ports #tcp dport $ACCEPT_TCP_PORTS ct state new counter accept #udp dport $ACCEPT_UDP_PORTS ct state new counter accept # Accept ICMPv4 ip protocol icmp icmp type { echo-reply, echo-request, destination-unreachable, time-exceeded, parameter-problem, router-advertisement, router-solicitation } counter accept # Accept basic ICMPv6 functionality ip6 nexthdr icmpv6 icmpv6 type { echo-reply, echo-request, destination-unreachable, packet-too-big, time-exceeded, parameter-problem } counter accept # Allow ICMPv6 SLAAC ip6 nexthdr icmpv6 icmpv6 type { nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, } ip6 hoplimit 255 counter accept # Allow ICMPv6 multicast listener discovery on link-local ip6 nexthdr icmpv6 icmpv6 type { mld-listener-query, mld-listener-report, mld-listener-reduction, mld2-listener-report, } ip6 saddr fe80::/10 counter accept counter comment "Count dropped packets" #log prefix "[nftables] Inbound Denied: " flags all counter drop } # Route your own packets! I'm not your router. # Can be enabled while using VPN (for tunneling) chain forward { type filter hook forward priority 0; policy drop; ct state { related, established } accept ct status dnat accept oif lo accept oif != lo ip daddr 127.0.0.1/8 counter drop oif != lo ip6 daddr ::1/128 counter drop counter jump libvirt_forward counter jump wireguard_forward counter comment "Count dropped packets" #log prefix "[nftables] Forward Denied: " flags all counter drop } # Accept all outbound traffic chain output { type filter hook output priority 0; policy accept; counter comment "Count accepted packets" #log prefix "[nftables] Outbound Accepted: " flags all counter accept } } table inet nat { chain libvirt_postrouting { ip saddr 192.168.122.0/24 ip daddr != 192.168.122.0/24 counter masquerade } chain wireguard_postrouting { # Masquerade WireGuard traffic # All WireGuard traffic will look like it comes from the servers IP address oifname $wan ip saddr $vpn_net counter masquerade } chain postrouting { type nat hook postrouting priority 100; policy accept; counter jump libvirt_postrouting counter jump wireguard_postrouting } }