Black list for failed login to IPSec VPN

Hello!
Got a VPN Server on my router. Of course periodically someone tries to knock on it and I get tons of email messages before I add IP to block list.
I don’t want to close ports. I want to make script that adds failed IPs to block list.

This command shows negotiation failed IPs

/log print where message~"negotiation"

This is what it shows (I’ve removed some digits from IP for not showing real IP):

apr/15 03:50:37 ipsec,error 216.xx.206.6 phase1 negotiation failed.
apr/16 03:31:44 ipsec,error 216.xx.206.118 phase1 negotiation failed.
apr/17 05:33:29 ipsec,error 216.xx.206.102 phase1 negotiation failed.
may/08 14:30:13 ipsec,error 122.xx.64.43 phase1 negotiation failed.
may/08 14:30:13 ipsec,error 122.xx.64.43 phase1 negotiation failed

Does anyone knows how to make script based on command above that parse log and adds IP to IP list?
Any information how alternatively secure IPSec would be useful.
Thanks.

Edit updated here: https://forum.mikrotik.com/viewtopic.php?p=905420#p905420


Here you go

Create a script with name Find_IPSEC that is used to find all lines with negotiation failed last 5m, extract the IP and add it to a access list.
Find_IPSEC

# Created Jotne 2019 v1.1
#
# This script add ip of user who failed IPSEC negotiation to a block list for 30 day
# Schedule the script to run every 5 min
# It should run on all routerOS version

# Find all "negotiation failed" error last 5 min
:local loglist [:toarray [/log find  time>([/system clock get time] - 5m) message~"negotiation failed"]]

# for all error do
:foreach i in=$loglist do={

# find message
	:local logMessage [/log get $i message]
# find ip
	:local ip [:pick $logMessage 0 [:find $logMessage " "]]

# Add ip to accesslist	
	/ip firewall address-list add address=$ip list=IPSEC timeout=30d
# Send a message to the log	
	:log info message="script=IPSEC_failed src_ip=$ip"
	}

Create a scheduler that do run the script Find_IPSEC every 5 min:

/system scheduler add interval=5m name="Find IPSEC" on-event=Find_IPSEC

Then add an access list high in your filter rules like this (change ether1 with your outside IP):

/ip firewall filter add action=drop chain=forward comment="Block wrong IPSEC" in-interface=ether1 src-address-list=IPSEC

You can change the timeout=24h to set it how long you will block all IP, or remove it to permanently block all IP

Very interesting application. I will definitely use this script. Thanks

Edit: Updated see this post https://forum.mikrotik.com/viewtopic.php?p=905420#p905420

Updated
Now also block user with these type of message:
SPI e14750001eda995ec not registred for 89.50.40.10[4500]


# Created Jotne 2019 v1.3
#
# This script add ip of user who with "IPSEC negotiation failed" and "SPI* not registered" to a block list for 24hour
# Schedule the script to run every 5 min
# It should run on all routerOS version
# 1.3 added "Invalid exchange"


# Find all "negotiation failed" error last 5 min
:local loglistN [:toarray [/log find  time>([/system clock get time] - 5m) message~"negotiation failed"]]

# for all error do
:foreach i in=$loglistN do={

# find message
	:local logMessageN [/log get $i message]
# find ip
	:local ipN [:pick $logMessageN 0 [:find $logMessageN " "]]

# Add ip to accesslist	
	/ip firewall address-list add address=$ipN list=IPSEC timeout=30d
# Send a message to the log	
	:log info message="script=IPSEC_failed src_ip=$ipN why=negotiation_failed"
	}

	

# Find all "SPI* not registered"" error last 5 min
:local loglistS [:toarray [/log find  time>([/system clock get time] - 5m) message~"SPI.*not regist"]]

# for all error do
:foreach j in=$loglistS do={

# find message
	:local logMessageS [/log get $j message]
# find ip
	:local ipS [:pick $logMessageS ([:find $logMessageS "for "]+4) [:find $logMessageS "["]]

# Add ip to accesslist	
	/ip firewall address-list add address=$ipS list=IPSEC timeout=30d
# Send a message to the log	
	:log info message="script=IPSEC_failed src_ip=$ipS why=SPI_not_registered"
	}
	
  
# Find all "Invalid exchange" error last 5 min
:local loglistS [:toarray [/log find  time>([/system clock get time] - 5m) message~"Invalid exchange"]]

# for all error do
:foreach j in=$loglistS do={

# find message
	:local logMessageS [/log get $j message]
# find ip
	:local ipS [:pick $logMessageS ([:find $logMessageS "for "]+4) [:find $logMessageS "["]]

# Add ip to accesslist	
	/ip firewall address-list add address=$ipS list=IPSEC timeout=7d
# Send a message to the log	
	:log info message="script=IPSEC_failed src_ip=$ipS why=SPI_not_registered"
	}

