Score:0

Best Practices for persisting nftables rules

in flag

I'm new to Ubuntu having moved from hosting on CentOS7 which was using iptables and I was comfortable with how apf and bfd handled (hid) iptables from me.. and it was working well

So, I've moved to Ubuntu (20.04 LSR) and the "ubuntu-way" to do firewall with auto banning attempts to break in appears to be nftables and fail2ban

I used iRedMail to set up a basic nginx, postfix, dovecot, clamav, roundcube etc.. based mail server and it configured fail2ban to watch the the logs for attempts at hacking into the mail services and ssh etc.. and I've seen it correctly parse my mail logs and store the fial2ban blocked ips in table inet f2b-table

This properly survives reboot but I noticed that the /etc/nftables.conf file does not have any of the fail2ban tables in it - it's got my base firewall which I am able to statically update to deny all then unblock tcp ports I want (basically mail and web server and ssh)

But as far as I can see, the fail2ban rules are not in a config file but seem to be reconstructed on boot from entries in a fail2ban table in a mysql database set up by iRedMail

This is fine..but here's the dilemma: if I manually add a rule to the firewall taking advantage of the existing table for fail2ban I can..

nft add element inet f2b-table addr-set-postfix-pregreet { spammer.ip.addr.here }

It shows up and works.. I tested by successfully locking myself out then removed via local console..

Except when I reboot, that entire table is lost and rebuilt by fail2ban from the table entries (again I'm cool with that)

So, I went ahead and added my own new table/stuff

table inet spammers {
    set blackhole {
            type ipv4_addr
            elements = { sample.ip.addr.here }
    }

    chain spammers-chain {
            type filter hook input priority filter - 2; policy accept;
            ip saddr @blackhole drop
    }
 }

Again, tested this and it works well but in order to persist this out I need to write it to /etc/nftables.conf or set up that directory to read in my rules etc..

I could do this by adding an include in my nftables.conf to a directory of arbitrary rules and then store my table out to it every time I add an address but this seems ugly and wrong

as in if I want to add a new spammer to my list I can do

nft add element inet spammers blackhole { new.ip.addr.here }

but then would need to .. I dunno? persist out the table to my file?

nft list table inet spammers > /etc/nftables.d/spammers.conf

So, that's one way I could do it, but I've seen others talk about netfilter-persist but that's not part of Ubuntu so before I go off either inventing my own wheel or down the netfilter-persist rabbit hole or (no thanks) do something akin to what fail2ban seems to have done... (storing the banned IPs in a database and reconstructing the list at login)

I can come up with several ways I could do this.. but I was wondering if there was a "best practice" "Ubuntu-way" I am missing here...

UPDATE/EDIT: Unless I get a better suggestion, my "solution" for now is

mkdir /etc/nftables.d/
nft list table inet spammers > /etc/nftables.d/spammers.conf

and then I edited my /etc/nftables.conf to add this line at the bottom

include "/etc/nftables.d/*.conf"

Now, when I add a block to the table it's 2 steps:

nft add element inet spammers blackhole { some.evildoer.ip.address }
nft list table inet spammers > /etc/nftables.d/spammers.conf

It's not the prettiest but it absolutely works.. I could of course wrap this all in my own custom script so I could jsut call something like

banspammer "badguy.ip.addr.here"

and banspammer would add the address element specified and save out the updated table def...

again, this just feels like "not a best practice" hence my question.

EDIT: 2021-12-22 OK so I'm kind of talking to myself but since I haven't gotten any feedback I went with my idea - it's working and I wrote this little banspammer script... it's raw and probably highly dangerous - I didn't do sanity checks like bothering to check if the config file path is valid and didn't do any backing up of said file etc...

Since the table entry is a type ipv4_addr nftables does validation so I don't worry too much about that

NOTE that my particular setup already had a fileter named filter in teh inet family - I specifically add this as a slightly lower priority list I also created the /etc/nftables.d directory and added in my code to have it parse the config directory as I mentioned above

here's hoping someone finds this useful.

I'd still be interested in a more "Ubuntu-way" if there was such a thing.

#!/usr/bin/sh

################################################################################
# banspammer                         2021-12-22                 DigitalSorceress
#  
# SUMMARY
# This script adds an ip or range of Ips (see element adding) to nftables
# specifically to my spammer blackhole
# it also persists it out to /etc/nftables.d/spammers.conf
#
# put this somewhere like /root/tandautils
# then go to /user/local/sbin and ln -s /root/tandautils/banspammer.sh banspammer
#
################################################################################

# Handle command line args
COMMAND=$1
ADDRESS=$2


# the location of the ssh daemon config file
# default for CentOS is CONFIG_FILE=/etc/ssh/sshd_config
#
CONFIG_FILE=/etc/nftables.d/spammers.conf



# Here are the subroutines for individual actions
ban_spammer () {
    # Issue the basic command to ban the spammer 
    echo "adding spammer to blackhole ..."
    
    nft add element inet spammers blackhole { ${ADDRESS} }
    BAN_SPAMMER_RESULT=$?
    if [ $BAN_SPAMMER_RESULT -eq 0 ] 
    then
        echo "  DONE: ${ADDRESS} added to spammer table"
    fi
    echo ""
}

unban_spammer () {
    # Issue the basic command to ban the spammer 
    echo "removing spammer from blackhole ..."
    
    nft delete element inet spammers blackhole { ${ADDRESS} }

    UNBAN_SPAMMER_RESULT=$?
    if [ $UNBAN_SPAMMER_RESULT -eq 0 ] 
    then
        echo "  DONE: ${ADDRESS} removed from table"
    fi
    echo ""
}

persist_spamtable () {
    echo "persisting out spamtable to ${CONFIG_FILE}..."
    # we need to persist out the spam table to the config
    nft list table inet spammers > ${CONFIG_FILE}
    if [ $? -eq 0 ]
    then
      echo "  done.. showing table..."
      echo ""
      nft list table inet spammers
    else
      echo "error persisting table.. "
    fi
    echo ""
}

list_spamtable () {
    echo "listing out spamtable"
    echo ""
    nft list table inet spammers
    echo ""
}

kill_spamtable () {
    echo "resetting /creating blank spamtable ${CONFIG_FILE}..."

    #rm -f /etc/nftables.d/spammers.conf
    tee /etc/nftables.d/spammers.conf <<EOF
table inet spammers {
    set blackhole {
        type ipv4_addr
    }

    chain spammers-chain {
        type filter hook input priority filter - 2; policy accept;
        ip saddr @blackhole drop
    }
}
EOF
    echo ""
    if [ $? -eq 0 ]
    then
        echo "success.. here's the new file:"
        echo ""
        cat /etc/hftables.d/spammers.conf
    else
        echo "error writing file... "
    fi
    
    echo ""
}

help_me () {
  echo "This is a tool to simplify blocking of IP addesses of spammers                  "
  echo "                                                                                "
  echo "banspammer                          2021-12-22                  DigitalSorceress" 
  echo "                                                                                "
  echo "SUMMARY                                                                         "
  echo " This script is used to simplify the act of adding/removing spammers from       "
  echo " a spammers table in the nftables ruleset                                       "
  echo "                                                                                "
  echo "                                                                                "
  echo "usage: $0 banspammer command [address]                                          "
  echo "                                                                                "
  echo " command options:                                                               "
  echo "           ban address                                                          "
  echo "             bans the target address; can be a singe IP or comma sep list       "
  echo "                                                                                "
  echo "           unban address                                                        "
  echo "             removes the target address can be a singe IP or comma sep list     "
  echo "                                                                                "
  echo "           reset                                                                "
  echo "             clears all entries from the spammers table                         "
  echo "             note this can be used to create a new empty table                  "
  echo "                                                                                "
  echo "           show                                                                 "
  echo "             shows the current spam table list                                  "
  echo "                                                                                "
  echo "           help                                                                 "
  echo "             Displays this help dialog                                          "
}



# Here is where we do the actual work based on the passed command
case "$COMMAND" in
  ban)
    if [ $# -eq 2 ] 
    then
        echo "banning address: ${ADDRESS}"
        ban_spammer ${ADDRESS}
      if [ $BAN_SPAMMER_RESULT -eq 0 ]
      then
        persist_spamtable
      fi
    else
        echo "ban command requires a single IP or comma separated list of IPs "
    fi
    ;;
  unban)
    if [ $# -eq 2 ] 
    then
      echo "unbanning address: ${ADDRESS}"
      unban_spammer ${ADDRESS}
      if [ $UNBAN_SPAMMER_RESULT -eq 0 ]
      then
        persist_spamtable
      fi
    fi
    ;;
  show)
      list_spamtable
      ;;
  reset)
      kill_spamtable
      ;;
  help)
    help_me
    ;;
  *)
      echo "Usage: $0 ban|unban comma-separated-ip-list  or $0 show|reset"
      exit 1
esac
The Busy Wizard avatar
gh flag
I personally would have it save to file on system shutdown / reboot, rather than dealing with a script to do it. That said, I found your question while looking for the "right" way to persist rules, so I'm not speaking as an authority here.
Score:0
us flag

If you apt-get install iptables-persistent it will pull in netfilter-persistent which seems to be what you were looking for to persist your netfilter rules?

Someone avatar
my flag
As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer).
ar flag
In Ubuntu 20.04, there is a "netfilter-persistent" package, but it's README file says : "netfilter-persistent does no work on its own. You need the accompanying plugins (for example, iptables-persistent) to load and save filter rules". So this is not what the OP is looking for, unless there are appropriate nftables plugins.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.