I wrote before how i blocked TOR exit nodes by iptables, disadvantages of method that i used before – big amount of rules (one per each ip). This solution easy and obvious, but had speed penalty. Today i want write about more effective solution that use ipset, let’s see what is ipset:
IP sets are a framework inside the Linux 2.4.x and 2.6.x kernel, which can be administered by the ipset utility. Depending on the type, currently an IP set may store IP addresses, (TCP/UDP) port numbers or IP addresses with MAC addresses in a way, which ensures lightning speed when matching an entry against a set. If you want to store multiple IP addresses or port numbers and match against the collection by iptables at one swoop; dynamically update iptables rules against IP addresses or ports without performance penalty; express complex IP address and ports based rulesets with one single iptables rule and benefit from the speed of IP sets then ipset may be the proper tool for you.
(c)
Because of exit nodes can exist on hosts that using dynamic IP i want to delete addresses from list after timeout (otherwise list will always growing and contain unused addresses or addresses without TOR exit nodes, larger list requires more memory and CPU time for processing). If addresses persist between list updates, timeout will be reseted.
To do this i used iptree set (you can learn more about set types in manual for ipset), because that type provide timeout for each address.
First i installed ipset:
# apt-get install xtables-addons-common |
# apt-get install xtables-addons-common
After that i modify scripts that i used before. In perl script i made a pair of minor bug fixes and in shell script i add new facility for loading rules into ipset.
Perl script:
#!/usr/bin/perl -w
use strict;
use LWP::Simple;
my $list = get("http://exitlist.torproject.org/exit-addresses");
my $i;
my @ips;
if( ! defined( $list ) ) {
exit( 1 );
}
if( $#ARGV == -1 ) {
exit(0);
}
foreach $i (split( /\n/, $list )) {
push( @ips, $1 ) if( $i =~ m/((?:\d{1,3}\.){3}\d{1,3})/);
}
if( $ARGV[0] eq "-ip" ) {
print( join( "\n", @ips ) . "\n");
} |
#!/usr/bin/perl -w
use strict;
use LWP::Simple;
my $list = get("http://exitlist.torproject.org/exit-addresses");
my $i;
my @ips;
if( ! defined( $list ) ) {
exit( 1 );
}
if( $#ARGV == -1 ) {
exit(0);
}
foreach $i (split( /\n/, $list )) {
push( @ips, $1 ) if( $i =~ m/((?:\d{1,3}\.){3}\d{1,3})/);
}
if( $ARGV[0] eq "-ip" ) {
print( join( "\n", @ips ) . "\n");
}
That script return exit code without arguments that signaled can this script fetch addresses or not, with parameter “-ip” they return list of addresses.
Next shell script:
#!/bin/bash
SOLVE="/root/bin/solve.pl"
IPSET="/usr/sbin/ipset"
case "$1" in
ipset)
if ! /usr/bin/perl $SOLVE
then
echo "Can not fetch Tor exit nodes" 1>&2
exit 1
fi
if ! $IPSET -L tor 2>&1 > /dev/null
then
$IPSET -N tor iptree --timeout 259200
fi
for i in `/usr/bin/perl $SOLVE -ip`
do
$IPSET -A tor $i 2> /dev/null
done
;;
iptables)
if /usr/bin/perl $SOLVE
then
/sbin/iptables -F TorExitnodes
/sbin/iptables -I TorExitnodes -j RETURN
for i in `/usr/bin/perl $SOLVE -ip`
do
/sbin/iptables -I TorExitnodes -s $i -j TorBlockAndLog
done
else
echo "Can not fetch Tor exit nodes" 1>&2
exit 1;
fi
;;
*)
echo "Usage ./$0 "
exit 1
;;
esac
exit 0 |
#!/bin/bash
SOLVE="/root/bin/solve.pl"
IPSET="/usr/sbin/ipset"
case "$1" in
ipset)
if ! /usr/bin/perl $SOLVE
then
echo "Can not fetch Tor exit nodes" 1>&2
exit 1
fi
if ! $IPSET -L tor 2>&1 > /dev/null
then
$IPSET -N tor iptree --timeout 259200
fi
for i in `/usr/bin/perl $SOLVE -ip`
do
$IPSET -A tor $i 2> /dev/null
done
;;
iptables)
if /usr/bin/perl $SOLVE
then
/sbin/iptables -F TorExitnodes
/sbin/iptables -I TorExitnodes -j RETURN
for i in `/usr/bin/perl $SOLVE -ip`
do
/sbin/iptables -I TorExitnodes -s $i -j TorBlockAndLog
done
else
echo "Can not fetch Tor exit nodes" 1>&2
exit 1;
fi
;;
*)
echo "Usage ./$0 "
exit 1
;;
esac
exit 0
This script can add rules into iptables (old variant) or into ipset. I added this script in cron and run every few hours. When ipset found that address all ready in list, they update timeout, if address will not observed in 72 hours, they will be automatically deleted.
Finishing touch – new rule in iptables:
# iptables -A INPUT -i eth+ -m set --match-set tor src -m comment --comment "Block TOR exit nodes thru IPSET" -j DROP |
# iptables -A INPUT -i eth+ -m set --match-set tor src -m comment --comment "Block TOR exit nodes thru IPSET" -j DROP
That’s all. Do not forget to place this rule before rules where you permit access to your server.
Tags: debian, linux, security
Posted in Linux related, Security related
[…] time ago i wrote about how to block access from TOR network. Few days ago i observed, that file with addresses of […]