Skip to content

Instantly share code, notes, and snippets.

@ankitraturi
Created April 9, 2019 12:33
Show Gist options
  • Select an option

  • Save ankitraturi/8714e4d8a234de4acc5d1e8e6840abf0 to your computer and use it in GitHub Desktop.

Select an option

Save ankitraturi/8714e4d8a234de4acc5d1e8e6840abf0 to your computer and use it in GitHub Desktop.
DDoS Mitigation Script: ban, unban and list IP to CloudFlare via API
#!/bin/sh
##############################################################################
# DDoS Mitigation Script: ban, unban and list IP to CF via API #
# #
##############################################################################
#TODO:: set ddosP in system path
load_conf()
{
CONF="/usr/local/ddos/ddosP.conf"
if [ -f "$CONF" ] && [ ! "$CONF" == "" ]; then
source $CONF
else
head
echo "\$CONF not found."
exit 1
fi
}
TMP_PREFIX='/tmp/ddos'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"
head()
{
echo
echo "DDoS-Prevention"
echo "================================================"
echo "Block, unblock, list IP in Cloudflare"
echo "Author: Ankit Raturi"
echo "================================================"
echo
}
showhelp()
{
head
echo 'Usage: ddosIP.sh [OPTIONS] [N]'
echo 'N : number of tcp/udp connections (default 150)'
echo
echo 'OPTIONS:'
echo ' -h | --help : Show this help screen'
echo ' -k | --kill : Block the offending IPs making more than N connections'
echo ' -b | --block : Blocks the IP, add after a space'
#echo ' -un | --unblock : Unblocks the IP, add after a space'
#echo ' -l | --list : list of the blocked IPs'
echo ' -z | --zones : list the zones in CloudFlare'
echo
}
block_ip()
{
block_ip_cf "$1"
echo $1 >> /usr/local/ddos/block.ip.list
echo "blocked: $1"
exit
}
block_ip_cf()
{
#block the IP from CloudFlare
CURR_DATE=`date -d '1 minute ago' +%d/%b/%Y:%H:%M`
#curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/firewall/ua_rules" -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $CF_AUTH_KEY" -H "Content-Type: application/json" --data '{"description":"Added to CF @ '"$curr_date"'","mode":"block","configuration":{"target":"ua","value":"'"$1"'"}}' > /dev/null
#curl -sSX POST "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules" -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $CF_AUTH_KEY" -H "Content-Type: application/json" --data "{\"mode\":\"block\",\"configuration\":{\"target\":\"ip\",\"value\":\"$1\"},\"notes\":\"Blocked via cf_ban script"}" > /usr/local/ddos/testlog.txt
curl -sSX POST "https://api.cloudflare.com/client/v4/user/firewall/access_rules/rules" \
-H "X-Auth-Email: $CF_EMAIL" \
-H "X-Auth-Key: $CF_AUTH_KEY" \
-H "Content-Type: application/json" \
--data "{\"mode\":\"block\",\"configuration\":{\"target\":\"ip\",\"value\":\"$1\"},\"notes\":\"Blocked via cf_ban script\"}" > /dev/null
#cat /usr/local/ddos/testlog.txt
return;
}
unblock_ip_cf()
{
TMP_PREFIX='/tmp/ddos'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"
IP_ID=`$TMP_FILE`
get_cf_zones #call function to get CF Zones
while read line; do
ZONE_ID=`echo $line | sed -e 's/^"//' -e 's/"$//'`
#get IP id from CF api using IP string passed in $1
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/firewall/ua_rules?page=1&per_page=20" -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $CF_AUTH_KEY" -H "Content-Type: application/json" | jq '.result[] | select ((.mode=="block") and .configuration.value=='"\"$1\""') | "\(.id)"' > $IP_ID
CF_ID=`cat $IP_ID | sed -e 's/^"//' -e 's/"$//'` #assign output to a new variable and sed for removing double quotes
if [ -z "$IP_ID" ]; then #check if IP is not found in CF
echo 'No IP rule defined'
continue
else
#unblock IP from id if found
curl -s -X DELETE "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/firewall/ua_rules/$CF_ID" -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $CF_AUTH_KEY" -H "Content-Type: application/json" > /dev/null
echo "unblocked IP for $ZONE_ID : $1"
continue
fi
done < $zone_identifier
ip_unblocked=$(echo $1 | sed 's/\//\\\//g')
#echo $ip_unblocked
sed -i '/$ip_unblocked/d' /usr/local/ddos/block.ip.list
exit
}
get_ip_rules_cf()
{
CF_LIST=`$TMP_FILE`
RESULT_LIST=`$TMP_FILE`
# Get results from CF API using curl
# -s for silent progress
# jq for parsing json in shell script
# store output to $CF_LIST
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$ES_ZONE_ID/firewall/ua_rules?page=1&per_page=20" -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $CF_AUTH_KEY" -H "Content-Type: application/json" | jq '.result[] | select (.mode=="block") | "\(.configuration.value)"' > $CF_LIST
# print CF IP list
cat $CF_LIST
exit
}
get_zones()
{
get_cf_zones
while read line; do
ZONE_ID=`echo $line | sed -e 's/^"//' -e 's/"$//'`
echo "$ZONE_ID"
continue
done < $zone_identifier
exit
}
get_cf_zones()
{
TMP_PREFIX='/tmp/ddos'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"
zone_identifier=`$TMP_FILE`
ZONE_IDS=`$TMP_FILE`
curl -s -X GET "https://api.cloudflare.com/client/v4/zones" -H "X-Auth-Email: $CF_EMAIL" -H "X-Auth-Key: $CF_AUTH_KEY" -H "Content-Type: application/json" | jq '.result[] | "\(.id)"' > $zone_identifier
#ZONE_IDS=`cat $zone_identifier | sed -e 's/^"//' -e 's/"$//'`
}
load_conf
while [ $1 ]; do
case $1 in
'-h' | '--help' | '?' )
showhelp
exit
;;
'--cron' | '-c' )
add_to_cron
exit
;;
'--kill' | '-k' )
KILL=1
;;
'--block' | '-b' )
block_ip "$2"
;;
'--unblock' | '-un' )
unblock_ip_cf "$2"
;;
'--list' | '-l' )
get_ip_rules_cf
;;
'--zones' | '-z' )
get_zones
;;
*[0-9]* )
NO_OF_CONNECTIONS_IP=$1
;;
* )
showhelp
exit
;;
esac
shift
done
TMP_PREFIX='/tmp/ddos'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"
BANNED_IP_MAIL=`$TMP_FILE`
BANNED_IP_LIST=`$TMP_FILE`
echo "Banned the following Non-concurrent IPs on `date`" > $BANNED_IP_MAIL
echo >> $BANNED_IP_MAIL
BAD_IP_LIST=`$TMP_FILE`
#get date format
CURR_DATE=`date -d '1 minute ago' +%d/%b/%Y:%H:%M`
#grep $CURR_DATE /var/www/vhosts/system/*/logs/access_log | awk -F\" '{print $6}' | sort | uniq -c | sort -rn > /usr/local/ddos/bad_ip_list
grep $CURR_DATE /var/www/vhosts/system/*/logs/access* | awk '{print $2}' | sort | uniq -c | sort -nr > /usr/local/ddos/bad_ip_list_2
cat /usr/local/ddos/bad_ip_list_2 | mail -s "IPs banneds on $CURR_DATE" "ankitraturi@mailinator.com"
#cat /usr/local/ddos/bad_ip_list_2
if [ $KILL -eq 1 ]; then
IP_BAN_NOW=0
while read line; do
#if no of connections is less than defined then ignore
#echo $line
#exit
CURR_LINE_CONN=$(echo $line | cut -d " " -f1)
#echo $NO_OF_CONNECTIONS_IP
#exit
CURR_LINE_IP=$(echo $line | cut -d " " -f2-)
if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS_IP ]; then
###break
#echo "less $CURR_LINE_CONN"
continue
fi
#echo $CURR_LINE_IP
#continue
#ignore IP (good bots)
IGNORE_IP=`grep -c "$CURR_LINE_IP" "$IGNORE_IP_LIST"`
if [ $IGNORE_IP -ge 1 ]; then
echo $CURR_LINE_IP
continue
fi
#echo "here"
#exit
#ignore already added IP
IGNORE_IP_2=`grep -c "$CURR_LINE_IP" "$BLOCK_IP_LIST"`
if [ $IGNORE_IP_2 -ge 1 ]; then
echo "Ignored: $CURR_LINE_IP"
continue
fi
#echo "here"
#continue
#ignores if match the string
flagkey=0
while read line; do
if `echo ${CURR_LINE_IP} | grep -i "${line}" 1>/dev/null 2>&1`
then
echo "Ignored with matched keyword ( $line ) : $CURR_LINE_IP"
flagkey=1
break
fi
done < $IGNORE_IP_KEYWORDS
#echo $CURR_LINE_IP
#exit
if [ $flagkey -eq 1 ]; then
continue
fi
#End: ignore IPs if match the string
IP_BAN_NOW=1
echo "matched: $CURR_LINE_IP"
#call block IP function
$CURR_LINE_IP >> /usr/local/ddos/block.ip.list
block_ip_cf "$CURR_LINE_IP"
#email list to the admin
echo "$CURR_LINE_IP with $CURR_LINE_CONN connections" >> $BANNED_IP_MAIL
continue
done < /usr/local/ddos/bad_ip_list_2
if [ $IP_BAN_NOW -eq 1 ]; then
dt=`date`
if [ $EMAIL_TO != "" ]; then
cat $BANNED_IP_MAIL | mail -s "Non-Concurrent IP banned on $dt" $EMAIL_TO
fi
fi
fi
exit
rm -f $TMP_PREFIX.*
echo "RUN";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment