fail2ban icon indicating copy to clipboard operation
fail2ban copied to clipboard

Support to ban subnets

Open K1LLUM1N471 opened this issue 10 years ago • 45 comments

Hello,

in which case does fail2ban support to ban subnets. I used the instruction from http://cup.wpcoder.de/fail2ban-ip-blacklist/ and entered the subnet instead of the single IP, but fail2ban seems not to handle them at all.

Kind regards, K1LLUM1N471

K1LLUM1N471 avatar Jan 26 '15 15:01 K1LLUM1N471

I'm not sure where on that page it refers to subnets being blacklisted (ignoring /32 as a subnet). Generally only one IP fails to authenticate and subsequently gets blacklisted by fail2ban. Do you have a specific use case you are attempting to achieve? How would the subnet be determined from a single IP failing to log in?

leeclemens avatar Jan 28 '15 00:01 leeclemens

There is no specific use case, just a server in a sensitive area of research and development, which has to be secured. The main reason is, to block iterated unauthorised access from foreign countries e.g. China, USA). The determination of the associating subnet is currently done by hand (Google + Subnet calculator).

Kind regards, K1LLUM1N471

Am 28.01.2015 um 01:27 schrieb Lee Clemens:

I'm not sure where on that page it refers to subnets being blacklisted (ignoring /32 as a subnet). Generally only one IP fails to authenticate and subsequently gets blacklisted by fail2ban. Do you have a specific use case you are attempting to achieve? How would the subnet be determined from a single IP failing to log in?

— Reply to this email directly or view it on GitHub https://github.com/fail2ban/fail2ban/issues/927#issuecomment-71758441.

K1LLUM1N471 avatar Feb 02 '15 13:02 K1LLUM1N471

@K1LLUM1N471 I have a branch doing that, at the moment written relative #716 (as part of increment feature - ban-time-incr within factor plugin for observer). It can be easy configured in [jail] resp. [default] or as include in it, here is an example:

...
# If geo feature enabled (dictionaries "geo.country" or "geo.region" are specified):
#   - for each failure this factor appears like a simple "divider" for "maxretry" (inside of "findtime" interval);
#   - for time of each ban it is a simple multiplier coefficient besides "bantime.factor" ("jail.Factor"), 
#     to change default behavior use "ban.Factor" in expression "bantime.formula".

# The larger value of factor leads to faster ban (few failures) and longest time of ban by increase.

# "geo.country" - dictionary to define factor by country of IP address (country:factor pair)
#               (using of this method needs a Country, Region, or City database)
#geo.country =
geo.country = default:10
              DE:1 RU:2
...

Example above means - the factor for each country is 10, however for DE it is 1 and RU it is 2.

This feature is configurable to use geoip databases or cymru-like dns resolvers...

I will commit it this or next week and let know if as far as.

sebres avatar Feb 02 '15 14:02 sebres

@K1LLUM1N471 Can you help me understand "block iterated unauthorised access". Do you mean if an IP from RU gets banned, all IP space from RU gets banned? Or if one IP gets banned, for fail2ban to ban the entire subnet it is a member of (as allocated by the RIR)?

leeclemens avatar Feb 04 '15 02:02 leeclemens

@sebres Thanks for info. How reliable is the identification of a IP this way?

@leeclemens I guess it is the second case. When one IP got banned, all the other IPs from this subnet got banned, too.

For this reason I would prefer to set manually the array of IPs or subnets by the use of a blacklist, which is interpreted by Fail2Ban. For example: "subnet.blacklist" > "198.27.100.224/29" or "ip.blacklist" > "198.27.100.224 - 198.27.100.231"

Ban array of IPs by subnet or by defined array

Kind regards, K1LLU1N471

K1LLUM1N471 avatar Feb 11 '15 11:02 K1LLUM1N471

@K1LLUM1N471

How reliable is the identification of a IP this way?

Good question, I would say how outdated the geoip database is. This applies of course to cymru-like services also.

