11-18-2014 08:50 AM
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
Solved! Go to Solution.
11-18-2014 04:15 PM
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 ########
11-18-2014 04:15 PM
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 ########
09-29-2015 02:51 AM
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.
02-18-2022 12:32 PM
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
Discover and save your favorite ideas. Come back to expert answers, step-by-step guides, recent topics, and more.
New here? Get started with these tips. How to use Community New member guide