Configuring the Firewall with nftables

This chapter describes configuring the firewall with nftables. It also provides examples for configuring nftables tables, chains, and rules that enforce network security on a system with the nft command. These examples are appropriate for learning about nftables; however, for more advanced users, consider editing nftables configurations from a file. For more information about nftables file syntax, see the nft(8) manual page.

Do the following:

  • For Oracle Linux 8, this chapter also describes how to convert iptables and ip6tables to the nftables framework.

  • For Oracle Linux 9 and Oracle Linux 10, use the conversion guidance in the release-specific firewall documentation instead.

Note

When you create nftables configurations using nft commands, these configurations reside in the system's memory until you flush the memory or restart the system. To make these configurations persistent across system boots, consider exporting the configurations to an .nft file and setup the system to include the file when starting the nftables service. For more information about exporting configurations to a file, see Disabling the firewalld Service. For more information about loading a configuration file into nftables either manually or automatically, see Disabling the firewalld Service.

Note

When working with nftables, it's good practice to keep a local connection (for example, with a serial console if possible) to recover from mistakes that might lock you out of the system.

Disabling the firewalld Service

In Oracle Linux, nftables isn't enabled by default because the firewalld service uses it as its backend. Disable firewalld before beginning to work with nftables.

Do the following:

  • For Oracle Linux 8, the output resembles:

    firewalld.service
       Loaded: masked (Reason: Unit firewalld.service is masked.)
       Active: inactive (dead)
    
    Jan 20 15:16:07 localhost.localdomain systemd[1]: Starting firewalld - dynamic firewall daemon...
    Jan 20 15:16:08 localhost.localdomain systemd[1]: Started firewalld - dynamic firewall daemon.
    Jan 20 15:16:08 localhost.localdomain firewalld[1635]: WARNING: AllowZoneDrifting is enabled. This is considered an insecure configuration option. It will be removed in a future release. Please consider disabling it now.
    Jan 20 15:52:27 localhost.localdomain systemd[1]: Stopping firewalld - dynamic firewall daemon...
    Jan 20 15:52:27 localhost.localdomain systemd[1]: firewalld.service: Succeeded.
    Jan 20 15:52:27 localhost.localdomain systemd[1]: Stopped firewalld - dynamic firewall daemon.
    
  • For Oracle Linux 9, the output resembles:

    firewalld.service
         Loaded: masked (Reason: Unit firewalld.service is masked.)
         Active: inactive (dead)
    
    Jan 20 15:10:08 localhost.localdomain systemd[1]: Starting firewalld - dynamic firewall daemon...
    Jan 20 15:10:08 localhost.localdomain systemd[1]: Started firewalld - dynamic firewall daemon.
    Jan 20 15:26:25 localhost.localdomain systemd[1]: Stopping firewalld - dynamic firewall daemon...
    Jan 20 15:26:25 localhost.localdomain systemd[1]: firewalld.service: Deactivated successfully.
    Jan 20 15:26:25 localhost.localdomain systemd[1]: Stopped firewalld - dynamic firewall daemon.
    
  • For Oracle Linux 10, the output resembles:

    firewalld.service
         Loaded: masked (Reason: Unit firewalld.service is masked.)
         Active: inactive (dead)
    
    Jan 20 16:05:39 localhost.localdomain systemd[1]: Starting firewalld.service - firewalld - dynamic firewall daemon...
    Jan 20 16:05:40 localhost.localdomain systemd[1]: Started firewalld.service - firewalld - dynamic firewall daemon.
    Jan 20 16:08:33 localhost.localdomain systemd[1]: Stopping firewalld.service - firewalld - dynamic firewall daemon...
    Jan 20 16:08:33 localhost.localdomain systemd[1]: firewalld.service: Deactivated successfully.
    Jan 20 16:08:33 localhost.localdomain systemd[1]: Stopped firewalld.service - firewalld - dynamic firewall daemon.
  1. Jan 20 16:08:33 localhost.localdomain systemd[1]: Stopping firewalld.service - firewalld - dynamic firewall daemon...
      Jan 20 16:08:33 localhost.localdomain systemd[1]: firewalld.service: Deactivated successfully.
      Jan 20 16:08:33 localhost.localdomain systemd[1]: Stopped firewalld.service - firewalld - dynamic firewall daemon.

About Rulesets and Tables

nftables includes rulesets that contain all configuration structures within nftables. Tables are the top level structure in rulesets within which are contained various objects such as chains, rules, and so on.

Managing Rulesets and Tables

To manage nftable rulesets and tables in memory, do the following:
  1. Create a table for a specific address family type using the following syntax:

    sudo nft add table <address_family> <table_name>
    In the previous,
    • <address_family> can be ip, ip6, inet, arp, bridge, or netdev. All nftables objects are in one of these address families. For more information about these address families, see the nft(8) manual page.
    • <table_name> is the name of the table. Tables contain chains, which in turn contain rules.
    For example, the following command creates a table called mytable with the inet family, which includes addresses for IP version 4 and 6:
    sudo nft add table inet mytable
  2. Do the following:
    • To view all rulesets, do the following:
      sudo nft list ruleset
    • To view a single table, do the following:
      sudo nft list table <address_family> <table_name> 

About Hooks and Chains

Hooks are points in a system where packets can be intercepted for processing. These hooks are where nftables applies chains and rules that decide what happens to a packet. Different hooks are available depending on the address family applied to a table.

The following table shows the hooks available to each address family.

Address Family

Hook

Description

IP, IPv6, inet / Bridge

prerouting

Processes all incoming packets before routing decisions.

input

Handles packets destined for the local system.

forward

Manages packets being forwarded to another host.

output

Processes packets originating from the local system.

postrouting

Deals with all outgoing packets after routing.

ingress

Manages incoming packets before the prerouting, available from Linux kernel 5.10 for inet family.

ARP

input

Processes packets for the local system.

output

Handles packets sent from the local system.

Netdev

ingress

Processes incoming packets after network taps, such as tcpdump, before layer 3 handling.

egress

Manages outgoing packets after layer 3 handling but before final network exit.

Base chains are attached to hooks. When a packet arrives at a hook, configured with a base chain, the packet then traverses the chain, and each rule in the chain is evaluated in order until a rule matches, or the end of the chain is reached. nftables includes the following chain types:
  • Base Chains: These are chains directly built on a hook. For example, a base chain named myinput might be linked to the inet input hook for IPv4 or IPv6 packets arriving at the local system.
  • Regular Chains: These are chains that jump from base chains or other regular chains. They aren't built directly at a hook but can be part of the decision-making process within a base chain.
Base chains must include the base chain type, hook, and priority parameters. Chain types include:
  • filter: Primarily used for packet filtering, such as whether to permit or block packets based on various criteria such as source and destination IP, ports, protocols, and so on. This chain type works with all address families and their hooks.
  • nat: Primarily used to change IP addresses, ports, or both in packet headers for NAT operations. This includes changes to Source NAT (SNAT) for outgoing traffic and Destination NAT (DNAT) for incoming traffic. This chain type can be configured with ip, ipv6, and inet address families and can take the prerouting, input, output, and postrouting hooks.
  • route: Primarily used for routing decisions such as altering a routing table or marking packets for specific routing policies. This chain type can be used with ip and ipv6 address families and can take the output hook only.

When using the ingress or egress hooks, specify a network interface name as a string with the device parameter. Any ingress or egress chains only filters traffic from the interface specified in the device parameter.

The priority parameter uses a signed integer or a standard priority name to determine the order in which chains with the same hook are processed. Chains process from lower priority values or names to higher priority values or names.

The following table shows priority names and values for each associated address family and hook type.

Name

Value

Family Hooks

raw

-300

ip, ip6, inet all

mangle

-150

ip, ip6, inet all

dsnat

-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

The following table shows priority names and values for the bridge address family and hook type.

Name

Value

Hooks

dsnat

-300

prerouting

filter

-200

all

out

100

output

srcnat

300

postrouting

You can also choose to set a policy on a chain that defines whether to accept or drop a packet when none of the rules defined on the chain matches the packet. By default, base chains accept all packets. However, setting a policy to drop all traffic not explicitly allowed by a chain is good practice for security reasons.

Creating Base Chains

To create nftable base chains in memory, do the following:
  1. Create a chain for a specific address family type and table using the following syntax:

    sudo nft add chain <address_family> <table_name> <chain_name>{ type <chain_type> hook <hook_type> device <network_interface_name> priority <priority> policy <policy> comment <comment> ; }
    
    In the previous,
    • <address_family> can be ip, ip6, inet, arp, bridge, or netdev. All nftables objects are in one of these address family. For more information about these address families, see the nft(8) manual page.
    • <table_name> is the name of the table. Tables are containers for chains, which in turn are containers for rules.
    • <chain_name> is an arbitrary name for the chain. People migrating from iptables based firewalls often use traditional iptables naming.
    • <chain_type> is the chain type. Valid values for base chains are filter, nat, and route. For more information, see About Hooks and Chains.
    • <hook_type> is the hook type. Possible values depend on the selected address family and chain type. For more information, see About Hooks and Chains.
    • <network_interface_name> is the network interface name for the device parameter. This parameter is only required when using the ingress or egress hooks.
    • <priority> is the priority value or name for the chain. Priority depends on the selected address family and hook type. For more information, see About Hooks and Chains.
    • <policy> is the action taken if all rules defined on the chain fail to match the packet. Valid values are accept or drop. If not specified, the default value is accept.
    For example, the following command creates a chain in the mytable table called mychain with the inet family. The chain type is filter, the hook is input, and the priority is 0. Finally, the policy is set to drop all packets that don't match a rule:
    sudo nft add chain inet mytable mychain "{ type filter hook input priority 0 ; policy drop ; }"
  2. Do the following:
    • To view all chains, do the following:
      sudo nft list chains
    • To view a single chain, do the following:
      sudo nft list chain <address_family> <table_name> <chain_name>

About Rules

You can add rules to chains in a table. Rules are made up of instructions that match packets based on various criteria and apply actions such as accept, drop, reject, and so on. These instructions include elements such as:
  • Sets, which are collections of elements used for the matching.
  • Expressions, which are building blocks for rules that define how packets are matched or manipulated. Examples include:
    • Protocol-specific matches (for example, ip, ip6, tcp, udp).
    • Address matching (saddr, daddr).
    • Port matching (sport, dport).
    • Network interface matches (iifname, oifname).
  • Maps, which are similar to sets but can map one value to another. Maps are used for more complex matching or transformation scenarios.
  • Verdict maps, which can change verdicts based on packet content, enable more dynamic policy decisions.
  • Counters, which track the number of packets matching a rule and display when running a list nft command that includes the rule. Counters are a useful way to test whether a rule is working.
  • Quotas, which limit the amount of data that can pass through a rule before an action changes (for example, from accept to drop).
  • Flowtables, which allows fast path packet forwarding, improving performance by bypassing regular packet processing for certain traffic.
  • Statements, which includes operations such as log, reject, jump, goto which change packet handling behavior.

Rules added to a chain are evaluated from top to bottom and from left to right.

For more information about configuring these instructions, see the nft(8) manual page.

Creating Rules - Examples

This section includes some common example of rules created within chains.

Allow local traffic

The following command creates a rule in mytable within mychain for the inet family that allows local traffic to enter through the loopback interface (iff lo):
sudo nft add rule inet mytable mychain iif lo accept

Allow incoming traffic for an existing connection or related to an existing connection

