Chapter 8. Getting started with nftables


The nftables framework classifies packets and it is the successor to the iptables, ip6tables, arptables, ebtables, and ipset utilities. It offers numerous improvements in convenience, features, and performance over previous packet-filtering tools, most notably:

  • Built-in lookup tables instead of linear processing
  • A single framework for both the IPv4 and IPv6 protocols
  • All rules applied atomically instead of fetching, updating, and storing a complete rule set
  • Support for debugging and tracing in the rule set (nftrace) and monitoring trace events (in the nft tool)
  • More consistent and compact syntax, no protocol-specific extensions
  • A Netlink API for third-party applications

The nftables framework uses tables to store chains. The chains contain individual rules for performing actions. The nft utility replaces all tools from the previous packet-filtering frameworks. You can use the libnftnl library for low-level interaction with nftables Netlink API through the libmnl library.

To display the effect of rule set changes, use the nft list ruleset command. Because these utilities add tables, chains, rules, sets, and other objects to the nftables rule set, be aware that nftables rule-set operations, such as the nft flush ruleset command, might affect rule sets installed using the iptables command.

8.1. Creating and managing nftables tables, chains, and rules

You can display nftables rule sets and manage them.

8.1.1. Basics of nftables tables

A table in nftables is a namespace that contains a collection of chains, rules, sets, and other objects.

Each table must have an address family assigned. The address family defines the packet types that this table processes. You can set one of the following address families when you create a table:

  • ip: Matches only IPv4 packets. This is the default if you do not specify an address family.
  • ip6: Matches only IPv6 packets.
  • inet: Matches both IPv4 and IPv6 packets.
  • arp: Matches IPv4 address resolution protocol (ARP) packets.
  • bridge: Matches packets that pass through a bridge device.
  • netdev: Matches packets from ingress.

If you want to add a table, the format to use depends on your firewall script:

  • In scripts in native syntax, use:

    table <table_address_family> <table_name> {
    }
  • In shell scripts, use:

    nft add table <table_address_family> <table_name>

8.1.2. Basics of nftables chains

Tables consist of chains which in turn are containers for rules. The following two rule types exists:

  • Base chain: You can use base chains as an entry point for packets from the networking stack.
  • Regular chain: You can use regular chains as a jump target to better organize rules.

If you want to add a base chain to a table, the format to use depends on your firewall script:

  • In scripts in native syntax, use:

    table <table_address_family> <table_name> {
      chain <chain_name> {
        type <type> hook <hook> priority <priority>
        policy <policy> ;
      }
    }
  • In shell scripts, use:

    nft add chain <table_address_family> <table_name> <chain_name> { type <type> hook <hook> priority <priority> \; policy <policy> \; }

    To avoid that the shell interprets the semicolons as the end of the command, place the \ escape character in front of the semicolons.

Both examples create base chains. To create a regular chain, do not set any parameters in the curly brackets.

Chain types

The following are the chain types and an overview with which address families and hooks you can use them:

TypeAddress familiesHooksDescription

filter

all

all

Standard chain type

nat

ip, ip6, inet

prerouting, input, output, postrouting

Chains of this type perform native address translation based on connection tracking entries. Only the first packet traverses this chain type.

route

ip, ip6

output

Accepted packets that traverse this chain type cause a new route lookup if relevant parts of the IP header have changed.

Chain priorities

The priority parameter specifies the order in which packets traverse chains with the same hook value. You can set this parameter to an integer value or use a standard priority name.

The following matrix is an overview of the standard priority names and their numeric values, and with which address families and hooks you can use them:

Textual valueNumeric valueAddress familiesHooks

raw

-300

ip, ip6, inet

all

mangle

-150

ip, ip6, inet

all

dstnat

-100

ip, ip6, inet

prerouting

-300

bridge

prerouting

filter

0

ip, ip6, inet, arp, netdev

all

-200

bridge

all

security

50

ip, ip6, inet

all

srcnat

100

ip, ip6, inet

postrouting

300

bridge

postrouting

out

100

bridge

output

Chain policies

The chain policy defines whether nftables should accept or drop packets if rules in this chain do not specify any action. You can set one of the following policies in a chain:

  • accept (default)
  • drop

8.1.3. Basics of nftables rules

Rules define actions to perform on packets that pass a chain that contains this rule. If the rule also contains matching expressions, nftables performs the actions only if all previous expressions apply.

If you want to add a rule to a chain, the format to use depends on your firewall script:

  • In scripts in native syntax, use:

    table <table_address_family> <table_name> {
      chain <chain_name> {
        type <type> hook <hook> priority <priority> ; policy <policy> ;
          <rule>
      }
    }
  • In shell scripts, use:

    nft add rule <table_address_family> <table_name> <chain_name> <rule>

    This shell command appends the new rule at the end of the chain. If you prefer to add a rule at the beginning of the chain, use the nft insert command instead of nft add.

8.1.4. Managing tables, chains, and rules using nft commands

To manage an nftables firewall on the command line or in shell scripts, use the nft utility.

Important

The commands in this procedure do not represent a typical workflow and are not optimized. This procedure only demonstrates how to use nft commands to manage tables, chains, and rules in general.

Procedure

  1. Create a table named nftables_svc with the inet address family so that the table can process both IPv4 and IPv6 packets:

    # nft add table inet nftables_svc
  2. Add a base chain named INPUT, that processes incoming network traffic, to the inet nftables_svc table:

    # nft add chain inet nftables_svc INPUT { type filter hook input priority filter \; policy accept \; }

    To avoid that the shell interprets the semicolons as the end of the command, escape the semicolons using the \ character.

  3. Add rules to the INPUT chain. For example, allow incoming TCP traffic on port 22 and 443, and, as the last rule of the INPUT chain, reject other incoming traffic with an Internet Control Message Protocol (ICMP) port unreachable message:

    # nft add rule inet nftables_svc INPUT tcp dport 22 accept
    # nft add rule inet nftables_svc INPUT tcp dport 443 accept
    # nft add rule inet nftables_svc INPUT reject with icmpx type port-unreachable

    If you enter the nft add rule commands as shown, nft adds the rules in the same order to the chain as you run the commands.

  4. Display the current rule set including handles:

    # nft -a list table inet nftables_svc
    table inet nftables_svc { # handle 13
      chain INPUT { # handle 1
        type filter hook input priority filter; policy accept;
        tcp dport 22 accept # handle 2
        tcp dport 443 accept # handle 3
        reject # handle 4
      }
    }
  5. Insert a rule before the existing rule with handle 3. For example, to insert a rule that allows TCP traffic on port 636, enter:

    # nft insert rule inet nftables_svc INPUT position 3 tcp dport 636 accept
  6. Append a rule after the existing rule with handle 3. For example, to insert a rule that allows TCP traffic on port 80, enter:

    # nft add rule inet nftables_svc INPUT position 3 tcp dport 80 accept
  7. Display the rule set again with handles. Verify that the later added rules have been added to the specified positions:

    # nft -a list table inet nftables_svc
    table inet nftables_svc { # handle 13
      chain INPUT { # handle 1
        type filter hook input priority filter; policy accept;
        tcp dport 22 accept # handle 2
        tcp dport 636 accept # handle 5
        tcp dport 443 accept # handle 3
        tcp dport 80 accept # handle 6
        reject # handle 4
      }
    }
  8. Remove the rule with handle 6:

    # nft delete rule inet nftables_svc INPUT handle 6

    To remove a rule, you must specify the handle.

  9. Display the rule set, and verify that the removed rule is no longer present:

    # nft -a list table inet nftables_svc
    table inet nftables_svc { # handle 13
      chain INPUT { # handle 1
        type filter hook input priority filter; policy accept;
        tcp dport 22 accept # handle 2
        tcp dport 636 accept # handle 5
        tcp dport 443 accept # handle 3
        reject # handle 4
      }
    }
  10. Remove all remaining rules from the INPUT chain:

    # nft flush chain inet nftables_svc INPUT
  11. Display the rule set, and verify that the INPUT chain is empty:

    # nft list table inet nftables_svc
    table inet nftables_svc {
      chain INPUT {
        type filter hook input priority filter; policy accept
      }
    }
  12. Delete the INPUT chain:

    # nft delete chain inet nftables_svc INPUT

    You can also use this command to delete chains that still contain rules.

  13. Display the rule set, and verify that the INPUT chain has been deleted:

    # nft list table inet nftables_svc
    table inet nftables_svc {
    }
  14. Delete the nftables_svc table:

    # nft delete table inet nftables_svc

    You can also use this command to delete tables that still contain chains.

    Note

    To delete the entire rule set, use the nft flush ruleset command instead of manually deleting all rules, chains, and tables in separate commands.

Additional resources

nft(8) man page on your system

8.2. Migrating from iptables to nftables

If your firewall configuration still uses iptables rules, you can migrate your iptables rules to nftables.

8.2.1. When to use firewalld, nftables, or iptables

The following is a brief overview in which scenario you should use one of the following utilities:

  • firewalld: Use the firewalld utility for simple firewall use cases. The utility is easy to use and covers the typical use cases for these scenarios.
  • nftables: Use the nftables utility to set up complex and performance-critical firewalls, such as for a whole network.
  • iptables: The iptables utility on Red Hat Enterprise Linux uses the nf_tables kernel API instead of the legacy back end. The nf_tables API provides backward compatibility so that scripts that use iptables commands still work on Red Hat Enterprise Linux. For new firewall scripts, Red Hat recommends to use nftables.
Important

To prevent the different firewall-related services (firewalld, nftables, or iptables) from influencing each other, run only one of them on a RHEL host, and disable the other services.

8.2.2. Concepts in the nftables framework

Compared to the iptables framework, nftables offers a more modern, efficient, and flexible alternative. There are several concepts and features that provide advanced capabilities and improvements over iptables. These enhancements simplify the rule management and improve performance to make nftables a modern alternative for complex and high-performance networking environments.

The nftables framework contains the following components:

Tables and namespaces
In nftables, tables represent organizational units or namespaces that group together related firewall chains, sets, flowtables, and other objects. In nftables, tables provide a more flexible way to structure firewall rules and related components. While in iptables, the tables were more rigidly defined with specific purposes.
Table families
Each table in nftables is associated with a specific family (ip, ip6, inet, arp, bridge, or netdev). This association determines which packets the table can process. For example, a table in the ip family handles only IPv4 packets. On the other hand, inet is a special case of table family. It offers a unified approach across protocols, because it can process both IPv4 and IPv6 packets. Another case of a special table family is netdev, because it is used for rules that apply directly to network devices, enabling filtering at the device level.
Base chains

Base chains in nftables are highly configurable entry-points in the packet processing pipeline that enable users to specify the following:

  • Type of chain, for example "filter"
  • The hook point in the packet processing path, for example "input", "output", "forward"
  • Priority of the chain

This flexibility enables precise control over when and how the rules are applied to packets as they pass through the network stack. A special case of a chain is the route chain, which is used to influence the routing decisions made by the kernel, based on packet headers.

Virtual machine for rule processing
The nftables framework uses an internal virtual machine to process rules. This virtual machine executes instructions that are similar to assembly language operations (loading data into registers, performing comparisons, and so on). Such a mechanism allows for highly flexible and efficient rule processing.

Enhancements in nftables can be introduced as new instructions for that virtual machine. This typically requires a new kernel module and updates to the libnftnl library and the nft command-line utility.

Alternatively, you can introduce new features by combining existing instructions in innovative ways without a need for kernel modifications. The syntax of nftables rules reflects the flexibility of the underlying virtual machine. For example, the rule meta mark set tcp dport map { 22: 1, 80: 2 } sets a packet’s firewall mark to 1 if the TCP destination port is 22, and to 2 if the port is 80. This demonstrates how complex logic can be expressed concisely.

Lessons learned and enhancements
The nftables framework integrates and extends the functionality of the ipset utility, which is used in iptables for bulk matching on IP addresses, ports, other data types and, most importantly, combinations thereof. This integration makes it easier to manage large and dynamic sets of data directly within nftables. Next, nftables natively supports matching packets based on multiple values or ranges for any data type, which enhances its capability to handle complex filtering requirements. With nftables you can manipulate any field within a packet.

In nftables, sets can be either named or anonymous. The named sets can be referenced by multiple rules and modified dynamically. The anonymous sets are defined inline within a rule and are immutable. Sets can contain elements that are combinations of different types, for example IP address and port number pairs. This feature provides greater flexibility in matching complex criteria. To manage sets, the kernel can select the most appropriate backend based on the specific requirements (performance, memory efficiency, and others). Sets can also function as maps with key-value pairs. The value part can be used as data points (values to write into packet headers), or as verdicts or chains to jump to. This enables complex and dynamic rule behaviors, known as "verdict maps".

Flexible rule format
The structure of nftables rules is straightforward. The conditions and actions are applied sequentially from left to right. This intuitive format simplifies rule creating and troubleshooting.

Conditions in a rule are logically connected (with the AND operator) together, which means that all conditions must be evaluated as "true" for the rule to match. If any condition fails, the evaluation moves to the next rule.

Actions in nftables can be final, such as drop or accept, which stop further rule processing for the packet. Non-terminal actions, such as counter log meta mark set 0x3, perform specific tasks (counting packets, logging, setting a mark, and others), but allow subsequent rules to be evaluated.

8.2.3. Concepts in the deprecated iptables framework

Similar to the actively-maintained nftables framework, the deprecated iptables framework enables you to perform a variety of packet filtering tasks, logging and auditing, NAT-related configuration tasks, and more.

The iptables framework is structured into multiple tables, where each table is designed for a specific purpose:

filter
The default table, ensures general packet filtering
nat
For Network Address Translation (NAT), includes altering the source and destination addresses of packets
mangle
For specific packet alteration, enables you to do modification of packet headers for advanced routing decisions
raw
For configurations that need to happen before connection tracking

These tables are implemented as separate kernel modules, where each table offers a fixed set of builtin chains such as INPUT, OUTPUT, and FORWARD. A chain is a sequence of rules that packets are evaluated against. These chains hook into specific points in the packet processing flow in the kernel. The chains have the same names across different tables, however their order of execution is determined by their respective hook priorities. The priorities are managed internally by the kernel to make sure that the rules are applied in the correct sequence.

