cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
3001
Views
10
Helpful
3
Replies

Permanently block an IP after X amount of login failures?

j_friedrich
Level 1
Level 1

Hi,

 

     I am struggling to find a solution to do the following:  Permanently block an ip after X amount of login failures?  I know it sounds simple, but, maybe someone can give me an example.  I don't want to block a range of ip's, just a specific one after X amount of login failures.  I wasn't sure if this is an EEM script that has to be created or something else?  Also, this can be a login that was tried after X amount of tries OR a login failure using different key words.  Basically, I am trying to keep out some of the crap coming from China and so on......

I am currently using a Cisco 3825.  We don't have a firewall solution yet.....

Thanks for everyone's help.

 

Cheers.

 

Jason

1 Accepted Solution

Accepted Solutions

Dan Frey
Cisco Employee
Cisco Employee

EEM can do this but your ACL will be entirely host based and thats going to be taxing on resources.   If you execute "login on-failure log every 1" this will generate a log message that includes the source IP address and username attempting to login.   The syslog message can be used to trigger EEM and after a configurable amount of login attempts from the same IP address, update ACL 51 to deny the ip address. 

In global config mode execute:

login on-failure log every 1

!### set to 2 login failures will block the ip address.  update as appropriate.

event manager environment _block 2

!### Place this TCL file on the router and register with EEM.  Assuming the local media is called

!### "flash" and you save the TCL file as "acl_block.tcl"

event manager directory user policy "flash:/"

event manager policy acl_block.tcl type user

 

 

 

######START acl_block.tcl #####

::cisco::eem::event_register_syslog pattern "SEC_LOGIN-4-LOGIN_FAILED: Login failed .*"

namespace import ::cisco::eem::*
namespace import ::cisco::lib::*
array set arr_einfo [event_reqinfo]
set msg "$arr_einfo(msg)"
regexp {\[user:\s+([A-Za-z0-9.]+)\]\s+\[Source:\s+([0-9.]+)\]} $msg match user ipaddress
# If CTXT exists read it, if not create it.
if { [catch {context_retrieve LOGINCTXT remoteips} result] } {
    array set remoteips [list]
} else {
    array set remoteips $result
}
# If LOGINCTXT does not exist create the counter as 1  and save LOGINCTXT.
        if { ! [info exists remoteips($ipaddress)] } {
            set remoteips($ipaddress) 1

            if { [catch {context_save LOGINCTXT remoteips} result] } {
                  error $result $errorInfo
                 }

        } else {
        incr remoteips($ipaddress)
        set loginfailures $remoteips($ipaddress)
        puts "loginfail = $loginfailures"

        }
if {[info exists loginfailures]} {
    if {($loginfailures >= $_block)} {
    # Open the CLI
    if [catch {cli_open} result] {
       error $result $errorInfo
    } else {
    array set cli1 $result
    }
# Go into enable mode
    if [catch {cli_exec $cli1(fd) "en"} result] {
        error $result $errorInfo
    }

    if [catch {cli_exec $cli1(fd) "conf t"} result] {
        error $result $errorInfo
    }

    if [catch {cli_exec $cli1(fd) "access-list 51 deny $ipaddress"} result] {
        error $result $errorInfo
    }
action_syslog msg "EEM writing access-list 51 deny $ipaddress"

catch {cli_close $cli1(fd) $cli1(tty_id)}
    }

            if { [catch {context_save LOGINCTXT remoteips} result] } {
                  error $result $errorInfo
                 }

}


########END acl_block.tcl ########

 

 

 

 

 

 

View solution in original post

3 Replies 3

Dan Frey
Cisco Employee
Cisco Employee

EEM can do this but your ACL will be entirely host based and thats going to be taxing on resources.   If you execute "login on-failure log every 1" this will generate a log message that includes the source IP address and username attempting to login.   The syslog message can be used to trigger EEM and after a configurable amount of login attempts from the same IP address, update ACL 51 to deny the ip address. 

In global config mode execute:

login on-failure log every 1

!### set to 2 login failures will block the ip address.  update as appropriate.

event manager environment _block 2

!### Place this TCL file on the router and register with EEM.  Assuming the local media is called

!### "flash" and you save the TCL file as "acl_block.tcl"

event manager directory user policy "flash:/"

event manager policy acl_block.tcl type user

 

 

 

######START acl_block.tcl #####

::cisco::eem::event_register_syslog pattern "SEC_LOGIN-4-LOGIN_FAILED: Login failed .*"

namespace import ::cisco::eem::*
namespace import ::cisco::lib::*
array set arr_einfo [event_reqinfo]
set msg "$arr_einfo(msg)"
regexp {\[user:\s+([A-Za-z0-9.]+)\]\s+\[Source:\s+([0-9.]+)\]} $msg match user ipaddress
# If CTXT exists read it, if not create it.
if { [catch {context_retrieve LOGINCTXT remoteips} result] } {
    array set remoteips [list]
} else {
    array set remoteips $result
}
# If LOGINCTXT does not exist create the counter as 1  and save LOGINCTXT.
        if { ! [info exists remoteips($ipaddress)] } {
            set remoteips($ipaddress) 1

            if { [catch {context_save LOGINCTXT remoteips} result] } {
                  error $result $errorInfo
                 }

        } else {
        incr remoteips($ipaddress)
        set loginfailures $remoteips($ipaddress)
        puts "loginfail = $loginfailures"

        }
if {[info exists loginfailures]} {
    if {($loginfailures >= $_block)} {
    # Open the CLI
    if [catch {cli_open} result] {
       error $result $errorInfo
    } else {
    array set cli1 $result
    }
# Go into enable mode
    if [catch {cli_exec $cli1(fd) "en"} result] {
        error $result $errorInfo
    }

    if [catch {cli_exec $cli1(fd) "conf t"} result] {
        error $result $errorInfo
    }

    if [catch {cli_exec $cli1(fd) "access-list 51 deny $ipaddress"} result] {
        error $result $errorInfo
    }
action_syslog msg "EEM writing access-list 51 deny $ipaddress"

catch {cli_close $cli1(fd) $cli1(tty_id)}
    }

            if { [catch {context_save LOGINCTXT remoteips} result] } {
                  error $result $errorInfo
                 }

}


########END acl_block.tcl ########

 

 

 

 

 

 

Please, How is possible to write the ACL 51 with a sequence number at the end (permit any) ? So when the script write the ACL 51 deny failed login IP dynamically and the others IP are allowed.

I applied the ACL 51 on the line vty 0 4

 

Thanks a lot for your help.

Hello Daniel, Thanks for this great script, it did the job for many years on my routers! But since a couple of IOSes, (using 15.x 16.x) it looks than the $ipaddress variable is no longer recognized. I looked around many sites to find out what are the variables than EEM now understands, whitout success.

 

What I get now in new versions is always:

%SEC_LOGIN-4-LOGIN_FAILED: Login failed [user: Invalid-Credentials] [Source: UNKNOWN] [localport: 0] [Reason: Login Authentication Failed - BadUser] at 15:21:02 EST Fri Feb 18 2022
%HA_EM-6-LOG: aclblkmagv2.tcl: can't read "ipaddress": no such variable
%HA_EM-6-LOG: aclblkmagv2.tcl: while executing
%HA_EM-6-LOG: aclblkmagv2.tcl: "info exists remoteips($ipaddress)"

 

I changed the name and some little things in it to test something else, but still get that error.

I'm using ZBF on 16.9 on this example. Is there a place to know the variable I should now use, or could it be the ZBF restricting some access to the memory?

 

Thanks, Mark

Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community:

Innovations in Cisco Full Stack Observability - A new webinar from Cisco