Let's start with a clean snapshot of our Ubuntu virtual machine, and install the nftables package.
Now, let's take a look at the list of installed tables:
sudo apt install nftables
sudo nft list tables
Hmmm... You didn't see any tables, did you? So, let's load some up.
If you look at the nftables.conf file in the /etc directory, you'll see the beginnings of a basic nft firewall configuration:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0;
# accept any localhost traffic
iif lo accept
# accept traffic originated from us
ct state established,related accept
# activate the following line to accept
common local services
# tcp dport { 22, 80, 443 } ct state new accept
# accept neighbour discovery otherwise
IPv6 connectivity breaks.
ip6 nexthdr icmpv6 icmpv6 type { nd-neighbor-solicit,
nd-router-advert, nd-neighbor-advert } accept
# count and drop any other traffic
counter drop
}
}
Here's the breakdown of what all this means:
- #!/usr/sbin/nft -f: Although you can create normal Bash shell scripts with nftables commands, it's better to use the built-in scripting engine that's included with nftables. That way, we can make our scripts more human-readable, and we don't have to type nft in front of everything we want to do.
- flush ruleset: We want to start with a clean slate, so we'll flush out any rules that may have already been loaded.
- table inet filter: This creates an inet family filter, which works for both IPv4 and IPv6. The name of this table is filter, but it could just as well have been something a bit more descriptive.
- chain input: Within the first pair of curly brackets, we have a chain with the name of input. (Again, the name could have been something more descriptive.)
- type filter hook input priority 0;: Within the next pair of curly brackets, we define our chain and then list the rules. This chain is defined as a filter type. hook input indicates that this chain is meant to process incoming packets. Because this chain has both a hook and a priority, it will accept packets directly from the network stack.
- Finally, we have the standard rules for a very basic host firewall, starting with the iif rule that allows the loopback interface to accept packets (iif stands for input interface.)
- Next is the standard connection tracking (ct) rule, which accepts traffic that's in response to a connection request from this host.
- Then, there's a commented-out rule to accept Secure Shell and both secure and nonsecure web traffic. The ct state new indicates that the firewall will allow other hosts to initiate connections to our server on these ports.
- The ipv6 rule accepts neighbor discovery packets, allowing for IPv6 functionality.
- The counter drop rule at the end silently blocks all other traffic, and counts both the number of packets and the number of bytes that it blocks. (This is an example of how you can have one rule perform two different actions.)
If all you need on your Ubuntu server is a basic, no-frills firewall, your best bet is to just edit this /etc/nftables.conf file to suit your own needs. For starters, let's remove the comment symbol from in front of the tcp dport line, and get rid of ports 80 and 443. The line should now look like:
tcp dport 22 ct state new accept
Note that when you're only opening one port, you don't need to enclose that port number within curly brackets. When opening multiple ports, just include the comma-separated list within curly brackets, with a blank space before the first element and after the last element.
Load the configuration file, and view the results:
sudo nft -f /etc/nftables.conf
donnie@ubuntu2:~$ sudo nft list table inet filter
table inet filter {
chain input {
type filter hook input priority 0; policy accept;
iif lo accept
ct state established,related accept
tcp dport ssh ct state new accept
ip6 nexthdr ipv6-icmp icmpv6 type { nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert} accept
counter packets 67 bytes 10490 drop
}
}
donnie@ubuntu2:~$
Now, let's say that we want to block certain IP addresses from reaching the Secure Shell port of this machine. We can edit the file, placing a drop rule above the rule that opens port 22. The relevant section of the file would look like this:
tcp dport 22 ip saddr { 192.168.0.7, 192.168.0.10 } drop
tcp dport 22 ct state new accept
After we reload the file, we'll be blocking SSH access from two different IPv4 addresses. Note that we've placed the drop rule ahead of the accept rule, because if the accept rule gets read first, the drop rule will never have any effect.
Another really cool thing to note is how we've mixed IPv4 (ip) rules with IPv6 (ip6) rules in the same configuration file. That's the beauty of using an inet-type table. For simplicity and flexibility, you'll want to use inet tables as much as possible, rather than separate ip and ip6 tables.
Most of the time, when all you need is just a simple host firewall, your best bet would be to just use this nftables.conf file as your starting point, and edit the file to suit your own needs. However, there's also a command-line component that you may at times find useful.