Banear IPs en linux automáticamente con IPTABLES

Banear IPs en linux automáticamente con IPTABLES

Cuando una de tus tareas es encargarte de gestionar un servidor web (o de cualquier otro tipo), más temprano que tarde acabas teniendo verdaderos quebraderos de cabeza con determinadas IPs que se conectan a tu servidor, llegando a tomar la decisión de que necesitas banear una serie de IPs. Partiendo de la base de que eres un auténtico kamikaze y que te has atrevido a gestionar un servidor LINUX con muy pocos conocimientos de este sistema operativo, es en este momento cuando decides buscarte un buen firewall para Linux.

Github: https://github.com/rolando-caldas/myOwnFirewall
Youtube: https://youtu.be/-vkbyvRA9fo

IPTABLES: El Firewall de LINUX y el banear IPs

Lo primero con que te encuentras buscando un firewall para Linux es algo llamado «IPTABLES». Pues sí, IPTABLES es el firewall de linux, todas las distribuciones tienen este genial firewall incluido por defecto. Como IPTABLES funciona a base de comandos, cuando se busca un firewall para linux, es normal encontrar resultados que hablen de APF o CSF. Tanto APF como CSF son programas encargados de proporcionar al usuario una «interfaz amigable» para gestionar las reglas IPTABLES, o sea, el firewall. Así pues, este tipo de software permite el baneo de IPs, por lo que podríamos instalar uno de los programillas de marras y olvidarnos del tema. Sin embargo, queremos ir un paso más allá y crear nuestro propio programa que se encargue de bloquear un listado de IPs.

Funcionamiento general de IPTABLES

IPTABLES es un sistema de firewall que funciona en base a una serie de reglas indicadas que se añaden como si un documento de texto con varias líneas se tratase. Cuando el sistema encuentra una regla que se ajusta a lo que está buscando, la ejecuta y deja de leer más reglas. Esto significa que una regla excesivamente permisiva al inicio puede hacer que reglas en «líneas posteriores» no se lean, por lo tanto el orden de las reglas es muy importante.

También, con IPTABLES lo que hacemos es «filtrar» todo el tráfico en el equipo dónde se ejecuta… esto significa que con IPTABLES podemos cerrar todos los puertos y dejar abierto, por ejemplo, el puerto 80 (htttp) para una determinadas IP’s… o dejar abierto el puerto del MySQL sólo en local, etc etc. IPTABLES es muy amplio y prácticamente todo queda fuera del alcance de este artículo, puesto que la finalidad de este artículo es, simplemente, tener un sistema que permita banear IPs.

Como banear IPs utilizando IPTABLES

Para poder banear una IP, debemos ejecutar un comando de consola. Lo que hacemos al banear una IP es bloquearle el tráfico, podemos bloquear sólo el tráfico de entrada (de esa IP a la máquina) o el de salida (de la máquina a esa IP), para ello son los siguientes comandos:

iptables -I INPUT -s [la ip] -j DROP
iptables -I OUTPUT -s [la ip] -j DROP

Así de sencillo! Lo siguiente que debemos hacer es decidir cómo será nuestro «automáticamente». He utilizado la opción -I (i mayúscula) para añadir la regla, aunque podemos usar tanto la -I como la -A. El motivo de utilizar la -I es que, de esta forma, la regla se inserta de primera (rulenum 1) mientras que, con append, se inserta al final.

Lo que vamos a hacer es un script que lea un fichero de texto dónde tendremos un listado de IPs (una por línea) que queremos bloquear en nuestro equipo, o sea, nuestra lista de banear IPs.

Nuestro fichero de texto con la lista de banear IPs

Nada más sencillo que crear un fichero de texto, que llamaremos banList.txt con, por ejemplo, una serie de IP’s locales que queremos bloquear:

192.168.0.123
192.168.0.124
192.168.0.125
192.168.0.126
192.168.0.127

Decidimos almacenarlo en el directorio /etc/myOwnFirewall/ por lo que nos conectamos como root ya sea con su o sudo y creamos el directorio:

rolando@rolando-interaccion:~$ sudo su
[sudo] password for rolando:
root@rolando-interaccion:/home/rolando# mkdir /etc/myOwnFirewall/
root@rolando-interaccion:/home/rolando#

una vez estamos como root, utilizamos nuestro editor favorito (en mi caso el vim) para crear el fichero banList.txt y pegar el contenido. Si usas el vim, una vez abierto debes pulsar la tecla «i» para entrar en modo de escritura y, una vez estén las IPs escritas, pulsar la tecla ESC para salir del modo de edición, escribir :wq para guardar y cerrar el fichero y pulsar enter para ejecutar la instrucción.

