Electrical-Forenics Home ray@RayFranco.com                       601.529.7473
   © Dr. Ray Franco, PhD, PE  -  208 Fairways Dr., Vicksburg, MS 39183

 This Page is a Work in Progress !

   Last updated on Nov 11, 2024

Network Filter Tables (nftable)

Nftables are a framework for packet filtering, firewalls and Network Address Translators (NATs). Support for nftables has been in the Linux Kernel since version 3.13. Nfables is the sucessor to iptables. In Debian 10 (buster - July 6, 2019), nftables replaced iptables. nftables has a compatibility mode for iptables.

To run nft commands requires root privillages. You can either prefix all nft commands with sudo or you can change to the root user:

sudo bash

To determine the verson of Nftables:

nft -v

nftables Hierarchy

At the top is a text file, /etc/nftables.conf. It contains one or more nftables. Nftables contains one or more chains, and chains contain one or more rules.

All of the nftables do not have to be inside of /etc/nftables.conf. You can include other nftables at the end of the file:

include "/Path/nftable_name.conf"

# anything after the hash sign (#) is a comment
; more commands or parameters to follow
\ break a rule into mutliple lines

Ntable Types

There are six ntable types or address families:

ipip4 addresses - default if none specified
ip6ip6 addresses
inetboth ip4 & ip6
bridge 
netdevingress filtering
arpAddress Resolution Protocol

Syntax: table [<address_family>] nft_table_name { }

Chain types

There are two types of chains: base and non-base.

Base Chains

Base chains are the entry point for packets from the networking stack. Base chains specify a type, hook, priority and policy.

Syntax: chain chain_name {type <type> hook <hook> priority <priority>; [policy <policy>;]}

type: filter, route, and nat
priority: -300 to 300
policy: accept or drop

The lower the priority number the higher the priority is. Priority numbers can be negaive.

If no policy is explicitively give the default policy accept will be used.

Non-Base Chains - Regular Chains

Non-base chains are jump target to better organize rules. Non-base type chains do not have a type, hook and priority.

Rules

Rules are some expression to be matched followed by a verdict statement.

The following is an incomplet list of expressions that can be matched:

 

Common IPv4 Matches
saddr <ip source address> Source addresses ip saddr 192.168.1.0
ip saddr 192.168.1.0/24
daddr <ip destination address> Destination addresses ip daddr 192.168.1.0
ip daddr 192.168.1.0/24
protocol <protocol> Upper layer protocol ip protocol tcp
ip protocol udp
ip protocol icmp

 

Common TCP Matches
sport <tcp source port> Source port tcp sport 22
tcp sport ssh
dport <tcp destination port> Destination port tcp dport 22
tcp dport ssh

 

Common UDP Matches
sport <udp source port> Source port udp sport 22
udp sport ssh
dport <udp destination port> Destination port udp dport 22
udp dport ssh

 

Common Meta Matches
iifname <input interface name> Input interface name meta iifname "lo"
meta iifname "eth0"
oifname <Output interface name> Outout interface name meta oifname "lo"
meta oifname "eth0"
iff <input interface index> input interface index meta iif lo
meta iff eth0
oif <Ouput interface index> output interface index meta oif lo
meta oif eth0
ifftype <input interface type> input interface type meta iiftype loopback
meta iiftype ether
meta iiftype ipip
meta iiftype ipip6
oif <Ouput interface type> output interface type meta oiftype loopback
meta oiftype ether
meta oiftype ipip
meta oiftype ipip6

* The prefix meta before the meta key is optional. See meta expressions - types qualified and unqualified in manpage.[]

Common Ether Matches
saddr <mac address> Source MAC address either saddr 00:0f:54:0c:11:04

Verdict statements include:

References

  1. Quick reference-nftables in 10 minutes
  2. Nftables.org - NFT Man Pages

Example

The follow is an ntable that accepts all IPv4 traffic with input source address 192.168.37.52, and its drops all other inputs, including all IPv6 traffic.


