Script to add IP of failed IPSEC login to block list

When a user tries IPSEC, but does not have correct credential, a message like this will be logged “negotiation failed”
This script take the IP from this attempt and add it to a block list to prevent multiple login attempt. (Blocked out)


script name: Find_IPSEC_negotian_failed

# Created Jotne 2019 v1.1
# 1.1 made sure "negotiation failed" is at end of line and it contains IP
#
# This script add ip of user who failed IPSEC negotiation to a block list for 24hour
# 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.\$" || message~"src_ip")]]

# 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=24h
# 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_negotian_failed every 5 min:

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

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

Thank you for this!

When I add the all the script code via copy/paste it fails. So this must be some CR issue on my end.

I will try and sort it later.

Thanks for your help

First part can not be copy/pasted directly to cli.

You ned from Web or Winbox, create a script, then copy/past the first part to the script.

Here is a version you can copy/past from cli (much harder do read and understand)

/system script add dont-require-permissions=no name=Find_IPSEC_negotian_failed owner=jotne policy=\
    ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="# Created Jotne 2019 v1.0\r\
    \n#\r\
    \n# This script add ip of user who failed IPSEC negotiation to a block list for 24hour\r\
    \n# Schedule the script to run every 5 min\r\
    \n# It should run on all routerOS version\r\
    \n\r\
    \n# Find all \"negotiation failed\" error last 5 min\r\
    \n:local loglist [:toarray [/log find  time>([/system clock get time] - 5m) message~\"negotiation failed\"]]\r\
    \n\r\
    \n# for all error do\r\
    \n:foreach i in=\$loglist do={\r\
    \n\r\
    \n# find message\r\
    \n\t:local logMessage [/log get \$i message]\r\
    \n# find ip\r\
    \n\t:local ip [:pick \$logMessage 0 [:find \$logMessage \" \"]]\r\
    \n\r\
    \n# Add ip to accesslist\t\r\
    \n\t/ip firewall address-list add address=\$ip list=IPSEC timeout=24h\r\
    \n# Send a message to the log\t\r\
    \n\t:log info message=\"script=IPSEC_failed src_ip=\$ip\"\r\
    \n\t}\r\
    \n"

I was trying to copy your original post in to the script windows and not CLI.

Adding it Via CLI worked better. It ran and gave me a FW entry this time, but it does not pull the IP from the log entry. Here is the log add from the script:

script=IPSEC_failed src_ip=phase1

That is the beginning of the line in the log that contains the “negotiation failed” bit.

Any ideas? Thanks again.

Try to run the script from cli
Output is to cli not to log in this version. Also changed to last 24h