root@rolando-interaccion:/home/rolando# vim /etc/myOwnFirewall/banList.txt

Este fichero será leído por un script bash que ejecutará los comandos IPTABLES para banear la IP por cada línea del fichero de texto. Como no deja de ser una tarea crítica y peligrosa… nuestro script bash debería ejecutarse con un usuario concreto que sólo tuviese permisos de lectura para este fichero de texto. Al mismo tiempo, sólo el autor del fichero de texto debería poder modificarlo y sólo los usuarios bajo los cuales corren los scripts que utilicen este fichero deberán tener permisos de lectura. De esta forma, sólo el dueño del fichero de texto podrá añadir nuevas IPs y sólo el usuario bajo el cual se ejecuta el script que banea podrá leer el fichero de texto. Por ahora, como vamos a estar trabajando con el usuario root, dejaremos que le fichero sea sólo lectura/escritura para el dueño (root) y para nadie más, por lo que entramos en nuestro directorio del firewall y le ponemos los permisos adecuados:

root@rolando-interaccion:/home/rolando# cd /etc/myOwnFirewall/
root@rolando-interaccion:/etc/myOwnFirewall# chmod 600 banList.txt

Podemos ver las diferencias en las siguientes imágenes:

Vemos cómo el fichero banList.txt puede ser leido por cualquiera y sólo root puede escribir

Vemos cómo el fichero banList.txt puede ser leido por cualquiera y sólo root puede escribir

Tras cambiar los permisos, sólo root puede leer y sólo root puede escribir

Tras cambiar los permisos, sólo root puede leer y sólo root puede escribir

Creando un pequeño script para leer nuestro listado de IPs a banear

Con nuestro fichero de texto con el listado para banear IPs, lo que necesitamos es que, de alguna forma, podamos leer el fichero y ejecutar los comandos de iptable para banear cada ip.

Esto lo hacemos creando un fichero que llamaremos «myOwnFirewall» en la misma ruta que nuestro fichero de texto y ponemos el siguiente código:


# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall

BANNED_IPS="/etc/myOwnFirewall/banList.txt"

for i in `cat $BANNED_IPS`; do

	iptables -I INPUT -s $i -j DROP
	iptables -I OUTPUT -s $i -j DROP

done

Lo que hacemos es declarar una variable «$BANNED_IPS» que tiene por valor la ruta a nuestro fichero banList.txt, es buena idea utilizar la ruta absoluta, por eso aunque mi script está en el mismo directorio que el fichero, he utilizado la ruta absoluta. Así, podré mover mi script de lugar sin preocuparme de si dejará de funcionar o no.