table inet FILTER {
    chain INPUT {
        type filter hook input priority 0; policy accept;
        ip saddr 192.168.37.52 accept
        drop
    }
}
        

References

  1. Wiki ArchLinux nftables

Documentation

There are very few good tutorials on nftables. Nftables just does not have the same amount of documentation that iptables has. The nftables organization's (nftables.org) documentation is good; however, it is not a begginers guide. They want to show off their latest advances over iptables and how concise and succinct they can be. They have a simple ruleset for a home router [13], but it uses the concatenation operator (.) [14], vmap [15], and th (transport header) [16]. You have to dig through their documention to learn what these are. In the end, I found out that the th parameter requires both nftables 0.92 and Linux Kernnel 5.3. As of 11-29-22, the Raspberry Pi OS-64 still uses kernnel 5.15.

References

  1. Get Started with nftable | Linode
  2. NFTables Beginners Guide
  3. Using nftables in Red Hat Enterprise Linux 8 | Red Hat
  4. Chapter 6. Getting Started with nftables - Linux 7 | Red Hat
  5. Chapter 52. Getting started with nftables Red Hat Enterprise Linux 8 | Red Hat Customer Portal
  6. Best Practices for persisting nftables rules | ask ubuntu
  7. Nftables rules and tables dissapiered after reboot | Linuxquestions.org
  8. systemctl Commands: Restart, Reload, Stop Service and More | Linode
  9. https://Wiki.nftables.org
  10. https://Wiki.nftables.org - Quick reference-nftables in 10 minutes
  11. Simple Ruleset for a Workstation - nftables.org
  12. Simple Ruleset for a Server - nftables.org
  13. Simple Ruleset for a Home Router - nftables.org
  14. Concatenatins - nftables.org
  15. Verdict Maps (vmaps) - nftables.org
  16. Matching Packet Headers - nftabes.org
  17. https://Wiki.nftables.org - Jumping to chain - nftables wiki
  18. Setting up nftables Firewall | Cryptsus Blog
  19. Firewall Configuration with nftables
  20. Protecting Incoming Traffic with Nftables - Linux Cloud Hacks
  21. Unleashing the Power of Nftables Chains and Verdict Maps - Linux Cloud Hacks
  22. Mastering Nftables Sets: A Comprehensive Guide - Linux Cloud Hacks
  23. Keep! - Setting up nftables Firewall
  24. SOCAT - you won't believe what this Linux tool can do! - Linux Cloud Hacks

tcp dport 22 accept

Most all of these commands require root privilages. To become the root in the Raspberry Pi OS:

sudo bash

Notice that prompt changes to the hash sign (#), and that

whoami

says "root".

To determine if the the modulel is in the Linux Kernel:

modinfo nf_tables

To determine if nftables is installed, check the version:

nft -v

To determine if nftables is active

systemctl status nftables

My general purpose everyday RPi has firewald and fail2band installed. The "system status nftable" command say that it is unactive. However, the "nft list ruleset" command shows a table with the table_name firewalld. I do not understand this.

Systemctl Commands to Enabling and Disabling Nftabes

Raspberry Pi has everthing installed, but by default it is unactive.

For the current session, you can start or stop nftables (not persistent), with the following commands.

systemctl start nftables
systemctl stop nftables

To enabled or disabled nftables after a reboot (persistent), use one of the following commands:

systemctl enable nftables
systemctl mask nftables

To see errors:

systemctl status nftables

Nftables - Configuration and Script Files

By defination, Nftables does not have defaults chains (like iptables). However, some Linux distributions have a default configuration file, which contains predefined chains. Also, some distrubtion have example nft script files. See table below:

Distribution Config. Files Script Files
Debian
Raspberry Pi
/etc/nftables.conf /usr/share/doc/nftables/examples/
Red Hat
Fedora
/etc/sysconfig/nftables.conf /etc/nftables/

Debian's default configuration file, which is listed below, contains three chains, an input chain, a forward chain, and an output chain. It accepts everything. It is just a skelton that can be added to.

Debian's Default nftables Configuration File

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
	chain input {
		type filter hook input priority filter;
	}
	chain forward {
		type filter hook forward priority filter;
	}
	chain output {
		type filter hook output priority filter;
	}
}
    

