Skip to content

Instantly share code, notes, and snippets.

@jacopotediosi
Last active February 16, 2024 06:52
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jacopotediosi/5dcad9c9b90f23935b08c1911f76f3c4 to your computer and use it in GitHub Desktop.
Save jacopotediosi/5dcad9c9b90f23935b08c1911f76f3c4 to your computer and use it in GitHub Desktop.
Block Tor Exit Nodes with Fail2ban

Below is the procedure to automate the ip ban of tor exit nodes with fail2ban.

  1. Install fail2ban:
sudo apt install fail2ban
  1. Create a new fail2ban jail (editing /etc/fail2ban/jail.local):
[tor]
enabled  = true
bantime  = 25h
action   = iptables-allports[name=fail2banTOR, protocol=all]
  1. Create a dummy filter file to /etc/fail2ban/filter.d/tor.conf:
[Definition]
failregex =
ignoreregex =
  1. Adjust fail2ban file limits (create /etc/systemd/system/fail2ban.service.d/limits.conf):
[Service]
LimitNOFILE=2048
  1. Restart systemctl and fail2ban:
sudo systemctl daemon-reload
sudo service fail2ban restart
  1. Create a bash script to download the list of tor exit nodes and add their ips to the fail2ban jail:
#!/bin/bash
set -o nounset -o xtrace -o errexit

IPV4_REGEX='^([0-9]{1,3}\.){3}[0-9]{1,3}(\/([0-9]|[1-2][0-9]|3[0-2]))?$'
IPV6_REGEX='^s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?s*(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$'

banned_ip_counter=0

for ip in $(curl -fsS https://check.torproject.org/torbulkexitlist); do
  if ! [[ $ip =~ $IPV4_REGEX || $ip =~ $IPV6_REGEX ]]; then
      echo "Error: $ip is not a valid IPv4/IPv6 address" >&2
      continue
  fi

  sudo fail2ban-client set "tor" banip "$ip"
  banned_ip_counter="$((banned_ip_counter+1))"
done

if [[ $banned_ip_counter -lt 1 ]]; then
  echo "Error: no IP banned" >&2
fi
  1. Run the script periodically by adding the following lines to crontab -e (install cronic to be notified via email in case of script failure):
MAILTO=mail@example.com
30 6 * * * cronic /etc/fail2ban/block-tor.sh
@psyray
Copy link

psyray commented Jul 1, 2020

Thanks for your script, but don't you need to delete IP in tor jail before re-add it via cron ?
I think it's more clean, no ?

fail2ban-client restart --unban tor
curl -fsSL "https://check.torproject.org/torbulkexitlist" | sed '/^#/d' | while read IP; do
  sudo fail2ban-client set "tor" banip "$IP"
done

@jacopotediosi
Copy link
Author

jacopotediosi commented Jul 1, 2020

Thanks for your script, but don't you need to delete IP in tor jail before re-add it via cron ?
I think it's more clean, no ?

fail2ban-client restart --unban tor
curl -fsSL "https://check.torproject.org/torbulkexitlist" | sed '/^#/d' | while read IP; do
  sudo fail2ban-client set "tor" banip "$IP"
done

@psyray thanks for your review!
On my server the script takes about ~20 minutes to add all Tor IPs to the list, so it may not be desirable to have a gap during which IPs are not banned.
The jail bans only for 25h, so it should clean up old IPs by itself.

@psyray
Copy link

psyray commented Jul 1, 2020

20 minutes ... it takes 2 on mine :)
You're right in your case, my gap is a little more restricted.
Anyway thanks for your script ;)

@BrixSat
Copy link

BrixSat commented Nov 17, 2021

Here takes about 34 seconds.

@jacopotediosi
Copy link
Author

Just added more checks to the bash script: regex on ip addresses and improved error messages.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment