Skip to content

User Guide

wirefalls edited this page Dec 17, 2020 · 14 revisions
GeoIP for nftables

User Guide - GeoIP for nftables

Usage

Depending on your file system permissions, you will likely have to execute some of the commands below with sudo or similar to avoid permission errors. You can use sudo -s in a terminal to keep from having to type sudo before every command, then type "exit" to return to your normal user command prompt when you're finished with the setup. Always exercise caution when running with elevated privileges.

See the Notes section near the end of this document for details on the variable and file naming convention used for GeoIP sets.

 

   Now that the installation is complete you can move on to setting up your ruleset. One decision you'll need to make is how to load the new IP address ranges after updating the GeoIP database each month. There are three basic ways to do this.

  • Reboot the computer (no further setup required).

  • Restart nftables automatically by setting restart_nftables=yes in the /etc/geoip-nft.conf configuration file. Be aware that restarting nftables may interrupt established connections between your system and other computers, such as ssh sessions and website connections, as well as resetting rule counters, etc.

  • Flush and refill your GeoIP sets atomically. This requires some additional setup and testing, but allows servers to continue uninterrupted after a database update.

Both options to restart nftables and flush/refill sets are turned off by default in the /etc/geoip-nft.conf configuration file.

If you're not planning to flush and refill GeoIP sets after a database update but would rather restart nftables, then set elements can be defined in your nftables.conf file or your main ruleset. You can include all GeoIP set files in the countrysets directory for easier configuration, or just the set files that you need. With this configuration you'll want to set restart_nftables=yes in /etc/geoip-nft.conf to automatically restart nftables after a database update. This configuration is typically used on workstations and systems that can tolerate the loss of established connections caused by restarting nftables.

After backing up your current nftables.conf file, copy and paste the following code to a new file /etc/nftables.conf (Ubuntu/Debian) or /etc/sysconfig/nftables.conf (Fedora/Red Hat):

nftables.conf

#!/usr/bin/nft -f

flush ruleset

# Include all country code set files to make things easier to configure.
# nftables >= v0.9.4 can include all sets with: include "/etc/nftables/geoip/countrysets/*"
include "/etc/nftables/geoip/include-all.ipv4"
include "/etc/nftables/geoip/include-all.ipv6"

table netdev filter {

  set geoip-netdev4 {
    type ipv4_addr
    flags interval
    # Add IPv4 country code elements for the United States and Great Britain.
    elements = { $US.ipv4, $GB.ipv4 }
  }

  set geoip-netdev6 {
    type ipv6_addr
    flags interval
    # Add IPv6 country code elements for the United States, Great Britain and Antarctica.
    elements = { $US.ipv6, $GB.ipv6, $AQ.ipv6 }
  }

  chain ingress {
    # Replace <device_name> below with the name of your WAN network interface from "ifconfig".
    type filter hook ingress device <device_name> priority 0; policy accept;
    # Count IPv4 traffic from the United States and Great Britain.
    ip saddr @geoip-netdev4 counter
    # Count IPv6 traffic from the United States, Great Britain and Antarctica.
    ip6 saddr @geoip-netdev6 counter
  }
}

# Your other ruleset content here...

Note the GeoIP "include" lines at the beginning of the file above. This allows you to reference any valid ipv4 or ipv6 country code in your ruleset. Also note that elements for GeoIP sets are defined here. Remember to run "ifconfig" in a terminal and replace the <device_name> in the ingress chain above with the name of your WAN network interface reported by ifconfig, which is typically something like enp16s0f0 or eth0. You can test the file above by restarting nftables.

systemctl restart nftables

If nftables fails to restart you can check the status of nftables to see what the problem is.

systemctl status nftables

Once the new nftables.conf file is working you can edit your /etc/geoip-nft.conf file to enable nftables to automatically restart after a database update. Open the file in your favorite text editor.

nano /etc/geoip-nft.conf

Set the restart_nftables setting to yes.

restart_nftables=yes

Confirm that enable_refill=no in the configuration file (the default), since that setting is mutually exclusive with the restart_nftables setting. After editing, save the file and exit the text editor.

The last step in testing this configuration is to manually run the geoip-nft.sh script.

sudo /etc/nftables/geoip/geoip-nft.sh

You can also manually run the script using the soft link that you created in /usr/sbin:

sudo geoip-nft

The script output should show that restarting nftables completed successfully. Once this is set up, the geoip-nft.sh script will update the GeoIP database each month and restart nftables to use the updated GeoIP sets. In the event that your system is turned off during the scheduled monthly database update then the geoip-nft.sh script will run during the next system startup. The setup for this configuration is now complete.  



     If you are planning to flush and refill GeoIP sets after a database update, then set elements should be defined in /etc/nftables/geoip/refill-sets.nft. GeoIP sets are still defined in your nftables.conf file, but without elements defined. The empty sets will be filled by including the refill-sets.nft file on the last line of your nftables.conf file. With this configuration you'll want to set enable_refill=yes in /etc/geoip-nft.conf to automatically flush and refill GeoIP sets after a database update. This configuration is typically used on servers that need to maintain established connections that would be interrupted by restarting nftables.

After backing up your current nftables.conf file, copy and paste the following code to a new file /etc/nftables.conf (Ubuntu/Debian) or /etc/sysconfig/nftables.conf (Fedora/Red Hat):

nftables.conf

#!/usr/bin/nft -f

flush ruleset

table netdev filter {

  set geoip-netdev4 {
    type ipv4_addr
    flags interval
    # Leave a reminder here as to where set elements are defined.
    # Elements for this set are defined in /etc/nftables/geoip/refill-sets.nft
  }

  set geoip-netdev6 {
    type ipv6_addr
    flags interval
    # Leave a reminder here as to where set elements are defined.
    # Elements for this set are defined in /etc/nftables/geoip/refill-sets.nft
  }

  chain ingress {
    # Replace <device_name> below with the name of your WAN network interface from "ifconfig".
    type filter hook ingress device <device_name> priority 0; policy accept;
    # Count IPv4 traffic from elements defined in /etc/nftables/geoip/refill-sets.nft.
    ip saddr @geoip-netdev4 counter
    # Count IPv6 traffic from elements defined in /etc/nftables/geoip/refill-sets.nft.
    ip6 saddr @geoip-netdev6 counter
  }
}

# Your other ruleset content here...

# Include the refill-sets.nft script at the end of your nftables.conf file or
# main ruleset to fill empty GeoIP sets at system startup. Uncomment the next
# line after your refill-sets.nft script is tested manually and working.
#include "/etc/nftables/geoip/refill-sets.nft"

Note that elements for GeoIP sets are not defined in the file above. Also note that there is no GeoIP "include" line at the beginning of the file, and the refill-sets.nft script is included on the last line. This will fill the empty GeoIP sets at system startup using the same nftables script that is used for flushing and refilling the sets after a database update. Remember to run "ifconfig" in a terminal and replace the <device_name> in the ingress chain above with the name of your WAN network interface reported by ifconfig, which is typically something like enp16s0f0 or eth0. The last line of the nftables.conf file above should remain commented out until your refill-sets.nft script is tested and working per the instructions below.

You can test the nftables.conf file above by restarting nftables.

systemctl restart nftables

If nftables fails to restart you can check the status of nftables to see what the problem is.

systemctl status nftables

  

The final step in the flush and refill process is to create and test the refill-sets.nft script. Create an empty file in the base geoip directory named refill-sets.nft.

touch /etc/nftables/geoip/refill-sets.nft

Open the new file in your favorite text editor.

nano /etc/nftables/geoip/refill-sets.nft

Copy and paste the following code to the file:

refill-sets.nft

#!/usr/bin/nft -f

#=============================================================================================
# /etc/nftables/geoip/refill-sets.nft
# An nftables script to atomically flush and update geoip sets defined in your ruleset.
#
# Example: The table address family is "netdev", supporting both IPv4 and IPv6. The table name
# is "filter", and the sets to flush and update are named "geoip-netdev4" and "geoip-netdev6".
# Both sets are defined (without elements) in /etc/nftables.conf or your main ruleset.
#
# After modifiying this file, run it manually from a terminal to ensure everything works.
#
#	sudo nft -f /etc/nftables/geoip/refill-sets.nft
#