Luego, utilizamos un bucle for para recorrer las líneas de nuestro fichero de texto. La condición del bucle lo que hace es asignar a «i» el valor de una línea de nuestro txt. Para leer el fichero de texto, se utiliza el comando cat que lo que hace es mostrar el contenido del fichero indicado (en nuestro caso $BANNED_IPS.

Por último, dentro de nuestro bucle for, sólo tenemos que lanzar los comandos para bloquear la IP.

Ejecutando nuestro script para banear ips

Con nuestro script inicial terminado, lo podemos lanzar con la siguiente orden

sh ./myOwnFirewall

Ponemos sh para que sea el «programa» sh quien ejecute nuestro script y, así, pueda ser interpretado correctamente.

Antes de lanzarlo, debemos es comprobar las reglas IPTABLES que tenemos actualmente en el equipo:

root@rolando-interaccion:/etc/myOwnFirewall# iptables -nL
Las reglas IPTABLES existentes

Las reglas IPTABLES existentes

Bueno, en mi caso no hay ninguna regla, así que lanzo mi script y, luego, compruebo si hubo cambios o no en mis reglas iptables.

root@rolando-interaccion:/etc/myOwnFirewall# sh myOwnFirewall
root@rolando-interaccion:/etc/myOwnFirewall# iptables -nL
Lanzo el script y se puede ver cómo las reglas de bloqueo se incorporaron correctamente

Lanzo el script y se puede ver cómo las reglas de bloqueo se incorporaron correctamente

Con esto ya tendríamos el listado de banear IPs aplicado tal y como se puede ver en la captura. Sin embargo, lanzar el script llamándolo desde sh no es cómodo, lo mejor es que se pudiese lanzar simplemente con:

/etc/myOwnFirewall/myOwnFirewall

Para lograr eso necesitamos dos cosas:

1. Que el fichero tenga permisos de ejecución.
2. Que en el fichero se indique que debe ser ejecutado con sh.

Dando permisos de ejecución a nuestro script para banear IPs

Para dar permisos de ejecución, podemos hacerlo con:

chmod +x /etc/myOwnFirewall/myOwnFirewall

Con esto conseguimos que cualquier usuario pudiera ejecutar nuestro script, tal y como se puede ver en la captura:

Así cualquier usuario puede ejecutar el script

Así cualquier usuario puede ejecutar el script

Vemos cómo el fichero pasa a tener permisos -rwxr-xr-x o sea… tenemos tres grupos de info:

  • rwx: Que significa que el usuario dueño (root) puede leer, escribir y ejecutar.
  • r-x: Que significa que el grupo dueño (root) puede leer y ejecutar, pero no escribir
  • r-x: Que significa que otro usuario/grupo puede leer y ejecutar, pero no escribir.

Esto es un poco inseguro, porque permitimos que cualquiera ejecute nuestro script. Lo suyo sería, al menos por ahora, que sólo el usuario root pudiese leer, escribir y ejecutar, de forma que nadie más pueda hacerlo. Así que, lo que tenemos que ejecutar es:

chmod 700 /etc/myOwnFirewall/myOwnFirewall

y ya tenemos el resultado deseado:

Así sólo root (que es el dueño) puede acceder al script

Así sólo root (que es el dueño) puede acceder al script

Incorporando a nuestro script para banear IPs la cabecera para indicar que es un bash-script

Además, incorporamos una cabecera a nuestro script para banear IPs de forma que quede así:

#!/bin/bash

# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall

BANNED_IPS="/etc/myOwnFirewall/banList.txt"

for i in `cat $BANNED_IPS`; do

	iptables -I INPUT -s $i -j DROP
	iptables -I OUTPUT -s $i -j DROP

done

De esta forma, ya podemos lanzar nuestro script sólo con:

/etc/myOwnFirewall/myOwnFirewall

Parando y reiniciando nuestro script para banear ips

Ya tenemos nuestro script base listo, aunque no tenemos todo el trabajo terminado, ni mucho menos. Tal y como lo tenemos, sólo podemos ejecutarlo para incorporar reglas. Esto quiere decir, que nosotros lo lanzamos y ale! lee el fichero y banea a todos. Pero… ¿Y si queremos banear una IP tras lanzarlo la primera vez? ¿Y si queremos eliminar nuestras reglas de bloqueo? ¿Y si queremos desbanear una IP?

Bien, bien… necesitamos mejorar nuestro script. Lo primero que vamos a hacer es permitir a nuestro script las siguientes acciones:

1. start para iniciarse y lanzar la orden de banear ips
2. stop para pararse y parar la orden de banear ips

Para lograr nuestro nuevo objetivo, tenemos que agregar al script banear IPs el «soporte» a parámetros, para que se pueda ejecutar de la siguiente manera:

/etc/myOwnFirewall/myOwnFirewall start
/etc/myOwnFirewall/myOwnFirewall stop
/etc/myOwnFirewall/myOwnFirewall restart

Por lo tanto, tenemos que «encapsular» nuestro código en una función, que se ejecutará cuando se lance el script con el parámetro start:

#!/bin/bash

# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall

BANNED_IPS="/etc/myOwnFirewall/banList.txt"

function startScript {
	for i in `cat $BANNED_IPS`; do

	iptables -I INPUT -s $i -j DROP
	iptables -I OUTPUT -s $i -j DROP

	done
}

Para completarlo, creamos las funciones stopScript y restartScript, aunque por ahora vacías:

#!/bin/bash

# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall

BANNED_IPS="/etc/myOwnFirewall/banList.txt"

function restartScript {
	echo "TODO"
}

function startScript {
	for i in `cat $BANNED_IPS`; do

		iptables -I INPUT -s $i -j DROP
		iptables -I OUTPUT -s $i -j DROP

	done
}

function stopScript {
	echo "TODO"
}

Ya sólo nos queda informar al script de qué function cargar en base al parámetro enviado (start | restart | stop), para lo cual utilizamos un switch:

#!/bin/bash

# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall

BANNED_IPS="/etc/myOwnFirewall/banList.txt"

function restartScript {
	echo "TODO"
}

function startScript {
	for i in `cat $BANNED_IPS`; do

		iptables -I INPUT -s $i -j DROP
		iptables -I OUTPUT -s $i -j DROP

	done
}

function stopScript {
	echo "TODO"
}

case "$1" in
	restart)
		restartScript
		exit
		;;
	start)
		startScript
		exit
		;;
	stop)
		stopScript
		exit
		;;