181.209.165.10 parsing packet failed, possible cause: wrong password
181.209.165.10 parsing packet failed, possible cause: wrong password
181.209.165.10 parsing packet failed, possible cause: wrong password
181.209.165.10 parsing packet failed, possible cause: wrong password
181.209.165.10 parsing packet failed, possible cause: wrong password
181.209.165.10 parsing packet failed, possible cause: wrong password
181.209.165.10 parsing packet failed, possible cause: wrong password
phase1 negotiation failed due to time up xx.xx.xx.xx[4500]<=>181.209.165.10[4500]
run script is
script=IPSEC_failed src_ip=phase1 why=negotiation failed

can

# Find all "negotiation failed due to time up" error last 60 min
:local loglistTimeout [:toarray [/log find  time>([/system clock get time] - 60m) message~"phase1 negotiation failed due to time up"]]

# for all error do
:foreach i in=$loglistTimeout do={

# find message
	:local logMessageTimeout [/log get $i message]
# find ip
                :local ip1 [:pick $logMessageTimeout [:find $logMessageTimeout ">"] [:len $logMessageTimeout]]
                :local ipTimeout [:pick $ip1 1 [:find $ip1 "["] ]
# Add ip to accesslist	
	/ip firewall address-list add address=$ipTimeout list=IPSEC_failed timeout=245d
# Send a message to the log	
	:log info message="script=IPSEC_failed src_ip=$ipTimeout why=negotiation_failed due to time up"
	}

I see that you have used 60 minutes.
The you also have to schedule the script to run 60 min , not 5 min as I have used as standard.
If not you will get double logging and IP will be added multiple times.

@Jotne,
I would like to use your script. But may I know how to set up the system clock so that [/log find time>([/system clock get time] - 60m) message~“xxx”] works. I found it only works with GMT 0 time. My setup is below, and script doesn’t work since afternoon local time ( in GMT 0 , it is tomorrow) because the log pr will produce “mmm/dd/yyyy” in front of time:

[brg3466@MikroTik] > /system clock pr
                  time: 11:03:45
                  date: may/18/2021
  time-zone-autodetect: yes
        time-zone-name: America/Los_Angeles
            gmt-offset: -07:00
            dst-active: yes

Methods like [/system clock get time] - 5m (I see often on forum…) is wrong at any timezone,
for example 00:01:00 - 5 min do -23:56:00 and no log have negative time

previous year:
may/17/2020 10:47:30 system,info,account user admin logged out from 100.80.8.1 via winbox
may/17/2020 10:49:30 system,info,account user admin logged out from 100.80.8.1 via winbox
yesterday:
may/17 10:49:43 system,info,account user admin logged in from 100.80.8.1 via winbox
may/17 13:05:12 system,info,account user admin logged out from 100.80.8.1 via winbox
may/17 13:05:25 system,info,account user admin logged in from 100.80.8.1 via winbox
today:
08:18:40 system,info,account user admin logged out from 100.80.8.1 via winbox
08:18:51 system,info,account user admin logged in from 100.80.8.1 via winbox
08:18:54 system,info,account user admin logged out from 100.80.8.1 via winbox

Also on another thread of sometime ago, differencies between three time format used in LOG are coming out.
MikroTik have done the wrong choice to use “friendly” time format… MUST BE ful date & time in log, everytime.
And MUST use international date and time format as ISO standard: 2021-05-18 21:08:00

It’s impossible to add same identical IP adress to same address-list
the script on that case throw an error and exit if the IP are finded twice
also on right interval, without add subsequent IP on logs

Before add, must be check if already are present.

I agree. With 3 formats of time, it is very confusing. Better to have just one uniform format.

Back to the topic, how to get the past, say, 10minutes log if we don’t use Jotne’s script ?

Do not worry to use any time interval, parse all logs, the script prevent double-set IP on address list than cause interruption for error on duplicate entry.

This script do not prevent the lock of your own remote IP if some error happen meantime.
Remember to create the whitelist of yours IPs and add permit rule before the blocking rule!

Revised script:

# Revised from Rextended v1.2.rex
#
# Created Jotne 2019 v1.2
#
# This script add ip of user who with "phase1 negotiation failed" and "SPI .* not registered for" to a block list for 7 days
# Schedule the script to run every 5 min
# It should run on all routerOS version - Rex test it on 6.47.9

# example:
# 1.2.3.4 phase1 negotiation failed.
# SPI 4f23346ef367ea12 not registered for 1.2.3.4[4500]

:local logMessage ""
:local logIp 10.6.6.6

/log

