On step 7 is present the final usable script.
step 1: use my RegEx to identify all log lines that containing one IP:
((25[0-5]|(2[0-4]|[01]?[0-9]?)[0-9])\\.){3}(25[0-5]|(2[0-4]|[01]?[0-9]?)[0-9])
on script test on terminal, \ must be added before ? on 6.x version
/log print where message~"((25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])\\.){3}(25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])"
step 2: RegEx can not be used for pick or find...
must be noticed all interested logs like:
SYSTEM: user admin logged in from 100.64.0.33 via winbox
login failure for user admin from 100.64.0.33 via winbox
step 3: ignore the PREFIX and find a pattern for the rules like:
when the string start with "user admin logged in from " and end with " via winbox" put the IP between on ok list
when the string start with "login failure for user admin from " and end with " via winbox" put the IP between on fail list
step 4: how obtain all logs than containing inside a valid IP with a cycle:
/log
:foreach rlog in=[find where message~"((25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])\\.){3}(25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])"] do={
:local rmess [get $rlog message]
:put $rmess
}
step 5: distinguish between actions (igoring the use of complex scripting with array, etc....:
THE SPACE MUST BE CONSIDERED
/log
:global okmsg "user admin logged in from "
:global failmsg "login failure for user admin from "
:global endmsg " via winbox"
:foreach rlog in=[find where message~"((25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])\\.){3}(25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])"] do={
:local rmess [get $rlog message]
:if (($rmess~$okmsg) and ($rmess~$endmsg)) do={
:put "OK: $rmess"
}
:if (($rmess~$failmsg) and ($rmess~$endmsg)) do={
:put "FAIL: $rmess"
}
}
step 6: with previous founded pattern, extract the IPs
/log
:global okmsg "user admin logged in from "
:global failmsg "login failure for user admin from "
:global endmsg " via winbox"
:foreach rlog in=[find where message~"((25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])\\.){3}(25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])"] do={
:local rmess [get $rlog message]
:if (($rmess~$okmsg) and ($rmess~$endmsg)) do={
:put "OK: $rmess"
:local ipinside [:pick $rmess ([:find $rmess $okmsg -1] + [:len $okmsg]) [:find $rmess $endmsg -1]]
:put "IP inside is $ipinside"
}
:if (($rmess~$failmsg) and ($rmess~$endmsg)) do={
:put "FAIL: $rmess"
:local ipinside [:pick $rmess ([:find $rmess $failmsg -1] + [:len $failmsg]) [:find $rmess $endmsg -1]]
:put "IP inside is $ipinside"
}
}
step 7: log the operations, remove the useless "put" for terminal, define the lists and add the IPs, if not already added, for prevent errors...
/log
:global okmsg "user admin logged in from "
:global failmsg "login failure for user admin from "
:global endmsg " via winbox"
:global listok "list_success_winbox_attempt"
:global listfail "list_failed_winbox_attempt"
:foreach rlog in=[find where message~"((25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])\\.){3}(25[0-5]|(2[0-4]|[01]\?[0-9]\?)[0-9])"] do={
:local rmess [get $rlog message]
:if (($rmess~$okmsg) and ($rmess~$endmsg)) do={
:local ipinside [:pick $rmess ([:find $rmess $okmsg -1] + [:len $okmsg]) [:find $rmess $endmsg -1]]
/ip firewall address-list
:if ([:len [find where list=$listok and address=$ipinside]] = 0) do={
add list=$listok address=$ipinside
:log warning "IP $ipinside added to $listok"
}
}
:if (($rmess~$failmsg) and ($rmess~$endmsg)) do={
:local ipinside [:pick $rmess ([:find $rmess $failmsg -1] + [:len $failmsg]) [:find $rmess $endmsg -1]]
/ip firewall address-list
:if ([:len [find where list=$listfail and address=$ipinside]] = 0) do={
add list=$listfail address=$ipinside
:log error "IP $ipinside added to $listfail"
}
}
}
REMEMBER TO REMOVE THE \ BEFORE ? IF USED INSIDE A SCRIPT OR SCHEDULER
Final notices:
The script do not prevent if one IP on whitelist go also on blacklist if sometime fail login.
for do that must be replaced
:if ([:len [find where list=$listfail and address=$ipinside]] = 0) do={
with
:if ([:len [find where (list=$listfail or list=$listok) and address=$ipinside]] = 0) do={
but if first found the failed login, the successful login do not remove IP from blacklist,
for do that must be added a new line
remove [find where list=$listfail and address=$ipinside]
just before ":log warning"
More complex scripting can be doed if the start / end patterns are putted on one bidimensional array,
with a cycle "foreach" pair of elements on the array for find IPs for more success/fail events at same time.
Another approach is possible, like first finding the start string, than later check if inside is present a valid IP,
But I prefer to find at start only the lines with at least one valid IP inside