CentOS 8 has been released, can you still use nftables?

If you have not lived in the last century and are a brick mover in cloud computing or related fields, then you should have heard that the official version of CentOS 8 has been released recently, and CentOS fully complies with the reissue of Red Hat Policy, and strive to be fully functionally compatible with upstream products. The main changes of CentOS 8 are the same as RedHat Enterprise Linux 8. It is based on Fedora 28 and kernel version 4.18. The main network change is to use **nftables** framework instead of * *iptables** The framework is used as the default network packet filtering tool. **If you haven't heard of nftables, now is the time to learn.

nftables is a netfilter project, which aims to replace the existing {ip,ip6,arp,eb}tables framework and provide a new packet filtering framework for {ip,ip6}tables and a new user space utility (nft ) And a compatibility layer. It uses existing hooks, link tracking system, user space queuing component and netfilter logging subsystem.

nftables is mainly composed of three components: kernel implementation, libnl netlink communication and nftables user space. The kernel provides a netlink configuration interface and runtime rule set evaluation, libnl contains basic functions for communicating with the kernel, and the user space can interact with users through nft.

This article mainly introduces the usage of user space command line tool nft.

1.

nftables VS iptables

Like iptables, nftables consists of a table, a chain, and a rule. The table contains the chain, the chain contains the rules, and the rule is the real action. Compared with iptables, nftables mainly has the following changes:

Back to nftables, let's take a look at the default rule set:

$ nft list ruleset

Nothing, there really is no built-in chain (if you turn off the firewalld service).

2.

Create table

Each table of nftables has only one address cluster, and it is only applicable to data packets of that cluster. The table can specify one of five clusters:

nftables cluster iptables command line tool
ip iptables
ip6 ip6tables
inet iptables and ip6tables
arp arptables
bridge ebtables

inet is applicable to both IPv4 and IPv6 packets, that is, the ip and ip6 clusters are unified, which makes it easier to define rules. The following examples will all use inet clusters.

First create a new table:

$ nft add table inet my_table

List all the rules:

$ nft list ruleset
table inet my_table {}

Now there are no rules in the table. You need to create a chain to save the rules.

3.

Create chain

Chains are used to store rules. Like tables, chains need to be created explicitly because nftables has no built-in chain. There are two types of chains:

Create a regular chain:

$ nft add chain inet my_table my_utility_chain

Create a basic chain:

$ nft add chain inet my_table my_filter_chain { type filter hook input priority 0 \;}

List all the rules in the chain:

$ nft list chain inet my_table my_utility_chain
table inet my_table {
  chain my_utility_chain {}}

$ nft list chain inet my_table my_filter_chain
table inet my_table {
  chain my_filter_chain {
    type filter hook input priority 0; policy accept;}}

4.

Create rule

Once you have the table and chain, you can create rules, which are composed of statements or expressions and are included in the chain. Add a rule below to allow SSH login:

$ nft add rule inet my_table my_filter_chain tcp dport ssh accept

add means to add the rule to the end of the chain. If you want to add the rule to the beginning of the chain, you can use insert.

$ nft insert rule inet my_table my_filter_chain tcp dport http accept

List all rules:

$ nft list ruleset
table inet my_table {
  chain my_filter_chain {
    type filter hook input priority 0; policy accept;
    tcp dport http accept
    tcp dport ssh accept
        }}

Note that http rules come before ssh rules because insert was used before.

You can also insert the rule into the specified position of the chain, there are two ways:

1、 Use index to specify the index of the rule. add means that the new rule is added after the rule at the index position, and inser means that the new rule is added before the rule at the index position. The value of index increases from 0.

$ nft insert rule inet my_table my_filter_chain index 1 tcp dport nfs accept
$ nft list ruleset
table inet my_table {
  chain my_filter_chain {
    type filter hook input priority 0; policy accept;
    tcp dport http accept
    tcp dport nfs accept
    tcp dport ssh accept
     }}