[
# Find all "negotiation failed" error last 5 min
:local loglist [:toarray [/log find  time>([/system clock get time] - 24h) 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=24h
# Send a message to the log	
	:put "script=IPSEC_failed src_ip=$ip"
	}
]

Also try this manually

:put [:toarray [/log find  time>([/system clock get time] - 24h) message~"negotiation failed"]]

Thanks Jotne - I will try it later and report back.

Thanks , one qustion why 5 min?
ipsec list will be created when the first block IP is added?

Hi - Here is what happens with the first part -

  1. dynamically created a FW address-list rule named IPSEC with and address of phase1. Timeout is correct.
  2. Terminal L1: script=IPSEC_failed src_ip=phase1
  3. Terminal L2: failure: already have such entry
    note: I deleted the previous phase1 entries form the FW address-list. I am unclear where the L2 report is coming from…?

I am running 6.44.3. The failure message in my log is:
phase1 negotiation failed due to time up
xxxxxx(xxx)<=>XXXXXX(xxx)
as;dlfkj;lkjw2l;j22as;lkdfa;lsfj;lasjkf

The first IP address is the target VPN and the second is my Cell phone IP. I am using my cell VPN to hit the correct address with invalid secret and credentials.

For the second piece you asked me to run manually I get some strange hex(?) back: *19da;*19db;*19ed;*19fe
I am guessing it is not pickup up the system time of that message, yet still trying to subtract 24h thus the hex. That is an uneducated guess.
edit: if I make - 0m it will return a blank. not sure why it is not picking up the time of the message containing “negotiation failed”

Hope this helps.

Not sure whats goes wrong.

But the code is correct. It represent ID number of the lines that represent what it finds. This is the way all script works in MT,

Try this and see the ID with the log lines.

[
:local list [:toarray [/log find  time>([/system clock get time] - 24h) message~"negotiation failed"]]
:put "ID-List"
:put $list
:put ""
:put "Log lines"
:foreach i in=$list do={
	:put [/log print as-value where .id=$i]}
]

When I copy that in CLI I get the following -

ID-List


Log lines

That is it - with two blanks between. The log is filled with at least 10 “negotiation failed” lines in the last 24 hours. Could the clock be causing a problem?

The log is stored in memory - I assume that is ok as default?

update - I typed the whole think in the terminal manually (each line) thinking there was a character set issue - but go the same result as above.

Thanks for your help - this would be a great script to have!

You get nothing since find does not fin anything.

Try this, should get all message with a in it.

[
:local list [:toarray [/log find message~"a"]]
:put "ID-List"
:put $list
:put ""
:put "Log lines"
:foreach i in=$list do={
	:put [/log print as-value where .id=$i]}
]

To test ting out, try a command with :put in front of it.

Eks for this:

:local list [:toarray [/log find message~"a"]]

Try this:

:put [:toarray [/log find message~"a"]]

To see how time works:

:put ([/system clock get time] - 2h)
[
:local list [:toarray [/log find message~"negotiation failed"]]
:put "ID-List"
:put $list
:put ""
:put "Log lines"
:foreach i in=$list do={
	:put [/log print as-value where .id=$i]}
]

So I ran that - and the log started filling up with lots of lines… I had to interrupt it :slight_smile: so that worked

Then I changed the “a” to “negotiation failed” and it work! I did get all the messages with negotiation failed to fill the logs.

:put ([/system clock get time] - 2h)

that worked as expected and report the correct time back.

To be honest I am not sure what output to expect from these -

:local list [:toarray [/log find message~"a"]]



:put [:toarray [/log find message~"a"]]

I got some output that looked like ;*lb60;*lb61;*lb62… - it was a long list.

Getting closer?

you are getting the line id, they looks like this:

*lb60;*lb61;*lb62"

so that is correct.
You should read trough the script manual and try to learn scripts.

Start with these pages:
https://wiki.mikrotik.com/wiki/Manual:Scripting
https://wiki.mikrotik.com/wiki/Scripts
https://wiki.mikrotik.com/wiki/Manual:Scripting-examples
http://forum.mikrotik.com/t/useful-scripts/36579/1

Ok Jotne -

Thank you for the links.

I assume the script you posted works on your MTs? I would have thought that I could copy a working script and duplicate the results.

I will struggle with it some more, but probably do not have the programming skills to work through it.

Thanks again for your efforts.

For anyone who may see this - here is some code I have cobbled together to produce the following output.

To be clear - this was code that Jotone wrote and is his credit. I am simply trying to find why it does not work for me.

[
:local loglist [:toarray [/log find message~"negotiation failed"]]
:foreach i in=$loglist do={
	:local logMessage [/log get $i message]
	:local ip [:pick $logMessage 0 [:find $logMessage " "]]
	:put "script=IPSEC_failed src_ip=$ip"
	}
]

Output CLI:

It appears there are two issues.

  1. It is not finding the IP addresses for each message - it only found for two of them.
  2. I am looking at the entire log of “negotiation failed” messages and getting the result. I think there is a problem with the this.
:put [:toarray [/log find  time>([/system clock get time] - 24h) message~"negotiation failed"]]

as it does not appear to find any results within the last 24 hours.

I am hoping someone with more scripting skill that I have (which is zero) may chime in. I will keep working at it in my own layman way and report back any progress.

Thanks in advance.

For clarity - when I use what I believe is the “within last 24 hour” part of the original script I get no output.

[
:local loglist [:toarray [/log find time>([/system clock get time] -24h) message~"negotiation failed"]]
:foreach i in=$loglist do={
	:local logMessage [/log get $i message]
	:local ip [:pick $logMessage 0 [:find $logMessage " "]]
	:put "script=IPSEC_failed src_ip=$ip"
	}
]

IP does come from the message. So if there are no IP, no IP will be shown.
I do see only IP, so it may be som wrong with your IPSec setup.

What do you get from this? (it show the message from the log as well)

[
:local loglist [:toarray [/log find message~"negotiation failed"]]
:foreach i in=$loglist do={
	:local logMessage [/log get $i message]
	:local ip [:pick $logMessage 0 [:find $logMessage " "]]
	:put "script=IPSEC_failed src_ip=$ip  msg=$logMessage"
	}
]

Thank you for your continued help in this.

This is a sample of what I get… it is about 20-30 lines longer.

Here is one where id work the IP and message = is the IP address