Chapter 6. Getting started with nftables

The nftables framework enables administrators to configure packet-filtering rules used by the Linux kernel firewall.

6.1. Introduction to nftables

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

  • 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

Similarly to iptables, nftables use tables for storing chains. The chains contain individual rules for performing actions. The nft tool replaces all tools from the previous packet-filtering frameworks. The libnftnl library can be used for low-level interaction with nftables Netlink API over the libmnl library.

Effect of the modules on the nftables rules set can be observed using the nft list rule set 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.

Additional resources

  • The nft(8) man page provides a comprehensive reference documentation for configuring and inspecting packet filtering with nftables using the nft command-line tool.

6.2. 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 to configure a firewall on workstations. The utility is easy to use and covers the typical use cases for this scenario.
  • nftables: Use the nftables utility to set up complex firewalls, such as for a whole network.
  • iptables: The iptables utility is deprecated in Red Hat Enterprise Linux 8. Use instead 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.

6.3. Converting iptables rules to nftables rules

Red Hat Enterprise Linux 8 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.

6.4. 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. You can either extend these files or write your scripts.

6.4.1. The required script header in nftables script

Similar to other scripts, nftables scripts require a shebang sequence in the first line of the script that sets the interpreter directive.

An nftables script must always start with the following line:

#!/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.

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

6.4.3. Running nftables scripts

To run an nftables script, the script must be executable. Only if the script is included in another script, it does not require to be executable. The procedure describes how to make a script executable and run the script.

Prerequisites

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

Procedure

  1. Steps that are required only once:

    1. Optionally, set the owner of the script to root:

      # chown root /etc/nftables/example_firewall.nft
    2. 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

6.4.4. Using comments in nftables scripts

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

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

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

Additional resources

6.4.6. 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 6.2. Including files from the default search directory

To include a file from the default search directory:

include "example.nft"

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

To include all files ending in *.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

  • For further details, see the Include files section in the nft(8) man page.

6.4.7. 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. Enable the nftables service.

    # systemctl enable nftables
  3. Optionally, start the nftables service to load the firewall rules without rebooting the system:

    # systemctl start nftables

6.5. Displaying nftables rule sets

Rule sets of nftables contain tables, chains, and rules. This section explains how to display these rule sets.

Procedure

  1. To display all rule sets, enter:

    # nft list ruleset
    table inet example_table {
      chain example_chain {
        type filter hook input priority 0; 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.

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

  • For further details about address families, see the Address families section in the nft(8) man page.
  • For details on other actions you can run on tables, see the Tables section in the nft(8) man page.

6.7. 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, you must escape the semicolons with a backslash.

    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 0; policy accept;
      }
    }

Additional resources

  • For further details about address families, see the Address families section in the nft(8) man page.
  • For details on other actions you can run on chains, see the Chains section in the nft(8) man page.

6.8. Adding a rule to an nftables chain

This section explains how to add a rule to an existing nftables chain. By default, the nftables add rule command appends a new rule to the end of the chain.

If you instead want to insert a rule at the beginning of chain, see Section 6.9, “Inserting a rule into an 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 0; policy accept;
        ...
        tcp dport ssh accept
      }
    }

Additional resources

  • For further details about address families, see the Address families section in the nft(8) man page.
  • For details on other actions you can run on rules, see the Rules section in the nft(8) man page.

6.9. Inserting a rule into an nftables chain

This section explains how to insert a rule at the beginning of an existing nftables chain using the nftables insert rule command. If you instead want to add a rule to the end of a chain, see Section 6.8, “Adding a rule to an 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 0; policy accept;
        tcp dport ssh accept
        ...
      }
    }

Additional resources

  • For further details about address families, see the Address families section in the nft(8) man page.
  • For details on other actions you can run on rules, see the Rules section in the nft(8) man page.

6.10. Configuring NAT using nftables

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

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

6.10.1. The different NAT types: masquerading, source NAT, and destination NAT

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 providers do not route reserved IP ranges, such as 10.0.0.0/8. If you use reserved 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 route incoming traffic to a different host. For example, if your web server uses an IP address from a reserved 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.

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

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

6.10.4. Configuring destination NAT using nftables

Destination NAT enables you to redirect traffic on a router to a host that is not directly accessible from the Internet.

The following procedure describes how to redirect incoming traffic sent to port 80 and 443 of the router to the host with the 192.0.2.1 IP address.

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 prerouting chain that redirects incoming traffic on the ens3 interface sent to port 80 and 443 to the host with the 192.0.2.1 IP:

    # 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:

    1. If the ens3 interface used 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

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

6.11.1. Using an anonymous sets in nftables