This command adds a rule to the mytable's mychain chain that accepts all incoming traffic that are part of or related to an existing connection:
sudo nft add rule inet mytable mychain ct state established, related accept
In the previous example,
  • ct is a connection tracking helper for IPv4, IPv6, or inet which is part of the nf_conntrack module. This module is typically loaded by default on most systems. You can list the state of all connections to the system using the following command:
    sudo cat /proc/net/nf_conntrack
    Possible connection states are:
    • NEW: The packet has started a new connection, or is associated with a connection that hasn't received and sent packets.
    • ESTABLISHED: The packet is associated with a connection which has received and sent packets.
    • RELATED: The packet is starting a new connection, but is associated with an existing connection.
    • INVALID: The packet is associated with no known connection.
  • state established, related indicates that the command applies only to connections in the ESTABLISHED or RELATED state.
  • accept indicates that any packet with the appropriate state can be accepted.

This is a common rule in firewall configurations to ensure that responses to outgoing traffic are allowed back in, enabling normal operations of network services such as web browsing, SSH connectivity, and so on, without needing to explicitly open all ports for incoming connections.

Allow incoming SSH traffic

The following adds a rule to the mytable's mychain chain that accepts all incoming TCP traffic on destination port 22.
sudo nft add rule inet mytable mychain tcp dport 22 accept

This port is typically used for SSH traffic and assumes that the SSH daemon is setup and running on the system.

Restricting all IPv4 and IPv6 traffic (Panic button)

The following adds a rule to the mytable table that drops all incoming and outgoing IPv4 and IPv6 and acts as a kind of panic button.
sudo nft add rule inet mytable drop

Exporting Configurations to a File

To keep nftable configurations across boots or to switch from one configuration to another, you can export an nftable in memory to a file.

To export nftable configurations to a file, do the following:
  1. List the rulesets and save the output to a file:

    sudo nft list ruleset > /etc/nftables/<export_file_name>.nft

    In the previous, <export_file_name> is the name of the file for the exported information. This file now contains all the tables, chains, and rules available in memory.

  2. List a table and save the output to a file:

    sudo nft list table <address_family> <table_name> > /etc/nftables/<export_file_name>.nft

    This file now contains a table and all associated chains and rules available in memory.

  3. List a chain and save the output to a file:

    sudo nft list chain <address_family> <table_name> <chain_name> > /etc/nftables/<export_file_name>.nft

    This file now contains a chain in a table and all associated rules within the chain available in memory.

  4. Ensure the files included in /etc/nftables are executable:
    sudo chmod +x /etc/nftables/<export_file_name>.nft

Loading Configurations from a File

To load an nftable ruleset, table, or chain from a file into memory, you can perform this task manually or automatically when rebooting a system.

To manually load an nftables file, do the following:
  1. Before loading a new configuration from a file, drop the existing tables:
    sudo nft flush ruleset
    Note

    This step is crucial to avoid conflicts between the new and old configurations and ensures a clean and consistent application of the new rules.
  2. Run the following command to load the file into memory:

    sudo nft -f /etc/nftables/<import_file_name>.nft
    In the previous, <import_file_name> is the name of the file with the information to be imported. This file might contain a ruleset, one or more tables, one or more chains within a table, and any associated rules.
    Note

    The atomic reload is an nftables feature that ensures that connection tracking is preserved during rule reloading, providing a seamless transition to the new configuration.
  3. List a ruleset to verify that the file imported correctly:

    sudo nft list rulesets
To automatically load a ruleset from a file when restarting the system, do the following:
  1. Edit the /etc/sysconfig/nftables.conf file to include the .nft table files you want to include at startup. If this file doesn't exist, create it. For example, the following shows that the /etc/sysconfig/nftables.conf now includes the exported /etc/nftables/myruleset.nft file.

    # Uncomment the include statement here to load the default config sample
    # in /etc/nftables for nftables service.
    
    include "/etc/nftables/myruleset.nft"
    
    # To customize, either edit the samples in /etc/nftables, append further
    # commands to the end of this file or overwrite it after first service
    # start by calling: 'nft list ruleset >/etc/sysconfig/nftables.conf'.
    
  2. Enable and start the nftables service:

    sudo systemctl enable --now nftables