Igor Bubelov About Blog Photos

Getting Familiar With nftables

September 18, 2020

The more I experiment with nftables, the more I like it. It’s not high level enough to be a black box Voodoo magic, and it’s not low level enough to be a royal pain in the ass.

Let’s look at the following example:

#!/usr/sbin/nft -f

flush ruleset

### CHAINS
# INPUT - all incoming packets are checked against the rules in this chain
# OUTPUT - all outgoing packets are checked against the rules in this chain
# FORWARD - all packets being sent to another computer are checked against the rules in this chain
###

### CT STATES
# NEW - the packet has started a new connection
# ESTABLISHED - the packet is associated with a connection which has seen packets in both directions
# RELATED - the packet is starting a new connection, but is associated with an existing connection
###

table inet filter {
  chain input {
    type filter hook input priority 0; policy drop;
    iif lo accept comment "Accept localhost traffic"
    ct state established,related accept comment "Accept traffic originated from here"
    tcp dport ssh accept comment "Accept SSH traffic"
  }

  chain output {
    type filter hook output priority 0;
  }

  # Drop everything, it's not a router
  chain forward {
    type filter hook forward priority 0; policy drop;
  }
}

Not much code to describe a firewall ruleset! It’s also easy to read and modify. All the action happens in the input chain which is responsible for filtering all the incoming traffic. It starts with a default-deny mode which means it doesn’t allow any incoming traffic to pass unless you add a special rule that allows certain kinds of traffic under certain conditions.

First, I wanted to allow any traffic coming from the loopback interface:

## iif = 'if input interface is ...'
## lo = loopback interface
iif lo accept comment "Accept localhost traffic"

Next, it would be nice to tell the firewall to accept any traffic sent to this computer as a response to locally initiated requests:

### CT STATES
# NEW - the packet has started a new connection
# ESTABLISHED - the packet is associated with a connection which has seen packets in both directions
# RELATED - the packet is starting a new connection, but is associated with an existing connection
###
ct state established,related accept comment "Accept traffic originated from here"

And finally, it would be nice to have an SSH port open if you’re dealing with the remote machine. Shutting yourself from your own server can be a costly mistake, and it’s not uncommon. Here is how to allow SSH traffic, assuming your SSH daemon is set to listen on a well known port 22 (which you should change, by the way):

tcp dport ssh accept comment "Accept SSH traffic"

If you don’t want to get spammed by unwelcome SSH login attempts, just set your SSH daemon to listen to another port. Let’s say our SSH daemon is listening on the port 40736. That would mean that we need to add the following rule:

tcp dport 40736 accept comment "Accept SSH traffic"

If you decide to host an HTTP server on this machine, just add http and https ports to that rule:

tcp dport { 40736, http, https } accept comment "Accept SSH and HTTP(S) traffic"

That’s it. Opening additional ports is trivial and the documentation is good. I’m just starting with this thing, and I’m planning to experiment with the logging and tracing tools in the following weeks.