Originally, iptables was designed to process IPv4 traffic. However, with the inception of the IPv6 protocol, the ip6tables utility needed to be introduced to provide comparable functionality (as iptables) and enable users to create and manage firewall rules for IPv6 packets. With the same logic, the arptables utility was created to process Address Resolution Protocol (ARP) and the ebtables utility was developed to handle Ethernet bridging frames. These tools ensure that you can apply the packet filtering abilities of iptables across various network protocols and provide comprehensive network coverage.

To enhance the functionality of iptables, the extensions started to be developed. The functionality extensions are typically implemented as kernel modules that are paired with user-space dynamic shared objects (DSOs). The extensions introduce "matches" and "targets" that you can use in firewall rules to perform more sophisticated operations. Extensions can enable complex matches and targets. For instance you can match on, or manipulate specific layer 4 protocol header values, perform rate-limiting, enforce quotas, and so on. Some extensions are designed to address limitations in the default iptables syntax, for example the "multiport" match extension. This extension allows a single rule to match multiple, non-consecutive ports to simplify rule definitions, and thereby reducing the number of individual rules required.

An ipset is a special kind of functionality extension to iptables. It is a kernel-level data structure that is used together with iptables to create collections of IP addresses, port numbers, and other network-related elements that you can match against packets. These sets significantly streamline, optimize, and accelerate the process of writing and managing firewall rules.

Additional resources

  • iptables(8) man page

8.2.4. Converting iptables and ip6tables rule sets to nftables

Use the iptables-restore-translate and ip6tables-restore-translate utilities to translate iptables and ip6tables rule sets to nftables.

Prerequisites

  • The nftables and iptables packages are installed.
  • The system has iptables and ip6tables rules configured.

Procedure

  1. Write the iptables and ip6tables rules to a file:

    # iptables-save >/root/iptables.dump
    # ip6tables-save >/root/ip6tables.dump
  2. Convert the dump files to nftables instructions:

    # iptables-restore-translate -f /root/iptables.dump > /etc/nftables/ruleset-migrated-from-iptables.nft
    # ip6tables-restore-translate -f /root/ip6tables.dump > /etc/nftables/ruleset-migrated-from-ip6tables.nft
  3. Review and, if needed, manually update the generated nftables rules.
  4. To enable the nftables service to load the generated files, add the following to the /etc/sysconfig/nftables.conf file:

    include "/etc/nftables/ruleset-migrated-from-iptables.nft"
    include "/etc/nftables/ruleset-migrated-from-ip6tables.nft"
  5. Stop and disable the iptables service:

    # systemctl disable --now iptables

    If you used a custom script to load the iptables rules, ensure that the script no longer starts automatically and reboot to flush all tables.

  6. Enable and start the nftables service:

    # systemctl enable --now nftables

Verification

  • Display the nftables rule set:

    # nft list ruleset

8.2.5. Converting single iptables and ip6tables rules to nftables

Red Hat Enterprise Linux provides the iptables-translate and ip6tables-translate utilities to convert an iptables or ip6tables rule into the equivalent one for nftables.

Prerequisites

  • The nftables package is installed.

Procedure

  • Use the iptables-translate or ip6tables-translate utility instead of iptables or ip6tables to display the corresponding nftables rule, for example:

    # iptables-translate -A INPUT -s 192.0.2.0/24 -j ACCEPT
    nft add rule ip filter INPUT ip saddr 192.0.2.0/24 counter accept

    Note that some extensions lack translation support. In these cases, the utility prints the untranslated rule prefixed with the # sign, for example:

    # iptables-translate -A INPUT -j CHECKSUM --checksum-fill
    nft # -A INPUT -j CHECKSUM --checksum-fill

Additional resources

  • iptables-translate --help

8.2.6. Comparison of common iptables and nftables commands

The following is a comparison of common iptables and nftables commands:

  • Listing all rules:

    iptablesnftables

    iptables-save

    nft list ruleset

  • Listing a certain table and chain:

    iptablesnftables

    iptables -L

    nft list table ip filter

    iptables -L INPUT

    nft list chain ip filter INPUT

    iptables -t nat -L PREROUTING

    nft list chain ip nat PREROUTING

    The nft command does not pre-create tables and chains. They exist only if a user created them manually.

    Listing rules generated by firewalld:

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

8.3. Writing and executing nftables scripts

The major benefit of using the nftables framework is that the execution of scripts is atomic. This means that the system either applies the whole script or prevents the execution if an error occurs. This guarantees that the firewall is always in a consistent state.

Additionally, with the nftables script environment, you can:

  • Add comments
  • Define variables
  • Include other rule-set files

When you install the nftables package, Red Hat Enterprise Linux automatically creates *.nft scripts in the /etc/nftables/ directory. These scripts contain commands that create tables and empty chains for different purposes.

8.3.1. Supported nftables script formats

You can write scripts in the nftables scripting environment in the following formats:

  • The same format as the nft list ruleset command displays the rule set:

    #!/usr/sbin/nft -f
    
    # Flush the rule set
    flush ruleset
    
    table inet example_table {
      chain example_chain {
        # Chain for incoming packets that drops all packets that
        # are not explicitly allowed by any rule in this chain
        type filter hook input priority 0; policy drop;
    
        # Accept connections to port 22 (ssh)
        tcp dport ssh accept
      }
    }
  • The same syntax as for nft commands:

    #!/usr/sbin/nft -f
    
    # Flush the rule set
    flush ruleset
    
    # Create a table
    add table inet example_table
    
    # Create a chain for incoming packets that drops all packets
    # that are not explicitly allowed by any rule in this chain
    add chain inet example_table example_chain { type filter hook input priority 0 ; policy drop ; }
    
    # Add a rule that accepts connections to port 22 (ssh)
    add rule inet example_table example_chain tcp dport ssh accept

8.3.2. Running nftables scripts

You can run an nftables script either by passing it to the nft utility or by executing the script directly.

Procedure

  • To run an nftables script by passing it to the nft utility, enter:

    # nft -f /etc/nftables/<example_firewall_script>.nft
  • To run an nftables script directly:

    1. For the single time that you perform this:

      1. Ensure that the script starts with the following shebang sequence:

        #!/usr/sbin/nft -f
        Important

        If you omit the -f parameter, the nft utility does not read the script and displays: Error: syntax error, unexpected newline, expecting string.

      2. Optional: Set the owner of the script to root:

        # chown root /etc/nftables/<example_firewall_script>.nft
      3. Make the script executable for the owner:

        # chmod u+x /etc/nftables/<example_firewall_script>.nft
    2. Run the script:

      # /etc/nftables/<example_firewall_script>.nft

      If no output is displayed, the system executed the script successfully.

Important

Even if nft executes the script successfully, incorrectly placed rules, missing parameters, or other problems in the script can cause that the firewall behaves not as expected.

Additional resources

8.3.3. Using comments in nftables scripts

The nftables scripting environment interprets everything to the right of a # character to the end of a line as a comment.

