Has anyone got a ROS script they could share that checks IP addresses against APIv2 on AbuseIPDB?
The documentation is here: https://docs.abuseipdb.com/#check-endpoint
and I need to write a fetch command to mimic this curl command:
I did finish it but I don’t use it anymore because I realised it was adding bad IP addresses to my blacklist (legitimately). It got to 10,000 entries and thought I was probably anonymous enough that no one would want to try a DOS attack (the firewall was stopping the attempts anyway). It was an interesting intelectual exercise and is probably the most sophisticated script I have written because it needed firewall ladders, start up scripts creating zeroed arrays, the ability to not trip over previously started versions and the need to not query IPDB more than 3000 times per day.
I attach the latest version of the script here BUT please don’t assume it will work “out of the box”; I haven’t used it for a year and I was “tidying it up” just before I stopped using it. However, the basics should be fine.
# This is used to check if an IP address that has tried to connect to VPN is bad
# It uses AbuseIPDB APIv2 to check
# if $IPConfidence is above IPConfidenceLevel, then move it to my blacklist
# the fetch was suggested by "rextended" - https://forum.mikrotik.com/memberlist.php?mode=viewprofile&u=68609
########################################################################################
# Radical Overhall on Nov 2022
# Radical Overhall on Jan 2024
########################################################################################
# 2021-11-27
# Moved the firewall rule that includes !whitelist to put entry on greylist rather than maybeblacklist
# changed last ladder to put entry on maybeblacklist, not myblacklist
#######################################################################################
#/log info "$logPrefix Script Started"
###############################################################################
# Vars
:global myEmail
:global IPDBBadIPlastRun
:global IPDBfetchResult
:global IPDBlimitNotSwitched
:global TestArray {"ipAddress"=0.0.0.0;"abuseConfidenceScore"=0;"isp"="";"domain"="";"countryCode"="";"myProblemSource"=""}
#countries that don't need to get to anything I do
:local badCountries {"\"RU\"";"\"CN\"";"\"BR\"";"\"HK\"";"\"TR\"";"\"IL\"";"\"IR\"";"\"VN\"";"\"AM\""}
#:local IPConfidenceLevel 60
:local AbuseIPDBkey "xxx"
:local IPtoTest "xxx"
:local thejson ""
#:local IPConfidence 0
:local logPrefix "[IPDB]"
:local IPDBfetchlimit 2990
:local fetchstatus ""
:local result ""
:local problemSource ""
:local from1 ""
:global IPDBarray
# Confidence Ladder ###########################################################
:local level1 {"Confidence"=100;"mylist"="myblacklist";"mytimeout"="static"}
:local level2 {"Confidence"=60; "mylist"="myblacklist";"mytimeout"="3d"}
:local level3 {"Confidence"=50; "mylist"="myblacklist";"mytimeout"="1d"}
:local levelCountry {"Confidence"=0; "mylist"="myblacklist";"mytimeout"="3d"}
:local levelZero {"Confidence"=0; "mylist"="mygreylist"; "mytimeout"="1h"}
###############################################################################
########################################################################################################
#### FUNCTIONs #########################################################################################
#######################################################
## Pick data from the JSON
#######################################################
:local pickfromjson do={
:local fromthis [:tostr $1]
:local tothis ","
:local thejson1 [:tostr $2]
:local offset [:len $fromthis]
:local varpos [:find $thejson1 $fromthis -1]
:local output1 [:pick $thejson1 ($varpos + $offset) [:find $thejson1 $tothis $varpos]]
:return $output1
}
#######################################################
### Pad to 3 chars
#######################################################
:local padChars do={
:local padded ""
:if ( [:len $1 ] = 3 ) do={
:set padded $1
} else={
:if ( [:len $1 ] = 2 ) do={
:set padded ("0".$1)
} else={
:set padded ("00".$1)
}
}
:return $padded
}
#######################################################
### Update AddressLists
#######################################################
:global fnupdateAddressList do={
:local theirConfidence ""
:local fnLevelArray $1
:local fnTestArray $2
:local logPrefix "[IPDB Fn]"
:if ( [:len ($fnTestArray->"abuseConfidenceScore") ] = 3 ) do={
:set theirConfidence ($fnTestArray->"abuseConfidenceScore")
} else={
:if ( [:len ($fnTestArray->"abuseConfidenceScore") ] = 2 ) do={
:set theirConfidence ("0".$fnTestArray->"abuseConfidenceScore")
} else={
:set theirConfidence ("00".$fnTestArray->"abuseConfidenceScore")
}
}
:local fnComment "$($fnTestArray->"myProblemSource") \
$theirConfidence - \
$($fnTestArray->"countryCode") - \
$($fnTestArray->"isp") - \
$($fnTestArray->"ipAddress") - \
$($fnLevelArray->"mylist") - \
$($fnTestArray->"domain")"
:local fnIPaddr [:pick ($fnTestArray->"ipAddress") 1 ( [:len ($fnTestArray->"ipAddress") ] - 1 ) ]
:if ( ($fnLevelArray->"mytimeout") = "static" ) do={
## add new so I can set the timeout to static then delete original
:do {
/ip firewall address-list add address=$fnIPaddr list=($fnLevelArray->"mylist") comment="$fnComment"
/ip firewall address-list remove [ find where address=$fnIPaddr and list=maybeBlacklist ]
} on-error={
/log info "$logPrefix (onAdd) Remove from maybeBlacklist, on $($fnLevelArray->"mylist") already - $fnIPaddr - $theirConfidence"
/ip firewall address-list remove [find where address=$fnIPaddr and list=maybeBlacklist ]
}
} else={
:do {
/ip firewall address-list set [ find where address="$fnIPaddr" and list="maybeBlacklist" ] list=($fnLevelArray->"mylist") comment="$fnComment" timeout=($fnLevelArray->"mytimeout")
} on-error={
/log info "$logPrefix (onChange) Remove from maybeBlacklist, on $($fnLevelArray->"mylist") already - $fnIPaddr - $theirConfidence"
/ip firewall address-list remove [ find where address=$fnIPaddr and list=maybeBlacklist ]
}
}
}
#######################################################
### Logger
#######################################################
:local myLogger do={
:local myLoggerSwitch false
:if ( $myLoggerSwitch ) do={
/log info "Debug $1"
}
}
#######################################################################
# MAIN SCRIPT
#######################################################################
:foreach i in=[ /ip firewall address-list find where list="maybeBlacklist" ] do={
:set IPtoTest ([/ip firewall address-list get $i address])
#$myLogger "1"
######################################################################
### Test if IP is already on Grey list
######################################################################
:if ( [/ip firewall address-list find where list="mygreylist" and address="$IPtoTest" ] = "" ) do={
#### Test if Dynamic AddressList entry - I put dynamic on for VPN captired ones
:if ([/ip firewall address-list get $i dynamic]=false ) do={
:set problemSource "VPN:"
/log info "Track: $IPtoTest added to Blacklist after it went for the VPN"
} else={
:set problemSource "FW :"
}
#### Test if IAbuseIPDB API Limit not reached
:if ( ( $IPDBarray->"resultsFetched" < $IPDBfetchlimit ) && ( $IPDBarray->"onErrorFetch" < 100 ) ) do={
#$myLogger "3"
#### Try to do the Fetch from IAbuseIPDB
:do {
:do {
#### get result from AbuseIPDB
:set result [/tool fetch http-header-field="Accept: application/json,Key: $AbuseIPDBkey" url="https://api.abuseipdb.com/api/v2/check?ipAddress=$IPtoTest" as-value output=user]
} on-error={
#[ /system logging set [find where topics~"info" and disabled=yes] disabled=no]
:set ($IPDBarray->"onErrorFetch") ( $IPDBarray->"onErrorFetch" + 1 )
#/log info "$logPrefix Fetch1 error $IPtoTest $[:tostr $result]"
}
#### get status #########################################################
:do {
#### get result from AbuseIPDB
:set fetchstatus ($result->"status")
} on-error={
/log info "$logPrefix Fetch2 error $IPtoTest $[:tostr $result]"
}
:if ( $fetchstatus= "finished" ) do={
:set thejson ($result->"data")
:set ($IPDBarray->"resultsFetched") ( $IPDBarray->"resultsFetched" + 1 )
#### Get data from the result from IAbuseIPDB
:foreach k,v in=$TestArray do={
:set from1 "\"$k\":"
:set ($TestArray->"$k") [$pickfromjson $from1 $thejson]
}
:set ($TestArray->"myProblemSource") $problemSource
#### Act on the confidence rating
#### Level 1
:if ( [:tonum ($TestArray->"abuseConfidenceScore") ] >= [:tonum ($level1->"Confidence") ] ) do={
$fnupdateAddressList $level1 $TestArray
:set ($IPDBarray->"Level1") ( $IPDBarray->"Level1" + 1 )
} else={
#### Level 2
:if ( [:tonum ($TestArray->"abuseConfidenceScore") ] >= [:tonum ($level2->"Confidence") ] ) do={
$fnupdateAddressList $level2 $TestArray
:set ($IPDBarray->"Level2") ( $IPDBarray->"Level2" + 1 )
} else={
#### Level 3
:if ( [:tonum ($TestArray->"abuseConfidenceScore") ] >= [:tonum ($level3->"Confidence") ] ) do={
$fnupdateAddressList $level3 $TestArray
:set ($IPDBarray->"Level3") ( $IPDBarray->"Level3" + 1 )
} else={
#### if not a nice country - check the array
:local p [:find $badCountries ($TestArray->"countryCode")]
:if ([:type $p]!="nil") do={
$fnupdateAddressList $levelCountry $TestArray
:set ($IPDBarray->"LevelCountry") ( $IPDBarray->"LevelCountry" + 1 )
} else={
#### else put on Zero #####
$fnupdateAddressList $levelZero $TestArray
:set ($IPDBarray->"LevelZero") ( $IPDBarray->"LevelZero" + 1 )
}
}
}
}
} else={
#######################################################################
##### If the fetch is bad
#######################################################################
:set IPDBfetchResult $fetchstatus
:set ($IPDBarray->"badFetch") ( $IPDBarray->"badFetch" + 1 )
}
#######################################################################
#### If fetch on-error
#######################################################################
} on-error={
:set ($IPDBarray->"onErrorFetch") ( $IPDBarray->"onErrorFetch" + 1 )
}
} else={
#######################################################################
# End of if for limit reached - don't fetch if limit reached
#######################################################################
:if ( ( $IPDBarray->"resultsFetched" = $IPDBfetchlimit ) && ( $IPDBlimitNotSwitched ) ) do={
:set IPDBBadIPlastRun ([/ system clock get date ] . " " . [ / system clock get time ])
:set IPDBlimitNotSwitched false
/log error "IPDBBadIPlastRun: $[ / system clock get date ] $[ / system clock get time ] "
# Send email ######################################################
/tool e-mail send to=$myEmail subject="ISSUE: $[/system identity get name] IPDB Daily fetch limit reached" body="IPDB Daily fetch limit reached at $[ / system clock get time ]"
}
:set ($IPDBarray->"overLimit") ( $IPDBarray->"overLimit" + 1 )
}
} else={
#######################################################################
# Else for when IP is already on Grey list
#######################################################################
:do {
/ip firewall address-list remove $i
:set ($IPDBarray->"onGreylistAlready") ( $IPDBarray->"onGreylistAlready" + 1 )
} on-error={
/log info "Could not delete from Greylist"
}
}
#######################################################################
# END OF LOOP
#######################################################################
}
#/log info "$logPrefix Script Ended"