esac

Bien, bien… ahora ya podemos lanzar el «comando» con el parámetro deseado!!

/etc/myOwnFirewall/myOwnFirewall start

myOwnFirewallStart-image7

Podemos ver cómo se ejecuta nuestro firewall y como se añaden las reglas. Como ya teníamos unas (de la primera ejecución), se incorporan de nuevo todas, por lo que tendremos las reglas duplicadas (importante detalle).

Primer objetivo conseguido puesto que así se ejecutaría la función para banear ips. Con todo, todavía tenemos que mejorar este punto de nuestro script. No estamos dando ninguna opción por defecto, ni pensando que al escribir el comando podemos poner como parámetro algo no contemplado, de forma que cualquiera de estas líneas no haría nada:

/etc/myOwnFirewall/myOwnFirewall
/etc/myOwnFirewall/myOwnFirewall inicia

Para evitar esto, sólo tenemos que incluir en nuestro switch una opción por defecto, lo que haremos será crear una función de ayuda que imprima las opciones a usar a modo de pequeña ayuda:

#!/bin/bash

# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall

BANNED_IPS="/etc/myOwnFirewall/banList.txt"

function restartScript {
	echo "TODO"
}

function showHelp {
	echo "Usage: myOwnFirewall {start|stop|restart}"
}

function startScript {
	for i in `cat $BANNED_IPS`; do

		iptables -I INPUT -s $i -j DROP
		iptables -I OUTPUT -s $i -j DROP

	done
}

function stopScript {
	echo "TODO"
}

case "$1" in
	restart)
		restartScript
		exit
		;;
	start)
		startScript
		exit
		;;
	stop)
		stopScript
		exit
		;;
	*)
		showHelp
		exit
		;;
esac

Probamos que funciona bien y se muestra el mensaje de ayuda:

/etc/myOwnFirewall/myOwnFirewall inicia
El script nos muestra el mensaje de ayuda. Yeah!

El script nos muestra el mensaje de ayuda. Yeah!

Ya tenemos nuestro script banear IPs alterado para que se pueda iniciar utilizando el parámetro 1 como start… ahora nos toca ver cómo pararlo y reiniciarlo.

Para pararlo, tenemos que escribir el código que elimine todos los bloqueos, en nuestra función stopScript. Para ello podemos recorrer el fichero de texto y eliminar cada una de las reglas de bloqueo creadas para la IP dada, o bien eliminar todas las reglas.

Para eliminar todas las reglas se hace un flush:

iptables -F

Sin embargo, esto elimina todas las reglas, no sólo las que nuestro script ha incorporado. Si tenemos, por ejemplo, un fail2ban funcionando para bloquear intentos de ataques por fuerza bruta, al hacer un flush eliminaríamos también las reglas que este programa incorporó al iptables, por lo que salvo que todo el control de nuestras reglas iptables esté en nuestro script, no es buena idea hacer un flush. ¿Que nos queda pues? eliminar las reglas creadas una a una, algo que es sencillo, puesto que para eliminar una regla, en vez de -I usamos -D

iptables -D INPUT -s [la ip] -j DROP
iptables -D OUTPUT -s [la ip] -j DROP

De esta forma, buscará la regla y la eliminará. También podemos usar el -D con el número de la regla en vez de la regla en sí, pero eso significa que tenemos que saber cual es el número que ocupa en «la pila» cosa que no sabemos así queeee nuestro script quedaría así:

#!/bin/bash

# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall
BANNED_IPS="/etc/myOwnFirewall/banList.txt"

function restartScript {
	echo "TODO"
}

function showHelp {
	echo "Usage: myOwnFirewall {start|stop|restart}"
}

function startScript {
	for i in `cat $BANNED_IPS`; do

		iptables -I INPUT -s $i -j DROP
		iptables -I OUTPUT -s $i -j DROP

	done
}

function stopScript {
	for i in `cat $BANNED_IPS`; do

		iptables -D INPUT -s $i -j DROP
		iptables -D OUTPUT -s $i -j DROP

	done
}

case "$1" in
	restart)
		restartScript
		exit
		;;
	start)
		startScript
		exit
		;;
	stop)
		stopScript
		exit
		;;
	*)
		showHelp
		exit
		;;
esac