Comments can start at the beginning of a line, or next to a command:

...
# Flush the rule set
flush ruleset

add table inet example_table  # Create a table
...

8.3.4. Using variables in nftables script

To define a variable in an nftables script, use the define keyword. You can store single values and anonymous sets in a variable. For more complex scenarios, use sets or verdict maps.

Variables with a single value

The following example defines a variable named INET_DEV with the value enp1s0:

define INET_DEV = enp1s0

You can use the variable in the script by entering the $ sign followed by the variable name:

...
add rule inet example_table example_chain iifname $INET_DEV tcp dport ssh accept
...
Variables that contain an anonymous set

The following example defines a variable that contains an anonymous set:

define DNS_SERVERS = { 192.0.2.1, 192.0.2.2 }

You can use the variable in the script by writing the $ sign followed by the variable name:

add rule inet example_table example_chain ip daddr $DNS_SERVERS accept
Note

Curly braces have special semantics when you use them in a rule because they indicate that the variable represents a set.

8.3.5. Including files in nftables scripts

In the nftables scripting environment, you can include other scripts by using the include statement.

If you specify only a file name without an absolute or relative path, nftables includes files from the default search path, which is set to /etc on Red Hat Enterprise Linux.

Example 8.1. Including files from the default search directory

To include a file from the default search directory:

include "example.nft"

Example 8.2. Including all *.nft files from a directory

To include all files ending with *.nft that are stored in the /etc/nftables/rulesets/ directory:

include "/etc/nftables/rulesets/*.nft"

Note that the include statement does not match files beginning with a dot.

Additional resources

  • The Include files section in the nft(8) man page on your system

8.3.6. Automatically loading nftables rules when the system boots

The nftables systemd service loads firewall scripts that are included in the /etc/sysconfig/nftables.conf file.

Prerequisites

  • The nftables scripts are stored in the /etc/nftables/ directory.

Procedure

  1. Edit the /etc/sysconfig/nftables.conf file.

    • If you modified the *.nft scripts that were created in /etc/nftables/ with the installation of the nftables package, uncomment the include statement for these scripts.
    • If you wrote new scripts, add include statements to include these scripts. For example, to load the /etc/nftables/example.nft script when the nftables service starts, add:

      include "/etc/nftables/_example_.nft"
  2. Optional: Start the nftables service to load the firewall rules without rebooting the system:

    # systemctl start nftables
  3. Enable the nftables service.

    # systemctl enable nftables

Additional resources

8.4. Configuring NAT using nftables

With nftables, you can configure the following network address translation (NAT) types:

  • Masquerading
  • Source NAT (SNAT)
  • Destination NAT (DNAT)
  • Redirect
Important

You can only use real interface names in iifname and oifname parameters, and alternative names (altname) are not supported.

8.4.1. NAT types

These are the different network address translation (NAT) types:

Masquerading and source NAT (SNAT)

Use one of these NAT types to change the source IP address of packets. For example, Internet Service Providers (ISPs) do not route private IP ranges, such as 10.0.0.0/8. If you use private IP ranges in your network and users should be able to reach servers on the internet, map the source IP address of packets from these ranges to a public IP address.

Masquerading and SNAT are very similar to one another. The differences are:

  • Masquerading automatically uses the IP address of the outgoing interface. Therefore, use masquerading if the outgoing interface uses a dynamic IP address.
  • SNAT sets the source IP address of packets to a specified IP and does not dynamically look up the IP of the outgoing interface. Therefore, SNAT is faster than masquerading. Use SNAT if the outgoing interface uses a fixed IP address.
Destination NAT (DNAT)
Use this NAT type to rewrite the destination address and port of incoming packets. For example, if your web server uses an IP address from a private IP range and is, therefore, not directly accessible from the internet, you can set a DNAT rule on the router to redirect incoming traffic to this server.
Redirect
This type is a special case of DNAT that redirects packets to the local machine depending on the chain hook. For example, if a service runs on a different port than its standard port, you can redirect incoming traffic from the standard port to this specific port.

8.4.2. Configuring masquerading using nftables

Masquerading enables a router to dynamically change the source IP of packets sent through an interface to the IP address of the interface. This means that if the interface gets a new IP assigned, nftables automatically uses the new IP when replacing the source IP.

Replace the source IP of packets leaving the host through the ens3 interface to the IP set on ens3.

Procedure

  1. Create a table:

    # nft add table nat
  2. Add the prerouting and postrouting chains to the table:

    # nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
    Important

    Even if you do not add a rule to the prerouting chain, the nftables framework requires this chain to match incoming packet replies.

    Note that you must pass the -- option to the nft command to prevent the shell from interpreting the negative priority value as an option of the nft command.

  3. Add a rule to the postrouting chain that matches outgoing packets on the ens3 interface:

    # nft add rule nat postrouting oifname "ens3" masquerade

8.4.3. Configuring source NAT using nftables

On a router, Source NAT (SNAT) enables you to change the IP of packets sent through an interface to a specific IP address. The router then replaces the source IP of outgoing packets.

Procedure

  1. Create a table:

    # nft add table nat
  2. Add the prerouting and postrouting chains to the table:

    # nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
    Important

    Even if you do not add a rule to the postrouting chain, the nftables framework requires this chain to match outgoing packet replies.

    Note that you must pass the -- option to the nft command to prevent the shell from interpreting the negative priority value as an option of the nft command.

  3. Add a rule to the postrouting chain that replaces the source IP of outgoing packets through ens3 with 192.0.2.1:

    # nft add rule nat postrouting oifname "ens3" snat to 192.0.2.1

8.4.4. Configuring destination NAT using nftables

Destination NAT (DNAT) enables you to redirect traffic on a router to a host that is not directly accessible from the internet.

For example, with DNAT the router redirects incoming traffic sent to port 80 and 443 to a web server with the IP address 192.0.2.1.

Procedure

  1. Create a table:

    # nft add table nat
  2. Add the prerouting and postrouting chains to the table:

    # nft -- add chain nat prerouting { type nat hook prerouting priority -100 \; }
    # nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
    Important

    Even if you do not add a rule to the postrouting chain, the nftables framework requires this chain to match outgoing packet replies.

    Note that you must pass the -- option to the nft command to prevent the shell from interpreting the negative priority value as an option of the nft command.

  3. Add a rule to the prerouting chain that redirects incoming traffic to port 80 and 443 on the ens3 interface of the router to the web server with the IP address 192.0.2.1:

    # nft add rule nat prerouting iifname ens3 tcp dport { 80, 443 } dnat to 192.0.2.1
  4. Depending on your environment, add either a SNAT or masquerading rule to change the source address for packets returning from the web server to the sender:

    1. If the ens3 interface uses a dynamic IP addresses, add a masquerading rule:

      # nft add rule nat postrouting oifname "ens3" masquerade
    2. If the ens3 interface uses a static IP address, add a SNAT rule. For example, if the ens3 uses the 198.51.100.1 IP address:

      # nft add rule nat postrouting oifname "ens3" snat to 198.51.100.1
  5. Enable packet forwarding:

    # echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/95-IPv4-forwarding.conf
    # sysctl -p /etc/sysctl.d/95-IPv4-forwarding.conf

Additional resources

8.4.5. Configuring a redirect using nftables

The redirect feature is a special case of destination network address translation (DNAT) that redirects packets to the local machine depending on the chain hook.

For example, you can redirect incoming and forwarded traffic sent to port 22 of the local host to port 2222.

Procedure

  1. Create a table:

    # nft add table nat
  2. Add the prerouting chain to the table:

    # nft -- add chain nat prerouting { type nat hook prerouting priority -100 \; }

    Note that you must pass the -- option to the nft command to prevent the shell from interpreting the negative priority value as an option of the nft command.

  3. Add a rule to the prerouting chain that redirects incoming traffic on port 22 to port 2222:

    # nft add rule nat prerouting tcp dport 22 redirect to 2222

Additional resources

8.4.6. Configuring flowtable by using nftables

The nftables utility uses the netfilter framework to provide network address translation (NAT) for network traffic and provides the fastpath feature-based flowtable mechanism to accelerate packet forwarding.

The flowtable mechanism has the following features:

  • Uses connection tracking to bypass the classic packet forwarding path.
  • Avoids revisiting the routing table by bypassing the classic packet processing.
  • Works only with TCP and UDP protocols.
  • Hardware independent software fast path.

Procedure

  1. Add an example-table table of inet family:

    # nft add table inet <example-table>
  2. Add an example-flowtable flowtable with ingress hook and filter as a priority type:

    # nft add flowtable inet <example-table> <example-flowtable> { hook ingress priority filter \; devices = { enp1s0, enp7s0 } \; }
  3. Add an example-forwardchain flow to the flowtable from a packet processing table:

    # nft add chain inet <example-table> <example-forwardchain> { type filter hook forward priority filter \; }

    This command adds a flowtable of filter type with forward hook and filter priority.

  4. Add a rule with established connection tracking state to offload example-flowtable flow:

    # nft add rule inet <example-table> <example-forwardchain> ct state established flow add @<example-flowtable>

Verification

  • Verify the properties of example-table:

    # nft list table inet <example-table>
    table inet example-table {
        	flowtable example-flowtable {
         		hook ingress priority filter
               devices = { enp1s0, enp7s0 }
        	}
    
        	chain example-forwardchain {
    type filter hook forward priority filter; policy accept;
    ct state established flow add @example-flowtable
        }
    }

Additional resources

  • nft(8) man page on your system

8.5. Using sets in nftables commands

The nftables framework natively supports sets. You can use sets, for example, if a rule should match multiple IP addresses, port numbers, interfaces, or any other match criteria.

8.5.1. Using anonymous sets in nftables

An anonymous set contains comma-separated values enclosed in curly brackets, such as { 22, 80, 443 }, that you use directly in a rule. You can use anonymous sets also for IP addresses and any other match criteria.

The drawback of anonymous sets is that if you want to change the set, you must replace the rule. For a dynamic solution, use named sets as described in Using named sets in nftables.

Prerequisites

  • The example_chain chain and the example_table table in the inet family exists.

Procedure

  1. For example, to add a rule to example_chain in example_table that allows incoming traffic to port 22, 80, and 443:

    # nft add rule inet example_table example_chain tcp dport { 22, 80, 443 } accept
  2. Optional: Display all chains and their rules in example_table:

    # nft list table inet example_table
    table inet example_table {
      chain example_chain {
        type filter hook input priority filter; policy accept;
        tcp dport { ssh, http, https } accept
      }
    }

8.5.2. Using named sets in nftables

The nftables framework supports mutable named sets. A named set is a list or range of elements that you can use in multiple rules within a table. Another benefit over anonymous sets is that you can update a named set without replacing the rules that use the set.

When you create a named set, you must specify the type of elements the set contains. You can set the following types:

  • ipv4_addr for a set that contains IPv4 addresses or ranges, such as 192.0.2.1 or 192.0.2.0/24.
  • ipv6_addr for a set that contains IPv6 addresses or ranges, such as 2001:db8:1::1 or 2001:db8:1::1/64.
  • ether_addr for a set that contains a list of media access control (MAC) addresses, such as 52:54:00:6b:66:42.
  • inet_proto for a set that contains a list of internet protocol types, such as tcp.
  • inet_service for a set that contains a list of internet services, such as ssh.
  • mark for a set that contains a list of packet marks. Packet marks can be any positive 32-bit integer value (0 to 2147483647).

Prerequisites

  • The example_chain chain and the example_table table exists.

Procedure

  1. Create an empty set. The following examples create a set for IPv4 addresses:

    • To create a set that can store multiple individual IPv4 addresses:

      # nft add set inet example_table example_set { type ipv4_addr \; }
    • To create a set that can store IPv4 address ranges:

      # nft add set inet example_table example_set { type ipv4_addr \; flags interval \; }
    Important

    To prevent the shell from interpreting the semicolons as the end of the command, you must escape the semicolons with a backslash.

  2. Optional: Create rules that use the set. For example, the following command adds a rule to the example_chain in the example_table that will drop all packets from IPv4 addresses in example_set.

    # nft add rule inet example_table example_chain ip saddr @example_set drop

    Because example_set is still empty, the rule has currently no effect.

  3. Add IPv4 addresses to example_set:

    • If you create a set that stores individual IPv4 addresses, enter:

      # nft add element inet example_table example_set { 192.0.2.1, 192.0.2.2 }
    • If you create a set that stores IPv4 ranges, enter:

      # nft add element inet example_table example_set { 192.0.2.0-192.0.2.255 }

      When you specify an IP address range, you can alternatively use the Classless Inter-Domain Routing (CIDR) notation, such as 192.0.2.0/24 in the above example.

8.5.3. Additional resources

  • The Sets section in the nft(8) man page on your system

8.6. Using verdict maps in nftables commands

Verdict maps, which are also known as dictionaries, enable nft to perform an action based on packet information by mapping match criteria to an action.

8.6.1. Using anonymous maps in nftables

An anonymous map is a { match_criteria : action } statement that you use directly in a rule. The statement can contain multiple comma-separated mappings.

The drawback of an anonymous map is that if you want to change the map, you must replace the rule. For a dynamic solution, use named maps as described in Using named maps in nftables.

For example, you can use an anonymous map to route both TCP and UDP packets of the IPv4 and IPv6 protocol to different chains to count incoming TCP and UDP packets separately.

