Skip to content

Instantly share code, notes, and snippets.

@andreacioni
Last active December 23, 2017 10:26
Show Gist options
  • Save andreacioni/a9b0b373929c809607db6354cc009cca to your computer and use it in GitHub Desktop.
Save andreacioni/a9b0b373929c809607db6354cc009cca to your computer and use it in GitHub Desktop.
Block access to your site from Tor network
#!/usr/bin/env python
"""
This utility script download Tor exit node list IP from the Web and
block every one of them with a dedicated iptables rule.
"""
import logging
from subprocess import call
from urllib2 import urlopen, URLError
from time import sleep
from re import match
IPTABLES_CMD='/sbin/iptables -A INPUT -s {} -j DROP'
TOR_EXIT_NODE_URL = 'https://check.torproject.org/cgi-bin/TorBulkExitList.py?ip=1.1.1.1'
IP_PATTERN = '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
CACHE_FILE = '.iptabman'
MAX_RETRY = 10
RETRY_TIMEOUT_SEC = 5
logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=logging.DEBUG, filename='/var/log/torblock.py', filemode='w')
def fetch_tor_ips():
exit_nodes = None
logging.debug('Fetching tor exit node ip list')
try:
request = urlopen(TOR_EXIT_NODE_URL)
exit_nodes=request.readlines()
logging.debug('Downloaded list contains: %s nodes', len(exit_nodes))
except URLError as url_exception:
logging.warn('Cannot dowload list from Web: %s', url_exception)
return exit_nodes
def update_cache(exit_nodes):
logging.debug('Updating cache file')
blklist_file=open(CACHE_FILE, 'w')
blklist_file.writelines(exit_nodes)
blklist_file.close()
def get_cache():
logging.debug('Getting cache file if exists')
blklist_file = None
exit_nodes = None
try:
blklist_file=open(CACHE_FILE, 'r')
exit_nodes=blklist_file.readlines()
except IOError as io_error:
logging.warn('Cannot open cache file %s', io_error)
finally:
if(blklist_file):
blklist_file.close()
return exit_nodes
def apply_rules(exit_nodes):
logging.info('Applying rules')
for ip in exit_nodes:
if match(IP_PATTERN, ip):
logging.debug('Applying rule for IP: %s', ip)
call(IPTABLES_CMD.format(ip).split())
else:
logging.warn('IP: %s is not valid', ip)
fail_fetch = True
exit_nodes = None
for i in range(MAX_RETRY):
logging.info('Trying to update tor exit nodes list (%i/%i)', i+1, MAX_RETRY)
exit_nodes = fetch_tor_ips()
if exit_nodes:
update_cache(exit_nodes)
fail_fetch = False
break
else:
sleep(RETRY_TIMEOUT_SEC)
if fail_fetch:
logging.error('Failed to fetch tor exit nodes list.')
cached_exit_nodes = get_cache()
if cached_exit_nodes:
exit_nodes = cached_exit_nodes
else:
logging.error('No cache is present, terminating witout setting any rule')
else:
logging.debug('New tor exit node list will be used')
logging.debug('Removing header and line termination char')
exit_nodes=[ip[:-1] for ip in exit_nodes[3:]]
apply_rules(exit_nodes)
logging.info('Done!')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment