#!/bin/sh
#============================================================================================================
# Descargado de: http://foro.seguridadwireless.net/openwrt/script-baneador-de-direcciones-ip-ssh-para-openwrt/
# Descargado de: http://foro.seguridadwireless.net/openwrt/script-baneador-de-direcciones-ip-ssh-para-openwrt/20/
# Descargado de: http://foro.seguridadwireless.net/openwrt/script-baneador-de-direcciones-ip-ssh-para-openwrt/40/
# Descargado de: https://micorreo.email/OpenWRT.html

# Script realizado inicialmente por hardwarer para el foro de seguridadwireless.net
# Este script lee del log los fallos de autentificacion SSH (por usuario o contraseña erronea)
# Ideal si tienes publicado el servicio ssh (no recomendado) o si hay algun dispositivo en la red local atancando al router
# Si los fallos exceden de cierta cantidad, banea su IP de origen utilizando iptables
# Tiempo de baneo configurable y tablas de baneo de IP permanentes al reinicio del sistema.
# Probado en OpenWrt 15.05.1 Chaos Chalmer, LEDE 17.01 y OpenWRT 18.06.4

# Modificaciones Tki2000 (2017-05-15)
# Modificaciones David   (2017-09-20) (Retocadas las funciones originales + puntos 5, 6, 7 y 8)
# Modificaciones David   (2017-09-23) (9)
# Modificaciones David   (2017-09-30) (10 + correccion de errores + cambio nombres de variables para legibilidad y asemeje a fail2ban original
# Modificaciones David   (2019-07-14) (Puntos 11, 12 y 13 + correcciones y otros cambios junto a amplicacion de informacion)
#
# 1.-  El fichero de baneos se escribe /tmp/, del que OpenWRT carga en RAM y asi no machaca la flash del router
# 2.-  Si grabamos previamente el fichero de baneos, de la RAM a la flash, lo usará al inicio.
# 3.-  Posibilidad de banear manualmente ips, además de las detectadas en el script, durante un tiempo indefinido.
# 4.-  Las IPs repetidas, entre ficheros, no se tienen en cuenta nada más que una vez al banear. (2017-05-30)
# 5.-  Puede leer el log n lineas hacia atras para ver si esa ip ha tenido un acceso correcto y omitir su baneo
# 6.-  Se puede anadir ips a una lista blanca para omitir baneo en caso de que tengas dedazos gordos.
# 7.-  Se pueden anadir DNS a la lista blanca para omitir baneo     # Requiere paquete hostip para resolver los dns
# 8.-  A la hora de quitar los baneos, revisa que tenga el comentario para evitar eliminar una regla que no toca
# 9.-  Opcion a registrar en el log los baneos y unbaneos
# 10.- Modo Debug en el que no aplica las reglas del firewall, en su lugar imprime lo que haria (tampoco escribira la accion en el log)
# 11.- Notifica por email el baneo y unbaneo aplicados              # Requiere paquete mailsend
# 12.- Notifica por Telegram el baneo y unbaneo aplicados           # Requiere paquete curl
# 13.- Se contabilizan también tanto los fallos como accesos correctos a luci
#
#    ======= A tener en cuenta =========
# Puede estar anadiendo y quitando constantemente un baneo si el log no rota, tiene un buffer pequeno o el tiempo de baneo es corto (amplia buffer, abajo detallo)
# En LEDE por defecto el log registra solo unas 340 lineas y perderias accesos no autorizados al rotar el log. Anade en la seccion "config system" del fichero /etc/config/system
#  option log_buffer_size 256  # En LEDE por defecto toma 16. Con 256 son a unas 5000 lineas y con 512 unas 10.000 (no poner mas de 999 ya que en LEDE se comporta erroneo)
# En OpenWRT 18 el log por defecto son de 64Ks pero registra tambien pocas lineas (unas 580). Anade en la seccion "config system" del fichero /etc/config/system
#  option log_size '256'
# Si cambiamos el ambito de bloqueo(chain) y todavía quedan reglas andidas, no seran eliminadas, aunque pase el tiempo de unbaneo. Borrar a mano o reiniciar
# Puede que no te banee por tener un acceso reciente pero que pasadas las lineas de busqueda seleccionadas ya no lea tu acceso y que ya no omita el baneo.
#    Lo ideal seria resetear y no buscar fallos hacia atras después de un acceso correcto de esa ip (pendiente por hacer, pero asi ya me funciona bien)
#
#============================================================================================================
#========================================== OPCIONES ========================================================