Procedure

  1. Create a new table:

    # nft add table inet example_table
  2. Create the tcp_packets chain in example_table:

    # nft add chain inet example_table tcp_packets
  3. Add a rule to tcp_packets that counts the traffic in this chain:

    # nft add rule inet example_table tcp_packets counter
  4. Create the udp_packets chain in example_table

    # nft add chain inet example_table udp_packets
  5. Add a rule to udp_packets that counts the traffic in this chain:

    # nft add rule inet example_table udp_packets counter
  6. Create a chain for incoming traffic. For example, to create a chain named incoming_traffic in example_table that filters incoming traffic:

    # nft add chain inet example_table incoming_traffic { type filter hook input priority 0 \; }
  7. Add a rule with an anonymous map to incoming_traffic:

    # nft add rule inet example_table incoming_traffic ip protocol vmap { tcp : jump tcp_packets, udp : jump udp_packets }

    The anonymous map distinguishes the packets and sends them to the different counter chains based on their protocol.

  8. To list the traffic counters, display example_table:

    # nft list table inet example_table
    table inet example_table {
      chain tcp_packets {
        counter packets 36379 bytes 2103816
      }
    
      chain udp_packets {
        counter packets 10 bytes 1559
      }
    
      chain incoming_traffic {
        type filter hook input priority filter; policy accept;
        ip protocol vmap { tcp : jump tcp_packets, udp : jump udp_packets }
      }
    }

    The counters in the tcp_packets and udp_packets chain display both the number of received packets and bytes.

8.6.2. Using named maps in nftables

The nftables framework supports named maps. You can use these maps in multiple rules within a table. Another benefit over anonymous maps is that you can update a named map without replacing the rules that use it.

When you create a named map, you must specify the type of elements:

  • ipv4_addr for a map whose match part contains an IPv4 address, such as 192.0.2.1.
  • ipv6_addr for a map whose match part contains an IPv6 address, such as 2001:db8:1::1.
  • ether_addr for a map whose match part contains a media access control (MAC) address, such as 52:54:00:6b:66:42.
  • inet_proto for a map whose match part contains an internet protocol type, such as tcp.
  • inet_service for a map whose match part contains an internet services name port number, such as ssh or 22.
  • mark for a map whose match part contains a packet mark. A packet mark can be any positive 32-bit integer value (0 to 2147483647).
  • counter for a map whose match part contains a counter value. The counter value can be any positive 64-bit integer value.
  • quota for a map whose match part contains a quota value. The quota value can be any positive 64-bit integer value.

For example, you can allow or drop incoming packets based on their source IP address. Using a named map, you require only a single rule to configure this scenario while the IP addresses and actions are dynamically stored in the map.

Procedure

  1. Create a table. For example, to create a table named example_table that processes IPv4 packets:

    # nft add table ip example_table
  2. Create a chain. For example, to create a chain named example_chain in example_table:

    # nft add chain ip example_table example_chain { type filter hook input priority 0 \; }
    Important

    To prevent the shell from interpreting the semicolons as the end of the command, you must escape the semicolons with a backslash.

  3. Create an empty map. For example, to create a map for IPv4 addresses:

    # nft add map ip example_table example_map { type ipv4_addr : verdict \; }
  4. Create rules that use the map. For example, the following command adds a rule to example_chain in example_table that applies actions to IPv4 addresses which are both defined in example_map:

    # nft add rule example_table example_chain ip saddr vmap @example_map
  5. Add IPv4 addresses and corresponding actions to example_map:

    # nft add element ip example_table example_map { 192.0.2.1 : accept, 192.0.2.2 : drop }

    This example defines the mappings of IPv4 addresses to actions. In combination with the rule created above, the firewall accepts packet from 192.0.2.1 and drops packets from 192.0.2.2.

  6. Optional: Enhance the map by adding another IP address and action statement:

    # nft add element ip example_table example_map { 192.0.2.3 : accept }
  7. Optional: Remove an entry from the map:

    # nft delete element ip example_table example_map { 192.0.2.1 }
  8. Optional: Display the rule set:

    # nft list ruleset
    table ip example_table {
      map example_map {
        type ipv4_addr : verdict
        elements = { 192.0.2.2 : drop, 192.0.2.3 : accept }
      }
    
      chain example_chain {
        type filter hook input priority filter; policy accept;
        ip saddr vmap @example_map
      }
    }

8.6.3. Additional resources

  • The Maps section in the nft(8) man page on your system

8.7. Example: Protecting a LAN and DMZ using an nftables script

Use the nftables framework on a RHEL router to write and install a firewall script that protects the network clients in an internal LAN and a web server in a DMZ from unauthorized access from the internet and from other networks.

Important

This example is only for demonstration purposes and describes a scenario with specific requirements.

Firewall scripts highly depend on the network infrastructure and security requirements. Use this example to learn the concepts of nftables firewalls when you write scripts for your own environment.

8.7.1. Network conditions

The network in this example has the following conditions:

  • The router is connected to the following networks:

    • The internet through interface enp1s0
    • The internal LAN through interface enp7s0
    • The DMZ through enp8s0
  • The internet interface of the router has both a static IPv4 address (203.0.113.1) and IPv6 address (2001:db8:a::1) assigned.
  • The clients in the internal LAN use only private IPv4 addresses from the range 10.0.0.0/24. Consequently, traffic from the LAN to the internet requires source network address translation (SNAT).
  • The administrator PCs in the internal LAN use the IP addresses 10.0.0.100 and 10.0.0.200.
  • The DMZ uses public IP addresses from the ranges 198.51.100.0/24 and 2001:db8:b::/56.
  • The web server in the DMZ uses the IP addresses 198.51.100.5 and 2001:db8:b::5.
  • The router acts as a caching DNS server for hosts in the LAN and DMZ.

8.7.2. Security requirements to the firewall script

The following are the requirements to the nftables firewall in the example network:

  • The router must be able to:

    • Recursively resolve DNS queries.
    • Perform all connections on the loopback interface.
  • Clients in the internal LAN must be able to:

    • Query the caching DNS server running on the router.
    • Access the HTTPS server in the DMZ.
    • Access any HTTPS server on the internet.
  • The PCs of the administrators must be able to access the router and every server in the DMZ using SSH.
  • The web server in the DMZ must be able to:

    • Query the caching DNS server running on the router.
    • Access HTTPS servers on the internet to download updates.
  • Hosts on the internet must be able to:

    • Access the HTTPS servers in the DMZ.
  • Additionally, the following security requirements exists:

    • Connection attempts that are not explicitly allowed should be dropped.
    • Dropped packets should be logged.

8.7.3. Configuring logging of dropped packets to a file

By default, systemd logs kernel messages, such as for dropped packets, to the journal. Additionally, you can configure the rsyslog service to log such entries to a separate file. To ensure that the log file does not grow infinitely, configure a rotation policy.

Prerequisites

  • The rsyslog package is installed.
  • The rsyslog service is running.

Procedure

  1. Create the /etc/rsyslog.d/nftables.conf file with the following content:

    :msg, startswith, "nft drop" -/var/log/nftables.log
    & stop

    Using this configuration, the rsyslog service logs dropped packets to the /var/log/nftables.log file instead of /var/log/messages.

  2. Restart the rsyslog service:

    # systemctl restart rsyslog
  3. Create the /etc/logrotate.d/nftables file with the following content to rotate /var/log/nftables.log if the size exceeds 10 MB:

    /var/log/nftables.log {
      size +10M
      maxage 30
      sharedscripts
      postrotate
        /usr/bin/systemctl kill -s HUP rsyslog.service >/dev/null 2>&1 || true
      endscript
    }

    The maxage 30 setting defines that logrotate removes rotated logs older than 30 days during the next rotation operation.

Additional resources

  • rsyslog.conf(5) and logrotate(8) man pages on your system