Editing Configuration Files and Creating Script Files

There are three ways to edit or create nftables.

  1. Edit the nftables configuration file directly with your favority text editor. Anything after a # is a comment. A Nft configuration file can include nft script files, but they must be located in the /etc directory i.e. include "etc/Exampe2.nft".
  2. Edit the nftables configuration file with the nft add command to add tables, chains, and rules. Similarly, there is an nft delete command. In addition, there are commands to change the order of the rules. This method will detect syntax errors; however, it is very cumberson. In addition the changes are not persistant after a reboot unless you write the debugged ruleset back to the configuration file. Unfortunately, many introductory nftables articles spend most of their time explaining how to edit the configuration file with this method, and they spend very little time on nftables.
  3. Create nftables script files.
    1. You can run the script directly.
    2. The script can be called (included) from another script.
    3. The script can be loaded and ran from the nft enviroment with the command nft -f or --file. The last method will check for syntax errors. However, again this method is not persistent. You can write the nft list ruleset output back to the configuration file, which will make it persistance, but there will be no comments in the configuration file.

The easest method, is to create and debug a script file wih method 3C, and to include the script file in the configuration file. Note, the configuration file can be just a single include statement.

Nft script files can have any name, but it is customary for them to have the .nft extension.

If you are going to pass a nft script file to the nft utility, it must have the folowing header:

#!/usr/sbin/nft -f

If you want to flush the existing ruleset then the next line after the header should be:

flush ruleset

To pass a script to the nft utility, enter:

nft -f your_script_filename.nft

To run a script directly, you have to make the script executable:

chmod +x your_script_filename.nft

In debugging nftables, the following command my be useful:

systemctl status nftables

Other than the extension, location and that the fact that the nftables.conf is ran at boot, there are no differences between the nftables.conf file and nftables script files.

Idiosyncrasies

References

  1. https://wiki.debian.org/nftables
  2. Using nftables in Red Hat Enterprise Linux 8
  3. Wiki Archlinux Nftables
  4. Red Hat Training for RHEL 8 - Chapter 41. Getting started with nftables
  5. Wiki-Nftables.org - Scipting

nft - viewing chains & rules

To view all chains and rules in all tables:

nft list ruleset

To view chains and rules in a particuar table:

nft list table address_family table_name

To view the rules in a particuar chain:

nft list chain address_family table_name chain_name

Although you should never use firewalld and nftables together, you can use the nft utility to the view the nftables and rules generatated by firewalld:

nft list table inet firewalld
nft list table ip firewalld
nft list table ip6 firewalld

nftable

# anything after the hash sign (#) is a comment
; more commands or parameters to follow
\ break a rule into mutliple lines

 

Address Families

ipip4 addresses
ip6ip6 addresses
inetboth ip4 & ip6
bridge 
netdevingress filtering
arp 

 

Both iftables and iptables have chains with policy (accept/drop) and rules.

Both nf and ip table can filter:

prerouting  
input  
forward  
output  
postrouting  
arp  

nft -v # version

/////////

sudo nft add table [address_famaily] example_table

sudo nft delete table [address_famaily] example_table

sudo nft flush table [address_famaily] example_table

The flush command deletes every rule in every chain in the table

sudo nft add chain inet example_table example_chain '{type filter hook input priority 0; }'

base chain
type is filter
hook is input
priority is 0

Chains with lower priority numbers get procesed first.

Chain Type

filter
NAT

Non-base chains aslo refered to as regular chains, do not have type, hook and priority

 

saddr = source address e.g., 192.168.0.0/24
daddr = destination address e.g., 192.168.0.2-192.168.0.59
iifname = input interface name e.g., "eth0", "wlan0"
oifname = output interface name e.g., "eth0", "wlan0"
ct= connection tracking  

 

