#!/usr/sbin/nft -f flush ruleset define ip6_mask = ffff:ffff:ffff:ffff:: define public_http = 8080 define public_https = 8443 define public_ssh = 3222 define system_ssh = 49152 table inet nat { chain prerouting { type nat hook prerouting priority dstnat; policy accept; tcp dport 80 redirect to :$public_http tcp dport 443 redirect to :$public_https tcp dport 22 redirect to :$public_ssh } } table inet firewall { set ssh_ratelimit_v4 { type ipv4_addr size 65535 flags dynamic,timeout timeout 60m } set ssh_ratelimit_v6 { type ipv6_addr size 65535 flags dynamic,timeout timeout 60m } set blackhole_v4 { type ipv4_addr size 65535 flags dynamic,timeout timeout 24h } set blackhole_v6 { type ipv6_addr size 65535 flags dynamic,timeout timeout 24h } set public_ssh_ratelimit_v4 { type ipv4_addr size 65535 flags dynamic,timeout timeout 10m } set public_ssh_ratelimit_v6 { type ipv6_addr size 65535 flags dynamic,timeout timeout 10m } set public_ssh_connections_v4 { type ipv4_addr size 65535 flags dynamic } set public_ssh_connections_v6 { type ipv6_addr size 65535 flags dynamic } chain inbound_rules_v4 { icmp type echo-request limit rate 15/second accept } chain inbound_rules_v6 { icmpv6 type { nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert } accept icmpv6 type echo-request limit rate 15/second accept } chain inbound_system_ssh_v4 { ct state new tcp dport $system_ssh add @ssh_ratelimit_v4 \ { ip saddr limit rate 1/hour burst 1 packets } counter accept counter drop } chain inbound_system_ssh_v6 { ct state new tcp dport $system_ssh add @ssh_ratelimit_v6 \ { ip6 saddr & ffff:ffff:ffff:ffff:: limit rate 1/hour burst 1 packets } counter accept counter drop } chain inbound_public_ssh_v4 { # Set a concurrent cap for each ip tcp dport $public_ssh ct state new add @public_ssh_connections_v4 { ip saddr ct count over 4 } counter reject with tcp reset # Set a per ip connection ratelimit tcp dport $public_ssh ct state new update @public_ssh_ratelimit_v4 { ip saddr limit rate over 20/minute burst 5 packets } counter reject with tcp reset tcp dport $public_ssh accept counter drop } chain inbound_public_ssh_v6 { # Set a concurrent cap for each ip tcp dport $public_ssh ct state new add @public_ssh_connections_v6 { ip6 saddr & $ip6_mask ct count over 4 } counter reject with tcp reset # Set a per ip connection ratelimit tcp dport $public_ssh ct state new update @public_ssh_ratelimit_v6 { ip6 saddr & $ip6_mask limit rate over 20/minute burst 5 packets } counter reject with tcp reset tcp dport $public_ssh accept counter drop } chain inbound_blackhole_v4 { # blacklist tcp traffic for 24h ct state new tcp flags & (fin | syn | rst | ack) == syn update @blackhole_v4 \ { ip saddr timeout 24h } counter drop # blacklist udp traffic ct state new meta l4proto udp update @blackhole_v4 \ { ip saddr timeout 24h } counter drop counter drop } chain inbound_blackhole_v6 { # TCP SYN to any unauthorized port: blacklist the /64 for 24h. ct state new tcp flags & (fin | syn | rst | ack) == syn update @blackhole_v6 \ { ip6 saddr & $ip6_mask timeout 24h } counter drop # UDP to any unauthorized port: blacklist the /64 for 24h. ct state new meta l4proto udp update @blackhole_v6 \ { ip6 saddr & $ip6_mask timeout 24h } counter drop counter drop } chain inbound { type filter hook input priority filter; policy drop; # Allow specific networks to go unblocked iifname "lo" accept iifname "tailscale0" accept # Allow replies ct state vmap { invalid : drop, established : accept, related : accept } # Basic ping allowances meta nfproto ipv4 jump inbound_rules_v4 meta nfproto ipv6 jump inbound_rules_v6 # Allow service ports tcp dport { $public_http , $public_https } counter accept # Ratelimit service ssh port (3222) # Global ssh connection rate limiter tcp dport $public_ssh ct state new limit rate over 120/minute burst 30 packets counter reject with tcp reset meta nfproto ipv4 tcp dport $public_ssh goto inbound_public_ssh_v4 meta nfproto ipv6 tcp dport $public_ssh goto inbound_public_ssh_v6 # Block likely bots ip saddr @blackhole_v4 counter drop ip6 saddr & $ip6_mask @blackhole_v6 counter drop # Fail2unban on vps' own ssh port meta nfproto ipv4 tcp dport $system_ssh goto inbound_system_ssh_v4 meta nfproto ipv6 tcp dport $system_ssh goto inbound_system_ssh_v6 # Decide to add ip to blackhole list (incase of bot) meta nfproto ipv4 goto inbound_blackhole_v4 meta nfproto ipv6 goto inbound_blackhole_v6 # Block other traffic (default to chain) } chain forward { type filter hook forward priority filter; policy drop; } }