Skip to content

User Guide

wirefalls edited this page Jul 6, 2022 · 14 revisions
Geolocation for nftables

User Guide - Geolocation 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.

   Now that the installation is complete you can move on to setting up your ruleset. Beginning with v2.1.0 of Geolocation for nftables you can flush and refill your geolocation sets atomically with an nftables script that's generated automatically from user settings you create. This is the preferred method for setting up geolocation filtering. The bash script now does error checking at run time to verify that the country codes that you specify to fill your sets actually exist in the current geolocation database. This is important as the free database can drop a few of the smaller country codes in the database from month to month. Manually referencing one of these dropped country codes in your ruleset will cause an nftables error if you're not using this feature.

We'll start by listing the major steps to be done and then go into detail on completing these steps.

  • Create an nftables.conf file for testing, using one of the example files in the Wiki.
  • Running the bash script for the first time to create the refill-sets.conf configuration file.
  • Configuring the refill-sets.conf file with user settings.
  • Running the bash script again to automatically create the refill-sets.nft nftables script to flush and refill the geolocation sets.
  • Testing the configuration.
  • Testing geolocation filtering.
  • Finalize user settings with preferred country codes.

   The setup is now much easier using the refill-sets.conf file for configuration settings. This file is automatically created when you run the bash script for the first time. Settings in this file will be used to automatically generate the refill-sets.nft nftables script that will flush and refill your geolocation sets at boot time and after the monthly database update.

Setup begins with creating an nftables.conf file for testing. Three example firewall rulesets are available in the Wiki that will allow you to test geolocation filtering. Note that these are basic firewalls for testing purposes and not complete firewalls to protect your system. The three example firewalls to choose from are IPv4 only, IPv6 only and one with both IPv4 and IPv6 support.

After backing up your current nftables.conf file, copy and paste code from one of the example firewalls below to a new file /etc/nftables.conf (Ubuntu/Debian) or /etc/sysconfig/nftables.conf (Fedora/Red Hat).

Example IPv4 only firewall
Example IPv6 only firewall
Example IPv4 and IPv6 firewall

Remember to modify the new nftables.conf file that you just created to change the device_name in the ingress chain to match the name of your WAN network interface reported by running ip a or ifconfig in a terminal. Leave the last line of the file (include line) commented out until all testing is complete. You can test the new nftables.conf file 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

Next, if you have an existing refill-sets.nft script that you created manually (prior to Geolocation for nftables v2.1.0) then you should create a backup of the file, as this file is now generated automatically and will be overwritten.

cp /etc/nftables/geo-nft/refill-sets.nft /etc/nftables/geo-nft/refill-sets.nft.bak

If you ran the bash script during installation then you should have a new configuration file /etc/nftables/geo-nft/refill-sets.conf. If this file doesn't exist then you can run the bash script to create it.

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

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

sudo geo-nft

The next step is to configure the refill-sets.conf file. This configuration file will allow you to define the 'include' lines to load country sets as well as which geolocation sets you wish to fill with specific country code address ranges. By default this file is created with all settings commented out, so it will be ignored by the bash script until you define settings. To begin configuration, first determine which version of nftables that you have installed.

nft -v

Next, open the configuration file in your favorite text editor.

nano /etc/nftables/geo-nft/refill-sets.conf

Uncomment the appropriate 'include' line(s) in the first section.

If your version of nftables is >= 0.9.4 then you can uncomment the last 'include' line to include all country sets like this (works with IPv4, IPv6, or both):

# Specify 'include' lines to add to refill-sets.nft.
# You can add as many include lines as required.
# ==============================================
#include "/etc/nftables/geo-nft/include-all.ipv4"
#include "/etc/nftables/geo-nft/include-all.ipv6"
include "/etc/nftables/geo-nft/countrysets/*"

If your version of nftables is 0.9.3 or older, then you can uncomment one or both of the first two 'include' lines to include all country sets like this:

# Specify 'include' lines to add to refill-sets.nft.
# You can add as many include lines as required.
# ==============================================
include "/etc/nftables/geo-nft/include-all.ipv4"
include "/etc/nftables/geo-nft/include-all.ipv6"
#include "/etc/nftables/geo-nft/countrysets/*"

Uncomment the first two 'include' lines above if your using both IPv4 and IPv6. Uncomment just the first 'include' line if you're using IPv4 only, or the second 'include' line if you're using IPv6 only.

The next configuration step is to uncomment the appropriate line(s) in the 'define' section. The default define lines provided in this section will work with any of the example firewall rulesets in the Wiki. The geolocation set names used in this section match the set names defined in the firewall rulesets. The example country codes used were chosen randomly due to having relatively few address ranges, making it easier to list and compare the geolocation sets. Each of the example rulesets allow you to geolocation filter in both the netdev table and the main table (inet, ip or ip6) depending on which ruleset you chose.

Continue the configuration by uncommenting the appropriate 'define-ipv4' or 'define-ipv6' line(s) in the second section.

If you're using both IPv4 and IPv6, then you'll want to uncomment both of the netdev entries and both of the inet entries like this:

# Define-Protocol   Table Family   Table Name   Set Name   Country Codes To Fill Set
# ==================================================================================
# For 'netdev' table uncomment either or both of the following:
define-ipv4 netdev filter geo-netdev4 AD,BI
define-ipv6 netdev filter geo-netdev6 AQ,BI

# For 'inet' table (IPv4-IPv6) uncomment either or both of the following:
define-ipv4 inet filter geo-inet4 AD
define-ipv6 inet filter geo-inet6 AQ

# For 'ip' table (IPv4 only) uncomment the following:
#define-ipv4 ip filter geo-ip4 AD

# For 'ip6' table (IPv6 only) uncomment the following:
#define-ipv6 ip6 filter geo-ip6 AD

# Add any additional 'define-ipv4' or 'define-ipv6' lines here:

  If you're using IPv4 only, then you'll want to uncomment the first netdev entry and the single ip entry like this:

# Define-Protocol   Table Family   Table Name   Set Name   Country Codes To Fill Set
# ==================================================================================
# For 'netdev' table uncomment either or both of the following:
define-ipv4 netdev filter geo-netdev4 AD,BI
#define-ipv6 netdev filter geo-netdev6 AQ,BI

# For 'inet' table (IPv4-IPv6) uncomment either or both of the following:
#define-ipv4 inet filter geo-inet4 AD
#define-ipv6 inet filter geo-inet6 AQ

# For 'ip' table (IPv4 only) uncomment the following:
define-ipv4 ip filter geo-ip4 AD

# For 'ip6' table (IPv6 only) uncomment the following:
#define-ipv6 ip6 filter geo-ip6 AD

# Add any additional 'define-ipv4' or 'define-ipv6' lines here:

  If you're using IPv6 only, then you'll want to uncomment the second netdev entry and the single ip6 entry like this:

# Define-Protocol   Table Family   Table Name   Set Name   Country Codes To Fill Set
# ==================================================================================
# For 'netdev' table uncomment either or both of the following:
#define-ipv4 netdev filter geo-netdev4 AD,BI
define-ipv6 netdev filter geo-netdev6 AQ,BI

# For 'inet' table (IPv4-IPv6) uncomment either or both of the following:
#define-ipv4 inet filter geo-inet4 AD
#define-ipv6 inet filter geo-inet6 AQ

# For 'ip' table (IPv4 only) uncomment the following:
#define-ipv4 ip filter geo-ip4 AD

# For 'ip6' table (IPv6 only) uncomment the following:
define-ipv6 ip6 filter geo-ip6 AD

# Add any additional 'define-ipv4' or 'define-ipv6' lines here:

Configuration of this file for testing is complete, so you can save the file and exit the text editor. Next run the bash script so that it reads the refill-sets.conf settings and creates the refill-sets.nft nftables script.

sudo geo-nft

If any errors in the configuration are found, then the bash script will notify you. If not, then you're ready to manually test the refill-sets.nft nftables script.

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

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 geolocation 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. Note that the filename includes an asterisk to allow nftables to load if the file isn't found.

include "/etc/nftables/geo-nft/*refill-sets.nft"

Save the file and exit the text editor.

The next step is to edit your /etc/geo-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/geo-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 the configuration by manually running the geo-nft.sh script with:

sudo geo-nft

The script output should show that the flush and refill operation completed successfully. Once this is set up, the geo-nft.sh script will update the geolocation database each month and atomically flush and refill geolocation sets without interrupting your system's connections to other computers. In the event that your system is turned off during the scheduled monthly database update then the geo-nft.sh script will run during the next system startup. This configuration is now ready for geolocation testing.

Testing geolocation filtering

To test geolocation filtering open the refill-sets.conf file in your favorite text editor.

nano /etc/nftables/geo-nft/refill-sets.conf

Add your own country code to the define line(s) in your main table so that you block outgoing traffic to your own country. For this example we'll assume you're located in the US and you're using both IPv4 and IPv6, so your main table is inet. Your define lines in the inet table currently look like this:

# For 'inet' table (IPv4-IPv6) uncomment either or both of the following:
define-ipv4 inet filter geo-inet4 AD
define-ipv6 inet filter geo-inet6 AQ

Add the US country code to the end of each line since the US has both IPv4 and IPv6 addresses.

# For 'inet' table (IPv4-IPv6) uncomment either or both of the following:
define-ipv4 inet filter geo-inet4 AD,US
define-ipv6 inet filter geo-inet6 AQ,US

Save the file and exit the text editor. Changes to the refill-sets.conf file won't take affect until the bash script is run, so launch the bash script to generate a new refill-sets.nft script that will flush and fill geolocation sets with the new address ranges.

sudo geo-nft

Next, open a Web browser and visit a website outside of the US, such as the CERN website https://home.cern. You should be able to view this website. Now visit a website in the US, such as https://www.google.com. If this site is unavailable, then geolocation filtering is working properly. Keep in mind that websites that appear to be located in your country may actually be hosted in a data center outside of your country. Likewise, websites that appear to be located outside of your country may be hosted in a data center that's inside your country.