Perfecto! nos falta reiniciar no? bueno, eso es tan sencillo como hacer que la function restart llame primero a stop y luego a start 😉

#!/bin/bash

# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall

BANNED_IPS="/etc/myOwnFirewall/banList.txt"

function restartScript {
	stopScript
	startScript
}

function showHelp {
	echo "Usage: myOwnFirewall {start|stop|restart}"
}

function startScript {
	for i in `cat $BANNED_IPS`; do

		iptables -I INPUT -s $i -j DROP
		iptables -I OUTPUT -s $i -j DROP

	done
}

function stopScript {
	for i in `cat $BANNED_IPS`; do

		iptables -D INPUT -s $i -j DROP
		iptables -D OUTPUT -s $i -j DROP

	done
}

case "$1" in
	restart)
		restartScript
		exit
		;;
	start)
		startScript
		exit
		;;
	stop)
		stopScript
		exit
		;;
	*)
		showHelp
		exit
		;;
esac

Probamos a detener nuestro script:

root@rolando-interaccion:/etc/myOwnFirewall# /etc/myOwnFirewall/myOwnFirewall stop
root@rolando-interaccion:/etc/myOwnFirewall# iptables -nL
Primera parada de nuestro script

Primera parada de nuestro script

Vemos como el script se lanza y se para correctamente. Sin embargo, las reglas siguen estando ahí! Hagamos un poco de memoria… como lanzamos dos veces el script, las reglas se insertaron dos veces… y al principio de todo comentamos, de pasada, que iptables leía las reglas por orden y cuando encontraba una que se ajustaba dejaba de leer, algo que parecía tan «tonto» tiene esta consecuencia 😉

Nada, por ahora lanzamos otra ver la parada del script y comprobamos que ya tenemos todo limpio.

Ahora sí que todo está limpito

Ahora sí que todo está limpito

Bien, bien… vamos a evitar entonces que podamos lanzar dos veces el script para evitar este error. ¿como lo evitamos? Lo más sencillo es crear un fichero oculto que llamaremos .lock en el directorio /etc/myOwnFirewall de forma que al iniciar el firewall si ese fichero existe, no se lanza y al parar el firewall eliminamos el fichero. Sencillo y rápido 😉

#!/bin/bash

# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall

BANNED_IPS="/etc/myOwnFirewall/banList.txt"
LOCK="/etc/myOwnFirewall/.lock"

function restartScript {
	stopScript
	startScript
}

function showHelp {
	echo "Usage: myOwnFirewall {start|stop|restart}"
}

function startScript {

        if [ -f ${LOCK} ]
        then
                echo "myOwnFirewall is already running"
                exit
        else
                ` > ${LOCK}`

                for i in `cat $BANNED_IPS`; do
                        iptables -I INPUT -s $i -j DROP
                        iptables -I OUTPUT -s $i -j DROP
                done

                echo "myOwnFirewall started"
        fi

}

function stopScript {
        if [ -f ${LOCK} ]
        then
                for i in `cat $BANNED_IPS`; do
                        iptables -D INPUT -s $i -j DROP
                        iptables -D OUTPUT -s $i -j DROP
                done
                rm ${LOCK}

                echo "myOwnFirewall stopped"
        else
                echo "myOwnFirewall is not running"
        fi
}

case "$1" in
	restart)
		restartScript
		exit
		;;
	start)
		startScript
		exit
		;;
	stop)
		stopScript
		exit
		;;
	*)
		showHelp
		exit
		;;
esac

¿Qué hemos hecho?

Bien, por un lado hemos declarado una nueva variable «LOCK» que es la ruta al fichero .lock que usaremos para saber si el script está funcionando o no.

Luego, tanto en startScript como en stopScript hemos creado una nueva condición, para que cada función haga una cosa u otra en función de si existe nuetro .lock o no.

En el caso de startScript, si el fichero existe lo que hacemos es mostrar un aviso de que el firewall ya está funcionando. En caso contrario, lo que hacemos es crear el fichero .lock vacío para luego leer nuestro fichero de listado banear ips y les bloqueamos el acceso.

En el caso del stopScript, la situación es la misma, si el fichero .lock existe, desbanea las ips y elimina el fichero .lock… si por el contrario no existe, avisa de que el firewall no se está ejecutando.

¿funcionará?

Lanzamos dos veces el firewall con start

Lanzamos dos veces el firewall con start

Perfecto! Hemos lanzado dos veces nuestro myOwnFirewall start, la primera vez se ha ejecutado e incluido las reglas. Las segunda vez nos ha avisado de que ya se está ejecutando y no ha incluido nuevamente las reglas.

