https://wiki.gbe0.com/en/linux/firewalling-and-filtering/nftables/template-inbound-outbound

#!/usr/sbin/nft -f

## Clear/flush all existing rules
flush ruleset

# Main inet family filtering table
table inet filter {

  # Rules for forwarded traffic
  chain forward {
    type filter hook forward priority 0; policy drop

    ## Log any unmatched traffic but rate limit logging to a maximum of 60 messages/minute
    ## The default policy will be applied to unmatched traffic
    limit rate 60/minute burst 100 packets \
      log prefix "Forward - Drop: " \
      comment "Log any unmatched traffic"

    ## Count the unmatched traffic
    counter \
      comment "Count any unmatched traffic"
  }

  # Rules for input traffic
  chain input {
    type filter hook input priority 0; policy drop

    ## Permit inbound traffic to loopback interface
    iif lo \
      accept \
      comment "Permit all traffic in from loopback interface"

    ## Permit established and related connections
    ct state established,related \
      counter \
      accept \
      comment "Permit established/related connections"

    ## Log and drop new TCP non-SYN packets
    tcp flags != syn ct state new \
      limit rate 100/minute burst 150 packets \
      log prefix "IN - New !SYN: " \
      comment "Rate limit logging for new connections that do not have the SYN TCP flag set"
    tcp flags != syn ct state new \
      counter \
      drop \
      comment "Drop new connections that do not have the SYN TCP flag set"

    ## Log and drop TCP packets with invalid fin/syn flag set
    tcp flags & (fin|syn) == (fin|syn) \
      limit rate 100/minute burst 150 packets \
      log prefix "IN - TCP FIN|SIN: " \
      comment "Rate limit logging for TCP packets with invalid fin/syn flag set"
    tcp flags & (fin|syn) == (fin|syn) \
      counter \
      drop \
      comment "Drop TCP packets with invalid fin/syn flag set"

    ## Log and drop TCP packets with invalid syn/rst flag set
    tcp flags & (syn|rst) == (syn|rst) \
      limit rate 100/minute burst 150 packets \
      log prefix "IN - TCP SYN|RST: " \
      comment "Rate limit logging for TCP packets with invalid syn/rst flag set"
    tcp flags & (syn|rst) == (syn|rst) \
      counter \
      drop \
      comment "Drop TCP packets with invalid syn/rst flag set"

    ## Log and drop invalid TCP flags
    tcp flags & (fin|syn|rst|psh|ack|urg) < (fin) \
      limit rate 100/minute burst 150 packets \
      log prefix "IN - FIN:" \
      comment "Rate limit logging for invalid TCP flags (fin|syn|rst|psh|ack|urg) < (fin)"
    tcp flags & (fin|syn|rst|psh|ack|urg) < (fin) \
      counter \
      drop \
      comment "Drop TCP packets with flags (fin|syn|rst|psh|ack|urg) < (fin)"

    ## Log and drop invalid TCP flags
    tcp flags & (fin|syn|rst|psh|ack|urg) == (fin|psh|urg) \
      limit rate 100/minute burst 150 packets \
      log prefix "IN - FIN|PSH|URG:" \
      comment "Rate limit logging for invalid TCP flags (fin|syn|rst|psh|ack|urg) == (fin|psh|urg)"
    tcp flags & (fin|syn|rst|psh|ack|urg) == (fin|psh|urg) \
      counter \
      drop \
      comment "Drop TCP packets with flags (fin|syn|rst|psh|ack|urg) == (fin|psh|urg)"

    ## Drop traffic with invalid connection state
    ct state invalid \
      limit rate 100/minute burst 150 packets \
      log flags all prefix "IN - Invalid: " \
      comment "Rate limit logging for traffic with invalid connection state"
    ct state invalid \
      counter \
      drop \
      comment "Drop traffic with invalid connection state"

    ## Permit IPv4 ping/ping responses but rate limit to 2000 PPS
    ip protocol icmp icmp type { echo-reply, echo-request } \
      limit rate 2000/second \
      counter \
      accept \
      comment "Permit inbound IPv4 echo (ping) limited to 2000 PPS"

    ## Permit all other inbound IPv4 ICMP
    ip protocol icmp \
      counter \
      accept \
      comment "Permit all other IPv4 ICMP"

    ## Permit IPv6 ping/ping responses but rate limit to 2000 PPS
    icmpv6 type { echo-reply, echo-request } \
      limit rate 2000/second \
      counter \
      accept \
      comment "Permit inbound IPv6 echo (ping) limited to 2000 PPS"

    ## Permit all other inbound IPv6 ICMP
    meta l4proto { icmpv6 } \
      counter \
      accept \
      comment "Permit all other IPv6 ICMP"

    ## Permit inbound traceroute UDP ports but limit to 500 PPS
    udp dport 33434-33524 \
      limit rate 500/second \
      counter \
      accept \
      comment "Permit inbound UDP traceroute limited to 500 PPS"

    ## Permit inbound SSH
    tcp dport ssh ct state new \
      counter \
      accept \
      comment "Permit inbound SSH connections"

    ## Permit inbound HTTP and HTTPS
    tcp dport { http, https } ct state new \
      counter \
      accept \
      comment "Permit inbound HTTP and HTTPS connections"

    ## Log any unmatched traffic but rate limit logging to a maximum of 60 messages/minute
    ## The default policy will be applied to unmatched traffic
    limit rate 60/minute burst 100 packets \
      log prefix "IN - Drop: " \
      comment "Log any unmatched traffic"

    ## Count the unmatched traffic
    counter \
      comment "Count any unmatched traffic"
  }

  # Rules for output traffic
  chain output {
    type filter hook output priority 0; policy drop

    ## Permit outbound traffic to loopback interface
    oif lo \
      accept \
      comment "Permit all traffic out to loopback interface"

    ## Permit established and related connections
    ct state established,related \
      counter \
      accept \
      comment "Permit established/related connections"

    ## Drop traffic with invalid connection state
    ct state invalid \
      limit rate 100/minute burst 150 packets \
      log flags all prefix "OUT - Invalid: " \
      comment "Rate limit logging for traffic with invalid connection state"
    ct state invalid \
      counter \
      drop \
      comment "Drop traffic with invalid connection state"

    ## Permit IPv4 ping/ping responses but rate limit to 2000 PPS
    ip protocol icmp icmp type { echo-reply, echo-request } \
      limit rate 2000/second \
      counter \
      accept \
      comment "Permit outbound IPv4 echo (ping) limited to 2000 PPS"

    ## Permit all other outbound IPv4 ICMP
    ip protocol icmp \
      counter \
      accept \
      comment "Permit all other IPv4 ICMP"

    ## Permit IPv6 ping/ping responses but rate limit to 2000 PPS
    icmpv6 type { echo-reply, echo-request } \
      limit rate 2000/second \
      counter \
      accept \
      comment "Permit outbound IPv6 echo (ping) limited to 2000 PPS"

    ## Permit all other outbound IPv6 ICMP
    meta l4proto { icmpv6 } \
      counter \
      accept \
      comment "Permit all other IPv6 ICMP"

    ## Permit outbound traceroute UDP ports but limit to 500 PPS
    udp dport 33434-33524 \
      limit rate 500/second \
      counter \
      accept \
      comment "Permit outbound UDP traceroute limited to 500 PPS"

    ## Allow outbound HTTP and HTTPS connections
    tcp dport { http, https } ct state new \
      counter \
      accept \
      comment "Permit outbound HTTP and HTTPS connections"

    ## Permit outbound DNS requests
    meta l4proto { tcp, udp } th dport 53 \
      counter \
      accept \
      comment "Permit outbound TCP and UDP DNS requests"

    ## Allow outbound NTP requests
    udp dport 123 \
      counter \
      accept \
      comment "Permit outbound NTP requests"

    ## Log any unmatched traffic but rate limit logging to a maximum of 60 messages/minute
    ## The default policy will be applied to unmatched traffic
    limit rate 60/minute burst 100 packets \
      log prefix "OUT - Drop: " \
      comment "Log any unmatched traffic"

    ## Count the unmatched traffic
    counter \
      comment "Count any unmatched traffic"
  }

}