But this impacts failures/bans only, so if IP makes no failures - it country will not be taken into account. After putting this into service I have decreased a value of banTime to 1m (because initial value, will be incremented + factored), to prevent a banning for long time for first failures from "foreign" countries.

sebres avatar Feb 13 '15 10:02 sebres

I +1 this request. I found that some China subnets work together for ssh-bruteforce : when one IP is banned an other one (very close to the 1st one) continues few minutes after. To handle this I created an ad hoc script that takes IP to ban and check in a subnet list (from a file) and returns a subnet ban to iptables (i.e. X.Y.Z.W/24 rather than just X.Y.Z.W). And of course the same for deban. But it would be better if this can be handled directly from fail2ban, because with my approach it don't consider several attempts from different IPs (in the subnet) as counting for the subnet ban: one IP must trigger the ban.

Regards,

Hexasoft

Hexasoft avatar May 11 '15 10:05 Hexasoft

You can try replacing <ip> in your action(s) with this:whois <ip> | grep route: | awk '{print $2}'. It will ban the whole subnet according to the whois data, not only /24 which may be not enough.

justabaka avatar May 11 '15 11:05 justabaka

As I had the same concern as you seem to have, I tried myself at making a special subnet banning system using fail2ban, called fail2ban-subnets, that you can find here. It's a simple python script that you can configure quite easily and that will use the fail2ban.log file to identify subnets and log the ones that have a consequent (configurable) number of bans and IPs involved. It only supports banning up to /24 subnets currently as I didn't want to make it find too big subnets if there's innocent IPs in that range. I plan however on making that configurable too in the future if some people would need it. I'm using it on a few servers currently and it works like a charm! Hope this helps.

xaf avatar Aug 02 '15 21:08 xaf

I see reference by sebres using geoip to modify behaviour based on country of origin. Is this a concept that will be added to fail2ban soon?

> # If geo feature enabled (dictionaries "geo.country" or "geo.region" are specified):
> #   - for each failure this factor appears like a simple "divider" for "maxretry" (inside of "findtime" interval);
> #   - for time of each ban it is a simple multiplier coefficient besides "bantime.factor" ("jail.Factor"), 
> #     to change default behavior use "ban.Factor" in expression "bantime.formula".
> 
> # The larger value of factor leads to faster ban (few failures) and longest time of ban by increase.
> 
> # "geo.country" - dictionary to define factor by country of IP address (country:factor pair)
> #               (using of this method needs a Country, Region, or City database)
> #geo.country =
> geo.country = default:10
>               DE:1 RU:2

infotek avatar Jun 06 '16 23:06 infotek

What i do every few months and that i'd like to be upstream is permaban full subnets after too many recidives.

It goes like ban an ip for a few minutes, unban it do this a few times you hit a recidive rule and get banned for a day a few dozen ip from the same subnet hit the recidive rule during a month, permaban the subnet ... no issues so far

it takes like 4_5_20 = 400 attempts before getting a permaban (didnt checked my exacts rules) Seems fair to me; it takes a lot of attempts during an extended period of time to get permabaned Any legitimate admin/user would notice an issue beforehand and the attempts would stop.

It works because these guys use whole ip ranges, are very persistant and switch to another ip of their subnet once they are hit by a recidive rule.

tbh 99 of the ips are from china, it s just that i thought it was too discrimnatory to geoban china

iptables-save | grep fail2ban |grep '/32' | wc -l 206 iptables-save | grep fail2ban |grep '/24' | wc -l 217 iptables-save | grep fail2ban |grep '/16' | wc -l 21

I ban /16 if there s too much /24 banned as reported by a whois

ptempier avatar Oct 05 '16 17:10 ptempier

Using @justabaka Idea, this works for me:

actionban = whois <ip> | grep route: | awk '{print $2}' | xargs -n1 -I{} iptables -I fail2ban-<name> 1 -s {} -j <blocktype>

actionunban = whois <ip> | grep route: | awk '{print $2}' | xargs -n1 -I{} iptables -D fail2ban-<name> 1 -s {} -j <blocktype>