8.7.4. Writing and activating the nftables script

This example is an nftables firewall script that runs on a RHEL router and protects the clients in an internal LAN and a web server in a DMZ. For details about the network and the requirements for the firewall used in the example, see Network conditions and Security requirements to the firewall script.

Warning

This nftables firewall script is only for demonstration purposes. Do not use it without adapting it to your environments and security requirements.

Prerequisites

Procedure

  1. Create the /etc/nftables/firewall.nft script with the following content:

    # Remove all rules
    flush ruleset
    
    
    # Table for both IPv4 and IPv6 rules
    table inet nftables_svc {
    
      # Define variables for the interface name
      define INET_DEV = enp1s0
      define LAN_DEV  = enp7s0
      define DMZ_DEV  = enp8s0
    
    
      # Set with the IPv4 addresses of admin PCs
      set admin_pc_ipv4 {
        type ipv4_addr
        elements = { 10.0.0.100, 10.0.0.200 }
      }
    
    
      # Chain for incoming trafic. Default policy: drop
      chain INPUT {
        type filter hook input priority filter
        policy drop
    
        # Accept packets in established and related state, drop invalid packets
        ct state vmap { established:accept, related:accept, invalid:drop }
    
        # Accept incoming traffic on loopback interface
        iifname lo accept
    
        # Allow request from LAN and DMZ to local DNS server
        iifname { $LAN_DEV, $DMZ_DEV } meta l4proto { tcp, udp } th dport 53 accept
    
        # Allow admins PCs to access the router using SSH
        iifname $LAN_DEV ip saddr @admin_pc_ipv4 tcp dport 22 accept
    
        # Last action: Log blocked packets
        # (packets that were not accepted in previous rules in this chain)
        log prefix "nft drop IN : "
      }
    
    
      # Chain for outgoing traffic. Default policy: drop
      chain OUTPUT {
        type filter hook output priority filter
        policy drop
    
        # Accept packets in established and related state, drop invalid packets
        ct state vmap { established:accept, related:accept, invalid:drop }
    
        # Accept outgoing traffic on loopback interface
        oifname lo accept
    
        # Allow local DNS server to recursively resolve queries
        oifname $INET_DEV meta l4proto { tcp, udp } th dport 53 accept
    
        # Last action: Log blocked packets
        log prefix "nft drop OUT: "
      }
    
    
      # Chain for forwarding traffic. Default policy: drop
      chain FORWARD {
        type filter hook forward priority filter
        policy drop
    
        # Accept packets in established and related state, drop invalid packets
        ct state vmap { established:accept, related:accept, invalid:drop }
    
        # IPv4 access from LAN and internet to the HTTPS server in the DMZ
        iifname { $LAN_DEV, $INET_DEV } oifname $DMZ_DEV ip daddr 198.51.100.5 tcp dport 443 accept
    
        # IPv6 access from internet to the HTTPS server in the DMZ
        iifname $INET_DEV oifname $DMZ_DEV ip6 daddr 2001:db8:b::5 tcp dport 443 accept
    
        # Access from LAN and DMZ to HTTPS servers on the internet
        iifname { $LAN_DEV, $DMZ_DEV } oifname $INET_DEV tcp dport 443 accept
    
        # Last action: Log blocked packets
        log prefix "nft drop FWD: "
      }
    
    
      # Postrouting chain to handle SNAT
      chain postrouting {
        type nat hook postrouting priority srcnat; policy accept;
    
        # SNAT for IPv4 traffic from LAN to internet
        iifname $LAN_DEV oifname $INET_DEV snat ip to 203.0.113.1
      }
    }
  2. Include the /etc/nftables/firewall.nft script in the /etc/sysconfig/nftables.conf file:

    include "/etc/nftables/firewall.nft"
  3. Enable IPv4 forwarding:

    # echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/95-IPv4-forwarding.conf
    # sysctl -p /etc/sysctl.d/95-IPv4-forwarding.conf
  4. Enable and start the nftables service:

    # systemctl enable --now nftables

Verification

  1. Optional: Verify the nftables rule set:

    # nft list ruleset
    ...
  2. Try to perform an access that the firewall prevents. For example, try to access the router using SSH from the DMZ:

    # ssh router.example.com
    ssh: connect to host router.example.com port 22: Network is unreachable
  3. Depending on your logging settings, search:

    • The systemd journal for the blocked packets:

      # journalctl -k -g "nft drop"
      Oct 14 17:27:18 router kernel: nft drop IN : IN=enp8s0 OUT= MAC=... SRC=198.51.100.5 DST=198.51.100.1 ... PROTO=TCP SPT=40464 DPT=22 ... SYN ...
    • The /var/log/nftables.log file for the blocked packets:

      Oct 14 17:27:18 router kernel: nft drop IN : IN=enp8s0 OUT= MAC=... SRC=198.51.100.5 DST=198.51.100.1 ... PROTO=TCP SPT=40464 DPT=22 ... SYN ...

8.8. Configuring port forwarding using nftables

Port forwarding enables administrators to forward packets sent to a specific destination port to a different local or remote port.

For example, if your web server does not have a public IP address, you can set a port forwarding rule on your firewall that forwards incoming packets on port 80 and 443 on the firewall to the web server. With this firewall rule, users on the internet can access the web server using the IP or host name of the firewall.

8.8.1. Forwarding incoming packets to a different local port

You can use nftables to forward packets. For example, you can forward incoming IPv4 packets on port 8022 to port 22 on the local system.

Procedure

  1. Create a table named nat with the ip address family:

    # nft add table ip nat
  2. Add the prerouting and postrouting chains to the table:

    # nft -- add chain ip nat prerouting { type nat hook prerouting priority -100 \; }
    Note

    Pass the -- option to the nft command to prevent the shell from interpreting the negative priority value as an option of the nft command.

  3. Add a rule to the prerouting chain that redirects incoming packets on port 8022 to the local port 22:

    # nft add rule ip nat prerouting tcp dport 8022 redirect to :22

8.8.2. Forwarding incoming packets on a specific local port to a different host

You can use a destination network address translation (DNAT) rule to forward incoming packets on a local port to a remote host. This enables users on the internet to access a service that runs on a host with a private IP address.

For example, you can forward incoming IPv4 packets on the local port 443 to the same port number on the remote system with the 192.0.2.1 IP address.

Prerequisites

  • You are logged in as the root user on the system that should forward the packets.

Procedure

  1. Create a table named nat with the ip address family:

    # nft add table ip nat
  2. Add the prerouting and postrouting chains to the table:

    # nft -- add chain ip nat prerouting { type nat hook prerouting priority -100 \; }
    # nft add chain ip nat postrouting { type nat hook postrouting priority 100 \; }
    Note

    Pass the -- option to the nft command to prevent the shell from interpreting the negative priority value as an option of the nft command.

  3. Add a rule to the prerouting chain that redirects incoming packets on port 443 to the same port on 192.0.2.1:

    # nft add rule ip nat prerouting tcp dport 443 dnat to 192.0.2.1
  4. Add a rule to the postrouting chain to masquerade outgoing traffic:

    # nft add rule ip nat postrouting daddr 192.0.2.1 masquerade
  5. Enable packet forwarding:

    # echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/95-IPv4-forwarding.conf
    # sysctl -p /etc/sysctl.d/95-IPv4-forwarding.conf