Paramos el firewall, las reglas se eliminal, volvemos a parar el firewall y esta vez nos dice que no se está ejecutando

Paramos el firewall, las reglas se eliminal, volvemos a parar el firewall y esta vez nos dice que no se está ejecutando

Maravilloso!! Ya nos aseguramos que sólo se va a iniciar/parar el firewall una vez.

Esto empieza a coger forma! Vamos a por más!

Permitir al script incluir/excluir una ip en nuestro listado de bannear ips

Bien, es muy bonito tener un fichero con IPs a banear, pero «de poco nos sirve» si tenemos que ir modificando a mano nuestro fichero de texto y reiniciando el script cada vez que cambiamos el listado de IPs. Lo que necesitamos es permitir a nuestro script que pueda incluir/excluir cualquier ip y que, además, al mismo tiempo añada o quite la regla de baneo. De esta forma podremos gestionar todo lo relacionado con el bloqueo de IPs desde nuestro script.

Así pues, tenemos que permitir pasar por parámetro dos opciones nuevas: ban y unban. Las incorporamos al switch y creamos funciones para ejecutar en cada caso:

#!/bin/bash

# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall

BANNED_IPS="/etc/myOwnFirewall/banList.txt"
LOCK="/etc/myOwnFirewall/.lock"

function banIP {
	echo "TODO"
}

function restartScript {
	stopScript
	startScript
}

function showHelp {
	echo "Usage: myOwnFirewall {start|stop|restart}"
}

function startScript {

        if [ -f ${LOCK} ]
        then
                echo "myOwnFirewall is already running"
                exit
        else
                ` > ${LOCK}`

                for i in `cat $BANNED_IPS`; do
                        iptables -I INPUT -s $i -j DROP
                        iptables -I OUTPUT -s $i -j DROP
                done

                echo "myOwnFirewall started"
        fi

}

function stopScript {
        if [ -f ${LOCK} ]
        then
                for i in `cat $BANNED_IPS`; do
                        iptables -D INPUT -s $i -j DROP
                        iptables -D OUTPUT -s $i -j DROP
                done
                rm ${LOCK}

                echo "myOwnFirewall stopped"
        else
                echo "myOwnFirewall is not running"
        fi
}

function unbanIP {
	echo "TODO"
}

case "$1" in
	ban)
		banIP
		exit
		;;
	restart)
		restartScript
		exit
		;;
	start)
		startScript
		exit
		;;
	stop)
		stopScript
		exit
		;;
	unban)
		unbanIP
		exit
		;;
	*)
		showHelp
		exit
		;;
esac

Para la función de banIP lo que vamos a hacer es que se recorra nuestro fichero de texto buscando la IP, si no la encuentra, la incorporará y la baneará, si la encuentra informará de que ya está en el listado, por supuesto, sólo si nuestro firewall está iniciado 😉

#!/bin/bash

# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall

BANNED_IPS="/etc/myOwnFirewall/banList.txt"
LOCK="/etc/myOwnFirewall/.lock"

function banIP {
        if [ -f ${LOCK} ]
        then
                if [ $# -eq 0 ]
                then
                        echo "You must enter an ip address"
                else
                        ip=$1
                        exists=false
                        for i in `cat $BANNED_IPS`; do
                                if [ $i = $ip ]
                                then
                                        exists=true
                                fi
                        done

                        if [ $exists = false ]
                        then
                                echo $ip >> $BANNED_IPS
                                iptables -I INPUT -s $ip -j DROP
                                iptables -I OUTPUT -s $ip -j DROP
                                echo "IP ${ip} banned"
                        else
                                echo "IP ${ip} already in the list"
                        fi
                fi
        else
                echo "myOwnFirewall is not running"
	fi
}

function restartScript {
	stopScript
	startScript
}

function showHelp {
	echo "Usage: myOwnFirewall {start|stop|restart}"
}

function startScript {

        if [ -f ${LOCK} ]
        then
                echo "myOwnFirewall is already running"
                exit
        else
                ` > ${LOCK}`

                for i in `cat $BANNED_IPS`; do
                        iptables -I INPUT -s $i -j DROP
                        iptables -I OUTPUT -s $i -j DROP
                done

                echo "myOwnFirewall started"
        fi

}

function stopScript {
        if [ -f ${LOCK} ]
        then
                for i in `cat $BANNED_IPS`; do
                        iptables -D INPUT -s $i -j DROP
                        iptables -D OUTPUT -s $i -j DROP
                done
                rm ${LOCK}

                echo "myOwnFirewall stopped"
        else
                echo "myOwnFirewall is not running"
        fi
}

function unbanIP {
	echo "TODO"
}

case "$1" in
	ban)
		banIP $2
		exit
		;;
	restart)
		restartScript
		exit
		;;
	start)
		startScript
		exit
		;;
	stop)
		stopScript
		exit
		;;
	unban)
		unbanIP
		exit
		;;
	*)
		showHelp
		exit
		;;