rodrigorojasmoraleda avatar Nov 24 '16 11:11 rodrigorojasmoraleda

@rodrigorojasmoraleda: this solution is really interesting, but unfortunately not generic enough.

Indeed, in my experience, I've seen that each whois server replies with its own format. The string "route:" does not necessarily appear in the answer. Thus many IP will get through your fail2ban.

For instance, using the 3 last IP addresses that attacked my companies' servers:

$ whois 14.119.66.49 | grep -F 'route:'
$ whois 139.227.188.103 | grep -F 'route:'
$ whois 138.219.29.18 | grep -F 'route:'

I've seen whois answers with "CIDR" or "inetnum" or "route", and generally they do not use the / notation, but an ip range :{

I'm sure it's possible to write a rule to manage all possible cases, but it would require to know them all. Also since the rule would be complicated, it would probably be better to wrap it inside a module/plug-in.

liar666 avatar May 02 '17 11:05 liar666

When I use : actionunban = whois | grep route: | awk '{print $2}' | xargs -n1 -I{} iptables -D fail2ban- 1 -s {} -j Then met below error :

2017-06-09 17:37:01,323 fail2ban.action         [26026]: ERROR   whois 159.226.71.236 | grep route: | awk '{print $2}' | xargs -n1 -I{} iptables -w -D f2b-sshd 1 -s {} -j REJECT --reject-with icmp-port-unreachable -- stderr: b"iptables v1.6.0: Illegal option `-s' with this command\n\nTry \`iptables -h' or 'iptables --help' for more information.\n"

So removed 1 then it is ok. (Can anyone give some comment for this?)

Also found that results of whois use different format & keyword by each hosting carrier such as route/CIDR/inetnum IPv4 etc. So wrote below script to handle this - referred several web pages(CIDR regex etc.)

#!/bin/bash
debug=0
checkCIDR="^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$"
checkIP="^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$"

