How can I block UDP connections from a specific IP using nftables?

I guess I don't understand why you need the drop's for the specific IP addresses when you follow it immediately with a drop for all traffic. Aren't these rules processed sequentially?

TIA


You're not wrong. But I like to maintain explicit control over things. Listing specific IP addresses before the general rule makes it clear that traffic from those IP addresses is being explicitly denied.. Improves readability of the ruleset and makes it easier to manage.

Performance considerations. Pakcet filtering frameworks are generally pretty good; but specifying rules for specific IP addresses before the gen rule may improve performance slightly if any.

Future proofing, because if you plan to add more rules or change the behaviors in the future, having specific drops listed seperately allows for more granular adjustments without needing to rewrite or rearrange the rules.

Then again, I might just be an OCD nutjob lol
 


I guess I don't understand why you need the drop's for the specific IP addresses when you follow it immediately with a drop for all traffic. Aren't these rules processed sequentially?

TIA
Another reason why explicitness is good even if redundant is because of updates you do to firewall months or years later, so you don't have to recall your intentions of dropping this or that.

Also if you happen to temporarily change default policy to accept you'll get desired result while maintaining your initial intention to drop UDP or what ever your intentions was.

So explicitness is virtue, not something bad.
 
if I wanted to block IP’s that continually bombard my SSH port and HTTPS port then I would place that DROP just before I ACCEPT access to those ports?
While that's possible it's not wise to permanently ban individual IP's because IP's change and then you might end up blocking legitimate requests.

Instead it's better to adapt and ban IP's dynamically based on TCP port connection limit per IP, for ex:

Bash:
add set filter CPS_hosts {
    type ipv4_addr
    flags dynamic
    # NOTE: Don't set too high timeout because host might spoof it's IP
    timeout 10m
    size 256
    comment "Temporary banned IP's due to CPS limit"
}

add set filter all_hosts {
    type ipv4_addr . inet_service
    flags dynamic
    timeout 10s
    size 65535
    comment "Record of host accessing ports"
}

# Is this host making new connection to same port too soon? if yes ban it's IP
add rule filter input ct state new ip saddr . tcp dport @all_hosts update @CPS_hosts { ip saddr }

# Drop connection from banned hosts and log the incident
add rule filter input ct state new ip saddr @CPS_hosts log prefix "DROP host ban: " drop

# Update port access record
add rule filter input ct state new update @all_hosts { ip saddr . tcp dport }

Note that I haven't tested this, it's not perfect, this is based on CPS DDoS detection (Connections Per Second DDoS), but here instead of detecting DDoS you're detecting excessive port connect while filtering out legitimate traffic dynamically.
Adjust all_hosts timeout as needed.
 
Last edited:
While that's possible it's not wise to permanently ban individual IP's because IP's change and then you might end up blocking legitimate requests.
To temporary ban ip's it would be better to just use fail2ban for that.
 
According to README fail2ban only handles failed logins, so it's not suitable for general traffic without login involved:
@rhumbliner said this.
I wanted to block IP’s that continually bombard my SSH port and HTTPS port then I would place that DROP just before I ACCEPT access to those ports?
If something is being bombarded there are bots hammering the ssh port or https port and it will be logged, then based on the new log file entrees fail2ban will be able to ban those ip's use filters and actions. And it's not only authentication, I have bots hammering on my mailserver sometimes just trying to send mail and when it fails too many times the ip is banned.
 
Once again, thanks to @AlphaObeisance and @CaffeineAddict for your help. This discussion led me to the documentation on Meters which was extremely enlightening.

@f33dm3bits I appreciate your input but decided to stick with an nftables solution since part of my endeavor was to learn enough about nftables to move off of iptables.

There is one thing that puzzles me. An example given in the page on Meters is this:

table ip filter {
set my_ssh_ratelimit {
type ipv4_addr
timeout 60s
flags dynamic
}
chain input {
type filter hook input priority 0; policy drop;
ct state new tcp dport 22 update @my_ssh_ratelimit { ip saddr limit rate 3/minute } accept
}
}

the condition { ip saddr limit rate 3/minute } is matched when the new connection is under 3/minute. Why shouldn't it be over 3/minute?

Another interesting discovery, for me at least, was the notion of IP leasing. I have a rule to drop all requests from non-ARIN IP blocks since my website is very local. No one outside of North America (Utah, actually) should be accessing it. But it turns out some bad actors from APNIC are using ARIN block IP's because that block was leased to APNIC by ARIN. So now I'm trying to learn about IANA leasing policies.
 
the condition { ip saddr limit rate 3/minute } is matched when the new connection is under 3/minute. Why shouldn't it be over 3/minute?
Because purpose of that rule is to allow up to 3 connections per most recent minute, any subsequent new connection within last minute that exceeds that value is NOT dropped immediately but it rather passes on to subsequent rules until either an explicit drop rule is hit or otherwise default chain action matches, which might as well be accept so you should pay attention to that because this sample is not perfect, so don't blindly copy it.

There are benefits of this, including but not limited to, preventing SSH traffic congestion caused by for example excess upload or download.
Another benefit might be preventing brute force attack against your server.

However, you should also be aware that there is another problem with this rule that the nftables wiki does not tell you!
The limit is not 3 as shown but 8, due to default burst value which is 5.

If you want to reduce burst value to zero to that limit is exactly 3 then rewrite the rule as follows:
Bash:
ct state new tcp dport 22 update @my_ssh_ratelimit { ip saddr limit rate 3/minute burst 0 } accept

burst value is "tolerance" treshold by which you may specify how much over the limit set do you tolerate.
 
Last edited:
If you want to reduce burst value to zero to that limit is exactly 3 then rewrite the rule as follows:
Bash:
ct state new tcp dport 22 update @my_ssh_ratelimit { ip saddr limit rate 3/minute burst 0 } accept
The nftables man page states:

The burst value influences the bucket size, i.e. jitter tolerance. With packet-based limit, the bucket holds exactly burst packets, by default five. If you specify packet burst, it must be a non-zero value.

So I guess I can a value of 1.
 


Top