Tor blacklist

Few month ago i was interested in how to block incoming traffic from Tor network. Tor network have finitely numbers of exit nodes, so the solution is to block traffic from this nodes. I see two solutions how to block exit nodes.  First use TorDNSEL service, in brief you can check that connection comes from Tor exit node or not by querying special domain name. It is useful if you want to do this check in your php script, for example. But i do not know how to protect  host completely by this method. Another way is to use exitlist. By this way you can get list of addresses. Both ways have advantages and disadvantages. I prefer last solution because, it allow to protect my hosts completely.  Cons of this method – near 700-800 rules into firewall, that can slow down your host.

Anyway, first that i done – simply script that fetch list of IP and print them to standard output. When i started to writing script i wanted to expand features in future. I want to generate ready firewall rules for example, but now this script can only print list of IP’s. I do not want to integrate firewall rules application in this script, because i think this job must be done by another script.
So, now this script only fetch and print IP addresses or exit with error code if can not do it:

#!/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 ) ) {
die( "Can not fetch addresses.\n" );
}
 
foreach $i (split( /\n/, $list )) {
push( @ips, $1 ) if( $i =~ m/((?:\d{1,3}\.){3}\d{1,3})/);
}
 
if( defined( $ARGV[0] ) &&  $ARGV[0]  eq "-ip" ) {
print( join( "\n", @ips ));
} else {
die( "Usage $0 -ip\n" );
}

Next that you need – apply rules on firewall. As i say before, when you apply one rule per IP you will get near 700-800 rules, it is reason why you need optimize sequence of your rules. It can be done in next manner:
1. Rule to accept packets flowing thru loopback.
2. Rule to accept packets of RELATED and ESTABLISHED connections.
3. Rule to drop packets from blacklisted sources.
4. Rule to accept packets from whitelisted sources.
5. Rule for fail2ban (optional).
6. Rule for Tor exit nodes.
7. Other rules (for example, accept connection on ssh, http, https ports).

This sequence allow  to make decision for most packets before they pass Tor filtration rules.
I organize Tor filtration rules in separate table called TorExitnodes, it allow to update only this table and do not touch other firewall rules. Also i create table TorBlockAndLog that contain LOG and DROP target, but it is optional, you can simply drop packets.

For updating firewall rules for exit nodes i made next bash scrip and place it in cront:

#!/bin/bash
SOLVE="/root/bin/solve.pl"
 
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

Where is variable “SOLVE” store path to previous perl script, as i say before, if you do not need special action for incoming connections from Tor, you can change “TorBlockAndLog” on “DROP”.

Thats all.

PS

It is good idea to place whitelist before TorExitnodes, who can know, may be one fine day you found -s 0.0.0.0/0 -j DROP into this table. =)

Port based routing

After i came on new work i found that can not send email thru SMTPS, because port 465 closed on router. At this point i already had configured VPN access on my home router, so i think that it is good idea to route SMTPS traffic thru VPN, let’s start.

For this purposes i needed iproute2 and iptables. First i created new route table and add default route:

$ echo "1    VPN" >> /etc/iproute2/rt_tables
$ ip route add default via 192.168.107.5 src 192.168.107.6 dev tun_vpn table VPN
$ ip route show table VPN
default via 192.168.107.5 dev tun_vpn src 192.168.107.6

Where 192.168.107.5 – ip of my router into VPN and tun_vpn – VPN interface.
After that i created rule, that route marked packets thru VPN route table:

$ ip rule add from all fwmark 0x16 lookup VPN
$ ip  ru sh
0:      from all lookup local
32765:  from all fwmark 0x16 lookup VPN
32766:  from all lookup main
32767:  from all lookup default

There is time to mark SMTPS packets:

$ iptables -t mangle -I PREROUTING -p tcp --dport 465 -j MARK --set-mark 0x16
$ iptables -t mangle -I OUTPUT -p tcp --dport 465 -j MARK --set-mark 0x16

Let’s check:

$ traceroute -n -T -p 993 imap.gmail.com
traceroute to imap.gmail.com (173.194.69.109), 30 hops max, 60 byte packets
1  192.168.130.1  0.217 ms  0.233 ms  0.201 ms
2  *  2.318 ms  2.377 ms  2.503 ms
3  *  1.411 ms  1.714 ms  1.947 ms
4  *  1.486 ms  1.733 ms  1.796 ms
5  * 12.762 ms 72.14.212.22  12.791 ms  12.836 ms
6  * 65.528 ms  61.534 ms  67.431 ms
7  216.239.43.250  66.606 ms 209.85.248.132  61.808 ms 216.239.43.250  60.219 ms
8  216.239.48.53  66.225 ms 209.85.254.153  61.190 ms 64.233.174.55  66.038 ms
9  66.249.95.67  60.271 ms 66.249.95.175  60.510 ms  60.956 ms
10  64.233.174.55  65.304 ms  65.610 ms 64.233.174.29  76.697 ms
11  173.194.69.109  66.954 ms  65.824 ms  61.563 ms
$ traceroute -n -T -p 465 imap.gmail.com
traceroute to imap.gmail.com (173.194.69.108), 30 hops max, 60 byte packets
1  192.168.107.1  26.088 ms  42.767 ms  42.748 ms
2  *  42.813 ms  42.799 ms  68.297 ms
3  *  42.668 ms  42.665 ms  42.619 ms
4  * 42.539 ms  42.521 ms  42.504 ms
5  *  42.522 ms  68.071 ms  68.039 ms
6  *  68.015 ms  76.070 ms  85.085 ms
7  * 136.618 ms  136.634 ms  136.555 ms
8  216.239.43.250  110.732 ms  110.744 ms  110.712 ms
9  64.233.174.55  136.549 ms 209.85.254.153  136.506 ms  136.463 ms
10  * 66.249.95.67  137.978 ms 66.249.95.175  137.887 ms
11  173.194.69.108  137.893 ms 216.239.48.53  137.846 ms 64.233.174.55  130.177 ms

Profit!
PS
In my situation i observed strange effect, although that i set src ip, my host trying to send packets with src ip of local ethernet interface, so i just add masquerade rule into iptables.

$ iptables -A POSTROUTING -o tun_vpn -J MASQUERADE

Another way to fix it, set route to local work network on router, but i too lazy to do it.