function debugEcho()
{
    if [ $debug == 1 ] ; then
        echo "$*" 1>&2
    fi
}
function findCIDR()
{
    if [ ! -z "$1" ]; then 
        cidrData="$1"
        if [[ ! "$cidrData" =~ $checkCIDR ]]; then
            debugEcho "Try xx.xx/xx type"
            cidrData=`echo $1 | sed -E 's/^([0-9]{1,3}\.[0-9]{1,3})(\/([0-9]|[1-2][0-9]|3[0-2]))/\1.0.0\2/'`
            if [[ ! "$cidrData" =~ $checkCIDR ]] ; then
                debugEcho "$cidrData is not CIDR"
                debugEcho "Try xx.xx.xx/xx type"
                cidrData=`echo $1 | sed -E 's/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})(\/([0-9]|[1-2][0-9]|3[0-2]))/\1.0\2/'`
                if [[ ! "$cidrData" =~ $checkCIDR ]] ; then
                    debugEcho "$cidrData is not CIDR"
                    cidrData=""
                else
                    debugEcho "$cidrData is CIDR"
                fi
            else
                debugEcho "$cidrData is CIDR"
            fi
        else
            debugEcho "$cidrData is CIDR"
        fi
    else
        cidrData=""
    fi
    echo $cidrData
}
function findRange()
{
    localSubnet=""
    rangeData="$*"
    debugEcho "rangeData : $rangeData"
    # Check Range or not
    IP_1=`echo $rangeData | awk '{print $1}'`
    IP_2=`echo $rangeData | awk '{print $3}'`
    debugEcho "IP_1 : $IP_1"
    debugEcho "IP_2 : $IP_2"
    if [[ "$IP_1" =~ $checkIP && "$IP_2" =~ $checkIP ]]; then
        localSubnet=`ipcalc $rangeData | grep -m 1 -v deaggregate | awk '{print $1}'`
        if [[ ! "$localSubnet" =~ $checkCIDR ]]; then
            debugEcho "$localSubnet is not CIDR"
            localSubnet=""
        else
            debugEcho "$localSubnet is CIDR"
        fi
    else
        debugEcho "Invald Range"
    fi
    echo $localSubnet
    
}
function getSubnet()
{
    IP="$1"
    debugEcho "Input : $IP"
    debugEcho "Try Keyword : route"
    whoisResult=$(whois $IP)
    Subnet=`echo "$whoisResult" | grep -m 1 route: | awk '{print $2}'`
    if [ -z "$Subnet" ]; then 
        debugEcho "Keyword route result for $IP is null"
        debugEcho "Try Keyword : CIDR"
        Subnet=`echo "$whoisResult" | grep -m 1 CIDR | awk '{print $2}'`
        debugEcho "CIDR result is $Subnet"
        Subnet=$(findCIDR $Subnet)
    fi
    if [ -z "$Subnet" ]; then 
        debugEcho "Keyword CIDR is null"
        debugEcho "Try Keyword : inetnum"
        NetRange=`echo "$whoisResult" | grep -m 1 inetnum: | awk '{print $2 " - " $4}'`
        if [ ! -z "$NetRange" ]; then
            Subnet=$(findRange $NetRange)
            if [ -z "$Subnet" ]; then
                # inetnum result is not Range, check whether CIDR or not
                debugEcho "Check CIDR of inetnum ($NetRange)"
                Subnet=`echo $NetRange | awk '{print $1}'`
                Subnet=$(findCIDR $Subnet);
            fi
        else
            debugEcho "Keyword inetnum result for $IP is null."
        fi
    fi
    if [ -z "$Subnet" ]; then 
        debugEcho "Try Keyword : IPv4"
        NetRange=`echo "$whoisResult" | grep -m 1 "IPv4 Address" | awk '{print $4 " - " $6}'`
        debugEcho "Result of IPv4 is $NetRange"
        if [ ! -z "$NetRange" ]; then
            Subnet=$(findRange $NetRange)
        else
            debugEcho "Result of IPv4 for $IP is null try NetRange"
            Subnet=""
        fi
    fi
    if [ -z "$Subnet" ]; then 
        debugEcho "Try Keyword : NetRange"
        NetRange=`echo "$whoisResult" | grep NetRange | awk '{print $2 " - " $4}'`
        Subnet=$(findRange $NetRange)
        if [ -z $Subnet ]; then
            Subnet=`echo $Netrange | awk '{print $1}'`
            Subnet=$(findCIDR $Subnet)
        fi
    fi
    if [ -z "$Subnet" ]; then 
        debugEcho "Couldn't find Subnet, Use IP instead"
        Subnet=$IP
    fi
    echo $Subnet
}

if [ ! -z "$1" ]; then
    if [ -f "$1" ]; then
        while read -r line
        do
            result=$(getSubnet $line)
            debugEcho $line : $result
            echo $result
        done < $1
    else
        if [[ "$1" =~ $checkIP ]]; then
            result=$(getSubnet $1)
            debugEcho "$1 : $result"
            echo $result
        else
            debugEcho "Invalid IP : $1"
            echo INVALID
        fi
    fi
fi

iptable-allports.conf & iptable-multiport.conf

actionban = f2b.getSubnet.sh <ip> | xargs -n1 -I{} <iptables> -I f2b-<name> 1 -s {} -j <blocktype>
actionunban = f2b.getSubnet.sh <ip> | xargs -n1 -I{} <iptables> -D f2b-<name> -s {} -j <blocktype>

will be better if those are included in the module/plug-in.

toreit avatar Jun 12 '17 08:06 toreit

Right now (Fail2Ban v0.9.6) we have

[jno@git fail2ban]$ sudo fail2ban-client -vvv set sshd banip 61.177.0.0/16
INFO   Loading configs for fail2ban under /etc/fail2ban 
DEBUG  Reading configs for fail2ban under /etc/fail2ban 
DEBUG  Reading config files: /etc/fail2ban/fail2ban.conf
INFO     Loading files: ['/etc/fail2ban/fail2ban.conf']
Level 7     Reading file: /etc/fail2ban/fail2ban.conf
INFO     Loading files: ['/etc/fail2ban/fail2ban.conf']
Level 7     Shared file: /etc/fail2ban/fail2ban.conf
INFO   Using socket file /var/run/fail2ban/fail2ban.sock
DEBUG  OK : '61.177.0.0/16'
DEBUG  Beautify '61.177.0.0/16' with ['set', 'sshd', 'banip', '61.177.0.0/16']
61.177.0.0/16