You can list your output chain to see how many packets have been rejected by your geolocation rules. Replace inet in the command below with ip if you're using IPv4 only, or ip6 if you're using IPv6 only.

sudo nft list chain inet filter output

Now that testing is complete you'll want to do your final configuration changes to add the country codes that you wish to geolocation filter with. You'll also want to add your standard ruleset rules and make any other changes necessary. Remember to run the bash script after making any changes to the refill-sets.conf file so that your changes take effect.

If you only want to geolocation filter incoming traffic then you can remove the geolocation sets/rules in your main table and keep the ones in the netdev table. You can list your ingress chain in the netdev table to see how many packets have been rejected by your geolocation rules. The following command works with any of the example rulesets.

sudo nft list chain netdev filter ingress

The geo-nft.sh script will now update the geolocation database each month and atomically flush and refill geolocation sets without interrupting your computer's connections. In the event that your system is turned off during the scheduled monthly database update then the geo-nft.sh script will run during the next system startup. The setup for this configuration is now complete. If you like Geolocation for nftables please consider giving the project a Star at the top of the main project page, as this allows people to find us easier here on GitHub.  



Notifications

A systemd path unit can be set up to monitor the /var/log/geo-nft-error.log file and notify you if a runtime error occurs with the bash script. This guide has details.

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 geo-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 geolocation database file ensures that new and expired country codes are accounted for in the address range sets.

   The db-ip.com geolocation 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 geo-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 geo-nft.sh script includes the XK country code and will generate sets associated with this code.    The geo-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

  • Geolocation for nftables v2.1.0 or greater now checks all country codes referenced in the refill-sets.conf file to verify that address ranges for those country codes exist in the updated database each month, preventing nftables errors. The refill-sets.nft script is now created on-the-fly to flush and fill your geolocation sets. The old method of manually creating the refill script is no longer recommended since it doesn't do any country code error checking.

  • 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 geolocation set file contains the IP address ranges for that particular country code and IP protocol. You can open any of the geolocation 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. Geolocation sets are located by default in /etc/nftables/geo-nft/countrysets.

  • By default the base directory for project files is /etc/nftables/geo-nft. If you change the base directory defined in the user configuration file /etc/geo-nft.conf, avoid using a pathname with spaces, links or special characters as this may cause the script to fail.

  • Errors detected by the geo-nft.sh script will be written to the log file /var/log/geo-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 geo-update.service
    journalctl -xe
    systemctl status nftables
    
  • Be aware that some countries may only have IPv4 addresses or IPv6 addresses. The geo-nft.sh script will list those country codes on the screen when run manually from a terminal. While creating your geolocation rules it's a good idea to first check the /etc/nftables/geo-nft/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.

  • 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 geo-nft.sh script to run. The script creates ~500 IPv4 and IPv6 set files from the geolocation database in about 11 seconds on a low power 4 core 2200ge server with SSD storage. The script takes about 90 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 geo-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.    

Troubleshooting

The following commands can help you troubleshoot your nftables ruleset. They will allow you to see how many packets match each firewall rule that uses a counter. The examples below support both IPv4 and IPv6, so each command uses the inet table family. You can replace inet with ip or ip6 if you're using IPv4 only or IPv6 only, respectively.

To list your input chain:

sudo nft list chain inet filter input

To list your output chain:

sudo nft list chain inet filter output

To list the set geo-inet4 in table inet filter:

sudo nft list set inet filter geo-inet4

To list the set geo-inet6 in table inet filter:

sudo nft list set inet filter geo-inet6

To list all sets in table inet filter:

sudo nft list sets inet filter

To list your entire ruleset including set elements:

sudo nft list ruleset

To list your entire ruleset without displaying set elements:

sudo nft -t list ruleset

   


Logging

   An additional troubleshooting method is to employ logging. If you want to see which packets don't match any of the rules in your input chain, add a rule just above the final rule so that it logs some of those packets (with a prefix) to your system log. Then you can grep your system log using the prefix to see which packets don't match your input chain rules. This is the rule to add, which also shows the existing final rule in your input chain:

# Log some of the packets (using a prefix) that don't match any rules in the input chain.
limit rate 3/second log prefix "[nft] input no match: " counter comment "Log and rate limit packets that don't match any input rules"

# Drop all other incoming traffic.
counter drop comment "Drop all other incoming traffic"

Then grep your system log for a list of packets that don't match any input rules. Substitute the pathname of your system log if different from the example below.

sudo grep -i -F '[nft] input no match:' /var/log/syslog

You can also add a similar rule to other chains, changing 'input' in the prefix to match the chain name.


Listening Ports

   Another good troubleshooting tip is to list all of the ports that your server is listening to, so that you can verify that your firewall rules match those ports, and protect all of them.

sudo netstat -ltup

You can print service names instead of port numbers by adding the -n flag.

sudo netstat -lntup

Nmap can also be used to give information about your server ports.

sudo nmap -n -PN -sT -sU -p- localhost

   


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