|
|
| (5 intermediate revisions by the same user not shown) |
| Line 107: |
Line 107: |
| This still has the disadvantage of having to type lots of repetitive text unnecessarily, which brings us to the recommended method editing the automatically installed descriptive file nftables.conf. Which is what we will do from now on. It has 2 big advantages, less typing but very readable seeing where each rule fits. It is still possible to intersperse the file with verbose text rules, nft does not mind. So let's analyse a near complete ruleset file and get to grips with the syntax and structure. | | This still has the disadvantage of having to type lots of repetitive text unnecessarily, which brings us to the recommended method editing the automatically installed descriptive file nftables.conf. Which is what we will do from now on. It has 2 big advantages, less typing but very readable seeing where each rule fits. It is still possible to intersperse the file with verbose text rules, nft does not mind. So let's analyse a near complete ruleset file and get to grips with the syntax and structure. |
|
| |
|
| ==Dissecting a Ruleset Script== | | ==What next== |
|
| |
|
| <syntaxhighlight lang="text">
| | [[Nftables Part 2]] will look at a near complete but fully workable ruleset and analyse the sections and rules to explain the syntax. Further pages will then look at more advanced configurations and how to get '''fail2ban''' working with nftables. |
| #!/usr/sbin/nft -f
| |
| flush ruleset
| |
| define web = {http,https}
| |
| define mail = {smtp,pop3,imap3,submission}
| |
| define altssh = 22
| |
| define ftpports = {ftp,ftps}
| |
| define portmap = 111
| |
| table inet filter {
| |
| chain input {
| |
| type filter hook input priority 0; policy drop;
| |
| meta iif lo accept
| |
| tcp dport $altssh counter accept
| |
| ct state {established,related} accept
| |
| ct state invalid drop
| |
| tcp dport {$web,$mail,$ftpports,webmin,domain} accept
| |
| udp dport domain accept
| |
| icmp type echo-request limit rate 10/second accept
| |
| ip6 nexthdr icmpv6 limit rate 10/second accept
| |
| ip saddr 192.0.2.10 accept
| |
| ip6 saddr 2001:db8:beef:cafe::/64 counter accept
| |
| tcp dport $portmap drop
| |
| udp dport $portmap drop
| |
| }
| |
|
| |
|
| chain output {
| | ==Other Resources== |
| type filter hook output priority 0; policy accept;
| |
| tcp dport $portmap drop
| |
| udp dport $portmap drop
| |
|
| |
|
| }
| | https://wiki.nftables.org/wiki-nftables/index.php/Main_Page is the wiki of the nftables project |
| }</syntaxhighlight>
| | This wiki entry is based upon Debian .deb packages. For details of .rpm packages see https://apps.fedoraproject.org/packages/nftables |
| | |
| This is a near complete firewall script for a VPS not operating as a router we will take it apart line by line and look at the syntax. Where as any of the text input methods will need to describe and action for nft and a relative position for the rule or chain to be placed, a descriptive ruleset file will not need that as it is already placed in the diagram.
| |
| | |
| ===Definition block===
| |
| | |
| The first 2 lines have been explained already. Next come the variable definitions. Each consists of a variable name = followed by the value(s) assigned. When referenced in a rule, the name has to be prefixed with $. These definitions contain many references to built in constants for readability. NFTables uses its own table of aliases for port numbers. These can be viewed using the command
| |
| | |
| <syntaxhighlight lang="text">:~# nft describe tcp dport
| |
| payload expression, datatype inet_service (internet network service) (basetype integer), 16 bits
| |
| | |
| pre-defined symbolic constants (in decimal):
| |
| tcpmux 1
| |
| echo 7
| |
| ...</syntaxhighlight>
| |
| The other feature demonstrated here is the set. There are two types of sets, named and anonymous. Both are enclosed in curly braces and elements are comma separated. Anonymous sets, as illustrated here are static and form an integral part of the rule. They cannot have rules added or deleted (except by removing and re-adding the rule) and only exist within the rule where they are defined. Named sets, however, must be defined before use and may be referenced (''@name'') in any rule following their definition. They can be added to or have elements deleted dynamically.
| |
| NOTE There are two definitions that will need a little further explanation
| |
| ''define altssh = 22'' This may seem surplus to requirements. Afterall, sshd is already defined internally as port 22. It therefore seems pointless defining another name for it when sshd could be added to the rule allowing access to certain fixed ports anyway. The reason I have done this is that I actually have sshd listening on a different non-standard port. Port 22 therefore remains closed and there is no one who has a legitimate reason to attempt to use it. Being such an important service though it does attract a lot of attention from hackers attempting dictionary attack, or it may also have been tested by port scanners. Neither of which do I want approaching my VPS I have therefore set up a system using a named set and dynamic editing of the set to block them for a while. I will introduce and describe this later in the article.
| |
| ''define portmap = 111'' An embarrassing legacy item this. Some years ago, I received a notification from Bitfolk that my VPS was running a very insecure open service on port 111. Not only was it insecure but totally unnecessary. I removed the service and blocked the port, the block has remained as a legacy item in every firewall since. This definition and the rules referencing it are probably not needed any more.
| |
| | |
| ===Table and Chain definitions===
| |
| | |
| <syntaxhighlight lang="text">add table inet filter</syntaxhighlight>
| |
| | |
| '''table inet filter {'''
| |
| The initial definition merely needs the family and a name as it only acts as a container for the chains within it. It defines the family of packets which it will handle. There are 5 different table families, ip, ip6, inet (a super family of the two previous), arp and bridge. The default is ip. The name can be anything though there are limits on length. For readability a simple descriptive name is obviously best.
| |
| | |
| There are two types of chains that can be used. The base chain and the non-base chain. The difference is that a base chain is defined with a hook (e.g. input) and all packets matching that hook will be passed through the chain. Non-base chains have no hook and and will only see packets sent to them by a jump or go to command in a rule in a base chain. We will add some non-base chains later, but the ruleset, at the moment, contains only base chains.
| |
| The definition of our first chain is
| |
| | |
| <syntaxhighlight lang="text"> chain input {
| |
| type filter hook input priority 0; policy drop;
| |
| (various rules placed here)
| |
| }
| |
| </syntaxhighlight>
| |
| | |
| A generic base chain definition entered in text form would be
| |
| | |
| <syntaxhighlight lang="text">add chain [<family>] <table-name> <chain-name> { type <type> hook <hook> priority <value>; [policy <policy>;] }</syntaxhighlight>
| |
| | |
| which in the case of our input chain would read
| |
| | |
| <syntaxhighlight lang="text">add chain inet filter input { type filter hook input priority 0; policy drop; }</syntaxhighlight>
| |
| | |
| Looking at this in more detail. There are three types of chain. Filter which does just that. Route, to reroute packets and nat. The hook parameter determines what packets will be operated on by the chain. The available hooks are prerouting, input, forward, output, postrouting and a new one ingress which operates on packets before even prerouting, it works only with the family netdev. Our chain is of the filter type operating on incoming packets.
| |
| The priority parameter determines the order in which chains are applied if there is more than one chain of that type. The lower the number given here the earlier in the order of the chains it appears. Note that this must be followed by a semicolon ";" The semicolon is used to mark the end of a command. If you are entering the definition from a bash command line the semicolon must be escaped "\;". The final part of the definition is the policy. The two usual values for this are accept or drop (others are possible) and it describes the default action (or verdict statement) to be applied to packets which have transversed the whole chain without matching any rules.
| |
| | |
| ===The Rules===
| |
| | |
| A rule in its basic form consists of an expression and a verdict statement to be acted on if the expression evaluates to true. There can be more than one expression and more than one statement. The expressions are evaluated from left to right, if the first is true, then the second is evaluated and so on. Similarly the statements are enacted from left to right. However, note that is the first statement is a terminal one e.g. drop, then the packet is dropped without consideration of further statements. So there can only be one terminal statement and it must appear last. An example log drop will make a log entry and then drop the packet, drop log will drop the packet and the log statement will be ignored. In this second instance you may get a warning when attempting to load the rule.
| |
| | |
| The expression to be matched may contain operators e.g. eq , != and so on. The default is eq tends not to be actually written. So the general structure is <property being examined> <operator> <value expected>
| |
| | |
| Let's consider the rules in the input chain
| |
| | |
| <syntaxhighlight lang="text">meta iif lo accept</syntaxhighlight>
| |
| | |
| Expression = ''meta iif lo'' action if true = ''accept''
| |
| | |
| Expand that expression into more human understandable language
| |
| | |
| In the metainformation the incoming interface is equal to lo In other words the result is true if it is traffic generated on this machine.
| |
| | |
| There are lists available of all acceptable selectors, these are too long to include here and links will be given.
| |
| | |
| ''iif'' deserves further explanation. There are 4 terms which can be used to describe the interface. iif and oif refer to the incoming and outgoing interfaces respectively. iifname and oifname do likewise. The difference is that the short forms refer to an index of the interfaces and are faster to lookup whereas the longer forms refer to the actual names of the interfaces and are thus slower. If the box only has say lo and eth0 then the short forms will work properly. If it has a number of interfaces that may be dynamically loaded then the long form has to be used.
| |
| | |
| The final action performed is the expression evaluates to true is accept which is a terminal action, the packet is allowed through and traverses no more rules
| |
| | |
| <syntaxhighlight lang="text">tcp dport $altssh counter accept</syntaxhighlight>
| |
| | |
| ''$altssh'' was defined in the definition block, at the moment it evaluates to 22 and so that is substituted in the rule. This can be read as a double expression rule in effect. If the protocol is tcp and if the destination port is 22. However that is not quite the case as dport has to be linked to a protocol. dport refers to the destination port and sport to the source port.
| |
| | |
| The interesting point about this rule is that it has 2 verdict statements, or operations. ''counter'' which counts the packets (and bytes) and then accept. ''iptables'' gave no choice, everything was counted. ''nftables'' however, allows you to just add counters to whichever rule you want.
| |
| | |
| Counters can be read by listing the ruleset or chain.
| |
| <syntaxhighlight lang="text">~# nft list ruleset
| |
| table inet filter {
| |
| chain input {
| |
| type filter hook input priority 0; policy drop;
| |
| iif "lo" accept
| |
| tcp dport 22 counter packets 9 bytes 824 accept</syntaxhighlight>
| |
| | |
| The next two rules make use of connection tracking. The first checks the state against the members of an unnamed set of values to see if the packet is part of an established connection, or related one. If so, then everything will have been checked before so the packet is accepted. The second rule of this pair just drops those invalid ones early. Gets them out of the way.
| |
| | |
| <syntaxhighlight lang="text">ct state {established,related} accept
| |
| ct state invalid drop</syntaxhighlight>
| |
| | |
| The next rule makes use of an unnamed set of ports defined at the beginning of the file and internally defined constants to open common ports. As always, if examining dports we have to state the protocol, in this case tcp. And the following rule opens port 53 on UDP as that is also used by DNS
| |
| | |
| <syntaxhighlight lang="text">tcp dport {$web,$mail,$ftpports,webmin,domain} accept
| |
| udp dport domain accept</syntaxhighlight>
| |
| | |
| If you were entering the rule using text input then you would need a line like
| |
| <syntaxhighlight lang="text">add rule filter input udp dport domain accept</syntaxhighlight>
| |
| | |
| It is possible to limit packets received which can help mitigate certain types of attack. The next two rules allow ping requests on ip and ip6 as well as permitting all the various ip6 icmp messages used to establish the network and interface on start up.
| |
| | |
| <syntaxhighlight lang="text">icmp type echo-request limit rate 10/second accept
| |
| ip6 nexthdr icmpv6 limit rate 10/second accept</syntaxhighlight>
| |
| | |
| The final four rules should be understandable and self explanatory now. Two (using dummy addresses here) allowing access to two addresses, in my proper table, I have inserted the addresses of my second VPS as the two machines will often communicate via ports which I do not want open to the world. The ip and ip6 protocols are explicitly named at the start of the rules here.
| |
Introduction
STILL NOT COMPLETE
Iptables (and its sister ip6tables) or programs based upon it has for many years been the standard firewall product for Linux machines. Its reign is coming to an end. Debian has announced that the next incarnation of its OS (Debian 10) will see its replacement by nftables. Current kernels already have the nftables engine powering their firewalls with iptables and firewalld rulesets running on this. Nftables offers many new features, incuding simpler syntax, integration of all the old xtables family into one unified package. It has reached a stage of development and maturity that now is the time to make the move
Having replaced iptables I have produced the guide below as an aid to those considering it. This is based on my experience with Debian 9 and kernel 4.9.0-8-amd64 other distributions will vary slightly especially in methods of installation. The other thing to note is that nft the front end requires root (or sudo su)
Getting and installing
The stretch repository contains nftables but a more recent version is available from stretch-backports, and as there has been a lot of recent development, this is the one to go for. If the backports repository is not in your sources.list then it will need to be added before you can install
~# echo "deb http://ftp.debian.org/debian stretch-backports main" >> /etc/apt/sources.list
~# apt-get update
~# apt-get -t stretch-backports install nftables
This will install all that is necessary. It sets up a new service nftables.service which is disabled on installation. That allows you to set up rules etc and clear iptables before you start the service.(It is not recommended to run both at the same time if there is conflict between any of the rules) On starting or restarting the service loads a minimalist ruleset, so we need to build our rules before running it.
Editing The Ruleset
The basic structure is the same as iptables – tables hold chains which are, in turn, containers for rules. The major difference is that iptables has tables predefined but nft has none. /etc/nftables.conf sets up a basic table and 3 empty chains which we will need to edit
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0;
}
chain forward {
type filter hook forward priority 0;
}
chain output {
type filter hook output priority 0;
}
}
The table and chains can have any name you like (there are length restrictions though), but these names are descriptive, they do what they say on the can. My VPS does not act as a router at the moment so I can safely remove the forward chain. The easiest way to do that is edit the file, I could of course use a command line instruction to do it. But we will look at that later. The other thing that needs doing is setting the policy for each chain. Again I will just do this by editing the file.
~#nano /etc/nftables.conf
So it will end up looking like this:-
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0;policy accept;
}
chain output {
type filter hook output priority 0;policy accept;
}
}
So we save it. NOTE I have set the policy to accept, first time round I set it to drop and of course cut myself off.
And load it up
~# nft -f /etc/nftables.conf
No error messages means it was all OK. But we can check by using the command
There is now a ruleset in operation, albeit very sparse and in fact doing nothing to filter traffic.
Adding a New Rule
As mentioned already there are a number of ways of adding a rule.
Let's look at adding a simple rule to allow any packets coming in on port 80, the http port. This rule will be added to the input chain in table "filter". Looking at the definition of the table "filter" above table inet filter this inet defines the family meaning rules within it unless specified differently apply to ip packets (ipv4) and ip6 (ipv6).
Commandline shell
~# nft add rule filter input tcp dport http accept
- nft call the nft commandline processor
- filter output to the table "filter" place it in the chain "input"
- tcp dport http The match condition = if it is protocol tcp and the destination port is http. (nft has its own table of port definitions built in)
- accept The verdict. This is the action to be taken if a match is made
Remember when using this method or any using the normal shell, semicolons must be escaped "\;"
Interactive command line
then rules can be added one at a time as above, just excluding the nft at the beginning, typing quit will take you out of interactive mode
Shell script
Listing all the commands to build the firewall in a shell script and making it executable was a favoured method with iptables
It is possible with nftables but not recommended.
#!/bin/bash
nft flush ruleset
nft add table <tabledefinition>
nft add chain <chain definition>
...
This has an advantage over the previous methods in that it can easily be edited then executed again.
Nftables rules script
This is similar to the shell script except that the first line differs. Instead of invoking the bash shell to interpret the instructions, it invoke nft to do so. If you remember to include also "flush ruleset" as the second line. nftables will read in the whole file, parse and compile it into memory then in a single atomic action replace the whole of the current ruleset with this one.
#!/usr/sbin/nft
flush ruleset
add table <tabledefinition>
add chain <chaindefinition>
add rule ...
This still has the disadvantage of having to type lots of repetitive text unnecessarily, which brings us to the recommended method editing the automatically installed descriptive file nftables.conf. Which is what we will do from now on. It has 2 big advantages, less typing but very readable seeing where each rule fits. It is still possible to intersperse the file with verbose text rules, nft does not mind. So let's analyse a near complete ruleset file and get to grips with the syntax and structure.
What next
Nftables Part 2 will look at a near complete but fully workable ruleset and analyse the sections and rules to explain the syntax. Further pages will then look at more advanced configurations and how to get fail2ban working with nftables.
Other Resources
https://wiki.nftables.org/wiki-nftables/index.php/Main_Page is the wiki of the nftables project
This wiki entry is based upon Debian .deb packages. For details of .rpm packages see https://apps.fedoraproject.org/packages/nftables