Simple Fail2Ban Alternative for FreePBX + SSH
Pete Lombardo
I created a script to help secure Asterisk servers: fail-to-ban. It runs once per minute and will block an IP that tries, unsuccessfully, to authenticate more than a certain number of tries within a certain interval. It keeps track of each block and will automatically unblock an IP after a given amount of time elapses. All of these variables are easily set/tuned at the beginning of the script. It’s basic, easy to install, and effective. The installation is just two extremely easy steps.
- Create the file /scripts/fail-to-ban.
- Add a cron job entry to run it.
/scripts/fail-to-ban
#!/bin/bash
PATH=$PATH:/usr/bin:/bin:/sbin:/usr/sbin
ATTEMPTS=10; # NUMBER OF ATTEMPTS IN A GIVEN INTERVAL
INTERVAL=300; # INTERVAL (IN SECONDS) TO WATCH FOR FAILED ATTEMPTS - HISTORICALLY FROM CURRENT TIME
PERMBAN=100; # AFTER THIS NUM OF FAILED ATTEMPTS, BAN UNTIL LOG ROTATES
BLOCKSECS=3600; # AFTER THIS TIME (IN SECONDS), UNBLOCK A BLOCKED IP
BLOCKED_ALREADY=""
BLOCKED_NOW=""
SKIPPED=""
EXPIRED_BLOCK=""
NOW=`date '+%s'`
isip() {
ISIP=$1
if [ $(echo $IP | sed 's/[^.]//g' | awk '{print length; }' 2> /dev/null) -eq 3 ]; then
ISIP=1
fi
}
fail2ban() {
# echo failing $IP with count $COUNT and lastcount $LASTCOUNT
IP=$IP
EXISTS=`iptables -n -L | grep $IP | wc -l`
IS_LOCAL=`echo $IP | grep -E '^10\.|192\.168|127\.' | wc -l`
if [ $EXISTS -gt 0 ]; then
BLOCKED_ALREADY+=",$IP:$COUNT"
# echo "IP $IP is already blocked"
elif [ $IS_LOCAL -eq 1 ]; then
SKIPPED+=",$IP:$COUNT"
# echo "IP is local IP. Not blocking"
else
if [ ! "$IP" == "" ]; then
# echo "Blocking IP $IP after $COUNT abuses."
BLOCKED_NOW+=",$IP:$COUNT"
iptables -I INPUT 1 -j DROP -s $IP
echo "`date`:$IP:$NEWCOUNT:$COUNT" >> /tmp/banned.log
fi
fi
}
updateList() {
NOW=`date '+%s'`
sed -i /tmp/ip-list.log -e "s/"$IP":"$LASTCOUNT".*$/"$IP":"$COUNT":"$NOW"/"
}
showList() {
LIST="$2"
DESCRIPTION="$1"
if [ ! "$LIST" == "" ]; then
echo "$DESCRIPTION"
for i in `echo "$LIST"`
do
BIP=$(echo $i | sed -e 's/:.*$//')
BCOUNT=$(echo $i | sed -e 's/^.*://')
if [ ! "$BIP" == "" ]; then
echo $BIP $BCOUNT
fi
done
fi
}
checkExpired() {
BLOCKED=$(iptables -L INPUT -n | grep "^DROP" | sed -e 's/^.*-- //' | sed -e 's/ .*$//')
for i in `grep -e "$BLOCKED" /tmp/ip-list.log`
do
IP=`echo $i | cut -d':' -f1`
isip $IP
COUNT=`echo $i | cut -d':' -f2`
LASTACTION=`echo $i | cut -d':' -f3`
if [ $((NOW-LASTACTION)) -gt $BLOCKSECS ] && [ ! "$IP" == "" ] && [ $ISIP -eq 1 ] && [ $COUNT -lt $PERMBAN ]; then
LINE=`iptables -L -n --line-numbers | grep "$IP" | cut -d' ' -f1`
if [ ! "$LINE" == "" ]; then
echo "Removing block on $IP"
# EXPIRED_BLOCK+=",$IP"
echo iptables -D INPUT $LINE
iptables -D INPUT $LINE
fi
fi
done
}
if [ ! -f /tmp/ip-list.log ]; then
touch /tmp/ip-list.log
fi
# Do some checking to see if the logs actually changed
if [ -f /tmp/this-run ]; then
mv /tmp/this-run /tmp/last-run
else
touch /tmp/last-run
fi
ls -al /var/log/asterisk/security > /tmp/this-run
CHANGE=$(diff /tmp/last-run /tmp/this-run | wc -l)
if [ $CHANGE -eq 0 ]; then
echo "No change since last run"
checkExpired
exit
fi
IPLIST=$(/bin/grep -E "InvalidAccount|ChallengeResponseFailed" /var/log/asterisk/security | sed -e 's/^.*RemoteAddress="IPV4\/UDP\/\([0-9.]*\)\/.*$/\1/' | sort | uniq -c | sed -e 's/^ *//' | sed -e 's/ /:/')
for i in `echo "$IPLIST"`
do
#echo $i
COUNT=`echo $i | cut -d':' -f1`
IP=`echo $i | cut -d':' -f2`
isip $IP
LASTCOUNT=`cat /tmp/ip-list.log | grep "^$IP:" | cut -d':' -f2`
ELAPSED=`cat /tmp/ip-list.log | grep "^$IP:" | cut -d':' -f3 | sed -e 's/\n//g'`
ELAPSED=$((NOW-ELAPSED))
if [ "$COUNT" == "" ]; then
COUNT=0
fi
if [ "$LASTCOUNT" == "" ]; then
LASTCOUNT=0
fi
NEWCOUNT=$((COUNT-LASTCOUNT))
if [ ! "$LASTCOUNT" == "" ] && [ $LASTCOUNT -eq 0 ] && [ $ISIP -eq 1 ]; then
echo "$IP:$COUNT:$NOW" >> /tmp/ip-list.log
# echo "Adding $IP to the IP tracking log with count $COUNT"
fi
if [ $NEWCOUNT -ge $ATTEMPTS ] && [ $ISIP -eq 1 ] && ( [ $ELAPSED -le $INTERVAL ] || [ $COUNT -gt $PERMBAN ] ); then
if [ $LASTCOUNT -ne 0 ]; then
# echo "Updating IP:$IP with NEWCOUNT:$NEWCOUNT ATTEMPTS:$ATTEMPTS ELAPSED:$ELAPSED INTERVAL:$INTERVAL ISIP:$ISIP"
updateList
fi
fail2ban
fi
done
checkExpired
IFS=","
showList "Blocked | Attempts" "$BLOCKED_ALREADY"
showList "Newly Blocked | Attempts" "$BLOCKED_NOW"
showList "Skipped | Attempts" "$SKIPPED"
showList "Expired" "$EXPIRED_BLOCK"
/scripts/crontab
* * * * * /scripts/fail-to-ban > /dev/null 2>&1 &
After creating these files, simply run the following commands:
chmod a+x /scripts/fail-to-ban
crontab /scripts/crontab
That’s really all you need to do!