8.9. Using nftables to limit the amount of connections

You can use nftables to limit the number of connections or to block IP addresses that attempt to establish a given amount of connections to prevent them from using too many system resources.

8.9.1. Limiting the number of connections by using nftables

By using the ct count parameter of the nft utility, you can limit the number of simultaneous connections per IP address. For example, you can use this feature to configure that each source IP address can only establish two parallel SSH connections to a host.

Procedure

  1. Create the filter table with the inet address family:

    # nft add table inet filter
  2. Add the input chain to the inet filter table:

    # nft add chain inet filter input { type filter hook input priority 0 \; }
  3. Create a dynamic set for IPv4 addresses:

    # nft add set inet filter limit-ssh { type ipv4_addr\; flags dynamic \;}
  4. Add a rule to the input chain that allows only two simultaneous incoming connections to the SSH port (22) from an IPv4 address and rejects all further connections from the same IP:

    # nft add rule inet filter input tcp dport ssh ct state new add @limit-ssh { ip saddr ct count over 2 } counter reject

Verification

  1. Establish more than two new simultaneous SSH connections from the same IP address to the host. Nftables refuses connections to the SSH port if two connections are already established.
  2. Display the limit-ssh meter:

    # nft list set inet filter limit-ssh
    table inet filter {
      set limit-ssh {
        type ipv4_addr
        size 65535
        flags dynamic
        elements = { 192.0.2.1 ct count over 2 , 192.0.2.2 ct count over 2 }
      }
    }

    The elements entry displays addresses that currently match the rule. In this example, elements lists IP addresses that have active connections to the SSH port. Note that the output does not display the number of active connections or if connections were rejected.

8.9.2. Blocking IP addresses that attempt more than ten new incoming TCP connections within one minute

You can temporarily block hosts that are establishing more than ten IPv4 TCP connections within one minute.

Procedure

  1. Create the filter table with the ip address family:

    # nft add table ip filter
  2. Add the input chain to the filter table:

    # nft add chain ip filter input { type filter hook input priority 0 \; }
  3. Add a rule that drops all packets from source addresses that attempt to establish more than ten TCP connections within one minute:

    # nft add rule ip filter input ip protocol tcp ct state new, untracked meter ratemeter { ip saddr timeout 5m limit rate over 10/minute } drop

    The timeout 5m parameter defines that nftables automatically removes entries after five minutes to prevent that the meter fills up with stale entries.

Verification

  • To display the meter’s content, enter:

    # nft list meter ip filter ratemeter
    table ip filter {
      meter ratemeter {
        type ipv4_addr
        size 65535
        flags dynamic,timeout
        elements = { 192.0.2.1 limit rate over 10/minute timeout 5m expires 4m58s224ms }
      }
    }

8.10. Debugging nftables rules

The nftables framework provides different options for administrators to debug rules and if packets match them.

8.10.1. Creating a rule with a counter

To identify if a rule is matched, you can use a counter.

Prerequisites

  • The chain to which you want to add the rule exists.

Procedure

  1. Add a new rule with the counter parameter to the chain. The following example adds a rule with a counter that allows TCP traffic on port 22 and counts the packets and traffic that match this rule:

    # nft add rule inet example_table example_chain tcp dport 22 counter accept
  2. To display the counter values:

    # nft list ruleset
    table inet example_table {
      chain example_chain {
        type filter hook input priority filter; policy accept;
        tcp dport ssh counter packets 6872 bytes 105448565 accept
      }
    }

8.10.2. Adding a counter to an existing rule

To identify if a rule is matched, you can use a counter.

Prerequisites

  • The rule to which you want to add the counter exists.

Procedure

  1. Display the rules in the chain including their handles:

    # nft --handle list chain inet example_table example_chain
    table inet example_table {
      chain example_chain { # handle 1
        type filter hook input priority filter; policy accept;
        tcp dport ssh accept # handle 4
      }
    }
  2. Add the counter by replacing the rule but with the counter parameter. The following example replaces the rule displayed in the previous step and adds a counter:

    # nft replace rule inet example_table example_chain handle 4 tcp dport 22 counter accept
  3. To display the counter values:

    # nft list ruleset
    table inet example_table {
      chain example_chain {
        type filter hook input priority filter; policy accept;
        tcp dport ssh counter packets 6872 bytes 105448565 accept
      }
    }

8.10.3. Monitoring packets that match an existing rule

The tracing feature in nftables in combination with the nft monitor command enables administrators to display packets that match a rule. You can enable tracing for a rule an use it to monitoring packets that match this rule.

Prerequisites

  • The rule to which you want to add the counter exists.

Procedure

  1. Display the rules in the chain including their handles:

    # nft --handle list chain inet example_table example_chain
    table inet example_table {
      chain example_chain { # handle 1
        type filter hook input priority filter; policy accept;
        tcp dport ssh accept # handle 4
      }
    }
  2. Add the tracing feature by replacing the rule but with the meta nftrace set 1 parameters. The following example replaces the rule displayed in the previous step and enables tracing:

    # nft replace rule inet example_table example_chain handle 4 tcp dport 22 meta nftrace set 1 accept
  3. Use the nft monitor command to display the tracing. The following example filters the output of the command to display only entries that contain inet example_table example_chain:

    # nft monitor | grep "inet example_table example_chain"
    trace id 3c5eb15e inet example_table example_chain packet: iif "enp1s0" ether saddr 52:54:00:17:ff:e4 ether daddr 52:54:00:72:2f:6e ip saddr 192.0.2.1 ip daddr 192.0.2.2 ip dscp cs0 ip ecn not-ect ip ttl 64 ip id 49710 ip protocol tcp ip length 60 tcp sport 56728 tcp dport ssh tcp flags == syn tcp window 64240
    trace id 3c5eb15e inet example_table example_chain rule tcp dport ssh nftrace set 1 accept (verdict accept)
    ...
    Warning

    Depending on the number of rules with tracing enabled and the amount of matching traffic, the nft monitor command can display a lot of output. Use grep or other utilities to filter the output.

8.11. Backing up and restoring the nftables rule set

You can backup nftables rules to a file and later restoring them. Also, administrators can use a file with the rules to, for example, transfer the rules to a different server.

8.11.1. Backing up the nftables rule set to a file

You can use the nft utility to back up the nftables rule set to a file.

Procedure

  • To backup nftables rules:

    • In a format produced by nft list ruleset format:

      # nft list ruleset > file.nft
    • In JSON format:

      # nft -j list ruleset > file.json

8.11.2. Restoring the nftables rule set from a file

You can restore the nftables rule set from a file.

Procedure

  • To restore nftables rules:

    • If the file to restore is in the format produced by nft list ruleset or contains nft commands directly:

      # nft -f file.nft
    • If the file to restore is in JSON format:

      # nft -j -f file.json

8.12. Additional resources

Red Hat logoGithubRedditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

© 2024 Red Hat, Inc.