I've been using the firewall custom rules to block
SIP brute force attacks on a server, 99% of them
originate from France, Russia and Germany.
So far I've been using the following syntax on every
new IP address that I come across:
iptables -I FORWARD -s 0.0.0.0/24 -j DROP
iptables -I INPUT -s 0.0.0.0/24 -j DROP
This has worked really well but I have thought about
blocking the entire ip range from those countries, so
I downloaded the ip ranges and tried it, around 50k
lines, the router crashed and had to hard reset.
Then I considered white-listing the US plus two other
countries that need to access the system but the US alone
will use over 34K lines of rules and that turned out to not be
the solution.
I head about a tool call ip-set and that it can be used to handle
those types of huge lists without hammering the cpu and memory
but I have very basic lede and linux experience and don't even know
how to get started.
My previous methodology was to apply this syntax for every IP:
iptables -I FORWARD -s 0.0.0.0/24 -j DROP
iptables -I INPUT -s 0.0.0.0/24 -j DROP
But with ip-set I have no clue how it works...
I would appreciate any hint on how to setup ip-set on LEDE (17.01) to
either black-list the ips I need or white-list the US list plus two other countries,
whatever would be easier.
It isn't recommended to use the main FORWARD or INPUT chains. Instead you can add your own custom rules into forwarding_wan_rule and input_rule. You can try the following entries inside /etc/firewall.user:
ipset -N badgeo hash:net
iptables -A forwarding_wan_rule -m set --match-set badgeo src -j DROP
iptables -A input_wan_rule -m set --match-set badgeo src -j DROP
These rules above will check the "badgeo" IPset table for IP addresses to filter.
You can run the following in your own script or add it inside /etc/rc.local to populate "badgeo" entries with French IP addresses:
(
for ip in $(curl -1ks http://www.ipdeny.com/ipblocks/data/aggregated/fr-aggregated.zone);
do ipset -q add badgeo $ip;
done
)&
Please note that you're filtering traffic on the INPUT chain, so there's a risk of completely locking yourself out of the router. While testing, you should have a whitelist rule for your internal private IP address; or don't commit any of these into the configuration that will survive reboot until you have fully tested them.
Hope this helps.
EDIT: the custom input table should have been "input_wan_rule" to filter only WAN traffic going to router. This will reduce risk of accidentally filtering LAN access. I've updated the iptables command above.
Awesome, I just did that but can't confirm it's working, I can see the badgeo in the firewall status but don't know if the ips were downloaded successfully.
Question, if I store the badgeo txt files in side let's say inside /tmp/badgeo, what's the command to replace curl download with the file on disk, is the script bellow correct?
cat /tmp/badgeo/fr-aggregated.zone.txt | while read $ip
do
do ipset -q add badgeo $ip;
done
Also, how can I confirm the ips were loaded without having to block myself to test it?
If you actually want to test that it is working, you'll need a host connected to your "WAN" interface either configured for each of the source addresses you want to test, or use a tool like scapy. (You'll want to be disconnected from your ISP when you do this.)
Do you really trust that the contents of fr-aggregated.zone.txt can be blindly executed by root?
What if the site or its DNS were compromised and the contents were
and your router was mysteriously hard bricked?
(Not so far fetched, unfortunately)
You might want to check it before execution with something perhaps as simple as (you should check and understand this, as it is suggestive and provided without any warranty)
egrep -E '[0-9./]+'
Something more specific to the file format and its contents would be highly recommended.
You can simply test if that rule works from a fixed IP machine on your internal network, say 192.168.0.45 by blocking it with:
ipset -q add badgeo 192.168.0.45
To check if your badgeo list is correctly populate, you can simply run:
ipset list badgeo
Thanks for pointing that out. You're perfectly correct! All external data should be sanitised before passing them to executable command. The following regex should only pass on IPs and CIDRs but without checking for correct octet format:
Hi Jeff, I will store the zone files in the internal memory instead, don't want to rely on fetching external urls.
I tested the script inside the terminal and it works perfectly, when I execute it I can confirm using ipset list badgeo that the all the ip addresses are there however it doesn't seem to work from within /etc/rc.local, I have the same script there but upon rebooting ipset list badgeo doesn't contain any ip.
This is what I came up with, I setup a new folder for the zones files:
for ip in $(cat /usr/badgeo/fr-aggregated.zone.txt) do ipset -q add badgeo $ip; done
I've added the semi-colon and rebooted but still no luck, tried to add a sleep before that code but also no luck after rebooting, copying and pasting the code in the terminal works no problem.
Thank you, updated the firewall script.
So what could be preventing this code from executing upon booting?
Got it guys, was a syntax issue, here's the working rc.local script:
(sleep 15;)
(for ip in $(cat /usr/badgeo/fr-aggregated.zone.txt)
do
ipset -q add badgeo $ip
done;)
exit 0;
Now, since ipset is pretty fast what would it be like to do it the other way around, a "white-list", let's say "http://www.ipdeny.com/ipblocks/data/aggregated/us-aggregated.zone" containing 17457 ip blocks, how can I allow only the ips on those zones to communicate and block everything else?
I realized that the aggregated zone files don't contain all the possible ips for the US for the instance, so I ended up purchasing a commercial list which contains 1,559,373,254 IP address for the US alone.
Since the list contains ranges of addresses I still need to write some code to fill the gaps on the IPs which will unfold the list to a total of 1,559,373,254.
Will see if I can do that tomorrow but I'm already wondering if the router/ipset will handle it without hurting the performance, I'm using a Linksys WRT1900ACv2, it has 512MB of RAM of which 460MB are currently free.
Thank you, this worked really well on my TL-WR1043N/ND v3 however when I tried to apply the same settings to my Linksys WRT1900ACv2 (OpenWrt 18.06.1) I had to manually install the ipset packages which my WR1043 already had installed (I'm using a FastPath build) and after everything got installed and rebooted it didn't seem to block outsiders like it did on the WR1043ND.
I had to manually install the following packages on the WRT1900ACv2:
kmod-ipt-ipset_4.14.63-1_arm_cortex-a9_vfpv3.ipk
kmod-nfnetlink_4.14.63-1_arm_cortex-a9_vfpv3.ipk
libmnl_1.0.4-1_arm_cortex-a9_vfpv3.ipk
libipset_6.34-1_arm_cortex-a9_vfpv3.ipk
ipset_6.34-1_arm_cortex-a9_vfpv3.ipk
Firewall - Custom Rules
ipset -N geolist hash:net maxelem 87000
#WHITE LIST
iptables -A forwarding_wan_rule -m set ! --match-set geolist src -j DROP
iptables -A input_wan_rule -m set ! --match-set geolist src -j DROP
rc.local
#(sleep 15;)
(for ip in $(cat /usr/geo/premium-aggregated.zone.txt)
do
ipset -q add geolist $ip
done;)
exit 0;
My WR1043ND has a lot more packages installed from factory, dunno if any additional package
would be missing on the WRT1900ACv2 to get the ipset rules working, "ipset list" on the 1900
does list all the IPs so I assume they are loaded but not being processed.
WR1043ND Firmware: https://github.com/gwlim/Fast-Path-LEDE-OpenWRT
WRT1900ACv2 Firmware: http://downloads.openwrt.org/snapshots/targets/mvebu/cortexa9/
Packages:
http://downloads.openwrt.org/releases/18.06.1/targets/mvebu/cortexa9/packages/
http://downloads.openwrt.org/releases/18.06.1/packages/arm_cortex-a9_vfpv3/base/
If "ipset list geolist" shows the IP addresses and "ipstables -vnL forwarding_wan_rule" shows that rule in the chain, it means you have no module issues and the firewall is running fine.
The issue might be that the source IP address isn't in your geolist or there are other rules / order of rules interfering.
/usr/geo$ ipset list geolist
Name: geolist
Type: hash:net
Revision: 6
Header: family inet hashsize 32768 maxelem 87000
Size in memory: 310608
References: 2
Number of entries: 86481
Members:
A HUGE LIST OF IP ADDRESSES.....
/usr/geo$ iptables -vnL forwarding_wan_rule
Chain forwarding_wan_rule (1 references)
pkts bytes target prot opt in out source destination
819 169K DROP all -- * * 0.0.0.0/0
Let's take for the instance this offending IP address from Germany: 85.114.138.129 whose network mask I think should be 85.114.138.129/32, my white list contains the following allowed networks that are just "near" that range:
Line 9829: 185.114.23.202
Line 19814: 185.114.226.0/24
Line 29067: 185.114.92.0/22
Line 38220: 185.114.79.0/24
Line 52636: 185.114.36.0/24
Line 58441: 185.114.152.0/22
Line 65261: 85.114.2.134
Line 77049: 185.114.248.80/28
Those masks were calculate from my white list IP address range table which also don't include the offending German IP:
185.114.23.202 -> 185.114.23.202
185.114.36.0 -> 185.114.36.255
185.114.79.0 -> 185.114.79.255
185.114.92.0 -> 185.114.95.255
185.114.152.0 -> 185.114.155.255
185.114.226.0 -> 185.114.226.255
185.114.248.84 -> 185.114.248.91
Still, that IP keeps hitting the PC (SIPVicious probably) since Friday, can't figure this one out.
In that case what do I need to change on the firewall custom rules to fix that?
ipset -N geolist hash:net maxelem 87000
#WHITE LIST
iptables -A forwarding_wan_rule -m set ! --match-set geolist src -j DROP
iptables -A input_wan_rule -m set ! --match-set geolist src -j DROP
You can simply test if that IP is in your geolist with ipset test:
Tested and it reports yes, which it shouldn't right? Cause it's a white list, but I don't see how that IP matches the networks I posted earlier, am I calculating the ip network incorrectly because my white list IP ranges don't include that offending IP but ipset reports it is in set.
/usr/geo$ ipset test geolist 85.114.138.129
85.114.138.129 is in set geolist.