Skip to content

Blocking countries using nftables

Recently after another day of chinese bots trying to get to my sever I had a though (unusual)- why did I even allow them to connect to my server.

So I decided that enough is enough and found this great project- nftables-geoip.

I guess there might be some chinese/russian people that visit my site, too bad for them :).

Setup

First read the readme - official documentation is really good, but you have to at least somewhat understand how nftables work to get it running. I’m not so good at network stuff, so it took me some time.

Depending on your use case, you just might want to get rid of some countries- I recommend setting up script that downloads IP data every X hours/days/weeks. Something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/bin/bash

# Path to git repo/unzipped git archive
cd /home/admin/firewall/nftables-geoip

./nft_geoip.py --download -c cn,kp,ru

cp geoip-ipv4-interesting.nft /etc/nftables.d/geoip-ipv4.nft
cp geoip-ipv6-interesting.nft /etc/nftables.d/geoip-ipv6.nft

echo "define CN = 156
define KP = 408
define RU = 643" > /etc/nftables.d/geoip-definitions.nft

Unless you use geoip-def-* files, you also need to include defines, since I only block 3 troublesome countries I put it in manually.

You can automate the step above to get up to date IPs- put the script into cron or something.

Then in your main nft file you just need to put:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Any name is fine
table inet filter {
    include "/etc/nftables.d/geoip-definitions.nft"
    include "/etc/nftables.d/geoip-ipv4.nft"
    include "/etc/nftables.d/geoip-ipv6.nft"

    # Not strictly needed, but cool to have
    counter blocked_countries {
        comment "count blocked requests from russia, china and north korea"
    }

    # Any name, hook below is the definition what comes into this
    chain input {
        type filter hook input priority 0; policy drop;

        mark set ip saddr map @geoip4
        mark set ip6 saddr map @geoip6

        # china, russia, north korea block
        mark { $CN, $RU, $KP } counter name blocked_countries drop

        [...]
        # Your other rules come here

        # allow established/related connections
        ct state {established, related} accept

        # early drop of invalid connections
        ct state invalid counter drop

        # allow from loopback
        iifname lo accept

        [...]
    }
}

After defining counter you can list all of them to see how many packets/ how much traffic was dropped with this command:

1
2
3
4
5
6
7
8
$ nft list counters

table inet filter {
	counter blocked_countries {
		comment "count blocked requests from russia, china and north korea"
		packets 8509 bytes 425791
	}
}

If you are unsure if your block is working fine you can use services that “test your website loading speed” or something worded like that- you should see an error on tcp level. I’ll leave finding these websites to the reader.