Menu Close

Chapter 2. Getting started with nftables

The nftables framework provides packet classification facilities. The most notable features are:

  • built-in lookup tables instead of linear processing
  • a single framework for both the IPv4 and IPv6 protocols
  • rules all 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 libnftnl library can be used for low-level interaction with nftables Netlink API over the libmnl library.

To display the effect of rule set changes, use the nft list ruleset command. Since these tools 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 formerly separate legacy commands.

2.1. Migrating from iptables to nftables

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

Important

The ipset and iptables-nft packages have been deprecated in Red Hat Enterprise Linux 9. This includes deprecation of nft-variants such as iptables, ip6tables, arptables, and ebtables utilities. If you are using any of these tools, for example, because you upgraded from an earlier RHEL version, Red Hat recommends migrating to the nft command line tool provided by the nftables package.

2.1.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 avoid that the different firewall services influence each other, run only one of them on a RHEL host, and disable the other services.

2.1.2. Converting iptables rules to nftables rules

Red Hat Enterprise Linux provides the iptables-translate and ip6tables-translate tools to convert existing iptables or ip6tables rules into the equivalent ones for nftables.

Note that some extensions lack translation support. If such an extension exists, the tool 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

Additionally, users can use the iptables-restore-translate and ip6tables-restore-translate tools to translate a dump of rules. Note that before that, users can use the iptables-save or ip6tables-save commands to print a dump of current rules. For example:

# iptables-save >/tmp/iptables.dump
# iptables-restore-translate -f /tmp/iptables.dump

# Translated by iptables-restore-translate v1.8.0 on Wed Oct 17 17:00:13 2018
add table ip nat
...

For more information and a list of possible options and values, enter the iptables-translate --help command.

2.1.3. 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.

    Example: Listing rules generated by firewalld

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

2.2. Writing and executing nftables scripts

The nftables framework provides a native scripting environment that brings a major benefit over using shell scripts to maintain firewall rules: 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, the nftables script environment enables administrators to:

  • add comments
  • define variables
  • include other rule set files

This section explains how to use these features, as well as creating and executing nftables scripts.

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.

2.2.1. Supported nftables script formats

The nftables scripting environment supports scripts in the following formats:

  • You can write a script in 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
      }
    }
  • You can use the same syntax for commands as in 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

2.2.2. Running nftables scripts

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

Prerequisites

  • The procedure of this section assumes that you stored an nftables script in the /etc/nftables/example_firewall.nft file.

Procedure

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

    # nft -f /etc/nftables/example_firewall.nft
  • To run an nftables script directly:

    1. Steps that are required only once:

      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.nft
      3. Make the script executable for the owner:

        # chmod u+x /etc/nftables/example_firewall.nft
    2. Run the script:

      # /etc/nftables/example_firewall.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

2.2.3. Using comments in nftables scripts

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

Example 2.1. Comments in an nftables script

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

...
# Flush the rule set
flush ruleset

add table inet example_table  # Create a table
...

2.2.4. Using variables in an 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 writing 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

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

2.2.5. Including files in an nftables script

The nftables scripting environment enables administrators to 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 2.2. Including files from the default search directory

To include a file from the default search directory:

include "example.nft"

Example 2.3. 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

2.2.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. This section explains how to load firewall rules when the system boots.

Prerequisites

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

Procedure

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

    • If you enhance *.nft scripts created in /etc/nftables/ when you installed the nftables package, uncomment the include statement for these scripts.
    • If you write scripts from scratch, 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. Optionally, 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

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

This section explains how to display nftables rule sets, and how to manage them.

2.3.1. Standard chain priority values and textual names

When you create a chain, the priority you can either set an integer value or a standard name that specifies the order in which chains with the same hook value traverse.

The names and values are defined based on what priorities are used by xtables when registering their default chains.

Note

The nft list chains command displays textual priority values by default. You can view the numeric value by passing the -y option to the command.

Example 2.4. Using a textual value to set the priority

The following command creates a chain named example_chain in example_table using the standard priority value 50:

# nft add chain inet example_table example_chain { type filter hook input priority 50 \; policy accept \; }

Because the priority is a standard value, you can alternatively use the textual value:

# nft add chain inet example_table example_chain { type filter hook input priority security \; policy accept \; }

Table 2.1. Standard priority names, family, and hook compatibility matrix

NameValueFamiliesHooks

raw

-300

ip, ip6, inet

all

mangle

-150

ip, ip6, inet

all

dstnat

-100

ip, ip6, inet

prerouting

filter

0

ip, ip6, inet, arp, netdev

all

security

50

ip, ip6, inet

all

srcnat

100

ip, ip6, inet

postrouting

All families use the same values, but the bridge family uses following values:

Table 2.2. Standard priority names, and hook compatibility for the bridge family

NameValueHooks

dstnat

-300

prerouting

filter

-200

all

out

100

output

srcnat

300

postrouting

Additional resources

  • The Chains section in the nft(8) man page