# Include country sets to reference in your ruleset.
# To include individual country sets:  include "/etc/nftables/geoip/countrysets/AQ.ipv6"
# To include only IPv4 country sets:  include "/etc/nftables/geoip/include-all.ipv4"
# To include only IPv6 country sets:  include "/etc/nftables/geoip/include-all.ipv6"
# nftables >= v0.9.4 can include all sets with: include "/etc/nftables/geoip/countrysets/*"
# To include all IPv4 and IPv6 country sets:
include "/etc/nftables/geoip/include-all.ipv4"
include "/etc/nftables/geoip/include-all.ipv6"


# Defines Section

# Define a list of ipv4 country codes (in table netdev filter) to refill your "geoip-netdev4" set with.
define elements-netdev4 = { $AD.ipv4, $BI.ipv4 }

# Define a list of ipv6 country codes (in table netdev filter) to refill your "geoip-netdev6" set with.
define elements-netdev6 = { $AD.ipv6, $BI.ipv6, $AQ.ipv6}

# Define any additional country code lists here.


# Flush your ipv4 set (in table netdev filter) named "geoip-netdev4".
flush set netdev filter geoip-netdev4

# Fill your ipv4 set (in table netdev filter) named "geoip-netdev4" with country codes defined above
# with "$elements-netdev4".
add element netdev filter geoip-netdev4 $elements-netdev4


# Flush your ipv6 set (in table netdev filter) named "geoip-netdev6".
flush set netdev filter geoip-netdev6

# Fill your ipv6 set (in table netdev filter) named "geoip-netdev6" with country codes defined above
# with "$elements-netdev6".
add element netdev filter geoip-netdev6 $elements-netdev6

# Repeat the "flush" and "add element" commands for any additional sets here.

Add the GeoIP set names defined in your ruleset that you want to flush and refill, as well as any other information necessary. After editing, save the file and exit the text editor. You can now test the refill-sets.nft script manually to ensure that your sets are properly flushed and reloaded.

sudo nft -f /etc/nftables/geoip/refill-sets.nft

If the previous command fails then you can check the status of nftables to determine the cause.

systemctl status nftables

Once you have the refill-sets.nft file working manually you'll need to uncomment the "include" line on the last line of your /etc/nftables.conf file. This will fill your GeoIP sets during system startup with set data defined in the refill-sets.nft file. Open the /etc/nftables.conf file in your favorite text editor.

nano /etc/nftables.conf

Uncomment the last "include" line in the file so it looks like this.

include "/etc/nftables/geoip/refill-sets.nft"

Save the file and exit the text editor.

The next step is to edit your /etc/geoip-nft.conf file to enable the refill script to run automatically after a database update. Open the file in your favorite text editor.

nano /etc/geoip-nft.conf

Set the enable_refill setting to yes.

enable_refill=yes

Confirm that restart_nftables=no in the configuration file (the default), since that setting is mutually exclusive with the enable_refill setting. After editing, save the file and exit the text editor. The refill-sets.nft script will now run automatically after a database update. You can test this by manually running the geoip-nft.sh script with:

sudo /etc/nftables/geoip/geoip-nft.sh

You can also manually run the script using the soft link that you created in /usr/sbin:

sudo geoip-nft

The script output should show that the flush and refill operation completed successfully. Once this is set up, the geoip-nft.sh script will update the GeoIP database each month and atomically flush and refill GeoIP sets without interrupting your server's connections. In the event that your system is turned off during the scheduled monthly database update then the geoip-nft.sh script will run during the next system startup. The setup for this configuration is now complete.  



You can also configure systemd to send you an e-mail if the monthly database update fails. Instructions can be found in this guide.  

Country Codes

   The geoip-nft.sh script builds the country code list from the database itself, instead of using a third party location file that may not be up-to-date. Generating the list directly from the latest GeoIP database file ensures that new and expired country codes are accounted for in the address range sets.

   The db-ip.com GeoIP database contains two non-standard country codes according to their FAQ. The ZZ country code is assigned to invalid address blocks that have by definition, no location and owner such as private or multicast IP addresses. The geoip-nft.sh script will exclude the ZZ country code and won't create any sets associated with this code.

   The database also contains a non-standard country code XK, which is used by the European Commission, Switzerland, the Deutsche Bundesbank, SWIFT, and other organizations as a temporary country code for Kosovo. This temporary code has been used for more than a decade, and the database contains approximately 100 IP address ranges representing tens of thousands of valid IPv4 and IPv6 addresses for this code. The geoip-nft.sh script includes the XK country code and will generate sets associated with this code.    The geoip-nft.sh script will automatically generate sets associated with any new country codes that db-ip.com may add to their database in the future.  

Notes

  • To keep things simple, the file naming convention used for country set files uses the same name for the defined country code variable as the filename itself. For example, the IPv4 set file for the United States is named US.ipv4 and contains a defined variable US.ipv4. When you reference $US.ipv4 in your ruleset you're actually referencing the defined variable name in the file rather than the filename itself. Following the same naming convention, the IPv6 set file for the United States is named US.ipv6 and contains a defined variable US.ipv6. The variable definition in each GeoIP set file contains the IP address ranges for that particular country code and IP protocol. You can open any of the GeoIP set files in your favorite text editor and view the data format as well as details such as the number of address range elements in each set. GeoIP sets are located by default in /etc/nftables/geoip/countrysets.

  • When you include GeoIP set filenames in your /etc/nftables/geoip/refill-sets.nft file, you can include all of the country code files at once to make it easier to add and remove country codes without having to include each individual country code file that you use in your ruleset. Newer versions of nftables starting with v0.9.4 have proper support for include wildcards, so you'll no longer need to include the include-all.ipv4 and include-all.ipv6 files to load all GeoIP sets even though they still work great with newer nftables versions. To include all IPv4 and IPv6 country set files with nftables v0.9.4 and above:

    include "/etc/nftables/geoip/countrysets/*"
    

    To include all IPv4 only country set files with nftables v0.9.4 and above:

    include "/etc/nftables/geoip/countrysets/*.ipv4"
    

    To include all IPv6 only country set files with nftables v0.9.4 and above:

    include "/etc/nftables/geoip/countrysets/*.ipv6"
    

    Older versions of nftables <= 0.9.3 can include all GeoIP sets with either or both of the following:

    include "/etc/nftables/geoip/include-all.ipv4"
    include "/etc/nftables/geoip/include-all.ipv6"
    

    If your system has a small amount of memory then you can save memory by only including the individual set files that you need.

  • Errors detected by the geoip-nft.sh script will be written to the log file /etc/geoip-nft-error.log. Any future errors will be appended to the end of the error log file, which can be deleted at any time. This logging is in addition to error logging done by the journal. If you experience an error, one or more of the following commands should help you determine the cause:

    journalctl -u geoip-update.service
    journalctl -xe
    systemctl status nftables
    
  • Be aware that some countries may only have IPv4 addresses or IPv6 addresses. The geoip-nft.sh script will list those country codes on the screen when run manually from a terminal. While creating your GeoIP rules it's a good idea to first check the /etc/nftables/geoip/countrysets directory to verify that the IPv4 or IPv6 country code file exists. If you reference an IPv4 or IPv6 country code variable that isn't defined then nftables will alert you.

  • Although the examples above define GeoIP sets in a netdev table, you can also define sets in ip, ip6, or inet tables to GeoIP filter incoming or outgoing packets. Outgoing packets can be rejected instead of dropped to notify the local client or app that the connection is blocked, saving them from having to wait for the connection to time out. A typical outgoing rule to count and reject IPv4 traffic that matches a GeoIP set named "geoip-inet4" would look like this:

    ip daddr @geoip-inet4 counter reject
    

    Notice that the previous example matches the destination IP address (daddr) for outgoing packets rather than the source IP address (saddr) used to match incoming packets.

  • If you would like to upgrade your version of nftables to something newer than what's available in your distribution's repository, then see the nftables Wiki for good information on building and installing nftables from sources.

  • The following examples will give you an idea of how long it takes the geoip-nft.sh script to run. The script creates ~500 IPv4 and IPv6 set files from the GeoIP database in about 10 seconds on a low power 4 core 2200ge server with SSD storage. The script takes about 80 seconds to do the same task on a Raspberry Pi 4 with SD card storage. A number of variables will affect the run time, such as processor speed, storage speed, and whether you generate IPv4 sets, IPv6 sets, or both.

  • While the geoip-nft.sh script does an extensive amount of error checking, nothing is infallible. If you find a use case that could benefit from better error checking, please use the issue tracker to report the problem.    


GeoIP for nftables documentation is licensed under the GNU GPLv2 (or at your option, any later version).

Clone this wiki locally