It could get into the database:

[jno@git fail2ban]$ echo 'select distinct ip from bans;' | sudo sqlite3 /var/lib/fail2ban/fail2ban.sqlite3 | grep 61.177
61.177.0.0/16
61.177.172.19
61.177.172.34

And finally:

[jno@git fail2ban]$ sudo iptables-save | grep 61.177
-A f2b-sshd -s 61.177.0.0/16 -j REJECT --reject-with icmp-port-unreachable
-A f2b-sshd -s 61.177.172.34/32 -j REJECT --reject-with icmp-port-unreachable

So, should it be closed now?

jn0 avatar Jun 16 '17 16:06 jn0

My point of view on the original issue and need for this, and maybe that can help decide if you can close it.

Some hackers have controls of /24 and /16. The pattern is that if fail2ban bans an ip, their tools will understand this, and switch to another ip try to continue to connect. In these cases, fail2ban is of little uses because it ban individual ip. So what is needed is to ban the whole block to stop it.

Personally one thing i would like to see before the issue is closed is more integration. Like what has been done for recidive (no need to fiddle yourself in the logs, you can just enable recidive in fail2ban conf).

Maybe allow to enable actions/detection to manage things as subnet. Like If the subnet hit 50 failed attempts or as already been banned 50 times, ban subnet?

ptempier avatar Jun 17 '17 10:06 ptempier

Good idea. But one have to decide on the source for that "subnets". Whois on various servers? RA.net? Looking glasses? How long should the found values be cached? Should the whole "criminal" AS be banned (route to null would be fine here) along with its IPs?

jn0 avatar Jun 19 '17 07:06 jn0

My process is manual. But what about toreit script to manage subnets?

Also maybe with an automated process, it s not necessary to handle anything else than /24 range as x.x.x.0-255 which are 90% of these ban anyway. As fail2ban would handle the ban in realtime and not every few months like me.

I permaban after about 400 attempts. If the process was automated maybe fail2ban could ban faster; unban after 1week or 1month then reban if needed to avoid to keep a very long list.

ptempier avatar Jun 23 '17 00:06 ptempier

FYI,

During 10 days, I got results using my script above.

fail2ban-client status sshd & recidive

Status for the jail: sshd
|- Filter
|  |- Currently failed: 22
|  |- Total failed:     419
|  `- File list:        /var/log/auth.log
`- Actions
   |- Currently banned: 12
   |- Total banned:     57
   `- Banned IP list:   181.21.52.102 45.55.207.181 91.134.133.251 81.228.144.32 195.14.163.214 171.244.18.196 188.22.239.233 216.243.62.206 74.74.132.156 186.62.15.142 201.254.78.45 190.48.114.151

Status for the jail: recidive
|- Filter
|  |- Currently failed: 42
|  |- Total failed:     113
|  `- File list:        /var/log/fail2ban.log
`- Actions
   |- Currently banned: 27
   |- Total banned:     27
   `- Banned IP list:   103.89.88.182 104.131.50.215 111.40.166.130 113.252.218.224 115.195.119.3 117.0.12.108 121.150.125.212 125.227.128.173 171.49.178.208 182.100.67.120 186.121.240.62 189.44.10.114 198.0.148.211 2.177.129.3 209.93.132.216 211.44.43.165 212.83.142.251 45.243.21.106 61.177.21.226 64.179.211.161 81.137.204.43 86.104.15.15 93.149.10.162 93.187.16.70 94.233.189.208 103.68.42.168 103.79.141.150

iptables -L -n