esac

Ahora toca hacer lo mismo para unbanIP, o sea, eliminar el baneo y eliminar la IP de la lista si existe:

#!/bin/bash

# myOwnFirewall v0.1            
#
# Script para banear IPs
#
# Author: Rolando Caldas        
# http://rolandocaldas.com      
# 
# https://github.com/rolando-caldas/myOwnFirewall

BANNED_IPS="/etc/myOwnFirewall/banList.txt"
LOCK="/etc/myOwnFirewall/.lock"

function banIP {
        if [ -f ${LOCK} ]
        then
                if [ $# -eq 0 ]
                then
                        echo "You must enter an ip address"
                else
                        ip=$1
                        exists=false
                        for i in `cat $BANNED_IPS`; do
                                if [ $i = $ip ]
                                then
                                        exists=true
                                fi
                        done

                        if [ $exists = false ]
                        then
                                echo $ip >> $BANNED_IPS
                                iptables -I INPUT -s $ip -j DROP
                                iptables -I OUTPUT -s $ip -j DROP
                                echo "IP ${ip} banned"
                        else
                                echo "IP ${ip} already in the list"
                        fi
                fi
        else
                echo "myOwnFirewall is not running"
	fi
}

function restartScript {
	stopScript
	startScript
}

function showHelp {
	echo "Usage: myOwnFirewall {start|stop|restart}"
}

function startScript {

        if [ -f ${LOCK} ]
        then
                echo "myOwnFirewall is already running"
                exit
        else
                ` > ${LOCK}`

                for i in `cat $BANNED_IPS`; do
                        iptables -I INPUT -s $i -j DROP
                        iptables -I OUTPUT -s $i -j DROP
                done

                echo "myOwnFirewall started"
        fi

}

function stopScript {
        if [ -f ${LOCK} ]
        then
                for i in `cat $BANNED_IPS`; do
                        iptables -D INPUT -s $i -j DROP
                        iptables -D OUTPUT -s $i -j DROP
                done
                rm ${LOCK}

                echo "myOwnFirewall stopped"
        else
                echo "myOwnFirewall is not running"
        fi
}

function unbanIP {
        if [ -f ${LOCK} ]
        then
                if [ $# -eq 0 ]
                then
                        echo "You must enter an ip address"
                else
                        ip=$1
                        exists=false

                        for i in `cat $BANNED_IPS`; do
                                if [ $i = $ip ]
                                then
                                        exists=true
                                else
                                        echo $i >> $TEMP
                                fi
                        done

                        if [ $exists = false ]
                        then
                                echo "IP ${ip} doesn't in the list"
                        else
                                cp $TEMP $BANNED_IPS && rm $TEMP
                                iptables -D INPUT -s $ip -j DROP
                                iptables -D OUTPUT -s $ip -j DROP
                                echo "IP ${ip} unbanned"
                        fi
                fi
        else
                echo "myOwnFirewall is not running"
        fi
}

case "$1" in
	ban)
		banIP $2
		exit
		;;
	restart)
		restartScript
		exit
		;;
	start)
		startScript
		exit
		;;
	stop)
		stopScript
		exit
		;;
	unban)
		unbanIP
		exit
		;;
	*)
		showHelp
		exit
		;;
esac

Como se puede ver, he creado una variable TEMP asociado a un fichero temporal, esto es así para que a al hora de desbanear una ip, lo que haga sea recorrer el listado de IPS y que vaya incorporando al fichero temporal todas las que no sean la IP a eliminar, una vez terminado el bucle, copia el contenido del fichero temporal al fichero del listado de IPS, de forma que en nuestra lista de banear IPs ya no tenemos la IP que hemos desbaneado. Seguro que hay soluciones más elegantes, pero ésta funciona 😉

Vamos a probarlo! Como siempre, lo primero es comprobar las reglas existentes, iniciarlo y ver que bloqueó bien la lista de banear ips