:foreach i in=[find where message~"phase1 negotiation failed" or message~"SPI .* not registered for"] do={

    :set logMessage [get $i message]

    :if ($logMessage~"phase1 negotiation failed") do={
        :set logIp [:toip [:pick $logMessage -1 [:find $logMessage " "]]]
        :if ([:len [/ip fire addr find where address=$logIp]] < 1) do={
            /ip fire addr add address=$logIp list=IPSEC timeout=7d
            :log info message="IPSEC_failed add $logIp because negotiation failed"
        }
    }

    :if ($logMessage~"SPI .* not registered for") do={
        :set logIp [:toip [:pick $logMessage ([:find $logMessage "for "]+4) [:find $logMessage "["]]]
        :if ([:len [/ip fire addr find where address=$logIp]] < 1) do={
            /ip fire addr add address=$logIp list=IPSEC timeout=7d
            :log info message="IPSEC_failed add $logIp because SPI not registered"
        }
    }

}

Mikrotik write this about time log format:

You can store the logs remotely in a Syslog server, and there choose format that you like most.

I do use Syslog and Splunk

There has been several request to MT to use propper time log format like ISO8601

Hello!

How to find the IP in this message? (second IP 27.115…)
phase1 negotiation failed due to time up 89.131.7.88[500]<=>27.115.124.74[30992] 0011223344556677:b59120571b9a18c2

:local ipS [:pick $logMessageS ([:find $logMessageS ">"]+1) ([:find $logMessageS "["]-1)]   ==> it does not work

Thanks!

I’m wondering if I can use the same concept for web fig logs.
After lets encrypt certificate enabled I get a few web fig login per day:(

login failure for user MikroTikSystem from 45.130.99.94 via web

You are close. Problem is the second find, it does find the first [ (before 500), not the second [ (before 30992) that you need.

This works:

{
:local test "phase1 negotiation failed due to time up 89.131.7.88[500]<=>27.115.124.74[30992] 0011223344556677:b59120571b9a18c2"
:local temp [:pick $test ([:find $test ">"]+1) 999] 
:put [:pick $temp 0 [:find $temp "["]] 
}

It gets the IP and the rest of the line in the first pick. Then second pick gets all until [.

All in one ugly long line (better to split it up)

:local ipS [:pick [:pick $logMessageS ([:find $logMessageS ">"]+1) [:len $logMessageS]] 0 [:find [:pick $logMessageS ([:find $logMessageS ">"]+1) [:len $logMessageS]] "["]]

Thank you very much for the help!
BR.

Was looking for such a script for a long time.
Trying to implement it on a router getting lots of “negotiation failed” but I have an issue.
The offending IP is added to IPSEC list, but it also adds 0.0.0.0 to the same list for some reason.
Any clue as to why? Nothing in log showing 0.0.0.0
ipsec.png
Thank you.

There is an 1.3 in this thread as well. Test it out. (Do not remember what was changed from 1.2 to 1.3)

The same thing happened to me doing tests. I think that when it cannot get the IP correctly it adds an IP of type 0.0.0.0.

It is important that the error messages in the log are correctly filtered to obtain an IP. Use “: toip”

This is my script that I run every hour, for now it works great for me.

:local logMessage ""
:local logIp ""
:local message1 " phase1 negotiation failed."
:local message2 "phase1 negotiation failed due to time up "
:local message3 "first L2TP UDP "

/log

:foreach i in=[find where message~$message1 or message~$message2 or message~$message3 ] do={

    :set logMessage [get $i message]

    :if ($logMessage~$message1) do={
        :set logIp [:toip [:pick $logMessage 0 [:find $logMessage " "]]]
        :if ([:len [/ip fire addr find where address=$logIp]] < 1) do={
            /ip fire addr add address=$logIp list=IPSEC timeout=7d
            :log info message="IPSEC failed: add $logIp because negotiation failed"
        }
    }

    :if ($logMessage~$message2) do={
        :set logIp [:toip [:pick [:pick $logMessage ([:find $logMessage ">"]+1) [:len $logMessage]] 0 [:find [:pick $logMessage ([:find $logMessage ">"]+1) [:len $logMessage]] "["]]]
        :if ([:len [/ip fire addr find where address=$logIp]] < 1) do={
            /ip fire addr add address=$logIp list=IPSEC timeout=7d
            :log info message="IPSEC failed: add $logIp because negotiation failed due to time up"
        }
    }

    :if ($logMessage~$message3) do={
        :set logIp [:toip [:pick $logMessage ([:find $logMessage "from"]+5) [:len $logMessage]]]
        :if ([:len [/ip fire addr find where address=$logIp]] < 1) do={
            /ip fire addr add address=$logIp list=IPSEC timeout=7d
            :log info message="L2TP failed: first UDP packet received from $logIp. Added to blacklist"
        }
    }

}

Luck!
BR.

Thank you for the code.
The problem is:

first L2TP UDP packet received from

is a valid and normal message on a successful connection.
Your script adds valid IPs to the list.
valid.png