An anonymous set contain comma-separated values enclosed in curly brackets, such as { 22, 80, 443 }, that you use directly in a rule. You can also use anonymous sets also for IP addresses or 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 Section 6.11.2, “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. 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 0; policy accept;
        tcp dport { ssh, http, https } accept
      }
    }

6.11.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 avoid that the shell interprets the semicolons as the end of the command, you must escape the semicolons with a backslash.

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

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

6.12.1. Using literal maps in nftables

A literal 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 a literal map is that if you want to change the map, you must replace the rule. For a dynamic solution, use named verdict maps as described in Section 6.12.2, “Using mutable verdict maps in nftables”.

The example describes how to use a literal 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 the example_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 a literal map to incoming_traffic:

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

    The literal 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 0; 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.

6.12.2. Using mutable verdict maps in nftables

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

When you create a mutable verdict 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.

The example describes how to allow or drop incoming packets based on their source IP address. Using a mutable verdict map, you require only a single rule to configure this scenario while the IP addresses and actions are dynamically stored in the map. The procedure also describes how to add and remove entries from 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 avoid that the shell interprets 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. Optionally, 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. Optionally, remove an entry from the map:

    # nft delete element ip example_table example_map { 192.0.2.1 }
  8. Optionally, 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 0; policy accept;
        ip saddr vmap @example_map
      }
    }

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

6.13.1. Forwarding incoming packets to a different local port

This section describes an example of how to 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 avoid that the shell interprets 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

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

The procedure describes how to 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.

Prerequisite

  • 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 avoid that the shell interprets 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 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

6.14. Limiting the number of connections using nftables

The ct count parameter of the nft utility enables administrators to limit the number of connections. The procedure describes a basic example of how to limit incoming connections.

Prerequisites

  • The base example_chain in example_table exists.

Procedure

  1. Add a rule that allows only two simultaneous connections to the SSH port (22) from an IPv4 address and rejects all further connections from the same IP:

    # nft add rule ip example_table example_chain tcp dport ssh meter example_meter { ip saddr ct count over 2 } counter reject
  2. Optionally, display the meter created in the previous step:

    # nft list meter ip example_table example_meter
    table ip example_table {
      meter example_meter {
        type ipv4_addr
        size 65535
        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.

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

The nftables framework enables administrators to dynamically update sets. This section explains how you use this feature to temporarily block hosts that are establishing more than ten IPv4 TCP connections within one minute. After five minutes, nftables automatically removes the IP address from the blacklist.

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 set named blacklist to the filter table:

    # nft add set ip filter blacklist { type ipv4_addr \; flags dynamic, timeout \; timeout 5m \; }

    This command creates a dynamic set for IPv4 addresses. The timeout 5m parameter defines that nftables automatically removes entries after 5 minutes from the set.

  4. Add a rule that automatically adds the source IP address of hosts that attempt to establish more than ten new TCP connections within one minute to the blacklist set:

    # nft add rule ip filter input ip protocol tcp ct state new, untracked limit rate over 10/minute add @blacklist { ip saddr }
  5. Add a rule that drops all connections from IP addresses in the blacklist set:

    # nft add rule ip filter input ip saddr @blacklist drop

6.16. Debugging nftables rules

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

6.16.1. Creating a rule with a counter

To identify if a rule is matched, you can use a counter. This section describes how to create a new rule with a counter.

For a procedure that adds a counter to an existing rule, see Section 6.16.2, “Adding a counter to an existing rule”.

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 0; policy accept;
        tcp dport ssh counter packets 6872 bytes 105448565 accept
      }
    }

6.16.2. Adding a counter to an existing rule

To identify if a rule is matched, you can use a counter. This section describes how to add a counter to an existing rule.

For a procedure to add a new rule with a counter, see Section 6.16.1, “Creating a rule with 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 0; 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 0; policy accept;
        tcp dport ssh counter packets 6872 bytes 105448565 accept
      }
    }

6.16.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. The procedure describes how to enable tracing for a rule as well as 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 0; 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.

6.17. Backing up and restoring nftables rule sets

This section describes how to backup nftables rules to a file, as well as restoring rules from a file.

Administrators can use a file with the rules to, for example, transfer the rules to a different server.

6.17.1. Backing up nftables rule sets to a file

This section describes how to back up nftables rule sets to a file.

Procedure

  1. To backup nftables rules:

    • In nft list ruleset format:

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

      # nft -j list ruleset > file.json

6.17.2. Restoring nftables rule sets from a file

This section describes how to restore nftables rule sets.

Procedure

  1. To restore nftables rules:

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

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

      # nft -j -f file.json