Tables have chains, and chains have rules. A table can have more than one chain, and a chain can have more than one rule.

Aliases

Common port aliases are:

Port No. Alias
22ssh
80http
443https
53domain (dns)

To list all aliases: cat /etc/services.

 

Priority No.|Alias
0filter

I am not a fan of using keywords as aliases e.g., a type of rule is a filter (keyword), but the word "filter" can also be used as an alias for the rule priority number 0, and when they are in the same line, this makes it confusing for newcomers.

Example 1A: Only allow incoming from 192.168.37.52


#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy accept;

        # Allow all traffic from 192.168.37.52 - Don't lock myself out 
        ip saddr 192.168.37.52 accept

        # Drop everything else
        drop
    }
}
     

Examble 1 does not allow loopback traffic that some services require. Modify it to allow loopback traffic.

Example 2:


#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy accept;

        # Allow loopback traffic from this computer
        meta iif lo accept

        # Drop loopback traffic not from this computer
        iif != lo ip daddr 127.0.0.1/8 drop  # IPv4
        iff != lo ip6 daddr ::1/128 drop     # IPv6

        # Allow all traffic from 192.168.37.52 - Don't lock myself out 
        ip saddr 192.168.37.52 accept

        # Drop everything else
        drop
    }
}
     

References:

  1. YouTube - A Brief Intro to Firewalls with nftables

Example 1A: Allow all incoming tcp traffic to ports 22 (ssh) and 5900 (VNC).



#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy accept;

        # Don't lock myself out 
        ip saddr 192.168.37.52 accept

        # Allow SSH traffic
        # matched by tcp and destination port 22 (alias ssh) 
        tcp dport ssh accept

        # Allow VNC traffic
        tcp dport 5900 accept # no alias for this port

        # Drop everything else
        drop
    }
}
     

I am not a fan of keywords and/or aliases having multiple meaning. However, this seems to be the norm with nftable. For example, a type of chain is a "filter" (keyword), and "filter" is also used as an alias for the priority number 0. Furthermore, in this example the table name is "filter", and the chain name is "input", which is a type of hook (keyword). This is very confusing for newcomers.

Inline Counters

Example 1B: To determine if this is working, you can add counters to count the number of packets and bytes. Counters should be paced immediately before accept or drop.

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy accept;

        # Don't lock myself out 
        ip saddr 192.168.37.52 accept

        # Allow SSH traffic
        # mached by tcp and destination port 22 (alias ssh) 
        tcp dport ssh counter accept

        # Allow VNC traffic
        tcp dport 5900 counter accept # no alias for this port

        # Drop everything else
        drop
    }

}
       

Named Counters

You can also defined named counters. Modify example 1a by naming the counter.

#!/usr/sbin/nft -f

flush ruleset

    counter ssh_or_Vnc {}

table inet filter {
    chain input {
        type filter hook input priority filter; policy accept;

        # Don't lock myself out 
        ip saddr 192.168.37.52 accept

        # Allow SSH traffic
        # mached by tcp and destination port 22 (alias ssh) 
        tcp dport ssh counter name ssh_or_vnc accept

        # Allow VNC traffic
        tcp dport 5900 counter name ssh_or_vnc accept 

        # Drop everything else
        drop
    }

}
       

Example 1C: Modify Example 1B to allow SSH and VNC traffic only from source addresses 192.168.37.45 and 192.168.37.45.

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy accept;

        # Don't lock myself out 
        ip saddr 192.168.37.52 accept

        # Allow SSH traffic
        # mached by tcp and destination port 22 (alias ssh) 
        ip saddr 192.168.37.45 tcp dport ssh counter accept
        ip saddr 192.168.37.46 tcp dport ssh counter accept

        # Allow VNC traffic
        ip saddr 192.168.37.45 tcp dport 5900 counter accept 
        ip saddr 192.168.37.46 tcp dport 5900 counter accept 

        # Drop everything else
        drop
    }

}
       

