This is some documentation I put together on using a Python script I wrote to parse DShield’s block list and import into the MikroTik automatically on schedule. I put some serious time into this and hope it helps some of you out. Please let me know what you think.
http://infaland.weebly.com/infablog/mikrotik-automating-dshield-block-list
Best Regards,
Infatuas
Great job. Have you considered updating the script to process the top 100 list instead?
http://feeds.dshield.org/topips.txt
The topip list is so simple to parse you may as well do it with scripting alone.
Here’s one code that I haven’t actually tested, but should probably work.
/system scheduler add name=DShield_top interval=1d on-event={
:local addressListName "DShield_TOP";
:local addressListTimeout 1d;
/tool fetch keep-result=yes url="http://feeds.dshield.org/topips.txt";
#To ensure the file is readable
:delay 2s;
:local contents [/file get "topips.txt" contents];
:while ($contents != "") do={
:local ip [:pick $contents 0 [:find $contents " "]];
/ip firewall address-list add address=$ip list=$addressListName timeout=$addressListTimeout;
:set contents [:pick $contents ([:find $contents "\n"]+1)];
};
};
boen
thanks for the script. I did create a python version that parses BOTH lists into a single file so that it can be imported by Mikrotik automatically if anyone wants it.
: Imports
import urllib.request
import re
: Specify URL_File To Be Parsed
dshield_url = “http://feeds.dshield.org/topips.txt ”
: Opens URL_File To Be Read
dshield_raw_text = urllib.request.urlopen(dshield_url)
dshield_string = str(dshield_raw_text.read())
: Regex Pattern To Locate IPv4 Addresses
ipv4_pattern = r’\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}’
: Find All IP Addresses From URL
iplist = re.findall( ipv4_pattern, dshield_string )
: Create RSC Formatted File & Open For Writing
dshield_rsc = “.\DShield.rsc”
dshield_clean_text = open(dshield_rsc, “w”)
: Write Results To File
dshield_clean_text.write(“/ip firewall address-list\n”)
for ip in iplist:
dshield_clean_text.write(“add list=DShield address=” + (ip) + “/32\n”)
: Specify URL_File To Be Parsed
dshield_url = “http://feeds.dshield.org/block.txt ”
: Opens URL_File To Be Read
dshield_raw_text = urllib.request.urlopen(dshield_url)
dshield_string = str(dshield_raw_text.read())
: Regex Pattern To Locate IPv4 Addresses Ending In 0
ipv4_pattern = r’\d{1,3}.\d{1,3}.\d{1,3}.0’
: Find All IP Addresses From URL
iplist = re.findall( ipv4_pattern, dshield_string )
: Write Results To File
for ip in iplist:
dshield_clean_text.write(“add list=DShield address=” + (ip) + “/24\n”)
: Close File
dshield_clean_text.close()
Another great list to process : http://rules.emergingthreats.net/blockrules/compromised-ips.txt
Be sure to use:
: Regex Pattern To Locate IPv4 Addresses
ipv4_pattern = r’\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}’
for processing this list in python.
Brian, the script you wrote to parse both works when writing to the output file but if you import into the mikrotik it probably wont import both the /32 and the /24 because in most cases the /32 addresses reside within the /24 subnets. The current OS only seems to take the /32 subnets on import instead of the /24 when they are on the same import list. I personally will continue to use the /24.
Boen,
I agree that it can be done on the router itself but I assume you are not considering needing to make a change to the script and manually changing it on 500 different devices. I prefer centralized scripting in that case.
In that case, using the API is a better approach. Instead of asking each router to “pull” in changes by setting a script on them, you “push” the changes to them (no script inside necessary).
What’s even better is that you don’t really need to check whether the first value is an IP address (or subnet), since the API protocol ensures that the value will not “leak” into other arguments. Worst case scenario is that the “add” command would fail - no harm done.
Using PHP f.e.
<?php
use PEAR2\Net\RouterOS;
require_once 'PEAR2_Net_RouterOS-1.0.0b5.phar';
//<config>
$url = 'http://feeds.dshield.org/topips.txt';
$listName = 'DShield_TOP';
$listTimeout = '1d';
$routers = array(
array('192.168.88.1', 'admin', 'password'),
array('192.168.88.2', 'admin', 'password'),
//etc.
array('192.168.88.254', 'admin', 'password')
);
//</config>
$addRequest = new RouterOS\Request('/ip firewall address-list add');
$addRequest
->setArgument('list', $listName)
->setArgument('timeout', $listTimeout);
foreach ($routers as $router) {
$client = new RouterOS\Client($router[0], $router[1], $router[2]);
foreach (file($url, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
$linePieces = preg_split('/\s+/', $line);
if ($linePieces[0][0] === '#') {//Line is a comment; skip it
continue;
}
$ip = $linePieces[0];
$domain = isset($linePieces[1]) ? $linePieces[1] : null;
$addRequest
->setArgument('address', $ip)
->setArgument('comment', $domain);
$client->sendSync($addRequest);
}
}
The same could be done analogously for Python too, or other languages.
(P.S. The script above is completely agnostic with regards to which one of all above URLs is used. Just switch it up, or make an array with all URLs and loop it to get all IPs of all lists)
I love your adaptation but I’m not able to get it to work with the HPB list
$url = ‘http://www.dshield.org/hpb.html?key=oiUTq74ue5KvKQXfZYxsXw== ’;
$listName = ‘DShield_HPB’;
Any ideas? No errors but no DSHIELD_HPB address lists added after its processed.
boen_robot:
In that case, using the API is a better approach. Instead of asking each router to “pull” in changes by setting a script on them, you “push” the changes to them (no script inside necessary).
What’s even better is that you don’t really need to check whether the first value is an IP address (or subnet), since the API protocol ensures that the value will not “leak” into other arguments. Worst case scenario is that the “add” command would fail - no harm done.
Using PHP f.e.
<?php
use PEAR2\Net\RouterOS;
require_once 'PEAR2_Net_RouterOS-1.0.0b5.phar';
//<config>
$url = 'http://feeds.dshield.org/topips.txt';
$listName = 'DShield_TOP';
$listTimeout = '1d';
$routers = array(
array('192.168.88.1', 'admin', 'password'),
array('192.168.88.2', 'admin', 'password'),
//etc.
array('192.168.88.254', 'admin', 'password')
);
//</config>
$addRequest = new RouterOS\Request('/ip firewall address-list add');
$addRequest
->setArgument('list', $listName)
->setArgument('timeout', $listTimeout);
foreach ($routers as $router) {
$client = new RouterOS\Client($router[0], $router[1], $router[2]);
foreach (file($url, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
$linePieces = preg_split('/\s+/', $line);
if ($linePieces[0][0] === '#') {//Line is a comment; skip it
continue;
}
$ip = $linePieces[0];
$domain = isset($linePieces[1]) ? $linePieces[1] : null;
$addRequest
->setArgument('address', $ip)
->setArgument('comment', $domain);
$client->sendSync($addRequest);
}
}
The same could be done analogously for Python too, or other languages.
(P.S. The script above is completely agnostic with regards to which one of all above URLs is used. Just switch it up, or make an array with all URLs and loop it to get all IPs of all lists)
I found the script imports both the /32 and /24 lists without issue. If you look at the lists, most of the /32 are NOT in the /24 subnet lists! If even one or two are the Mikrotik will treat them as two separate entries. I have the block/top lists processed in one python script as DSHIELD_BLOCK list. I have the emergingthreats processed in another script as EmergingThreats address list. I’ve removed the ‘comment’ line because its not needed when using timeout=2h
Infatuas:
Brian, the script you wrote to parse both works when writing to the output file but if you import into the mikrotik it probably wont import both the /32 and the /24 because in most cases the /32 addresses reside within the /24 subnets. The current OS only seems to take the /32 subnets on import instead of the /24 when they are on the same import list. I personally will continue to use the /24.
A duplicate entry in the import will cause RouterOS to stop and not process further down the list (v6.16)
ie
/ip firewall address-list
add list=DShield address=122.226.73.131/32 timeout=2h
add list=DShield address=85.13.160.11/32 timeout=2h
add list=DShield address=50.23.113.146/32 timeout=2h
add list=DShield address=50.23.113.146/32 timeout=2h
add list=DShield address=122.225.109.212/32 timeout=2h
add list=DShield address=176.31.251.200/32 timeout=2h
add list=DShield address=218.2.0.137/32 timeout=2h
add list=DShield address=122.226.73.131/32 timeout=2h
It will import 50.23.113.146 and stop processing the import. Bug in RouterOS?
Using /import file-name=dshield.rsc; to import list
Well, THAT new list is a little more complicated to parse, so no wonder.
Replace
$ip = $linePieces[0];
$domain = isset($linePieces[1]) ? $linePieces[1] : null;
with
if ($linePieces[0] === 'Start') {//Line is a header; skip it
continue;
}
if (count($linePieces) >= 4) {//network-zerofilled, broadcast-zerofilled, mask, attacks(, name(, country(, email)?)?)?
//Remove zerofill
$ipPieces = explode('.', $linePieces[0]);
for ($i=0, $c=count($ipPieces); $i<$c; ++$i) {
$ipPieces[$i] = ltrim($ipPieces[$i], '0');
if ('' === $ipPieces[$i]) {
$ipPieces[$i] = '0';
}
}
$ip = implode('.', $ipPieces) . '/' . $linePieces[2];
$domain = implode(' ; ', array_slice($linePieces, 3));//Use all meta data as the comment
} else {//subnet(, dns)?
$ip = $linePieces[0];
$domain = isset($linePieces[1]) ? $linePieces[1] : null;
}
Gave it a try but it still doesn’t add any lists. no error. Thanks for trying
I forgot to restore the octet to “0” when removing the zeroes in “000”. I’ve corrected the above. Try it now.
(If that too fails, I’ll do something which I hadn’t actually done yet: Test the code)
It works great! Populates the router firewall with DSHIELD_HPB addresses on a 24 hour timeout. Scheduled it to run once a day so it keeps the list fresh and have firewall blocking any traffic from DSHIELD_HPB. Very nice! Thank you!
boen_robot:
The topip list is so simple to parse you may as well do it with scripting alone.
Here’s one code that I haven’t actually tested, but should probably work.
/system scheduler add name=DShield_top interval=1d on-event={
:local addressListName "DShield_TOP";
:local addressListTimeout 1d;
/tool fetch keep-result=yes url="http://feeds.dshield.org/topips.txt";
#To ensure the file is readable
:delay 2s;
:local contents [/file get "topips.txt" contents];
:while ($contents != "") do={
:local ip [:pick $contents 0 [:find $contents " "]];
/ip firewall address-list add address=$ip list=$addressListName timeout=$addressListTimeout;
:set contents [:pick $contents ([:find $contents "\n"]+1)];
};
};
I apologize for revisiting what seems to be a older thread. I’ve imported this script, but it doesn’t seem to create the address list. Any suggestions?