pvxe / nftables-geoip

Python script that generates nft maps of ip address blocks and corresponding geolocation. This data is taken from db-ip.com, so yo don't have to worry about accepting any EULA.
GNU General Public License v2.0
115 stars 17 forks source link

Performance #9

Closed mvasi90 closed 6 months ago

mvasi90 commented 6 months ago

Hello.

I am concerned about performance. Marking each packet wastes processor cycles. Subsequently, another rule must be evaluated to check the country of each packet.

Wouldn't be more efficient to directly check if the IP address exists in a set?

For example, I need to allow access to some ports only from two countries: it,de. I would add to a set the IP address ranges of both countries (together). Then I would accept incoming traffic to the port 8043 (for example), only to the IP addresses that exists in that set:

...
chain in {
   type filter hook input priority srcnat; policy drop;
   ...
   tcp dport 8043 ip saddr @itde accept
   ...
}
...

But how to adapt the nftables-geoip maps to that kind of sets?

mvasi90 commented 6 months ago

nftables-geoip is not compatible. I have created my own script.

pvxe commented 6 months ago

As you have pointed out, as of now the script is focused on using the "meta mark" feature by creating dicts that map address blocks to countries. But it seems reasonable that offering two options would be good thing (dicts for meta mark or regular sets).

I'm considering creating a "only sets" script apart from the current script or integrating both options (dicts and sets) into a single python file.

In any case, thank you for you feedback!

mvasi90 commented 6 months ago

There is a problem with the sets. The set cannot be deleted or flushed when it is in use (a rule is using it).

To deal with that, the script is doing this:

Executed with -d (or --download) parameter:

  1. Download the dbip.csv

Executed with -u (or --update) parameter:

  1. Check which of the two sets exists: geoip_allowed_a or geoip_allowed_b.
  2. Generate the deired country/ies to geoip-ipv4/6-set.nft with the geoip_allowed_ a or b set.
  3. Replace the existing rule that uses geoip_allowd_a to ..._b or vice-versa.
  4. Delete the old set.

I don't like that at all, but the current nftables does not allow runtime set flush without removing all the elements one by one. A lot of wasted resources.

If anyone have a better idea, please comment it. I will publish here my script, if you want to adapt it and integrate it to your project.

Note: I'm using two different options --download and --update because my systems are designed with security in mind. No process/daemon/user has internet access. The output policy is drop.

Any process that needs internet access is isolated in systemd service for better integration or bubblewrap.

This way I separate the internet access needed to downlaod and the nftables access in offline mode, with full isolation, without root access (using cap_net_admin).

pvxe commented 6 months ago

If anyone have a better idea, please comment it. I will publish here my script, if you want to adapt it and integrate it to your project.

I'd recommend uploading your own repo or gist and providing a link in here :)

I don't like that at all, but the current nftables does not allow runtime set flush without removing all the elements one by one. A lot of wasted resources.

This might be offtopic to this project but I have a couple questions to better understand what you're implying.

Which distro, nft version are you running and what kernel are you using? Are you concerned about the operation not being atomic or just that some elements might be removed and then added again?

The flush operation will remove all the elements, as per nft(8). But if you are concerned about the atomic nature of the set update operation then you just need to place the flush+add it in a file and execute using nft -f.

If you are concerned about the optimization of the set update operation that would probably be a question better suited for the netfilter user mailing list.

mvasi90 commented 6 months ago

I'm using Arch Linux and Gentoo. Kernel 6.7.6 nftables v1.0.9

When I'm trying to flush a set:

flush.nft:3:1-31: Error: Could not process rule: Device or resource busy
flush set inet main banned
^^^^^^^^^^^^^^^^^^^^^^

Does not matter whether it is run from a file or interactive nft. The same result, because a set cannot be flushed when is used in a rule. This is why I'm using two sets on update.

pvxe commented 6 months ago

That doesn't seem right. I'm able to flush a set that is referenced inside a rule (little example below).

Can you provide more details regarding your ruleset? I'd recommend asking about this problem of yours to the netfilter user mailing list.

$ sudo nft list ruleset
table inet filter {
    set blackhole {
        type ipv4_addr
        comment "drop all packets from these hosts"
        elements = { 1.0.169.38 }
    }

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

    chain output {
        type filter hook output priority filter; policy accept;
    }
}
$ sudo nft flush set inet filter blackhole
$ sudo nft list ruleset
table inet filter {
    set blackhole {
        type ipv4_addr
        comment "drop all packets from these hosts"
    }

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

    chain output {
        type filter hook output priority filter; policy accept;
    }
}
mvasi90 commented 6 months ago

It seems to be a bug. I can't reproduce your example. The same error. But, when I'm trying to replace a complex rule, nft exits with core dump...

I will report that. Thank you.