To use MAC addresses in lieu of IP addresses, prefix saddr with ether. For example:

        ether saddr 04:0D:0A:3D:FE:06 tcp dport ssh counter accept
       

References

  1. Protecting Incoming Traffic with Nftables

In-Line Sets or Anonymous Sets

Sets are enclosed in brackets, and the elements are separated by commas. You can use sets to combine rules.

Example 1D: Modify Example 1C using sets.

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority filter; policy accept;

        # Don't lock myself out 
        ip saddr 192.168.37.52 accept

        # Allow SSH and VNC traffic
        # matched by source address and tcp protocol with destination port 22 or 5900. 
        ip saddr {192.168.37.45, 192.168.37.46} tcp dport {ssh, 5900} counter accept

        # Drop everything else
        drop
    }

}
       

References

  1. nftables Wiki - Sets
  2. Mastering Nftables Sets: A Comprehensive Guide - Linux Cloud Hacks

Introduction to Named Sets

Example 1E: Modify Example 1D to use named sets.


#!/usr/sbin/nft -f

flush ruleset

# set_example.nft


table inet filter {

    set allowable_ip_addresses{
        typeof ip saddr  
        elements = {192.168.37.45, 192.168.37.46}
    }

    set allowable_dports{
        typeof tcp dport 
        elements = {ssh, 5900}
    }

    chain input {
        type filter hook input priority filter;

        # Don't lock myself out 
        ip saddr 192.168.37.52 accept

        # Only allow SSH & VNC traffic
        # matched by source IP and tcp destination ports   
        ip saddr @allowable_ip_addresses tcp dport @allowable_dports accept

        # Drop everything else
        drop
     }

}
     

Having to declare named sets in advance is more complicated than in-line sets. However, we have just touched on named sets. Named sets are more powerful than in-line sets. For example, as will be in "Advanced Named Sets", you can use nft rules to add elements to named sets.

References

  1. nftables Wiki - Sets
  2. Mastering Nftables Sets: A Comprehensive Guide - Linux Cloud Hacks

Allow and Limit Pings

For troubleshooting, you should accept pings but limit them

Example 1F: Modify Example 1E to accept pings.


#!/usr/sbin/nft -f

flush ruleset

# set_example.nft


table inet filter {

    set allowable_ip_addresses{
        typeof ip saddr  
        elements = {192.168.37.45, 192.168.37.46}
    }

    set allowable_dports{
        typeof tcp dport 
        elements = {ssh, 5900}
    }

    chain input {
        type filter hook input priority filter;

        # Don't lock myself out 
        ip saddr 192.168.37.52 accept

        # Only allow SSH & VNC traffic
        # matched by source IP and tcp destination ports   
        ip saddr @allowable_ip_addresses tcp dport @allowable_dports accept

        # allow pings without limits.
        ip protocol icmp accept;

        # Drop everything else
        drop
     }

}
     

Example 1G: Modify Example 1F to limit pings.


#!/usr/sbin/nft -f

flush ruleset

# set_example.nft


table inet filter {

    set allowable_ip_addresses{
        typeof ip saddr  
        elements = {192.168.37.45, 192.168.37.46}
    }

    set allowable_dports{
        typeof tcp dport 
        elements = {ssh, 5900}
    }

    chain input {
        type filter hook input priority filter;

        # Don't lock myself out 
        ip saddr 192.168.37.52 accept

        # Only allow SSH & VNC traffic
        # matched by source IP and tcp destination ports   
        ip saddr @allowable_ip_addresses tcp dport @allowable_dports accept

        # allow pings without limits.
        ip protocol icmp limit rate 1/second accept;

        # Drop everything else
        drop
     }

}
     

References

  1. Firewall Configuration with nftables - Travis Horn

Non-base Chains and Verdict Maps

Thus far, all our chains have been "base" chains. Non-base chains do NOT have a type, hook, priority or default policy. They only contain rules. They are used to group your rules according to some criteria. Non-based chains are the targets of jump or goto actions.

Connection Tracking - Conntrack / Stateful Firewald