2.3.2. Displaying the nftables rule set

The rule sets of nftables contain tables, chains, and rules. This section explains how to display the rule set.

Procedure

  • To display the rule set, enter:

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

    By default, nftables does not pre-create tables. As a consequence, displaying the rule set on a host without any tables, the nft list ruleset command shows no output.

2.3.3. Creating an nftables table

A table in nftables is a name space that contains a collection of chains, rules, sets, and other objects. This section explains how to create a table.

Each table must have an address family defined. The address family of a table defines what address types the 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 traverse a bridge device.
  • netdev: Matches packets from ingress.

Procedure

  1. Use the nft add table command to create a new table. For example, to create a table named example_table that processes IPv4 and IPv6 packets:

    # nft add table inet example_table
  2. Optionally, list all tables in the rule set:

    # nft list tables
    table inet example_table

Additional resources

  • The Address families section in the nft(8) man page
  • The Tables section in the nft(8) man page

2.3.4. Creating an nftables chain

Chains 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 and to better organize rules.

The procedure describes how to add a base chain to an existing table.

Prerequisites

  • The table to which you want to add the new chain exists.

Procedure

  1. Use the nft add chain command to create a new chain. For example, to create a chain named example_chain in example_table:

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

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

    This chain filters incoming packets. The priority parameter specifies the order in which nftables processes chains with the same hook value. A lower priority value has precedence over higher ones. The policy parameter sets the default action for rules in this chain. Note that if you are logged in to the server remotely and you set the default policy to drop, you are disconnected immediately if no other rule allows the remote access.

  2. Optionally, display all chains:

    # nft list chains
    table inet example_table {
      chain example_chain {
        type filter hook input priority filter; policy accept;
      }
    }

Additional resources

  • The Address families section in the nft(8) man page
  • The Chains section in the nft(8) man page

2.3.5. Appending a rule to the end of an nftables chain

This section explains how to append a rule to the end of an existing nftables chain.

Prerequisites

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

Procedure

  1. To add a new rule, use the nft add rule command. For example, to add a rule to the example_chain in the example_table that allows TCP traffic on port 22:

    # nft add rule inet example_table example_chain tcp dport 22 accept

    Instead of the port number, you can alternatively specify the name of the service. In the example, you could use ssh instead of the port number 22. Note that a service name is resolved to a port number based on its entry in the /etc/services file.

  2. Optionally, 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 accept
      }
    }

Additional resources

  • The Address families section in the nft(8) man page
  • The Rules section in the nft(8) man page

2.3.6. Inserting a rule at the beginning of an nftables chain

This section explains how to insert a rule at the beginning of an existing nftables chain.

Prerequisites

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

Procedure

  1. To insert a new rule, use the nft insert rule command. For example, to insert a rule to the example_chain in the example_table that allows TCP traffic on port 22:

    # nft insert rule inet example_table example_chain tcp dport 22 accept

    You can alternatively specify the name of the service instead of the port number. In the example, you could use ssh instead of the port number 22. Note that a service name is resolved to a port number based on its entry in the /etc/services file.

  2. Optionally, 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 accept
        ...
      }
    }

Additional resources

  • The Address families section in the nft(8) man page
  • The Rules section in the nft(8) man page

2.3.7. Inserting a rule at a specific position of an nftables chain

This section explains how to insert rules before and after an existing rule in an nftables chain. This way you can place new rules at the right position.

Prerequisites

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

Procedure

  1. Use the nft -a list ruleset command to display all chains and their rules in the example_table including their handle:

    # nft -a list table inet example_table
    table inet example_table { # handle 1
      chain example_chain { # handle 1
        type filter hook input priority filter; policy accept;
        tcp dport 22 accept # handle 2
        tcp dport 443 accept # handle 3
        tcp dport 389 accept # handle 4
      }
    }

    Using the -a displays the handles. You require this information to position the new rules in the next steps.

  2. Insert the new rules to the example_chain chain in the example_table:

    • To insert a rule that allows TCP traffic on port 636 before handle 3, enter:

      # nft insert rule inet example_table example_chain position 3 tcp dport 636 accept
    • To add a rule that allows TCP traffic on port 80 after handle 3, enter:

      # nft add rule inet example_table example_chain position 3 tcp dport 80 accept
  3. Optionally, display all chains and their rules in example_table:

    # nft -a list table inet example_table
    table inet example_table { # handle 1
      chain example_chain { # 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
        tcp dport 389 accept # handle 4
      }
    }

Additional resources

  • The Address families section in the nft(8) man page
  • The Rules section in the nft(8) man page

2.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.

2.4.1. The different NAT types: masquerading, source NAT, destination NAT, and redirect

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 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.

Both masquerading and SNAT are very similar. 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.

2.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.

The following procedure describes how to 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 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 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 avoid that the shell interprets 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

2.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 following procedure describes how to replace the source IP of packets leaving the router through the ens3 interface to 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 avoid that the shell interprets 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