Block tor traffic in cloudflare firewall
![tor logo] ({filename}/static/images/tor.png)
If you don’t want to allow access to your server through the tor network you can ask nicely or just add every malicious looking client to a list. I tried to come up with a better solution.
I started with a script that blocked incomming connections on a loadbalancer (can also be used on a webserver) Then I realized that if you use cloudclare in front of that the tcp connections come from cloudare and not from the tor endpoints. So I needed to rebuild it to add the rules to the cloudflrae firewall.
I started of the same way, create a variable which contains all the tor ip adresses. Then It get’s a bit more complex, working with a json api and bash is often not without struggle. The code is commented, if that is not enough contact me and I will try to make i more readable. Use it, cron it, love it ;-)
#!/bin/bash
echo "Tor endpoint list loading"
TORLIST=$(curl -s https://check.torproject.org/exit-addresses |grep ExitAddress | awk '{print $2 }' | sort | uniq)
ZONE="" # can be left empty this script will pick the first one from your account for you
KEY="" # API key can be found at: https://www.cloudflare.com/a/account/my-account
EMAIL="" # email adress used to login to cloudflare
# If the ZONE is not defined get the first one from the api
if [ -z "$ZONE" ]; then
ZONE=$( curl -s -X GET "https://api.cloudflare.com/client/v4/zones/" \
-H "Content-Type:application/json" \
-H "X-Auth-Key:${KEY}" \
-H "X-Auth-Email:${EMAIL}" | python -m json.tool |grep -A 1 development_mode | grep id | awk '{print $2}' | sed 's/"//g' |sed 's/,//g')
echo "Add zone string to config for faster result and less api calls"
echo ${ZONE}
fi
echo "Retrieving active firewall rules"
IP_LIST=""
# The api is paged so we first qeury the api and check how many apges we have
PAGES=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE}/firewall/access_rules/rules?mode=block&configuration_target=ip&page=1&per_page=1000&order=scope_type&direction=desc&match=all " \
-H "Content-Type:application/json" \
-H "X-Auth-Key:${KEY}" \
-H "X-Auth-Email:${EMAIL}" | python -m json.tool | grep total_pages | awk '{print $2}')
# for every page gat the response rinse out the bullshit keep the ip adresses
# Because we can have multiple pages I cocatinate the srings into IP_LIST
for ((page=1; $page <= ${PAGES}; page++)); do
DATA=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE}/firewall/access_rules/rules?mode=block&configuration_target=ip&page=${page}&per_page=1000&order=scope_type&direction=desc&match=all " \
-H "Content-Type:application/json" \
-H "X-Auth-Key:${KEY}" \
-H "X-Auth-Email:${EMAIL}" | python -m json.tool | grep value | awk '{print $2}')
IP_LIST="${IP_LIST} ${DATA}"
done
# Now we have a list with all the tor ip exit nodes, and all the ip adresses in the firewall.
# I creatd the next loop to take all the tor endpoints and check if they are already in the firewall.
# If the tor ip is not found in the firewall list it will be added to the firewall
# There is a sleep in the loop to keep the api from blocking your reqeusts (max 1200 per 5 min.)
for ip in ${TORLIST}; do
sleep 0.3
IN_FIREWALL=$(echo ${IP_LIST} | grep ${ip})
if [ -z "$IN_FIREWALL" ]; then
echo "adding ${ip} to blocklist"
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE}/firewall/access_rules/rules" \
-H "Content-Type:application/json" \
-H "X-Auth-Key:${KEY}" \
-H "X-Auth-Email:${EMAIL}" \
--data '{"mode":"block","configuration":{"target":"ip","value":"'"${ip}"'"},"notes":"This site is not available through tor endpoints"}' > /dev/null
# else
# echo "already in blocklist"
fi
done