root@rolando-interaccion:/etc/myOwnFirewall# iptables -nL
root@rolando-interaccion:/etc/myOwnFirewall# /etc/myOwnFirewall/myOwnFirewall start
root@rolando-interaccion:/etc/myOwnFirewall# iptables -nL
Firewall lanzado

Firewall lanzado

Vamos a añadir una nueva IP, la 192.168.0.128

root@rolando-interaccion:/etc/myOwnFirewall# /etc/myOwnFirewall/myOwnFirewall ban 192.168.0.128
IP baneada correctamente!

IP baneada correctamente!

Vemos que la IP se banea sin problemas… vamos a parar el firewall y luego iniciarlo para ver si esta nueva IP también se banea al reiniciar.

root@rolando-interaccion:/etc/myOwnFirewall# /etc/myOwnFirewall/myOwnFirewall stop
root@rolando-interaccion:/etc/myOwnFirewall# iptables -nL
root@rolando-interaccion:/etc/myOwnFirewall# /etc/myOwnFirewall/myOwnFirewall start
root@rolando-interaccion:/etc/myOwnFirewall# iptables -nL
myOwnFirewallTest-image15

Al parar e iniciar el firewall, la ip que baneamos vía script también se bloquea

Todo funciona como es debido al banear vía script. ¿y al desbloquear?

Vamos a desbanear la IP 192.168.0.125

root@rolando-interaccion:/etc/myOwnFirewall# /etc/myOwnFirewall/myOwnFirewall unban 192.168.0.125
IP desbaneada!

IP desbaneada!

Vemos que la IP se desbanea sin problemas… vamos a parar el firewall y luego iniciarlo para ver si esta IP no se bloquea al reiniciar.

root@rolando-interaccion:/etc/myOwnFirewall# /etc/myOwnFirewall/myOwnFirewall stop
root@rolando-interaccion:/etc/myOwnFirewall# iptables -nL
root@rolando-interaccion:/etc/myOwnFirewall# /etc/myOwnFirewall/myOwnFirewall start
root@rolando-interaccion:/etc/myOwnFirewall# iptables -nL
La IP no se vuelve a banear al reiniciar

La IP no se vuelve a banear al reiniciar

Genial! Ya tenemos un script completito, ya podemos iniciarlo, pararlo, reiniciarlo, incorporar IPs y eliminarlas. Nos quedarían varias tareas para perfeccionar nuestro script:

  • Que el bloqueo de una ip pueda ser temporal no sólo definitivo.
  • Que se bloqueen los accesos a los diferentes servicios/puertos.
  • Activar el acceso a una IP a un servicio/puerto concreto (www, mysql, ssh)
  • Administración a través de una interfaz web.
  • etc

Pero todo esto se irá atacando en futuros artículos, para ir poco a poco perfeccionando el firewall hasta que sea una opción completa 😉

¿Alguna tarea a incorporar que me haya dejado y sea interesante?

Github: https://github.com/rolando-caldas/myOwnFirewall
Youtube: https://youtu.be/-vkbyvRA9fo

12 pensamientos en “Banear IPs en linux automáticamente con IPTABLES

  1. Felicitaciones por la guía! No podría estar mas claro y está hecho a la medida!! Donde puedo ver la continuación del artículo?

  2. felicidades, y gratitud a quien se gasto tanto tiempo explicando, como se nota cuando alguien hace algo con cariño.

    Ni te imaginas como ayuda encontrarse con ejemplos paso a paso, explicados por puntos separados.

    Llegue aquí buscando una cosa y me quede siguiendo el tema de script en bash, 😀 genial!!!!

    requetecontra super.

  3. Felicitaciones. Más claro el agua, y las técnicas sencillas que usas. He hecho varios scripts bash pero tu enfoque limpio me agrada. Me gustaría añadirle una interfaz tipo Newt o ncurses.

  4. iptables -I INPUT -s $i -j DROP
    iptables -I OUTPUT -d $i -j DROP

    no tendria que ser

    iptables -I INPUT -s $i -j DROP
    iptables -I OUTPUT -d $i -j DROP

  5. Buenas…

    Falta definir la variable TEMP:

    BANNED_IPS=»/etc/myOwnFirewall/banList.txt»
    LOCK=»/etc/myOwnFirewall/.lock»
    TMP=»/etc/myOwnFirewall/banList.tmp

    Y también añadir en la opción «unban» hay que añadirle la variable ($2) con la IP que se le pasa desde Consola, si no, lo toma como que no se le pasa parámetro:

    unban)
    unbanIP $2
    exit
    ;;

    Saludos y gracias

Deja un comentario