TCP has connection tracking (ct) states. UDP does NOT have connection tracking states [1].

There are five (5) conntrack (ct) states [4]:

new Netfilter has so far seen packets between this pair of hosts only in one direction. At least on of theses packets is part of a valid initialization sequence,e.g. SYN packet for a TCP Connection.
established Netfilter has seen valid packets travel in both directions between this pair of hosts. For TCP connections, the three-way-handshake has been successfully completed.
related This connection was initiated after the main connection, as expected from normal operation of the main connection. A common example is an FTP data channel established at the behest of an FTB control channel.
invalid Assigned to packets that do not follow the expected behavior of a connection. These include malformed, corrupted packets and maliceware. Another example, would be a late TCP packet that is received after retransmission has been performed [3]. These should be dropped.
untracked Dummy state assigned to packets.

The following example uses connection tracking to only allow outbound DNS, HTTP, and HTTPS traffic and only allow inbound return traffic and loop back traffic.


#!/usr/bin/nft -f

flush ruleset

table ip transport {

   chain INPUT  {
       type filter hook input priority filter; policy drop;
       ct state established,related counter accept
       iif lo ip daddr 127.0.0.1/8 counter accept
   }

   chain OUTPUT {
      type filter hook output  priority filter; policy drop;
      # allow established and existing traffic
      ct state established,related counter accept
      # allow DNS out
      udp dport 53 ct state new counter accept
      tcp dport 53 ct state new counter accept
      # allow HTTP/HTTS  out
      tcp dport { 80, 443 } ct state new counter accept
      udp dport { 80, 443 } ct state new counter accept
      # drop everyting else
   }

}
         

The reasons for the two DNS statements and the two HTTP/HTTPS statements are:

References:

  1. TCP vs UDP: What Is the Difference Between TCP and UDP?
  2. Why does DNS use UDP and not TCP?
  3. Is DNS TCP or UDP port 53?
  4. Matching connection tracking stateful metainformation
  5. Sever Fault - Good Examples of CT States
  6. MokroTik: Firewall - Default Config & Basic for Dummies
  7. MokroTik Firewall Basic Concept

Matching Both TCP & UDP with Transport Header (th)

Altought, tcp and udp are seperate protocols with their own headers, the raw location of dport is the same in both headers. This allows merging of dport statements that only differ in whether the protocol is tcp or udp.

For example the two statements:

 tcp  dport 53 accept
 upd dport 53 accept

Can be replaced by the single statement:

meta l4proto {tcp,udp} th dport 53 accept

The first part of the statement, meta l4proto {tcp, udp}, ensures that it is a level 4 protocol - either tcp or udp and excludes other level 4 protocols. The second part of the statement, th dport 53, does a raw read of the transport header (th) and checks to see if dport matches 53.

With the transport header raw read feature, four lines in the previous example can be condensed to one line:


#!/usr/bin/nft -f

flush ruleset

table ip transport {

   chain INPUT  {
       type filter hook input priority filter; policy drop;
       ct state established,related counter accept
       iif lo ip daddr 127.0.0.1/8 counter accept
   }

   chain OUTPUT {
      type filter hook output  priority filter; policy drop;
      # allow established and existing traffic
      ct state established,related counter accept
      # allow DNS, HTTP and HTTP out
      meta l4proto {udp,tcp} th dport { 53, 80, 443 } ct state new counter accept
      # drop everyting else

   }

}
         

This should be more efficent and faster than executng four sepearate tests.

References:

  1. Wikipedia - Google's QUIC Protocol.
  2. StackExchange - How to match both UDP and TCP for given ports in one line with nft.
  3. Mankier.coom - Raw Payload Expression
  4. LWN.net - nftables 0.83 Release
  5. How-to-Greek - How to Block Facebook (or Any Distracting Website)

You can use variables:

define accepted_input_ports = {22,5900}

tcp dport $accepted_input_ports accept

Advance Named Sets - Limit Connection Attemps

As previously stated, you can used nft rules to add elements to a named set. The example below illustrates this and the use of timers.