Chain f2b-recidive (1 references)
target     prot opt source               destination         
REJECT     all  --  103.79.140.0/22      0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  103.68.40.0/22       0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  94.233.184.0/21      0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  93.187.16.0/21       0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  93.149.0.0/16        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  86.104.15.0/24       0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  81.128.0.0/12        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  64.179.208.0/20      0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  61.177.0.0/16        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  45.240.0.0/13        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  212.83.128.0/19      0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  211.44.0.0/17        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  209.93.0.0/16        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  2.177.0.0/16         0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  198.0.0.0/16         0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  189.44.0.0/16        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  186.121.192.0/18     0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  182.96.0.0/12        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  171.49.160.0/19      0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  125.224.0.0/13       0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  121.128.0.0/11       0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  117.0.0.0/13         0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  115.194.0.0/15       0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  113.252.0.0/14       0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  111.0.0.0/10         0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  104.131.0.0/16       0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  103.89.88.0/22       0.0.0.0/0            reject-with icmp-port-unreachable
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           

Chain f2b-sshd (1 references)
target     prot opt source               destination         
REJECT     all  --  190.48.0.0/16        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  201.254.0.0/16       0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  186.60.0.0/14        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  74.64.0.0/12         0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  216.243.0.0/18       0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  188.20.0.0/14        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  171.224.0.0/11       0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  195.14.160.0/19      0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  81.224.0.0/12        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  91.134.0.0/16        0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  45.55.0.0/16         0.0.0.0/0            reject-with icmp-port-unreachable
REJECT     all  --  181.20.0.0/14        0.0.0.0/0            reject-with icmp-port-unreachable
RETURN     all  --  0.0.0.0/0            0.0.0.0/0           

toreit avatar Jun 23 '17 01:06 toreit

As I see the conversation is still running here, just a following comment to my previous one https://github.com/fail2ban/fail2ban/issues/927#issuecomment-127075107 to say that I'm still using my fail2ban-subnets tool on a few servers and that it still work to protect them. I am also still actively supporting it to improve it as needed.

xaf avatar Jun 23 '17 04:06 xaf

I agree with XaF, I'm also still actively supporting an improvement of the subnet banning feature.

What I would see as important features:

  1. easy ban by geoip Indeed, China is the source of 99% of attacks on my companies' server and we don't do business with china, so being able to simply bann china as a whole woulb be great.

  2. easy ban of the whole subnet of the originating IP. But this would not act by blindly, by banning /xx , rather "intelligently", using the "whois" solution, but taking into account that different whois servers reply in different manners ("route:"/"CDIR:"/"inetnum:"/... , followed with either an IP-range or a subnet "shortcut")

liar666 avatar Jun 23 '17 08:06 liar666

Please do support ban by network number. Many attackers change the IP address to circunvent fail2ban, with IP address within a range.

ramon-garcia avatar Sep 26 '17 12:09 ramon-garcia

Reading over this I don't think I saw this suggested -- would it make more sense rather than banning a /24 when a particular /32 within the /24 hits a threshold to instead keep track of the entire /24 and when the aggregate /24 hits the threshold, ban the /24?

In a sense you would be treating the /24 as a single IP.

rrauenza avatar Dec 27 '17 17:12 rrauenza

I support @rrauenza's suggestion as an option alongside single-IP banning -- what would be REALLY great is an option to track failures on a given /24 subnet (or, even better, the entire CIDR determined from whois) so that it can track attackers who are using subnets to evade single-IP-based tracking. @XaF's and @toreit's scripts require the IPs to already have been banned, but many modern distributed botnets push an attack through a given IP only once every few hours, or maybe days, so single IPs may never get banned. But tracking failures per subnet would help to combat this.

So, for example, one could have:

findtime_perIP = 600 ; 10 minutes
maxretry_perIP = 3
findtime_perSubnet = 604800 ; 1 week
maxretry_perSubnet = 30

so that a given IP is banned for 3 failures in 10 minutes, but an entire subnet is banned for 30 failures (from that subnet) in 1 week even if no single IP within that subnet ever met the perIP ban criteria. This can help to combat these "slow burn" distributed botnets that use multiple IPs per subnet.