$ nft add rule inet my_table my_filter_chain index 0 tcp dport 1234 accept
$ nft list ruleset
table inet my_table {
  chain my_filter_chain {
    type filter hook input priority 0; policy accept;
    tcp dport http accept
    tcp dport 1234 accept
    tcp dport nfs accept
    tcp dport ssh accept
     }}

index is similar to the -I option of iptables, but there are two points to note: one is that the value of index starts from 0; the other is that index must point to an existing rule, such as nft insert rule… index 0 Unlawful.

2、 Use handle to specify the handle of the rule. add means that the new rule is added after the rule at the index position, and inser means that the new rule is added before the rule at the index position. The value of handle can be obtained by parameter --handle.

$ nft --handle list ruleset
table inet my_table { # handle 10
  chain my_filter_chain { # handle 2
    type filter hook input priority 0; policy accept;
    tcp dport http accept # handle 4
    tcp dport 1234 accept # handle 6
    tcp dport nfs accept # handle 5
    tcp dport ssh accept # handle 3}}

$ nft add rule inet my_table my_filter_chain handle 4 tcp dport 1234 accept
$ nft insert rule inet my_table my_filter_chain handle 5 tcp dport nfs accept
$ nft --handle list ruleset
table inet my_table { # handle 10
  chain my_filter_chain { # handle 2
    type filter hook input priority 0; policy accept;
    tcp dport http accept # handle 4
    tcp dport 2345 accept # handle 8
    tcp dport 1234 accept # handle 6
    tcp dport 3456 accept # handle 9
    tcp dport nfs accept # handle 5
    tcp dport ssh accept # handle 3}}

In nftables, the handle value is fixed, unless the rule is deleted, which provides a stable index for the rule. The value of index is variable, as long as there are new rules inserted, it may change. It is generally recommended to use handle to insert new rules.

You can also get the handle value of the rule when you create the rule. You only need to add the parameters --echo and --handle at the same time when creating the rule.

$ nft --echo --handle add rule inet my_table my_filter_chain udp dport 3333 accept
add rule inet my_table my_filter_chain udp dport 3333 accept # handle 10

5.

Delete rule

A single rule can only be deleted through its handle. First, you need to find the rule handle you want to delete:

$ nft --handle list ruleset
table inet my_table { # handle 10
  chain my_filter_chain { # handle 2
    type filter hook input priority 0; policy accept;
    tcp dport http accept # handle 4
    tcp dport 2345 accept # handle 8
    tcp dport 1234 accept # handle 6
    tcp dport 3456 accept # handle 9
    tcp dport nfs accept # handle 5
    tcp dport ssh accept # handle 3
    udp dport 3333 accept # handle 10}}

Then use the handle value to delete the rule:

$ nft delete rule inet my_table my_filter_chain handle 8
$ nft --handle list ruleset
table inet my_table { # handle 10
  chain my_filter_chain { # handle 2
    type filter hook input priority 0; policy accept;
    tcp dport http accept # handle 4
    tcp dport 1234 accept # handle 6
    tcp dport 3456 accept # handle 9
    tcp dport nfs accept # handle 5
    tcp dport ssh accept # handle 3
    udp dport 3333 accept # handle 10}}

6.

List rules

The previous examples list all the rules, and we can also list part of the rules according to our needs. E.g:

List all rules in a table:

$ nft list table inet my_table
table inet my_table {
  chain my_filter_chain {
    type filter hook input priority 0; policy accept;
    tcp dport http accept
    tcp dport 1234 accept
    tcp dport 3456 accept
    tcp dport nfs accept
    tcp dport ssh accept
    udp dport 3333 accept
        }}

List all the rules in a chain:

$ nft list chain inet my_table my_other_chain
table inet my_table {
 chain my_other_chain {
  udp dport 12345 log prefix "UDP-12345"}}

7.

set

The syntax of nftables natively supports collections, which can be used to match multiple IP addresses, port numbers, network cards or any other conditions.

Anonymous collection

Collections are divided into anonymous collections and named collections. Anonymous collections are more suitable for rules that do not need to be changed in the future.

For example, the following rule allows traffic from hosts whose source IP is in the range of 10.10.10.123 ~ 10.10.10.231.

$ nft add rule inet my_table my_filter_chain ip saddr {10.10.10.123,10.10.10.231} accept
$ nft list ruleset
table inet my_table {
  chain my_filter_chain {
    type filter hook input priority 0; policy accept;
    tcp dport http accept
    tcp dport nfs accept
    tcp dport ssh accept
    ip saddr {10.10.10.123,10.10.10.231} accept
        }}

The disadvantage of anonymous collections is that if you need to modify the collection, you have to replace the rules. If you need to modify the collection frequently later, it is recommended to use a named collection.

The rules added in the previous example can also be simplified by sets:

$ nft add rule inet my_table my_filter_chain tcp dport { http, nfs, ssh } accept

iptables can use ipset to use collections, and nftables natively supports collections, so there is no need to resort to ipset.

Name the collection

nftables also supports named collections, and named collections can be modified. To create a collection, you need to specify the type of its elements. The currently supported data types are:

First create an empty named collection:

$ nft add set inet my_table my_set { type ipv4_addr \;}
$ nft list sets
table inet my_table {set my_set {
    type ipv4_addr
        }}

To refer to the collection when adding rules, you can use the @ symbol to keep up with the name of the collection. The following rule means adding the IP addresses in the set my_set to the blacklist.

$ nft insert rule inet my_table my_filter_chain ip saddr @my_set drop
$ nft list chain inet my_table my_filter_chain
table inet my_table {
  chain my_filter_chain {
    type filter hook input priority 0; policy accept;
    ip saddr @my_set drop
    tcp dport http accept
    tcp dport nfs accept
    tcp dport ssh accept
    ip saddr {10.10.10.123,10.10.10.231} accept
        }}

Add elements to the collection:

$ nft add element inet my_table my_set {10.10.10.22,10.10.10.33}
$ nft list set inet my_table my_set
table inet my_table {set my_set {
    type ipv4_addr
    elements ={10.10.10.22,10.10.10.33}}}

If you add an interval to the collection, an error will be reported:

$ nft add element inet my_table my_set {10.20.20.0-10.20.20.255}

Error: Set member cannot be range, missing interval flag on declaration
add element inet my_table my_set {10.20.20.0-10.20.20.255}^^^^^^^^^^^^^^^^^^^^^^^

If you want to use the interval in the collection, you need to add a flag interval, because the kernel must confirm the type of data stored in the collection in advance in order to adopt the appropriate data structure.

Support interval

Create a named collection of support intervals:

$ nft add set inet my_table my_range_set { type ipv4_addr \; flags interval
$ nft add element inet my_table my_range_set {10.20.20.0/24}
$ nft list set inet my_table my_range_set
table inet my_table {set my_range_set {
    type ipv4_addr
    flags interval
    elements ={10.20.20.0/24}}}

The subnet mask notation will be implicitly converted to the range of the IP address. You can also directly use the range 10.20.20.0-10.20.20.255 to get the same effect.

Cascade different types

Named collections also support the cascade of different types of elements, separated by the cascade operator .. For example, the following rules can match IP addresses, protocols, and port numbers all at once.

$ nft add set inet my_table my_concat_set  { type ipv4_addr . inet_proto . inet_service \;}

$ nft list set inet my_table my_concat_set
table inet my_table {set my_concat_set {
    type ipv4_addr . inet_proto . inet_service
        }}

Add elements to the collection:

$ nft add element inet my_table my_concat_set {10.30.30.30. tcp . telnet }

The set of cascading types referenced in the rule is the same as before, but it is necessary to indicate which position in the rule each element in the set corresponds to.

$ nft add rule inet my_table my_filter_chain ip saddr . meta l4proto . tcp dport @my_concat_set accept

This means that if the source IP, protocol type, and destination port of the packet match 10.30.30.30, tcp, telnet, nftables will allow the packet to pass.

Anonymous collections can also use cascading elements, for example:

$ nft add rule inet my_table my_filter_chain ip saddr . meta l4proto . udp dport {10.30.30.30. udp . bootps } accept

Now you should be able to appreciate the power of the nftables collection.

The collection of nftables cascade types is similar to the aggregation type of ipset, such as hash:ip,port.

8.

dictionary

Dictionary is an advanced feature of nftables. It can use different types of data and map matching conditions to a certain rule, and because it is a hash mapping method, it can perfectly avoid the performance overhead of chain rule jumps.

For example, in order to logically separate the processing rules for TCP and UDP data packets, a dictionary can be used to achieve the above requirements.

$ nft add chain inet my_table my_tcp_chain
$ nft add chain inet my_table my_udp_chain
$ nft add rule inet my_table my_filter_chain meta l4proto vmap { tcp : jump my_tcp_chain, udp : jump my_udp_chain }
$ nft list chain inet my_table my_filter_chain
table inet my_table {
 chain my_filter_chain {...
 meta nfproto ipv4 ip saddr . meta l4proto . udp dport {10.30.30.30. udp . bootps } accept
 meta l4proto vmap { tcp : jump my_tcp_chain, udp : jump my_udp_chain }}}

Like collections, in addition to anonymous dictionaries, you can also create named dictionaries:

$ nft add map inet my_table my_vmap { type inet_proto : verdict \;}

Add elements to the dictionary:

$ nft add element inet my_table my_vmap {192.168.0.10: drop,192.168.0.11: accept }

The elements in the dictionary can be referenced in the rules later:

$ nft add rule inet my_table my_filter_chain ip saddr vmap @my_vmap

9.

Tables and namespaces

In nftables, each table is an independent namespace, which means that chains, sets, dictionaries, etc. in different tables can all have the same name. E.g:

$ nft add table inet table_one
$ nft add chain inet table_one my_chain
$ nft add table inet table_two
$ nft add chain inet table_two my_chain
$ nft list ruleset
...
table inet table_one {
 chain my_chain {}}
table inet table_two {
 chain my_chain {}}

With this feature, different applications can manage the rules in their own tables without affecting each other, which cannot be achieved with iptables.

Of course, this feature is also flawed. Since each table is regarded as an independent firewall, a certain data packet must be released by the rules in all tables before it is truly released. Even if the table_one allows the data packet to pass, the Data packets may still be rejected by table_two. To solve this problem, nftables introduces priority. The higher the value of priority, the lower the priority, so the chain with the lower value of priority will be executed before the chain with the higher value of priority. If the two chains have the same priority, they will enter a competitive state.

10.

Backup and restore

The rules in all the above examples are temporary. To take effect permanently, we can back up the rules and automatically load and restore them after restarting. In fact, the systemd service of nftables works like this.

Backup rules:

$ nft list ruleset >/root/nftables.conf

Load recovery:

$ nft -f /root/nftables.conf

In CentOS 8, the rules of nftables.service are stored in /etc/nftables.conf, which includes some other example rules, generally located in the file /etc/sysconfig/nftables.conf, but by default Will be commented out.

11.

to sum up

I hope that through the explanation of this article, you can understand the function and usage of nftables. Of course, this article only involves some simple usage. For more advanced usage, please check the official wiki of nftables, or wait for my next article. I believe that with the knowledge of this article, you should be able to happily use nftables to implement Linux intelligent distribution. For details, scan the QR code below and refer to this article: Linux Global Intelligent Distribution Scheme.

Recommended Posts

CentOS 8 has been released, can you still use nftables?