Fail-To-Ban (Lite) – EdgeRouter

Here’s how to create a fail-to-ban type of functionality on an EdgeRouter completely using BASH, without installing any 3rd party packages. We are going to create a single script and add a scheduled job to run it. That’s all there is to it.

Step 1
Run the following

vi /config/scripts/fail-to-ban

Now we need to turn off auto indent before copying and pasting the script below. Type the following:

:set noai
i

Now paste the following script into the file:

#!/bin/bash

PATH=$PATH:/usr/bin:/bin:/sbin:/usr/sbin

ATTEMPTS=10;    # NUMBER OF ATTEMPTS IN A GIVEN INTERVAL
INTERVAL=600;   # 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=`nice -19 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:BLOCKED" >> /tmp/banned.log
    fi
        fi
}

updateList() {
        NOW=`date '+%s'`
        sed -i /tmp/ip-list.log -e "s/:"$IP":"$LASTCOUNT".*$/:"$IP":"$COUNT":"$NOW"/"
}

updateTime() {
  NOW=`date '+%s'`
  sed -i /tmp/ip-list.log -e "s/:"$IP":"$LASTCOUNT".*$/:"$IP":"$LASTCOUNT":"$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=$(nice -19 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':' -f2`                                                                                                                                         
          isip $IP                                                                                                                                                             
          COUNT=`echo $i | cut -d':' -f3`                                                                                                                                      
          LASTACTION=`echo $i | cut -d':' -f4`                                                                                                                                 
                                                                                                                                                                               
          if [ $((NOW-LASTACTION)) -gt $BLOCKSECS ] && [ ! "$IP" == "" ] && [ $ISIP -eq 1 ] && [ $COUNT -lt $PERMBAN ]; then                                                   
                  LINE=`nice -19 iptables -L -n --line-numbers | grep "$IP" | cut -d' ' -f1`                                                                                            
                  if [ ! "$LINE" == "" ]; then                                                                                                                                 
                          echo "Removing block on $IP"                                                                                                                         
        echo "$(date):$IP:UNBLOCKED" >> /tmp/banned.log
                          # 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

# CLEANUP - KEEP ONLY HACKERS FROM TODAY
echo -n "" > /tmp/ip-list.new
IFS="
"
for i in `grep "^$(date +%Y%m%d):" /tmp/ip-list.log`
do
  if [ ! "$i" == "" ]; then
    echo $i >> /tmp/ip-list.new
  fi
done
mv /tmp/ip-list.new /tmp/ip-list.log

# 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 -1 --full-time /var/log/auth.log > /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=`nice -19 grep failure /var/log/auth.log | grep rhost | sed -e 's/^.*rhost=//' | sed -e 's/ .*$//' | sort | uniq -c | sed -e 's/^ *//' | sed -e 's/ /:/' | grep -E ":[0-9.]*$" | sed -e "s/^\(.*\)$/$(date +%Y%m%d):\1/"`

for i in `echo "$IPLIST"`
do
  #echo $i
        COUNT=`echo $i | cut -d':' -f2`
        IP=`echo $i | cut -d':' -f3`
  DATE=`echo $i | cut -d':' -f1`
  isip $IP
        LASTCOUNT=`cat /tmp/ip-list.log | grep ":$IP:" | cut -d':' -f3`
        ELAPSED=`cat /tmp/ip-list.log | grep ":$IP:" | cut -d':' -f4 | 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 "$DATE:$IP:$COUNT:$NOW" >> /tmp/ip-list.log
               # echo "Adding $IP to the IP tracking log with count $COUNT"
        fi
  # echo "IP:$IP NEWCOUNT:$NEWCOUNT LASTCOUNT:$LASTCOUNT COUNT:$COUNT ELAPSED:$ELAPSED ISIP:$ISIP ATTEMPTS:$ATTEMPTS INTERVAL:$INTERVAL"
  
        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
  elif [ $NEWCOUNT -ge $ATTEMPTS ] && [ $ISIP -eq 1 ]; then
    echo "Updating the timestamp for IP $IP; +$NEWCOUNT since last update"
    updateTime
        fi
done

checkExpired

IFS=","

showList "Blocked | Attempts" "$BLOCKED_ALREADY"
showList "Newly Blocked | Attempts" "$BLOCKED_NOW"
showList "Skipped | Attempts" "$SKIPPED"
showList "Expired" "$EXPIRED_BLOCK"

Now you need to exit and save your changes.
esc
:wq

Now let’s make the script executable:

chmod a+x /config/scripts/fail-to-ban

And lastly, we need to add a job to run this periodically and also disable hostname lookups in SSH to make the script’s IP validation work properly and remove a possible DoS vector.

configure
set system task-scheduler task failtoban executable path /config/scripts/fail-to-ban
set system task-scheduler task failtoban interval 1m
set service ssh disable-host-validation
commit
save

Done! Now your EdgeRouter should be protected against brute force login attacks.

Prepping VyOS + OpenVPN for use with a Chromebook

First, you need to create the certificates. Use EasyRSA for this. Follow the instructions at the OpenVPN site for that.
https://openvpn.net/index.php/open-source/documentation/miscellaneous/77-rsa-key-management.html

To build the CA certificate, use the command:
./easyrsa build-ca

To build the server certificate, use the command:
./easyrsa build-server-full server

To build a client, use the command:
./easyrsa build-client-full client

You may want to remove the password for the private keys for the server and client certificates. To do that, use these commands.

cd private
openssl rsa -in server.key -out server-nopass.key
openssl rsa -in client.key -out client-nopass.key
cd ..

Now copy the ./issued/server.crt, the ./ca.crt, and the ./private/client-nopass.key to the VyOS server. Create a new folder called /config/auth/openvpn and store them there.

The OpenVPN connection for VyOS should look like this:

 openvpn vtun0 {
     local-port 1194
     mode server
     openvpn-option --persist-tun
     protocol udp
     replace-default-route {
         local
     }
     server {
         domain-name mydomain.com
         max-connections 10
         name-server 10.0.0.1
         push-route 10.0.0.0/23
         subnet 10.0.1.0/24
         topology subnet
     }
     tls {
         ca-cert-file /config/auth/openvpn/ca.crt
         cert-file /config/auth/openvpn/server.crt
         dh-file /config/auth/openvpn/dh.pem
         key-file /config/auth/openvpn/server-nopass.key
     }
 }

Now we have to prep the Chromebook certificate.

openssl pkcs12 -export -out client.pfx -inkey private/client-nopass.key -in issued/client.crt -certfile ca.crt

Now upload the ca.crt and the client.pfx files to the Chromebook (You can use the SCP addon for the file manager to transfer them there.)

Navigate to chrome://certificate-manager.
Click on Authorities and then click Import.
Navigate to the ca.crt and import it.
Now click back to “Your certificates” and click “Import and Bind to Device”.
Navigate to the client.pfx and import it. When it asks for a password, hit enter without one if you did not set one.
Now navigate to chrome://settings and click “Add Connection”. Choose OpenVPN.
Set the “Provider Type” to be OpenVPN.
Set the CA certificate to be the one that we uploaded.
Set the client certificate to be the client.pfx. Note: It will show up with the common name of the certificate, not the file name.
Now type in any username and password. It doesn’t matter since we are using certificate based authentication. Save the user/pass so that you do not have to type it every time. Again, it does not matter what you type here.

Now you should be able to connect!