maxtretry=6            # Cuantos fallos de contrasena sumados a login de usuarios inexistentes ha de encontrar en el log para banear a la ip (ojo, cada usuario no valido computa como 2)
LinesToFindAcces=2000  # Cuantos eventos revisa atras en el log para ver si hay un acceso correcto de esa ip. Si lo hay, no la baneara. 0 para infinito
ban_time=3600          # Tiempo de baneo (en segundos). Transcurrido ese tiempo se desactiva el baneo y se quita la IP de la lista.
                       #  3600 = 1h   36000 = 10h   # 86400 = Un dia = 24 horas   # Dos semanas = 86400 * 14 = 1209600
chain="input_rule"     # Ambito/cadena de iptables donde se aplicaran los baneos.  Para hacer pruebas con mejor seguridad desde lan, utiliza "zone_wan_input". Tmb puedes reiniciar si te equivocas.
                       # Con "input_rule" o "INPUT" sirve para proteger todos los origenes, pero puedes poner que solo banea a lan, wan u otras cadenas. ver con  iptables -nvL | grep Chain
                       # En LEDE 17    ->  Local="input_lan_rule"  internet="input_wan_rule"  Ambas="input_rule"
                       # En OpenWRT 18 ->  Local="zone_lan_input"  internet="zone_wan_input"  Ambas="input_rule"

WhitelistIPs="" #192.168.1.1 192.168.1.1"               # Ips que omitiran al baneo. Para mas de una, anadir con un espacio. Se puede poner rango cidr
WhitelistDNS="" #casa.duckdns.org oficina.duckdns.org"  # DNS que omitiran al baneo. Requiere hostip para resolver los DNS

file_banapplied="/tmp/banned.ips"                # Fichero de baneos. Almacezadlo siempre en /tmp/ para asegurar que se escribe en RAM
file_banstored="/etc/fail2ban/banned.ips"        # Fichero que podemos guardar en flash antes de reiniciar para volver a comenzar los baneos donde los dejamos
file_banstatic="/etc/fail2ban/banned_static.ips" # Para añadir manualmente ips que queramos banear desde el principio y de manera indefinida. (Una IP o CIDR por línea)

# Log / Debug
WriteLogWithAction=1        # 1 para escribir en el log con el tag f2b_action cuando banea o unbanea una ip. Cualquier otra cosa para omitir. (Con Debug activo no escribira en log)
Report_Actions_eMail=True   # Envia un email cuando banea o unbanea a una ip (debes rellenar los datos para el correo y requiere del paquete mailsend)
Report_Actions_botTG=False  # Envia un Telegram cuando banea o unbanea una ip (debes rellenar los datos para el bot de Telegram y requiere paquete curl)
Debug="False"               # "True" prints only the actions without doing changes on system, any other to normal without debug

####### Datos para los envios de correo #######
SMTP_SERVER=smtp.gmail.com   # Si pones la ip podras recibir notificaciones aunque todavia no haya levantado dnscrypt/adblock o haya alguna incidencia al respecto
SMTP_SERVER_PORT=587
SMTP_AUTH=tucorreo
SMTP_PASS="tupass"
MAIL_FROM=$SMTP_AUTH
MAIL_TO=correodestino
MAIL_SUBJECT_BAN="Baneada en tabla $chain de $(uname -n) a la ip "                       # Al enviar anadira la ip al final
MAIL_SUBJECT_UBAN="Eliminado baneo en tabla $chain de $(uname -n) a la ip "              # Al enviar anadira la ip al final
MAIL_SUBJECT_Recover="Recuperado baneo en tabla $chain al iniciar $(uname -n) a la ip "  # Al enviar anadira la ip al final

#### Datos para la notificacion por Telegram ####
BOT_TG_CHAT_ID='[CHAT_ID]'
BOT_TG_TOKEN_KEY='[TOKEN_KEY]'
BOT_TG_API="https://api.telegram.org/bot$BOT_TG_TOKEN_KEY"   #  Crea bot con @BotFather para enviar info directamente a tu chat.  https://core.telegram.org/bots#6-botfather


#=============================================================================================================
#=====================    Se debe añadir al cron para que se lance cada cierto tiempo    =====================
# Copiar en /etc/config/scripts/fail2ban_openwrt.sh y dar permisos de ejecución: chmod +x /etc/config/scripts/fail2ban_openwrt.sh
# Para que se lance cada 11 minutos copia la linea de abajo editando con "crontab -e" o en luci "Sheluded Tasks"
# */11 * * * *  /etc/config/scripts/fail2ban_openwrt.sh >> /dev/null 2>&1

# Si cronificas cada muy poco tiempo (dos minutos), anades carga al router.
# Si lo haces cada mucho (una hora) y tienes muchos otros eventos en el log, podria no detectar y banear adecuadamente.


########################################################################
#########  A partir de aqui no es necesario ver o tocar nada  ##########
########################################################################

#======================================================================================================
#=====================================   Funciones   ==================================================

EnviaMail(){
         mailsend -smtp $SMTP_SERVER -port $SMTP_SERVER_PORT -t "$MAIL_TO" -f "$MAIL_FROM" -sub "$1" -starttls -auth -user "$SMTP_AUTH" -pass "$SMTP_PASS"  -M "$2" > /dev/null
}

EnviaBot(){
         curl -s -X POST ${BOT_TG_API}/sendMessage -d chat_id=$BOT_TG_CHAT_ID -d parse_mode=Markdown --data-urlencode text="$1" > /dev/null
}

#======================================================================================================
#================================    Opciones internas    =============================================
fecha_actual=`date +%s`
unban_date=`expr $fecha_actual + $ban_time`

WhitelistLocalIps=True                                                  # Anadira las ips de los interface locales a la whitelist (No deberia ser necesario, por precaucion)
if [ "$WhitelistLocalIps" == "True" ]; then
    MyIPs=$(ifconfig | grep "inet addr" | awk '{ print $2}' | cut -d ":" -f 2 | xargs)                # Todas las ips locales del router (LAN, WAN, local y VPN levantadas)
fi
Comentario="BRUTEBAN"                                                                                 # Comentario que ira asociado a la regla iptables para distinguirla
WhitelistIPs="$MyIPs $WhitelistIPs $(for DNS in $WhitelistDNS; do hostip $DNS; done | xargs)"         # Suma todas las ips de lista blanca e ips de DNS
file_running="/tmp/fail2ban.run"   # Para saber si se ha ejecutado al menos una vez (para carga inicial). Almacenar en /tmp/
WriteLogTag="f2b_action"           # Tag al escribir en log el baneo o unbaneo (si lo has activado con WriteLogWithAction=1)


#======================================================================================================
#=================================  COMPROBACION INICIAL  =============================================

# Comprobamos si acabamos de iniciar el router y en ese caso preelaboramos el fichero de baneos
# Modificación : Si tenemos un fichero previo, lo usamos.
# Modificación : Si tenemos un fichero de ips para banear estático, preelaboramos el fichero de baneos.

if [ ! -f $file_running ]; then
   touch $file_running

   if [ -f $file_banstatic ]; then
      [ -f $file_banapplied ] && rm $file_banapplied

      for x in `cat $file_banstatic | awk '{print $1}' FS=":"`; do
         echo 2100000000:$x >> $file_banapplied
      done
   fi

   if [ -f $file_banstored ]; then
      for l in `cat $file_banstored | awk '{print $2}' FS=":"`; do
         exists=`cat $banfile | grep $l | wc -l`
         if [ "$exists" -lt 1 ]; then
            echo "$unban_date:$l" >> $file_banapplied
         fi
      done
   fi

   # Si es la primera vez que lanzamos el script, debemos crear el fichero donde se guardan
   # las ips baneadas, si no lo hemos creado ya, en los procedimientos anteriores
   [ ! -f $file_banapplied ] && touch $file_banapplied

   for x in `cat $file_banapplied | awk '{print $2}' FS=":"`; do
		if [ "$Debug" == "True" ];then
			echo "Anade Baneo:   iptables -w 5 -I $chain 1 -s $x -j DROP -m comment --comment \"${Comentario}\""
        else
			iptables -w 5 -I $chain 1 -s $x -j DROP -m comment --comment "${Comentario}" #:${fecha_actual}:${unban_date}"
			[[ "$WriteLogWithAction" == "1" ]] && logger -t tag "$WriteLogTag" "Recuperado baneo en inicio a ip ${x} en tabla $chain"
            [ "$Report_Actions_eMail" == "True" ] && EnviaMail "$MAIL_SUBJECT_Recover ${x}" "Recuperado baneo en inicio a ip ${x} en tabla $chain"
            [ "$Report_Actions_botTG" == "True" ] && EnviaBot "MAIL_SUBJECT_Recover ${x}"
		fi
   done
fi

#================================================================================================
#====================== BANEO INTENTOS USUARIOS NO VALIDOS Y MAL PASSWORD =======================

# Entraran al for aquellas ips que hayan excedido mas del numero $Trigger los fallos de contrasena y usuario"
for badip in `logread | grep -e 'Login attempt for nonexistent user' -e 'Bad password' | cut -d':' -f 4 | cut -d ' ' -f 8 | sort | uniq -c -f 7 | awk '{ if ( $1 > '$maxtretry' ) print $2}'` $(logread | grep -e 'luci: failed login on' | cut -d' ' -f 16 | sort | uniq -c -f 7 | awk '{ if ( $1 > '$maxtretry' ) print $2}'); do
    # Si la direccion IP no esta en la lista de baneados, ni en la de recientes accesos correctos ni en la lista blanca, la anadimos a iptables
    # if ! [ "$(grep $badip $file_banapplied)" ] && ! [ "$(logread -l $LinesToFindAcces -e 'Password auth succeeded' | cut -d ':' -f 4 | cut -d ' ' -f 8 | grep $badip)" ] && ! [[ "$WhitelistIPs" == *"$badip"* ]] ; then  	# La consola de openwrt es ash y no soporta el like con [[ "algo" == *"algo"* ]] cambiada por linea de abajo
    if ! [ "$(grep $badip $file_banapplied)" ] && ! [ "$(logread -l $LinesToFindAcces -e 'Password auth succeeded' | cut -d ':' -f 4 | cut -d ' ' -f 8 | sort | uniq | grep $badip)" ] && ! [ "$(logread -l $LinesToFindAcces -e 'luci: accepted login on' | cut -d ' ' -f 16 | sort | uniq | grep $badip)" ] && ! $(echo "$WhitelistIPs" | grep -q "badip") ; then
        if [ "$Debug" == "True" ];then
			echo "Anade Baneo:   iptables -w 5 -I $chain 1 -s $badip -j DROP -m comment --comment \"${Comentario}\""
        else
			iptables -w 5 -I $chain 1 -s $badip -j DROP -m comment --comment "${Comentario}" #:${fecha_actual}:${unban_date}"
			[[ "$WriteLogWithAction" == "1" ]] && logger -t tag "$WriteLogTag" "Baneo a ip ${badip} en tabla $chain"
            [ "$Report_Actions_eMail" == "True" ] && EnviaMail "$MAIL_SUBJECT_BAN ${badip}" "Baneo a ip ${badip} en tabla $chain"
            [ "$Report_Actions_botTG" == "True" ] && EnviaBot "MAIL_SUBJECT_BAN ${badip}"

			# Anadimos la IP al archivo de baneados "$file_banapplied" y la hora a la que terminara el baneo de esa IP en formato epoch (segundos desde 01-01-1970)
			echo "$unban_date:$badip" >> $file_banapplied;
		fi
   fi
done

#================================================================================================
#======================== BORRADO DE BANEO TRANSCURRIDO EL TIEMPO ASIGNADO ======================

# Comprueba si alguno de los registros del fichero $file_banapplied tiene una fecha anterior a la actual. En caso afirmativo borra las reglas de iptables
for z in `cat $file_banapplied`; do
	unban_time=`echo $z | awk -F ":" '{print $1}'`
	unban_ip=`echo $z | awk -F ":" '{print $2}'`
	if [ "$unban_time" -lt "$fecha_actual" ]; then
		sed -i '/'$unban_ip'/d' $file_banapplied
		unban_rule=`iptables -w 5 -L $chain -n --line-numbers | grep "${Comentario}" | grep -m 1 $unban_ip|awk '{print $1}'`

		if [ "$Debug" == "True" ];then
			echo "Elimina Baneo:  iptables -w 5 -D $chain $unban_rule -m comment --comment \"${Comentario}\""
		else
			iptables -w 5 -D $chain $unban_rule -m comment --comment "${Comentario}"
			[[ "$WriteLogWithAction" == "1" ]] && logger -t tag "$WriteLogTag" "Unbaneo a ip ${z} en tabla $chain"
            [ "$Report_Actions_eMail" == "True" ] && EnviaMail "$MAIL_SUBJECT_UBAN ${z}" "Unbaneo a ip ${z} en tabla $chain"
            [ "$Report_Actions_botTG" == "True" ] && EnviaBot "MAIL_SUBJECT_UBAN ${z}"
		fi
	fi
done