Example 5 - If an IP address attemps to SSH into a machine more than twice in one minute, the IP address is locked out for 24 hours. This is the last example in referencet [1]. The code is below:


#!/usr/sbin/nft -f

flush ruleset

table ip filter {

    set stage1 {
        typeof ip saddr
        flags timeout
    }

    set stage2 {
        typeof ip saddr
        flags timeout
    }

    set stage3 {
        typeof ip saddr
        flags timeout
    }
    chain input {
        type filter hook input priority filter; policy accept;
        # allowed established and related traffic
        ct state related,established accept
        # do not lock yourself out
        ct state new ip saddr 172.27.96.76 tcp dport 22 accept

        # To understand the lockout code below, read it in the order of the comments numbers
        ct state new ip saddr @stage2 tcp dport 22 add @stage3 { ip saddr timeout 1d } #3
        ct state new ip saddr @stage1 tcp dport 22 add @stage2 { ip saddr timeout 1m } #2
        ct state new tcp dport 22 add @stage1 { ip saddr timeout 1m } #1
        ct state new ip saddr @stage3 tcp dport 22 drop #4
    }    
}

         

You can test this by trying to SSH into the machine, and when prompted for the password, type in an incorrect passwords three times. Then try to SSH into the machine a second time. Again type in an incorrect password three times. If you attempt to SSH into the machine for a third time, it wil NOT prompt you for a password. You are now locked out for 24 hours.

Thus if you know nftables, you do not need a program such as fail2ban that has dependaces.

You do not need to lock an IP address out for one day to prevent a brute force attach. Five minutes is probably sufficent. I belive the one day is so you can see that someone tried to attach this machine.

Example 2

There is a Red Hat example that claims to limit login attempts. See reference [2]. The code us below:


#!/usr/sbin/nft -f

flush ruleset

table ip filter {

    set denylist {
        type ipv4_addr
            flags dynamic, timeout; timeout 5m;
    }

    chain input {
        type filter hook input priority 0;

        # I changed the rate from over 10/min to over 2/min for testing
        # I am not sure what "untracked" does, if anything,  in the statement below.
        ip protocol tcp ct state new,untracked limit rate over 2/minute add @denylist {ip saddr}

        ip saddr @denylist drop
    }
}
         

When you attemp to remotely SSH into a machine, you usally get three (3) attemps to type in the correct password, before you have to SSH into the machine again. I was not able to type an incorrect password fast enought for my IP address to be placed in the denylist. However, when prompted for the password, you can hit Control-C, and then you will have to SSH again into the machine. This method took six (6) attemps before my IP address was put into the denylist. On at least one occassion it took seven (7).

The Youtube video in reference [3], uses the same code except he changed the rate to 3/minute. It took him seven (7) attemps before his IP address was placed into the denylist.

References:

  1. Mastering Nftables Sets: A Comprehensive Guide - Linux Cloud Hacks
  2. Red_Hat - Using nftables to limit the amount of connections
  3. YouTube - Block SSH brute force attacks for 5 minutes using nftables on Linux firewall
  4. Wiki Sets
  5. Unleashing the Power of Nftables Chains and Verdict Maps - Linux Cloud Hacks
  6. Matching connection tracking stateful metainformation
  7. Mikrotik - Firewall Basic Concepts

Port Knocking

References:

  1. How to Secure SSH with Port Knocking and Nftables on CentOS 8 - The Urban Penguin


#!/usr/sbin/nft -f

flush ruleset

table inet filter {

    set ssh_clients {
        type ipv4_addr 
        flags timeout
    }

    chain input {
        type filter hook input priority filter; policy accept;

        # do not lock myself out
        ct state new ip saddr 192.168.37.45 tcp dport 22 accept
        # accept loopback trafic
        iif "lo" accept
        # allowed established and related traffic
        ct state related,established accept

        tcp dport 10 add @ssh_clients {ip saddr timeout 30s}
        ip saddr @ssh_clients tcp dport ssh accept
        drop
    }    
}


Port Knocking with Limited Attemps

Sets - type or typeof

Concatenation

False add @ { } statements before drop or accept

LAN Monitoring

nft script:


#!/usr/sbin/nft -f

flush ruleset

table ip filter {

# 11monitor.nft - version 11
#
# The following code helps you to idenify tcp, udp and icmp traffic 
# on your local area network. 
#
# For now (7-22-2022), I have decided to implement this only for IPv4 traffic
# and to drop, all IPv6 traffic.

    set log_tcp_traffic { typeof ip saddr . tcp sport . ip daddr . tcp dport; }
    set log_udp_traffic { typeof ip daddr . udp dport . ip saddr . udp sport; }
    set log_ct_established_tcp { typeof ip daddr . tcp dport . ip saddr . tcp sport; }
    set log_ct_established_udp { typeof ip daddr . udp dport . ip saddr . udp sport; }
    set log_ct_invalid { typeof ip saddr; }
    set log_loopback_traffic { typeof ip saddr; }
    set log_loopback_bad { typeof ip saddr; }
    set log_pings { typeof ip saddr; }
    set log_dropped { typeof ip saddr; }

chain input {
        type filter hook input priority filter; policy accept;

        #log source ip of all input
        add @log_tcp_traffic { ip daddr . tcp dport . ip saddr . tcp sport}
        add @log_udp_traffic { ip daddr . udp dport . ip saddr . udp sport }

        # do not lock myself out - allow local network
        ct state new ip saddr 192.168.37.0/24 accept

        # allowed established and related traffic
        ct state related,established add @log_ct_established_tcp \
            {ip daddr . tcp dport . ip saddr . tcp sport} accept
        ct state related,established add @log_ct_established_udp \
            {ip daddr . udp dport . ip saddr . udp sport} accept
        ct state invalid add @log_ct_invalid {ip saddr} drop

        # accept loopback trafic
        iif "lo" add @log_loopback_traffic {ip saddr} accept

        # loopback bad - I am currently working on this
#        iff != lo  ip daddr 127.0.0.1/8 add @loopback_bad {ip saddr} drop
#        iff != lo ip6 daddr ::1/128 drop
       
        # allow pings but limit the rate  
        ip protocol icmp limit rate 1/second add @log_pings {ip saddr} accept

        # log dropped traffic
        add @log_dropped {ip saddr} drop
    } 

	chain forward {type filter hook forward priority filter; policy drop; }

	chain output { type filter hook output priority filter; policy accept; }
    
}

# Drop all IPv6 Traffic:
table ip6 filter {
	chain input   { type filter hook forward priority filter; policy drop; }
	chain forward { type filter hook forward priority filter; policy drop; }
	chain output  { type filter hook output priority filter; policy drop;  }
}

Why Semicoln in single line set definations?

References:

  1. Beginners guide to traffic filtering with nftables - Break Long Lines

Source Network Address Translation (SNAT)

References

  1. Mastering Source Network Address Translation (SNAT) in Nftables - Linux Cloud Hacks

Destination Network Address Translation (DNAT)

References

  1. Unlock the Power of Nftables: Mastering Destination Network Address Translation (DNAT) - Linux Cloud Hacks

Debian's - Default nftable and chains

systemctl start nftables

nft list ruleset

table inet filter {
  chain input {
    type filter hook input priority filter; policy accept;
 }
 
  chain forward {
    type filter hook forward priority filter; policy accept;
# only routers should forward packets
counter drop
 }
 
  chain output {
  type filter hook output priority filter; policy accept;
 }

-- -- -- -- -- -- -- -- -- -- --

To stop nftables from doing anything, just drop all the rules:

nft flush ruleset

To uninstall it and purge any traces of nftables in your system:

aptitude purge nftables

///////////////////////////

nft Kernel Modules

modinfo nf_tables

Show Active nftables Kernel Module

lsmod | grep nf_tables

///////////////////////

Disable iptables

iptables -F

ip6tables -F