I wouldn't recommend JUST tracking the subnet because you don't want to ban an entire subnet when only one IP has failed... but tracking both single-IP and subnet, with different findtime/maxretry for each, allows knocking out single-IP attacks on a short-term basis while simultaneously handling subnet attacks longer-term.

cepheid666 avatar Apr 02 '19 21:04 cepheid666

Is there any solution for #2627 and https://github.com/fail2ban/fail2ban/issues/927#issuecomment-479216812 I am a little confused because it seems that there are two things mixed in this old tread: Banning a complete subnet when a some IPs of the class C net are already banned, and findig and recognicing a spamming subnet when the spammer is using one ip less than f.e three times in during the find time.

etron770 avatar Feb 10 '20 17:02 etron770

You have surely noticed that this is still open?

In fact there is a couple of approaches that could help to "solve" (well rather implement) that, but still nothing to "automatically recognize a subnet and ban it" or "let automatically grow a subnet starting from single IP". So the answer to your question is rather - No.

If you want to do banning of subnet per default (every ban of single IP causes a ban of some predefined subnet), you can either try to use an action that support subnet banning or even extend some fail2ban's action by yourself or write your own action.

Simplest local config for such action (here iptables-multiport.local) may look like:

[Definition]
actionban = <iptables> -I f2b-<name> 1 -s <ip>/24 -j <blocktype>
actionunban = <iptables> -D f2b-<name> -s <ip>/24 -j <blocktype>

But it'd look differently for different actions (we have no common tag or parameter in actions for subnet at the moment).

Pull request #2560 solves only part of this. so only for the case if the filter could capture a subnet, and an action would support the same notation for the subnet (e. g. if filter captures 192.0.2.1/24 as failure-ID, the action should be able to ban the subnet using this 192.0.2.1/24 notation).

As for #2627 - it was closed as duplicate of this.

there are two things mixed in this old tread findig and recognicing a spamming subnet

This issue has nothing with "spamming subnet". Fail2ban is able to recognize every kind of failures (spamming inclusive) if some filter allow that, but the main word of this issue is "subnet" (not the "spamming", what does not matter at all).

What you're noticed as a mix - is one of many prerequirements that would help to implement this enhancement.

sebres avatar Feb 10 '20 18:02 sebres

Thanks for your answer. as a quick and dirty apporach i created a batch script to build a logfile to find subnet spammers. Those Ips do not hit any other jail mostly because of spamming/attacing from a subnet. Maybe there will be some false positive, but still not - all of them are subnet attacs/spammer If I run the script the new IPs will be recogniced (and banned) (the script could be called in a cronjob)

#!/bin/bash
# first initialize
# mkdir /var/log/fail2ban.subnets
# touch /var/log/fail2ban.subnets/fail2ban.sub.log
# touch /var/log/fail2ban.subnets/fail2ban.processed.log
# touch /var/log/fail2ban.subnets/fail2ban.sub.log.diff
rm /var/log/fail2ban.subnets/fail2ban.sub.log.diff
touch /var/log/fail2ban.subnets/fail2ban.sub.log.diff
diff /var/log/fail2ban.log /var/log/fail2ban.subnets/fail2ban.processed.log >> /var/log/fail2ban.subnets/fail2ban.sub.log.diff
filename='/var/log/fail2ban.subnets/fail2ban.sub.log.diff'
n=1
while read ip; do
# reading each line
# ip=$1
# 2020-02-11 09:57:26,961 fail2ban.actions        [24467]: NOTICE  [courier-auth] Unban 119.167.182.138
# search for 4th dot and cut

baseip=`echo $ip | cut -d"." -f1-4`
sub="Found"
if [[ "$baseip" == *"$sub"* ]]; then
echo $baseip".0" >> /var/log/fail2ban.subnets/fail2ban.sub.log
fi
done < $filename
cp /var/log/fail2ban.log /var/log/fail2ban.subnets/fail2ban.processed.log

Jail .local:

banaction_subnets = iptables-subnets
....
[subnets]
maxretry = 50
enabled  = true
logpath  = /var/log/fail2ban.subnets/fail2ban.sub.log
banaction = %(banaction_subnets)s
#for testing
bantime  = 180  ;
#live
#bantime  = 604800  ; 1 week
findtime = 86400   ; 1 day

I used for banaction_subnets.conf the iptables-allports.conf genommen and changed

actionban = <iptables> -I f2b-<name> 1 -s <ip>/24 -j <blocktype>
actionunban = <iptables> -D f2b-<name> -s <ip>/24 -j <blocktype>

Finding the ips is working but they are banned als single ip xxx.yyy.zzz.0 Thats the point where i am looking for the error since a a couple of hours now

etron770 avatar Feb 11 '20 16:02 etron770

they are banned als single ip xxx.yyy.zzz.0

Hmm... possibly you just misinterpret the output of iptables. Here is a test dropping some test subnet:

$ sudo iptables -w -I INPUT 1 -s 192.0.2.123/24 -j DROP
$ sudo iptables -nL INPUT | grep 192.0.2
DROP       all  --  192.0.2.0/24         0.0.0.0/0
$ sudo iptables -w -D INPUT -s 192.0.2.123/24 -j DROP

So if you mean this automatic wrapping of 192.0.2.123/24 to 192.0.2.0/24 - this is pretty correct, because the CIDR that corresponding the mask /24 implies removing of last 8 bits from the address (32 bits - 24 bits) - they are simply irrelevant by this mask. Please take a look at wikipedia CIDR-de (or CIDR-en)

Otherwise I would like to see your excerpt (output of iptables), what you exactly meant.

sebres avatar Feb 11 '20 16:02 sebres

thank you for your help, there is something what I do not understand doing the banning wiht /24 (if it is really working) the result is:

$ fail2ban-client status subnets
Status for the jail: subnets
|- Filter
|  |- Currently failed: 805
|  |- Total failed:     1961
|  `- File list:        /var/log/fail2ban.subnets/fail2ban.sub.log
`- Actions
   |- Currently banned: 5
   |- Total banned:     5
   `- Banned IP list:   134.73.51.0 217.112.142.0 78.128.113.0 193.56.28.0
$ iptables -nL INPUT | grep 134.73.51

No entry for 134.73.51

doing:
fail2ban-client set subnets  banip 134.73.51.0/24
$ fail2ban-client status subnets

Status for the jail: subnets
|- Filter
|  |- Currently failed: 805
|  |- Total failed:     1995
|  `- File list:        /var/log/fail2ban.subnets/fail2ban.sub.log
`- Actions
   |- Currently banned: 6
   |- Total banned:     6
   `- Banned IP list:   134.73.51.0/24  134.73.51.0 217.112.142.0 78.128.113.0 193.56.28.0
$ iptables -nL INPUT | grep 134.73.51

No entry for 134.73.51Chain INPUT (policy ACCEPT))

$ iptables -nL INPUT
target     prot opt source               destination
f2b-subnets  tcp  --  0.0.0.0/0            0.0.0.0/0
f2b-recidive  tcp  --  0.0.0.0/0            0.0.0.0/0
f2b-mysqld-auth  tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 3306

and maybe it is a good idea to add in recidive.conf

_jailnameignore = subnets
ignoreregex =   ^(%(__prefix_line)s| %(_daemon)s%(__pid_re)s?:\s+)NOTICE\s+\[(?!%(_jailnameignore)s\])(?:.*)\]\s+Ban\s+<HOST>\s*$

but the subnets jail is definitvely working

etron770 avatar Feb 11 '20 17:02 etron770

$ iptables -nL INPUT | grep 134.73.51 No entry for 134.73.51Chain INPUT (policy ACCEPT))

of course there is no entry, your chain (where fail2ban would add it) is called f2b-subnets, so take a look in:

iptables -nL f2b-subnets | grep 134.73.51

sebres avatar Feb 11 '20 17:02 sebres