Difference between revisions of "Nftables"

From ArchWiki
Jump to navigation Jump to search
m (add the language link)
 
(257 intermediate revisions by 62 users not shown)
Line 1: Line 1:
 
{{DISPLAYTITLE:nftables}}
 
{{DISPLAYTITLE:nftables}}
 
[[Category:Firewalls]]
 
[[Category:Firewalls]]
 +
[[es:Nftables]]
 +
[[ja:Nftables]]
 +
[[ru:Nftables]]
 +
[[zh-hans:Nftables]]
 
{{Related articles start}}
 
{{Related articles start}}
{{Related|Firewalls}}
 
 
{{Related|iptables}}
 
{{Related|iptables}}
 
{{Related articles end}}
 
{{Related articles end}}
[http://netfilter.org/projects/nftables/ nftables] is a netfilter project that aims to replace the existing ip-, ip6-, arp-, and ebtables framework. It provides a new packet filtering framework, a new user-space utility (nft), and a compatibility layer for ip- and ip6tables. It uses the existing hooks, connection tracking system, user-space queueing component, and logging subsystem of netfilter.
+
[http://netfilter.org/projects/nftables/ nftables] is a netfilter project that aims to replace the existing {ip,ip6,arp,eb}tables framework. It provides a new packet filtering framework, a new user-space utility (nft), and a compatibility layer for {ip,ip6}tables. It uses the existing hooks, connection tracking system, user-space queueing component, and logging subsystem of netfilter.
  
The first release is available in Linux 3.13, which is currently in the [testing] repository ({{Pkg|linux}}), and nftables (the user-space components) is available in the [community-testing] repository ({{Pkg|nftables}}), and on the [[AUR]] in package {{AUR|nftables-git}}.
+
It consists of three main components: a kernel implementation, the libnl netlink communication and the nftables user-space front-end.
 +
The kernel provides a netlink configuration interface, as well as run-time rule-set evaluation, libnl contains the low-level functions for communicating with the kernel, and the nftables front-end is what the user interacts with via nft.
  
{{Expansion|nftables is an entirely new utility, and lacks sufficient documentation on this wiki, as well as elsewhere.}}
+
You can also visit the [https://wiki.nftables.org/wiki-nftables/index.php/Main_Page official nftables wiki page] for more information.
  
==Overview==
+
== Installation ==
nftables consists of three main components: a kernel implementation, the libnl netlink communication and the nftables user-space front-end. The kernel provides a netlink configuration interface, as well as run-time rule-set evaluation using a small classification language interpreter. libnl contains the low-level functions for communicating with the kernel; the nftables front-end is what the user interacts with.
 
  
==nft==
+
{{Expansion|Mention {{Pkg|iptables-nft}}.[https://www.redhat.com/en/blog/using-iptables-nft-hybrid-linux-firewall]}}
nftables' user-space utility '''nft''' now performs most of the rule-set evaluation before handing rule-sets to the kernel. Because of this, nftables provides no default tables or chains; although, a user can emulate an iptables-like setup.
 
  
It works in a fashion similar to ifconfig or iproute2. The commands are a long, structured sequence rather than using argument switches like in iptables. For example:
+
[[Install]] the userspace utilities package {{Pkg|nftables}} or the git version {{AUR|nftables-git}}.
nft add rule ip6 filter input ip saddr ::1 accept
+
 
'''add''' is the command. '''rule''' is a subcommand of '''add'''. '''ip6''' is an argument of '''rule''', telling it to use the ip6 family. '''filter''' and '''input''' are arguments of '''rule''' specifying the table and chain to use, respectively. The rest that follows is a rule definition, which includes matches ('''ip'''), their parameters ('''saddr'''), parameter arguments ('''::1'''), and jumps ('''accept''').
+
{{Tip|Most [[iptables#Front-ends|iptables front-ends]] feature no direct or indirect support of nftables, but may introduce it.[https://www.spinics.net/lists/netfilter/msg58215.html] One graphical front-end that supports both, nftables and iptables, is [[firewalld]].[https://firewalld.org/2018/07/nftables-backend]}}
  
The following is an incomplete list of the commands available in nft:
+
== Usage ==
<nowiki>
 
list
 
  tables [family]
 
  table [family] <name>
 
  chain [family] <table> <name>
 
  
add
+
''nftables'' makes a distinction between temporary rules made in the commandline and permanent ones loaded from or saved to a file.
  table [family] <name>
+
The default file is {{ic|/etc/nftables.conf}} which already contains a simple ipv4/ipv6 firewall table named "inet filter".
  chain [family] <table> <name> [chain definitions]
 
  rule [family] <table> <chain> <rule definition>
 
  
table [family] <name> (shortcut for `add table`)
+
To use it [[start/enable]] the {{ic|nftables.service}}.
  
insert
+
You can check the ruleset with
  rule [family] <table> <chain> <rule definition>
 
  
delete
+
# nft list ruleset
  table [family] <name>
 
  chain [family] <table> <name>
 
  rule [family] <table> <handle>
 
  
flush
+
{{Note|You may have to create {{ic|/etc/modules-load.d/nftables.conf}} with all of the nftables related modules you require as entries for the systemd service to work correctly. You can get a list of modules using this command: {{bc|$ grep -Eo '^nf\w+' /proc/modules}}
  table [family] <name>
 
  chain [family] <table> <name></nowiki>
 
'''family''' is optional, but it will default to '''ip'''.
 
  
==Tables==
+
Otherwise, you could end up with the dreaded {{ic|Error: Could not process rule: No such file or directory}} error.}}
The purpose of tables is to hold chains. Unlike tables in iptables, there are no built-in tables in nftables. Tables can have one of four families specified, which unifies the various iptables utilities into one:
+
 
*ip     (iptables)
+
== Configuration ==
*ip6   (ip6tables)
+
 
*arp   (arptables)
+
nftables' user-space utility ''nft'' performs most of the rule-set evaluation before handing rule-sets to the kernel. Rules are stored in chains, which in turn are stored in tables. The following sections indicate how to create and modify these constructs.
*bridge (ebtables)
+
 
'''ip''' is the default family.
+
All changes below are temporary. To make changes permanent, save your ruleset to {{ic|/etc/nftables.conf}} which is loaded by {{ic|nftables.service}}:
A fifth family is scheduled for Linux 3.15 that allows for the unification of the ip and ip6 families to make defining rules for both easier.
+
# nft -s list ruleset > /etc/nftables.conf
 +
 
 +
{{Note|{{ic|nft list}} does not output variable definitions, if you have any in {{ic|/etc/nftables.conf}} they will be lost. Any variables used in rules will be replaced by their value.}}
 +
 
 +
To read input from a file use the {{ic|-f}}/{{ic|--file}} option:
 +
 
 +
# nft -f ''filename''
 +
 
 +
Note that any rules already loaded are not automatically flushed.
 +
 
 +
See {{man|8|nft}} for a complete list of all commands.
 +
 
 +
=== Tables ===
 +
 
 +
Tables hold [[#Chains]]. Unlike tables in iptables, there are no built-in tables in nftables. The number of tables and their names is up to the user. However, each table only has one address family and only applies to packets of this family. Tables can have one of five families specified:
 +
 
 +
{| class="wikitable"
 +
! nftables family || iptables utility
 +
|-
 +
| ip || iptables
 +
|-
 +
| ip6 || ip6tables
 +
|-
 +
| inet || iptables and ip6tables
 +
|-
 +
| arp || arptables
 +
|-
 +
| bridge || ebtables
 +
|-
 +
|}
 +
 
 +
{{ic|ip}} (i.e. IPv4) is the default family and will be used if family is not specified.
 +
 
 +
To create one rule that applies to both IPv4 and IPv6, use {{ic|inet}}. {{ic|inet}} allows for the unification of the {{ic|ip}} and {{ic|ip6}} families to make defining rules for both easier.
 +
 
 +
See the section {{ic|ADDRESS FAMILIES}} in {{man|8|nft}} for a complete description of address families.
 +
 
 +
In all of the following, {{ic|''family''}} is optional, and if not specified is set to {{ic|ip}}.
 +
 
 +
==== Create table ====
 +
 
 +
The following adds a new table:
 +
 
 +
# nft add table ''family'' ''table''
 +
 
 +
==== List tables ====
 +
 
 +
To list all tables:
  
===Listing===
 
You can list the current tables in a family with the {{ic|nft list}} command.
 
 
  # nft list tables
 
  # nft list tables
# nft list tables ip6
 
  
You can list a full table definition by specifying a table name:
+
==== List chains and rules in a table ====
  # nft list table foo
+
 
  # nft list table ip6 foo
+
To list all chains and rules of a specified table do:
 +
 
 +
  # nft list table ''family'' ''table''
 +
 
 +
For example, to list all the rules of the {{ic|filter}} table of the {{ic|inet}} family:
 +
 
 +
  # nft list table inet filter
 +
 
 +
==== Delete table ====
 +
 
 +
To delete a table do:
  
===Creation===
+
  # nft delete table ''family'' ''table''
Tables can be added via two commands &mdash; one just being a shortcut for the other. Here is an example of how to add an ip table called foo and an ip6 table called foo:
 
  # nft add table foo
 
# nft table ip6 foo
 
You can have two tables with the same name as long as they are in different families.
 
  
===Deletion===
 
 
Tables can only be deleted if there are no chains in them.
 
Tables can only be deleted if there are no chains in them.
# nft delete table foo
 
# nft delete table ip6 foo
 
  
==Chains==
+
==== Flush table ====
The purpose of chains is to hold rules. Unlike chains in iptables, there are no built-in chains in nftables. This means that if no chain uses any types or hooks in the netfilter framework, packets that would flow through those chains will not be touched by nftables, unlike iptables.
+
 
 +
To flush all rules from a table do:
 +
 
 +
# nft flush table ''family'' ''table''
 +
 
 +
=== Chains ===
 +
 
 +
The purpose of chains is to hold [[#Rules]]. Unlike chains in iptables, there are no built-in chains in nftables. This means that if no chain uses any types or hooks in the netfilter framework, packets that would flow through those chains will not be touched by nftables, unlike iptables.
 +
 
 +
Chains have two types. A ''base'' chain is an entry point for packets from the networking stack, where a hook value is specified. A ''regular'' chain may be used as a jump target for better organization.
 +
 
 +
In all of the following {{ic|''family''}} is optional, and if not specified is set to {{ic|ip}}.
 +
 
 +
==== Create chain ====
  
===Listing===
+
===== Regular chain =====
You can list the current chains in a chain with the {{ic|nft list}} command, using the same method as listing a table. You can also list rules from an individual chain.
 
# nft list chain foo bar
 
# nft list chain ip6 foo bar
 
These commands will list the '''bar''' chains in the ip and ip6 '''foo''' tables.
 
  
===Creation===
+
The following adds a regular chain named {{ic|''chain''}} to the table named {{ic|''table''}}:
Chains can be added when a table is created in a file definition or one at time via the {{ic|nfc add chain}} command.
 
# nft add chain foo bar
 
# nft add chain ip6 foo bar
 
These commands will add a chain called '''bar''' to the ip and ip6 '''foo''' tables.
 
  
====Properties====
+
  # nft add chain ''family'' ''table'' ''chain''
Because nftables has no built-in chains, it allows chains to access certain features of the netfilter framework.
 
  # nft add chain filter input { type filter hook input priority 0; }
 
This command tells nftables to add a chain called '''input''' to the '''filter''' table and defines its type, hook, and priority. These properties essentially replace the built-in tables and chains in iptables.
 
  
=====Types=====
+
For example, to add a regular chain named {{ic|tcpchain}} to the {{ic|filter}} table of the {{ic|inet}} address family do:
There are three types a chain can have and they correspond to the tables used in iptables:
 
*filter
 
*nat
 
*route  (mangle)
 
  
=====Hooks=====
+
# nft add chain inet filter tcpchain
There are five hooks a chain can use and they correspond to the chains used in iptables:
 
*input
 
*output
 
*forward
 
*prerouting
 
*postrouting
 
  
=====Priorities=====
+
===== Base chain =====
Priorities tell nftables which chains packets should pass through first. They are integers, with the highest priority being 0 and lower ones being negative numbers.
 
  
===Deletion===
+
To add a base chain specify hook and priority values:
Chains can only be deleted if there are no rules in them.
 
# nft delete chain foo bar
 
# nft delete chain ip6 foo bar
 
These commands delete the '''bar''' chains from the ip and ip6 '''foo''' tables.
 
  
==Rules==
+
# nft add chain ''family'' ''table'' ''chain'' '{ type ''type'' hook ''hook'' priority ''priority'' ; }'
The purpose of rules is to identify packets (match) and carry out tasks (jump). Like in iptables, there are various matches and jumps available, though not all of them are feature-complete in nftables.
 
  
===Listing===
+
{{ic|''type''}} can be {{ic|filter}}, {{ic|route}}, or {{ic|nat}}.
You can list the current rules in a table with the {{ic|nft list}} command, using the same method as listing a table. You can also list rules from an individual chain.
 
# nft list chain foo bar
 
# nft list chain ip6 foo bar
 
These commands will list the rules in the '''bar''' chains in the ip and ip6 '''foo''' tables.
 
  
===Creation===
+
For IPv4/IPv6/Inet address families {{ic|''hook''}} can be {{ic|prerouting}}, {{ic|input}}, {{ic|forward}}, {{ic|output}}, or {{ic|postrouting}}. See {{man|8|nft}} for a list of hooks for other families.
Rules can be added when a table is created in a file definition or one at time via the {{ic|nfc add rule}} command.
 
# nft add rule foo bar ip saddr 127.0.0.1 accept
 
# nft add rule ip6 foo bar ip saddr ::1 accept
 
These commands will add a rule to the '''bar''' chains in the ip and ip6 '''foo''' tables that matches an '''ip''' packet when its '''saddr''' (source address) is 127.0.0.1 (IPv4) or ::1 (IPv6) and accepts those packets.
 
  
====Matches====
+
{{ic|''priority''}} takes an integer value. Chains with lower numbers are processed first and can be negative. [https://wiki.nftables.org/wiki-nftables/index.php/Configuring_chains#Base_chain_types]
There are various matches available in nftables and, for the most part, coincide with their iptables counterparts. The most noticeable difference is that there are no generic or implicit matches anymore. A generic match was one that was always available, such as {{ic|--protocol}} or {{ic|--source}}. Implicit matches were protocol-specific, such as {{ic|--sport}} when a packet was determined to be TCP.
+
 
 +
For example, to add a base chain that filters input packets:
 +
 
 +
# nft add chain inet filter input '{ type filter hook input priority 0; }'
 +
 
 +
Replace {{ic|add}} with {{ic|create}} in any of the above to add a new chain but return an error if the chain already exists.
 +
 
 +
==== List rules ====
 +
 
 +
The following lists all rules of a chain:
 +
 
 +
# nft list chain ''family'' ''table'' ''chain''
 +
 
 +
For example, the following lists the rules of the chain named {{ic|output}} in the {{ic|inet}} table named {{ic|filter}}:
 +
 
 +
# nft list chain inet filter output
 +
 
 +
==== Edit a chain ====
 +
 
 +
To edit a chain, simply call it by its name and define the rules you want to change.
 +
 
 +
# nft chain ''family table chain'' '{ [ type ''type'' hook ''hook'' device ''device'' priority ''priority'' ; policy ''policy'' ; ] }'
 +
 
 +
For example, to change the input chain policy of the default table from {{ic|accept}} to {{ic|drop}}
 +
 
 +
# nft chain inet filter input '{ policy drop ; }'
 +
 
 +
==== Delete a chain ====
 +
 
 +
To delete a chain do:
 +
 
 +
# nft delete chain ''family'' ''table'' ''chain''
 +
 
 +
The chain must not contain any rules or be a jump target.
 +
 
 +
==== Flush rules from a chain ====
 +
 
 +
To flush rules from a chain do:
 +
 
 +
# nft flush chain ''family'' ''table'' ''chain''
 +
 
 +
=== Rules ===
 +
 
 +
Rules are either constructed from expressions or statements and are contained within chains.
 +
 
 +
==== Add rule ====
 +
 
 +
{{Tip|The ''iptables-translate'' utility translates [[iptables]] rules to nftables format.}}
 +
 
 +
To add a rule to a chain do:
 +
 
 +
# nft add rule ''family'' ''table'' ''chain'' handle ''handle'' ''statement''
 +
 
 +
The rule is appended at {{ic|''handle''}}, which is optional. If not specified, the rule is appended to the end of the chain.
 +
 
 +
To prepend the rule to the position do:
 +
 
 +
# nft insert rule ''family'' ''table'' ''chain'' handle ''handle'' ''statement''
 +
 
 +
If {{ic|''handle''}} is not specified, the rule is prepended to the chain.
 +
 
 +
===== Expressions =====
 +
 
 +
Typically a {{ic|''statement''}} includes some expression to be matched and then a verdict statement. Verdict statements include {{ic|accept}}, {{ic|drop}}, {{ic|queue}}, {{ic|continue}}, {{ic|return}}, {{ic|jump ''chain''}}, and {{ic|goto ''chain''}}. Other statements than verdict statements are possible. See {{man|8|nft}} for more information.
 +
 
 +
There are various expressions available in nftables and, for the most part, coincide with their iptables counterparts. The most noticeable difference is that there are no generic or implicit matches. A generic match was one that was always available, such as {{ic|--protocol}} or {{ic|--source}}. Implicit matches were protocol-specific, such as {{ic|--sport}} when a packet was determined to be TCP.
  
 
The following is an incomplete list of the matches available:
 
The following is an incomplete list of the matches available:
*meta    (meta properties, e.g. interfaces)
 
*icmp  (ICMP protocol)
 
*icmpv6 (ICMPv6 protocol)
 
*ip*    (IP protocol)
 
*ip6*    (IPv6 protocol)
 
*tcp*    (TCP protocol)
 
*udp*    (UDP protocol)
 
*sctp*  (SCTP protocol)
 
*ct      (connection tracking)
 
The ones marked with an asterisk (*) can be used as a standalone match, similar to using {{ic|--protocol}}. The others require arguments.
 
  
The following is an incomplete list of match arguments:
+
* meta    (meta properties, e.g. interfaces)
<nowiki>
+
* icmp    (ICMP protocol)
 +
* icmpv6  (ICMPv6 protocol)
 +
* ip      (IP protocol)
 +
* ip6    (IPv6 protocol)
 +
* tcp    (TCP protocol)
 +
* udp    (UDP protocol)
 +
* sctp    (SCTP protocol)
 +
* ct      (connection tracking)
 +
 
 +
The following is an incomplete list of match arguments (for a more complete list, see {{man|8|nft}}):
 +
 
 +
{{bc|<nowiki>
 
meta:
 
meta:
 
   oif <output interface INDEX>
 
   oif <output interface INDEX>
Line 189: Line 268:
  
 
ct:
 
ct:
   state <new | established | related | invalid></nowiki>
+
   state <new | established | related | invalid>
 +
</nowiki>}}
  
====Jumps====
+
==== Deletion ====
Jumps work the same as they do in iptables, except multiple jumps can now be used in one rule.
 
# nft add rule filter input tcp dport 22 log accept
 
  
The following is an incomplete list of jumps:
 
*accept  (accept a packet)
 
*reject  (reject a packet)
 
*drop    (drop a packet)
 
*snat    (perform source NAT on a packet)
 
*dnat    (perform destination NAT on a packet)
 
*log    (log a packet)
 
*counter (keep a counter on a packet; counters are optional in nftables)
 
 
===Insertion===
 
Rules can be prepended to chains with the {{ic|nft insert rule}} command.
 
# nft insert rule filter input ct state established,related accept
 
 
===Deletion===
 
 
Individual rules can only be deleted by their handles. The {{ic|nft --handle list}} command must be used to determine rule handles. Note the {{ic|--handle}} switch, which tells {{ic|nft}} to list handles in its output.
 
Individual rules can only be deleted by their handles. The {{ic|nft --handle list}} command must be used to determine rule handles. Note the {{ic|--handle}} switch, which tells {{ic|nft}} to list handles in its output.
  
 
The following determines the handle for a rule and then deletes it. The {{ic|--number}} argument is useful for viewing some numeric output, like unresolved IP addresses.
 
The following determines the handle for a rule and then deletes it. The {{ic|--number}} argument is useful for viewing some numeric output, like unresolved IP addresses.
{{hc|# sudo nft --handle --numeric list chain filter input|2=
+
 
<nowiki>
+
{{hc|# nft --handle --numeric list chain inet filter input|2=<nowiki>
table ip filter {
+
table inet fltrTable {
 
     chain input {
 
     chain input {
 
           type filter hook input priority 0;
 
           type filter hook input priority 0;
Line 220: Line 284:
 
     }
 
     }
 
}
 
}
</nowiki>
+
</nowiki>}}
}}
+
 
  # nft delete rule filter input handle 10
+
  # nft delete rule inet fltrTable input handle 10
  
 
All the chains in a table can be flushed with the {{ic|nft flush table}} command. Individual chains can be flushed using either the {{ic|nft flush chain}} or {{ic|nft delete rule}} commands.
 
All the chains in a table can be flushed with the {{ic|nft flush table}} command. Individual chains can be flushed using either the {{ic|nft flush chain}} or {{ic|nft delete rule}} commands.
 +
 
  # nft flush table foo
 
  # nft flush table foo
 
  # nft flush chain foo bar
 
  # nft flush chain foo bar
 
  # nft delete rule ip6 foo bar
 
  # nft delete rule ip6 foo bar
The first command flushes all of the chains in the ip '''foo''' table. The second flushes the '''bar''' chain in the ip '''foo''' table. The third deletes all of the rules in '''bar''' chain in the ip6 '''foo''' table.
 
  
==File Definitions==
+
The first command flushes all of the chains in the ip {{ic|foo}} table. The second flushes the {{ic|bar}} chain in the ip {{ic|foo}} table. The third deletes all of the rules in {{ic|bar}} chain in the ip6 {{ic|foo}} table.
File definitions can be used by the {{ic|nft -f}} command, which acts like the {{ic|iptables-restore}} command.
+
 
{{hc|/etc/nftables/filter.rules|2=
+
=== Atomic reloading ===
<nowiki>
+
 
table ip filter {
+
Flush the current ruleset:
    chain input {
+
 
          type filter hook input priority 0;
+
# echo "flush ruleset" > /tmp/nftables
          ct state established,related accept
+
 
          ip saddr 127.0.0.1 accept
+
Dump the current ruleset:
          tcp dport 22 log accept
+
 
          reject
+
# nft -s list ruleset >> /tmp/nftables
    }
+
 
 +
Now you can edit /tmp/nftables and apply your changes with:
 +
 
 +
# nft -f /tmp/nftables
 +
 
 +
== Examples ==
 +
 
 +
=== Workstation ===
 +
 
 +
{{hc|/etc/nftables.conf|2=<nowiki>
 +
flush ruleset
 +
 
 +
table inet filter {
 +
chain input {
 +
type filter hook input priority 0; policy drop;
 +
 
 +
iif lo accept comment "Accept any localhost traffic"
 +
ct state invalid drop comment "Drop invalid connections"
 +
ct state established,related accept comment "Accept traffic originated from us"
 +
 
 +
ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept comment "Accept ICMPv6"
 +
ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept comment "Accept ICMP"
 +
ip protocol igmp accept comment "Accept IGMP"
 +
 
 +
udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
 +
udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"
 +
 
 +
udp sport 1900 udp dport >= 1024 ip6 saddr { fd00::/8, fe80::/10 } meta pkttype unicast limit rate 4/second burst 20 packets accept comment "Accept UPnP IGD port mapping reply"
 +
udp sport 1900 udp dport >= 1024 ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } meta pkttype unicast limit rate 4/second burst 20 packets accept comment "Accept UPnP IGD port mapping reply"
 +
 
 +
udp sport netbios-ns udp dport >= 1024 meta pkttype unicast ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept Samba Workgroup browsing replies"
 +
udp sport netbios-ns udp dport >= 1024 meta pkttype unicast ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept Samba Workgroup browsing replies"
 +
 
 +
counter comment "Count any other traffic"
 +
}
 +
 
 +
chain forward {
 +
type filter hook forward priority 0; policy drop;
 +
}
 +
 
 +
chain output {
 +
type filter hook output priority 0; policy accept;
 +
}
 +
 
 +
}
 +
</nowiki>}}
 +
 
 +
=== Server ===
 +
 
 +
{{hc|/etc/nftables.conf|2=<nowiki>
 +
flush ruleset
 +
 
 +
table inet filter {
 +
chain input {
 +
type filter hook input priority 0; policy drop;
 +
 
 +
iif lo accept comment "Accept any localhost traffic"
 +
ct state invalid drop comment "Drop invalid connections"
 +
ct state established,related accept comment "Accept traffic originated from us"
 +
 
 +
ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept comment "Accept ICMPv6"
 +
ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept comment "Accept ICMP"
 +
ip protocol igmp accept comment "Accept IGMP"
 +
 
 +
udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
 +
udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"
 +
 
 +
tcp dport ssh accept comment "Accept SSH on port 22"
 +
 
 +
tcp dport { http, https, 8008, 8080 } accept comment "Accept HTTP (ports 80, 443, 8008, 8080)"
 +
 
 +
meta l4proto { tcp, udp } th dport 2049 ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept NFS"
 +
meta l4proto { tcp, udp } th dport 2049 ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept NFS"
 +
 
 +
udp dport netbios-ns ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept NetBIOS Name Service (nmbd)"
 +
udp dport netbios-ns ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept NetBIOS Name Service (nmbd)"
 +
udp dport netbios-dgm ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept NetBIOS Datagram Service (nmbd)"
 +
udp dport netbios-dgm ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept NetBIOS Datagram Service (nmbd)"
 +
tcp dport netbios-ssn ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept NetBIOS Session Service (smbd)"
 +
tcp dport netbios-ssn ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept NetBIOS Session Service (smbd)"
 +
tcp dport microsoft-ds ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept Microsoft Directory Service (smbd)"
 +
tcp dport microsoft-ds ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept Microsoft Directory Service (smbd)"
 +
 
 +
udp sport bootpc udp dport bootps ip saddr 0.0.0.0 ip daddr 255.255.255.255 accept comment "Accept DHCPDISCOVER (for DHCP-Proxy)"
 +
udp sport { bootpc, 4011 } udp dport { bootps, 4011 } ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept PXE"
 +
udp dport tftp ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept TFTP"
 +
udp dport tftp ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept TFTP"
 +
 
 +
}
 +
 
 +
chain forward {
 +
type filter hook forward priority 0; policy drop;
 +
}
 +
 
 +
chain output {
 +
type filter hook output priority 0; policy accept;
 +
}
 +
 
 
}
 
}
</nowiki>
+
</nowiki>}}
}}
+
 
 +
=== Limit rate ===
 +
 
 +
{{bc|1=<nowiki>
 +
table inet filter {
 +
chain input {
 +
type filter hook input priority 0; policy drop;
 +
 
 +
iif lo accept comment "Accept any localhost traffic"
 +
ct state invalid drop comment "Drop invalid connections"
  
==Getting Started==
+
ip protocol icmp icmp type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"
To get an [[iptables]]-like chain set up, you will first need to use the provided IPv4 filter file:
+
ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"
  
# nft -f /etc/nftables/ipv4-filter
+
ct state established,related accept comment "Accept traffic originated from us"
  
To list the resulting chain:
+
ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept comment "Accept ICMPv6"
 +
ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept comment "Accept ICMP"
 +
ip protocol igmp accept comment "Accept IGMP"
  
# nft list table filter
+
tcp dport ssh ct state new limit rate 15/minute accept comment "Avoid brute force on SSH"
  
Drop output to a destination:
+
}
  
# nft add rule ip filter output ip daddr 1.2.3.4 drop
+
}
 +
</nowiki>}}
  
Drop packets destined for local port 80:
+
=== Jump ===
  
# nft add rule ip filter input tcp dport 80 drop
+
When using jumps in config file, it is necessary to define the target chain first. Otherwise one could end up with {{ic|Error: Could not process rule: No such file or directory}}.
  
Delete all rules in a chain:
+
{{bc|1=<nowiki>
 +
table inet filter {
 +
    chain web {
 +
        tcp dport http accept
 +
        tcp dport 8080 accept
 +
    }
 +
    chain input {
 +
        type filter hook input priority 0;
 +
        ip saddr 10.0.2.0/24 jump web
 +
        drop
 +
    }
 +
}
 +
</nowiki>}}
  
# nft delete rule filter output
+
=== Different rules for different interfaces ===
  
==Samples==
+
If your box has more than one network interface, and you would like to use different rules for different interfaces, you may want to use a "dispatching" filter chain, and then interface-specific filter chains. For example, let us assume your box acts as a home router, you want to run a web server accessible over the LAN (interface {{ic|enp3s0}}), but not from the public internet (interface {{ic|enp2s0}}), you may want to consider a structure like this:
A simple IP/IPv6 firewall:
 
{{hc|firewall.rules|2=
 
<nowiki>
 
# A simple firewall
 
  
table firewall {
+
{{bc|<nowiki>
   chain incoming {
+
table inet filter {
 +
   chain input { # this chain serves as a dispatcher
 
     type filter hook input priority 0;
 
     type filter hook input priority 0;
  
     # established/related connections
+
     iif lo accept # always accept loopback
     ct state {established, related} accept
+
     iifname enp2s0 jump input_enp2s0
 +
    iifname enp3s0 jump input_enp3s0
  
     # invalid connections
+
     reject with icmp type port-unreachable # refuse traffic from all other interfaces
 +
  }
 +
  chain input_enp2s0 { # rules applicable to public interface interface
 +
    ct state {established,related} accept
 +
    ct state invalid drop
 +
    udp dport bootpc accept
 +
    tcp dport bootpc accept
 +
    reject with icmp type port-unreachable # all other traffic
 +
  }
 +
  chain input_enp3s0 {
 +
    ct state {established,related} accept
 
     ct state invalid drop
 
     ct state invalid drop
 +
    udp dport bootpc accept
 +
    tcp dport bootpc accept
 +
    tcp port http accept
 +
    tcp port https accept
 +
    reject with icmp type port-unreachable # all other traffic
 +
  }
 +
  chain ouput { # we let everything out
 +
    type filter hook output priority 0;
 +
    accept
 +
  }
 +
}
 +
</nowiki>}}
 +
 +
Alternatively you could choose only one {{ic|iifname}} statement, such as for the single upstream interface, and put the default rules for all other interfaces in one place, instead of dispatching for each interface.
 +
 +
=== Masquerading ===
 +
 +
nftables has a special keyword {{ic|masquerade}} "where the source address is automagically set to the address of the output interface" ([http://wiki.nftables.org/wiki-nftables/index.php/Performing_Network_Address_Translation_%28NAT%29#Masquerading source]). This is particularly useful for situations in which the IP address of the interface is unpredictable or unstable, such as the upstream interface of routers connecting to many ISPs. Without it, the Network Address Translation rules would have to be updated every time the IP address of the interface changed.
  
    # loopback interface
+
To use it:
    iifname lo accept
 
  
    # icmp
+
* make sure masquerading is enabled in the kernel (true if you use the default kernel), otherwise during kernel configuration, set {{ic|1=CONFIG_NFT_MASQ=m}}.
    ip protocol icmp accept
+
* the {{ic|masquerade}} keyword can only be used in chains of type {{ic|nat}}.
 +
* masquerading is a kind of source NAT, so only works in the output path.
  
    # open tcp ports: sshd (22), httpd (80)
+
Example for a machine with two interfaces: LAN connected to {{ic|enp3s0}}, and public internet connected to {{ic|enp2s0}}:
    tcp dport {ssh, http} accept
 
  
     # everything else
+
{{bc|<nowiki>
     reject
+
table inet nat {
 +
  chain prerouting {
 +
    type nat hook prerouting priority 0;
 +
  }
 +
  chain postrouting {
 +
     type nat hook postrouting priority 100;
 +
     oifname "enp2s0" masquerade
 
   }
 
   }
 
}
 
}
 +
</nowiki>}}
  
table ip6 firewall {
+
Since the table type is {{ic|inet}} both IPv4 and IPv6 packets will be masqueraded. If you want only ipv4 packets to be masqueraded (since extra adress space of IPv6 makes NAT not required) {{ic|meta nfproto ipv4}} expression can be used infront of {{ic|oifname "enp2s0" masquerade}} or the table type can be changed to {{ic|ip}}.
   chain incoming {
+
 
     type filter hook input priority 0;
+
=== NAT with port forwarding ===
 +
 
 +
This example will forward ports 22 and 80 to {{ic|''destination_ip''}}. You will need to set {{ic|net.ipv4.ip_forward}} and {{ic|net.ipv4.conf.''wan_interface''.forwarding}} to {{ic|1}} via [[sysctl]].
 +
 
 +
{{bc|
 +
table ip nat {
 +
  chain prerouting {
 +
    type nat hook prerouting priority 0;
 +
 
 +
    tcp dport { ssh, http } dnat to ''destination_ip''
 +
  }
 +
 
 +
   chain postrouting {
 +
     type nat hook postrouting priority 100;
 +
 
 +
    ip daddr ''destination_ip'' masquerade
 +
  }
 +
}
 +
</nowiki>}}
 +
 
 +
== Tips and tricks ==
 +
 
 +
=== Simple stateful firewall ===
 +
 
 +
See [[Simple stateful firewall]] for more information.
 +
 
 +
==== Single machine ====
 +
 
 +
Flush the current ruleset:
 +
 
 +
# nft flush ruleset
 +
 
 +
Add a table:
 +
 
 +
# nft add table inet filter
 +
 
 +
Add the input, forward, and output base chains. The policy for input and forward will be to drop. The policy for output will be to accept.
 +
 
 +
# nft add chain inet filter input '{ type filter hook input priority 0 ; policy drop ; }'
 +
# nft add chain inet filter forward '{ type filter hook forward priority 0 ; policy drop ; }'
 +
# nft add chain inet filter output '{ type filter hook output priority 0 ; policy accept ; }'
 +
 
 +
Add two regular chains that will be associated with tcp and udp:
 +
 
 +
# nft add chain inet filter TCP
 +
# nft add chain inet filter UDP
 +
 
 +
Related and established traffic will be accepted:
 +
 
 +
# nft add rule inet filter input ct state related,established accept
 +
 
 +
All loopback interface traffic will be accepted:
 +
 
 +
# nft add rule inet filter input iif lo accept
 +
 
 +
Drop any invalid traffic:
 +
 
 +
# nft add rule inet filter input ct state invalid drop
 +
 
 +
Accept ICMP and IGMP:
 +
 
 +
# nft add rule inet filter input ip6 nexthdr icmpv6 icmpv6 type '{ destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report }' accept
 +
# nft add rule inet filter input ip protocol icmp icmp type '{ destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem }' accept
 +
# nft add rule inet filter input ip protocol igmp accept
 +
 
 +
New udp traffic will jump to the UDP chain:
 +
 
 +
# nft add rule inet filter input ip protocol udp ct state new jump UDP
 +
 
 +
New tcp traffic will jump to the TCP chain:
 +
 
 +
# nft add rule inet filter input 'ip protocol tcp tcp flags & (fin|syn|rst|ack) == syn ct state new jump TCP'
 +
 
 +
Reject all traffic that was not processed by other rules:
 +
 
 +
# nft add rule inet filter input ip protocol udp reject
 +
# nft add rule inet filter input ip protocol tcp reject with tcp reset
 +
# nft add rule inet filter input counter reject with icmp type prot-unreachable
 +
 
 +
At this point you should decide what ports you want to open to incoming connections, which are handled by the TCP and UDP chains. For example to open connections for a web server add:
 +
 
 +
# nft add rule inet filter TCP tcp dport 80 accept
 +
 
 +
To accept HTTPS connections for a webserver on port 443:
 +
 
 +
# nft add rule inet filter TCP tcp dport 443 accept
 +
 
 +
To accept SSH traffic on port 22:
 +
 
 +
# nft add rule inet filter TCP tcp dport 22 accept
 +
 
 +
To accept incoming DNS requests:
 +
 
 +
# nft add rule inet filter TCP tcp dport 53 accept
 +
# nft add rule inet filter UDP udp dport 53 accept
 +
 
 +
Be sure to make your changes permanent when satisifed.
 +
 
 +
=== Prevent brute-force attacks ===
  
    # established/related connections
+
[[Sshguard]] is program that can detect brute-force attacks and modify firewalls based on IP addresses it temporarily blacklists. See [[Sshguard#nftables]] on how to set up nftables to be used with it.
    ct state {established,related} accept
 
  
    # invalid connections
+
== Troubleshooting ==
    ct state invalid drop
 
  
    # loopback interface
+
=== Working with Docker ===
    iifname lo accept
 
  
    # open tcp ports: sshd (22), httpd (80)
+
Using nftables can interfere with [[Docker]] networking (and probably other container runtimes as well). In particular the drop policy for the {{ic|forward}} chain will block packets originating in docker containers. If you want to keep the {{ic|forward}} rule in your {{ic|inet}} table, you can use the following:
    tcp dport {22,80} accept
 
  
    # everything else
+
# [[Install]] {{Pkg|iptables-nft}} to provide an iptables compatible interface for nftables that docker can use.
     reject
+
# Use the following for the {{ic|forward}} chain in your {{ic|inet}} table:
 +
#:{{bc|<nowiki>
 +
chain forward {
 +
  type filter hook forward priority security; policy drop
 +
  mark 1 accept
 +
</nowiki>}}
 +
# Add a rule to the DOCKER-USER chain in the {{ic|ip filter}} table to mark packets if docker is running:
 +
#:{{bc|<nowiki>
 +
table ip filter {
 +
  chain DOCKER-USER {
 +
     mark set 1
 
   }
 
   }
 
}
 
}
</nowiki>
+
</nowiki>}}
}}
+
 
 +
This works by marking packets if docker is active, and accepting the packets in this case, since docker has already filtered them (the forward chain defined by docker uses a drop policy).
 +
 
 +
== See also ==
  
==See also==
+
* [https://wiki.nftables.org/ netfilter nftables wiki]
* [http://people.netfilter.org/wiki-nftables/index.php/ netfilter nftables wiki]
+
* [[debian:nftables]]
 +
* [[gentoo:nftables]]
 
* [https://lwn.net/Articles/324251/ First release of nftables]
 
* [https://lwn.net/Articles/324251/ First release of nftables]
 
* [https://home.regit.org/netfilter-en/nftables-quick-howto/ nftables quick howto]
 
* [https://home.regit.org/netfilter-en/nftables-quick-howto/ nftables quick howto]
 
* [https://lwn.net/Articles/564095/ The return of nftables]
 
* [https://lwn.net/Articles/564095/ The return of nftables]
 +
* [http://developers.redhat.com/blog/2016/10/28/what-comes-after-iptables-its-successor-of-course-nftables/ What comes after ‘iptables’? Its successor, of course: `nftables`]

Latest revision as of 16:44, 5 December 2019

nftables is a netfilter project that aims to replace the existing {ip,ip6,arp,eb}tables framework. It provides a new packet filtering framework, a new user-space utility (nft), and a compatibility layer for {ip,ip6}tables. It uses the existing hooks, connection tracking system, user-space queueing component, and logging subsystem of netfilter.

It consists of three main components: a kernel implementation, the libnl netlink communication and the nftables user-space front-end. The kernel provides a netlink configuration interface, as well as run-time rule-set evaluation, libnl contains the low-level functions for communicating with the kernel, and the nftables front-end is what the user interacts with via nft.

You can also visit the official nftables wiki page for more information.

Installation

Tango-view-fullscreen.pngThis article or section needs expansion.Tango-view-fullscreen.png

Reason: Mention iptables-nft.[1] (Discuss in Talk:Nftables#)

Install the userspace utilities package nftables or the git version nftables-gitAUR.

Tip: Most iptables front-ends feature no direct or indirect support of nftables, but may introduce it.[2] One graphical front-end that supports both, nftables and iptables, is firewalld.[3]

Usage

nftables makes a distinction between temporary rules made in the commandline and permanent ones loaded from or saved to a file. The default file is /etc/nftables.conf which already contains a simple ipv4/ipv6 firewall table named "inet filter".

To use it start/enable the nftables.service.

You can check the ruleset with

# nft list ruleset
Note: You may have to create /etc/modules-load.d/nftables.conf with all of the nftables related modules you require as entries for the systemd service to work correctly. You can get a list of modules using this command:
$ grep -Eo '^nf\w+' /proc/modules
Otherwise, you could end up with the dreaded Error: Could not process rule: No such file or directory error.

Configuration

nftables' user-space utility nft performs most of the rule-set evaluation before handing rule-sets to the kernel. Rules are stored in chains, which in turn are stored in tables. The following sections indicate how to create and modify these constructs.

All changes below are temporary. To make changes permanent, save your ruleset to /etc/nftables.conf which is loaded by nftables.service:

# nft -s list ruleset > /etc/nftables.conf
Note: nft list does not output variable definitions, if you have any in /etc/nftables.conf they will be lost. Any variables used in rules will be replaced by their value.

To read input from a file use the -f/--file option:

# nft -f filename

Note that any rules already loaded are not automatically flushed.

See nft(8) for a complete list of all commands.

Tables

Tables hold #Chains. Unlike tables in iptables, there are no built-in tables in nftables. The number of tables and their names is up to the user. However, each table only has one address family and only applies to packets of this family. Tables can have one of five families specified:

nftables family iptables utility
ip iptables
ip6 ip6tables
inet iptables and ip6tables
arp arptables
bridge ebtables

ip (i.e. IPv4) is the default family and will be used if family is not specified.

To create one rule that applies to both IPv4 and IPv6, use inet. inet allows for the unification of the ip and ip6 families to make defining rules for both easier.

See the section ADDRESS FAMILIES in nft(8) for a complete description of address families.

In all of the following, family is optional, and if not specified is set to ip.

Create table

The following adds a new table:

# nft add table family table

List tables

To list all tables:

# nft list tables

List chains and rules in a table

To list all chains and rules of a specified table do:

# nft list table family table

For example, to list all the rules of the filter table of the inet family:

# nft list table inet filter

Delete table

To delete a table do:

# nft delete table family table

Tables can only be deleted if there are no chains in them.

Flush table

To flush all rules from a table do:

# nft flush table family table

Chains

The purpose of chains is to hold #Rules. Unlike chains in iptables, there are no built-in chains in nftables. This means that if no chain uses any types or hooks in the netfilter framework, packets that would flow through those chains will not be touched by nftables, unlike iptables.

Chains have two types. A base chain is an entry point for packets from the networking stack, where a hook value is specified. A regular chain may be used as a jump target for better organization.

In all of the following family is optional, and if not specified is set to ip.

Create chain

Regular chain

The following adds a regular chain named chain to the table named table:

# nft add chain family table chain

For example, to add a regular chain named tcpchain to the filter table of the inet address family do:

# nft add chain inet filter tcpchain
Base chain

To add a base chain specify hook and priority values:

# nft add chain family table chain '{ type type hook hook priority priority ; }'

type can be filter, route, or nat.

For IPv4/IPv6/Inet address families hook can be prerouting, input, forward, output, or postrouting. See nft(8) for a list of hooks for other families.

priority takes an integer value. Chains with lower numbers are processed first and can be negative. [4]

For example, to add a base chain that filters input packets:

# nft add chain inet filter input '{ type filter hook input priority 0; }'

Replace add with create in any of the above to add a new chain but return an error if the chain already exists.

List rules

The following lists all rules of a chain:

# nft list chain family table chain

For example, the following lists the rules of the chain named output in the inet table named filter:

# nft list chain inet filter output

Edit a chain

To edit a chain, simply call it by its name and define the rules you want to change.

# nft chain family table chain '{ [ type type hook hook device device priority priority ; policy policy ; ] }'

For example, to change the input chain policy of the default table from accept to drop

# nft chain inet filter input '{ policy drop ; }'

Delete a chain

To delete a chain do:

# nft delete chain family table chain

The chain must not contain any rules or be a jump target.

Flush rules from a chain

To flush rules from a chain do:

# nft flush chain family table chain

Rules

Rules are either constructed from expressions or statements and are contained within chains.

Add rule

Tip: The iptables-translate utility translates iptables rules to nftables format.

To add a rule to a chain do:

# nft add rule family table chain handle handle statement

The rule is appended at handle, which is optional. If not specified, the rule is appended to the end of the chain.

To prepend the rule to the position do:

# nft insert rule family table chain handle handle statement

If handle is not specified, the rule is prepended to the chain.

Expressions

Typically a statement includes some expression to be matched and then a verdict statement. Verdict statements include accept, drop, queue, continue, return, jump chain, and goto chain. Other statements than verdict statements are possible. See nft(8) for more information.

There are various expressions available in nftables and, for the most part, coincide with their iptables counterparts. The most noticeable difference is that there are no generic or implicit matches. A generic match was one that was always available, such as --protocol or --source. Implicit matches were protocol-specific, such as --sport when a packet was determined to be TCP.

The following is an incomplete list of the matches available:

  • meta (meta properties, e.g. interfaces)
  • icmp (ICMP protocol)
  • icmpv6 (ICMPv6 protocol)
  • ip (IP protocol)
  • ip6 (IPv6 protocol)
  • tcp (TCP protocol)
  • udp (UDP protocol)
  • sctp (SCTP protocol)
  • ct (connection tracking)

The following is an incomplete list of match arguments (for a more complete list, see nft(8)):

meta:
  oif <output interface INDEX>
  iif <input interface INDEX>
  oifname <output interface NAME>
  iifname <input interface NAME>

  (oif and iif accept string arguments and are converted to interface indexes)
  (oifname and iifname are more dynamic, but slower because of string matching)

icmp:
  type <icmp type>

icmpv6:
  type <icmpv6 type>

ip:
  protocol <protocol>
  daddr <destination address>
  saddr <source address>

ip6:
  daddr <destination address>
  saddr <source address>

tcp:
  dport <destination port>
  sport <source port>

udp:
  dport <destination port>
  sport <source port>

sctp:
  dport <destination port>
  sport <source port>

ct:
  state <new | established | related | invalid>

Deletion

Individual rules can only be deleted by their handles. The nft --handle list command must be used to determine rule handles. Note the --handle switch, which tells nft to list handles in its output.

The following determines the handle for a rule and then deletes it. The --number argument is useful for viewing some numeric output, like unresolved IP addresses.

# nft --handle --numeric list chain inet filter input
table inet fltrTable {
     chain input {
          type filter hook input priority 0;
          ip saddr 127.0.0.1 accept # handle 10
     }
}
# nft delete rule inet fltrTable input handle 10

All the chains in a table can be flushed with the nft flush table command. Individual chains can be flushed using either the nft flush chain or nft delete rule commands.

# nft flush table foo
# nft flush chain foo bar
# nft delete rule ip6 foo bar

The first command flushes all of the chains in the ip foo table. The second flushes the bar chain in the ip foo table. The third deletes all of the rules in bar chain in the ip6 foo table.

Atomic reloading

Flush the current ruleset:

# echo "flush ruleset" > /tmp/nftables 

Dump the current ruleset:

# nft -s list ruleset >> /tmp/nftables

Now you can edit /tmp/nftables and apply your changes with:

# nft -f /tmp/nftables

Examples

Workstation

/etc/nftables.conf
flush ruleset

table inet filter {
	chain input {
		type filter hook input priority 0; policy drop;

		iif lo accept comment "Accept any localhost traffic"
		ct state invalid drop comment "Drop invalid connections"
		ct state established,related accept comment "Accept traffic originated from us"

		ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept comment "Accept ICMPv6"
		ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept comment "Accept ICMP"
		ip protocol igmp accept comment "Accept IGMP"

		udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
		udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"

		udp sport 1900 udp dport >= 1024 ip6 saddr { fd00::/8, fe80::/10 } meta pkttype unicast limit rate 4/second burst 20 packets accept comment "Accept UPnP IGD port mapping reply"
		udp sport 1900 udp dport >= 1024 ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } meta pkttype unicast limit rate 4/second burst 20 packets accept comment "Accept UPnP IGD port mapping reply"

		udp sport netbios-ns udp dport >= 1024 meta pkttype unicast ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept Samba Workgroup browsing replies"
		udp sport netbios-ns udp dport >= 1024 meta pkttype unicast ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept Samba Workgroup browsing replies"

		counter comment "Count any other traffic"
	}

	chain forward {
		type filter hook forward priority 0; policy drop;
	}

	chain output {
		type filter hook output priority 0; policy accept;
	}

}

Server

/etc/nftables.conf
flush ruleset

table inet filter {
	chain input {
		type filter hook input priority 0; policy drop;

		iif lo accept comment "Accept any localhost traffic"
		ct state invalid drop comment "Drop invalid connections"
		ct state established,related accept comment "Accept traffic originated from us"

		ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept comment "Accept ICMPv6"
		ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept comment "Accept ICMP"
		ip protocol igmp accept comment "Accept IGMP"

		udp dport mdns ip6 daddr ff02::fb accept comment "Accept mDNS"
		udp dport mdns ip daddr 224.0.0.251 accept comment "Accept mDNS"

		tcp dport ssh accept comment "Accept SSH on port 22"

		tcp dport { http, https, 8008, 8080 } accept comment "Accept HTTP (ports 80, 443, 8008, 8080)"

		meta l4proto { tcp, udp } th dport 2049 ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept NFS"
		meta l4proto { tcp, udp } th dport 2049 ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept NFS"

		udp dport netbios-ns ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept NetBIOS Name Service (nmbd)"
		udp dport netbios-ns ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept NetBIOS Name Service (nmbd)"
		udp dport netbios-dgm ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept NetBIOS Datagram Service (nmbd)"
		udp dport netbios-dgm ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept NetBIOS Datagram Service (nmbd)"
		tcp dport netbios-ssn ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept NetBIOS Session Service (smbd)"
		tcp dport netbios-ssn ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept NetBIOS Session Service (smbd)"
		tcp dport microsoft-ds ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept Microsoft Directory Service (smbd)"
		tcp dport microsoft-ds ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept Microsoft Directory Service (smbd)"

		udp sport bootpc udp dport bootps ip saddr 0.0.0.0 ip daddr 255.255.255.255 accept comment "Accept DHCPDISCOVER (for DHCP-Proxy)"
		udp sport { bootpc, 4011 } udp dport { bootps, 4011 } ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept PXE"
		udp dport tftp ip6 saddr { fd00::/8, fe80::/10 } accept comment "Accept TFTP"
		udp dport tftp ip saddr { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16 } accept comment "Accept TFTP"

	}

	chain forward {
		type filter hook forward priority 0; policy drop;
	}

	chain output {
		type filter hook output priority 0; policy accept;
	}

}

Limit rate

table inet filter {
	chain input {
		type filter hook input priority 0; policy drop;

		iif lo accept comment "Accept any localhost traffic"
		ct state invalid drop comment "Drop invalid connections"

		ip protocol icmp icmp type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"
		ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 10/second burst 4 packets drop comment "No ping floods"

		ct state established,related accept comment "Accept traffic originated from us"

		ip6 nexthdr icmpv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report } accept comment "Accept ICMPv6"
		ip protocol icmp icmp type { destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem } accept comment "Accept ICMP"
		ip protocol igmp accept comment "Accept IGMP"

		tcp dport ssh ct state new limit rate 15/minute accept comment "Avoid brute force on SSH"

	}

}

Jump

When using jumps in config file, it is necessary to define the target chain first. Otherwise one could end up with Error: Could not process rule: No such file or directory.

table inet filter {
    chain web {
        tcp dport http accept
        tcp dport 8080 accept
    }
    chain input {
        type filter hook input priority 0;
        ip saddr 10.0.2.0/24 jump web
        drop
    }
}

Different rules for different interfaces

If your box has more than one network interface, and you would like to use different rules for different interfaces, you may want to use a "dispatching" filter chain, and then interface-specific filter chains. For example, let us assume your box acts as a home router, you want to run a web server accessible over the LAN (interface enp3s0), but not from the public internet (interface enp2s0), you may want to consider a structure like this:

table inet filter {
  chain input { # this chain serves as a dispatcher
    type filter hook input priority 0;

    iif lo accept # always accept loopback
    iifname enp2s0 jump input_enp2s0
    iifname enp3s0 jump input_enp3s0

    reject with icmp type port-unreachable # refuse traffic from all other interfaces
  }
  chain input_enp2s0 { # rules applicable to public interface interface
    ct state {established,related} accept
    ct state invalid drop
    udp dport bootpc accept
    tcp dport bootpc accept
    reject with icmp type port-unreachable # all other traffic
  }
  chain input_enp3s0 {
    ct state {established,related} accept
    ct state invalid drop
    udp dport bootpc accept
    tcp dport bootpc accept
    tcp port http accept
    tcp port https accept
    reject with icmp type port-unreachable # all other traffic
  }
  chain ouput { # we let everything out
    type filter hook output priority 0;
    accept
  }
}

Alternatively you could choose only one iifname statement, such as for the single upstream interface, and put the default rules for all other interfaces in one place, instead of dispatching for each interface.

Masquerading

nftables has a special keyword masquerade "where the source address is automagically set to the address of the output interface" (source). This is particularly useful for situations in which the IP address of the interface is unpredictable or unstable, such as the upstream interface of routers connecting to many ISPs. Without it, the Network Address Translation rules would have to be updated every time the IP address of the interface changed.

To use it:

  • make sure masquerading is enabled in the kernel (true if you use the default kernel), otherwise during kernel configuration, set CONFIG_NFT_MASQ=m.
  • the masquerade keyword can only be used in chains of type nat.
  • masquerading is a kind of source NAT, so only works in the output path.

Example for a machine with two interfaces: LAN connected to enp3s0, and public internet connected to enp2s0:

table inet nat {
  chain prerouting {
    type nat hook prerouting priority 0;
  }
  chain postrouting {
    type nat hook postrouting priority 100;
    oifname "enp2s0" masquerade
  }
}

Since the table type is inet both IPv4 and IPv6 packets will be masqueraded. If you want only ipv4 packets to be masqueraded (since extra adress space of IPv6 makes NAT not required) meta nfproto ipv4 expression can be used infront of oifname "enp2s0" masquerade or the table type can be changed to ip.

NAT with port forwarding

This example will forward ports 22 and 80 to destination_ip. You will need to set net.ipv4.ip_forward and net.ipv4.conf.wan_interface.forwarding to 1 via sysctl.

table ip nat {
  chain prerouting {
    type nat hook prerouting priority 0;

    tcp dport { ssh, http } dnat to destination_ip
  }

  chain postrouting {
    type nat hook postrouting priority 100;

    ip daddr destination_ip masquerade
  }
}
</nowiki>

Tips and tricks

Simple stateful firewall

See Simple stateful firewall for more information.

Single machine

Flush the current ruleset:

# nft flush ruleset

Add a table:

# nft add table inet filter

Add the input, forward, and output base chains. The policy for input and forward will be to drop. The policy for output will be to accept.

# nft add chain inet filter input '{ type filter hook input priority 0 ; policy drop ; }'
# nft add chain inet filter forward '{ type filter hook forward priority 0 ; policy drop ; }'
# nft add chain inet filter output '{ type filter hook output priority 0 ; policy accept ; }'

Add two regular chains that will be associated with tcp and udp:

# nft add chain inet filter TCP
# nft add chain inet filter UDP

Related and established traffic will be accepted:

# nft add rule inet filter input ct state related,established accept

All loopback interface traffic will be accepted:

# nft add rule inet filter input iif lo accept

Drop any invalid traffic:

# nft add rule inet filter input ct state invalid drop

Accept ICMP and IGMP:

# nft add rule inet filter input ip6 nexthdr icmpv6 icmpv6 type '{ destination-unreachable, packet-too-big, time-exceeded, parameter-problem, mld-listener-query, mld-listener-report, mld-listener-reduction, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, ind-neighbor-solicit, ind-neighbor-advert, mld2-listener-report }' accept
# nft add rule inet filter input ip protocol icmp icmp type '{ destination-unreachable, router-solicitation, router-advertisement, time-exceeded, parameter-problem }' accept
# nft add rule inet filter input ip protocol igmp accept

New udp traffic will jump to the UDP chain:

# nft add rule inet filter input ip protocol udp ct state new jump UDP

New tcp traffic will jump to the TCP chain:

# nft add rule inet filter input 'ip protocol tcp tcp flags & (fin|syn|rst|ack) == syn ct state new jump TCP'

Reject all traffic that was not processed by other rules:

# nft add rule inet filter input ip protocol udp reject
# nft add rule inet filter input ip protocol tcp reject with tcp reset
# nft add rule inet filter input counter reject with icmp type prot-unreachable

At this point you should decide what ports you want to open to incoming connections, which are handled by the TCP and UDP chains. For example to open connections for a web server add:

# nft add rule inet filter TCP tcp dport 80 accept

To accept HTTPS connections for a webserver on port 443:

# nft add rule inet filter TCP tcp dport 443 accept

To accept SSH traffic on port 22:

# nft add rule inet filter TCP tcp dport 22 accept

To accept incoming DNS requests:

# nft add rule inet filter TCP tcp dport 53 accept
# nft add rule inet filter UDP udp dport 53 accept

Be sure to make your changes permanent when satisifed.

Prevent brute-force attacks

Sshguard is program that can detect brute-force attacks and modify firewalls based on IP addresses it temporarily blacklists. See Sshguard#nftables on how to set up nftables to be used with it.

Troubleshooting

Working with Docker

Using nftables can interfere with Docker networking (and probably other container runtimes as well). In particular the drop policy for the forward chain will block packets originating in docker containers. If you want to keep the forward rule in your inet table, you can use the following:

  1. Install iptables-nft to provide an iptables compatible interface for nftables that docker can use.
  2. Use the following for the forward chain in your inet table:
    chain forward {
      type filter hook forward priority security; policy drop
      mark 1 accept
    
  3. Add a rule to the DOCKER-USER chain in the ip filter table to mark packets if docker is running:
    table ip filter {
      chain DOCKER-USER {
        mark set 1
      }
    }
    

This works by marking packets if docker is active, and accepting the packets in this case, since docker has already filtered them (the forward chain defined by docker uses a drop policy).

See also