Tag Archives: incron

Evitar ataques DOS con mod_evasive e iptables

Vamos a explicar como proteger un servidor web Apache contra ataques DOS y DDOS mediante el módulo mod_evasive y el firewall nativo de Linux IPTables.

El servidor corre Debian Lenny, y hago incapie en esto porque la configuración de Apache varía mucho según la distribución que estemos utilizando.

Ya tenemos el servidor instalado y configurado y nos interesa protegerlo, para ello instalamos el módulo de Apache mod_evasive:

apt-get install libapache2-mod-evasive

También es posible descargar y compilar, pero prefiero siempre en la medida de lo posible, usar paquetes proporcionados por la distribución.

En Debian Lenny, el archivo de configuración de Apache es /etc/apache2/apache2.conf en el podemos escribir la configuración del módulo, o mejor, comprobar que existe la línea

Include /etc/apache2/mods-enabled/*.conf

y añadir la configuración del módulo en un fichero nuevo dedicado, por mantener el orden, por ejemplo mod-evasive.conf La configuración que viene por defecto es la siguiente.

<IfModule evasive20_module>
  DOSHashTableSize 3097
  DOSPageCount 2
  DOSSiteCount 50
  DOSPageInterval 1
  DOSSiteInterval 1
  DOSBlockingPeriod 300
</IfModule>

Lo primero que debemos observar es el nombre del módulo, es variable según la distribución en la que nos encontremos o si lo hemos compilado nosotros eligiendo otro nombre.

La explicación de cada parámetro la transcribo de tail -f | systemadmin

  • DOSHashTableSize <valor> – Establece el número de nodos a almacenar para cada proceso de peticiones de la tabla hash (contenedor asociativo de recuperación de peticiones por medio de claves que agiliza las respuestas del servidor). Si aplicamos un número alto a este parámetro obtendremos un rendimiento mayor, ya que las iteraciones necesarias para obtener un registro de la tabla son menores. Por contra, y de forma evidente, aumenta el consumo de memoria necesario para el almacenamiento de una tabla mayor. Se hace necesario incrementar este parámetro si el servidor atiende un número abultado de peticiones, aunque puede no servir de nada si la memoria de la máquina es escasa.
  • DOSPageCount <valor> – Indica el valor del umbral para el número de peticiones de una misma página (o URI) dentro del intervalo definido en DOSPageInterval. Cuando el valor del parámetro es excedido, la IP del cliente se añade a la lista de bloqueos.
  • DOSSiteCount <valor> – Cuenta cuántas peticiones de cualquier tipo puede hacer un cliente dentro del intervalo definido en DOSSiteInterval. Si se excede dicho valor, el cliente queda añadido a la lista de bloqueos.
  • DOSPageInterval <valor> – El intervalo, en segundos, para el umbral de petición de páginas.
  • DOSSiteInterval <valor> – El intervalo, en segundos, para el umbral de petición de objetos de cualquier tipo.
  • DOSBlockingPeriod <valor> – Establece el tiempo, en segundos, que un cliente queda bloqueado una vez que ha sido añadido a la lista de bloqueos. Como ya se indicó unas líneas atrás, todo cliente bloqueado recibirá una respuesta del tipo 403 (Forbidden) a cualquier petición que realice durante este periodo.
  • DOSEmailNotify <e-mail> – Un e-mail será enviado a la dirección especificada cuando una dirección IP quede bloqueada. La configuración del proceso de envío se establece en el fichero mod_evasive.c de la forma /bin/mail -t %s, siendo %s el parámetro que queda configurado en este parámetro. Será necesario cambiar el proceso si usamos un método diferente de envío de e-mails y volver a compilar el módulo con apxs (por ejemplo, la opción t ha quedado obsoleta en las últimas versiones del comando).
  • DOSSystemCommand <comando> – El comando reflejado se ejecutará cuando una dirección IP quede bloqueada. Se hace muy útil en llamadas a herramientas de filtrado o firewalls. Usaremos %s para especificar la dirección IP implicada. Por ejemplo, podemos establecer su uso con iptables de la forma siguiente:
    DOSSystemCommand “/sbin/iptables –I INPUT –p tcp –-dport 80 –s %s –j DROP”
  • DOSLogDir <ruta> – Establece una ruta para el directorio temporal. Por defecto, dicha ruta queda establecida en /tmp, lo cual puede originar algunos agujeros de seguridad si el sistema resulta violado.
  • DOSWhitelist <IP> – La dirección IP indicada como valor del parámetro no será tenida en cuenta por el módulo en ningún caso. Para cada dirección IP a excluir ha de añadirse una nueva línea con el parámetro. Por ejemplo, dejaremos fuera del chequeo del módulo a un posible bot que use los siguientes rangos de direcciones:
    DOSWhitelist 66.249.65.*
    DOSWhitelist 66.249.66.*
    

Para activar el módulo simplemente

# a2enmod mod-evasive

Como vemos tiene más opciones, la que ahora nos insteresa es la de DOSSystemCommand que es la que permite ejecutar acciones en el sistema.

IPTables es un programa para crear reglas de filtrado de tráfico de red, es un programa que debe ser ejecutado por un usuario con privilegios (root), pero siguiendo la Ley del permiso mínimo el servidor corre bajo un usuario no privilegiado, esta medida protege que ante un desbordamiento no quede una sesión privilegiada colgada.

El usuario que corre el servidor web no puede correr IPTables pero no está todo perdido, lo que tenemos que hacer es pasarle un mensaje a root con la IP que tiene que bloquear, y eso hemos visto ya como se puede hacer.

Primero añadimos la siguiente línea a nuestro fichero de configuración del módulo (en apache2.conf o en mod-evasive.conf)

DOSSystemCommand "/home/$USER/bin/checkIP %s"

En $USER/bin hemos escrito un script que actualizará un registro de IP que están haciendo más peticiones de la cuenta, el script tiene una pinte así

#!/bin/bash
# Este script llevara un fichero de log de los
# intentos de ataques DOS DDOS al servidor

DATE=$(date +%Y-%m-%d-%k-%M-%S);
LOGFILE="/home/$USER/bin/CheckIp.log";

echo $DATE $1 >> $LOGFILE;

Simplemente crea un log de IP’s con este formato:

2010-10-31-20-16-41 1.1.1.2
2010-10-31-20-16-41 86.217.15.159
2010-10-31-20-16-41 92.21.26.197

Ahora entra en juego root, que se encargará de supervisar este fichero y actuar si cambia, para ello nos apoyamos en incron, creamos una nueva regla de supervisión del sistema de fichero así

Abrimos el fichero
# incrontab -e
Editamos el fichero
/home/$USER/bin/CheckIP.log IN_MODIFY /root/bin/banIP

El script que hemos escrito banIP se ejecutará cada vez que se modifique el fichero CheckIP.log y pasara la IP a la lista de IPTables,

#!/bin/bash
LOGFILE="/home/$USER/bin/CheckIP.log";
IP=$(tail -n 1 $LOGFILE | cut -d" " -f 2);
/sbin/iptables -I INPUT -p tcp --dport 80 -s $IP -j DROP;

Para probar si está funcionando podemos ejecutar el script perl que proporciona el paquete mod_evasive, veremos algo como esto:

$ perl /usr/share/doc/libapache2-mod-evasive/examples/test.pl
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 403 Forbidden
Expiró el tiempo de conexión at test.pl line 12.

Con el reinicio del servidor se perderan las reglas si no se guardan.

Con esto hemos ganado unos cuantos puntos de seguridad, de por sí, mod_evasive evita exceso de peticiones, al combinarlo con IPTables liberamos a Apache de ese trabajo dejandolo en manos de un programa diseñado para eso.

Notificando Nuevas Series Compartidas

Como todo buen friki que se precie, sigo un montón de series, tantas que mis usuarios se pierden cada día buscando las novedades en la carpeta compartida por NFS que tengo en mi red. El directorio compartido es /mnt/almacen/Media, un Seagate Barracuda de 1TB, de ahí cuelgan las series cada una en su directorio, muy bien ordenado.

Pues bien, apoyandome en el demonio Incron, he creado un sistema para notificar por correo electrónico a mi lusers cada novedad. El problema es que incron no implementa todavía la recursividad sobre subdirectorios, así que hay que supervisar cada uno por separado.

Este es el contenido:

bigBangTheory            Dollhouse           House          Mental               Numbers         RobinsonCrusoe    TheBeast
Bones                    EntreFantasmas      Hung           MentesCriminales     Olvidados       RomaCriminal      TheITCrowd
cocheFantastico          Eureka              incoming       Mercy                PadreDeFamilia  SamanthaWho       TheListener
ComoConociAVuestraMadre  EverybodyHateChris  kyle           Mienteme             pelisVistas     scrubs            TorchWood
csi                      FlashForward        LaPeceraDeEva  Monk                 Psych           siesta            Trauma
Deadwood                 Greek               Life           MP3s                 Reaper          Simpson           TrueBlood
Dexter                   Heroes              LifeOnMars     MujeresDesesperadas  Ritchies        StargateAtlantis  videosChicos
Docus                    Historicas          meLlamoEarl    MyOwnWorstEnemy      RobinHood       supernatural      Weeds

Escribir una línea de incrontab para cada uno es algo que no estaba dispuesto a hacer, así que se recurre a find y awk.

find /mnt/almacen/Media/ -maxdepth 1 | awk '{ print $1 " IN_MOVED_TO /home/nordri/bin/incron/nuevoMedio.py $@ $# " }'

Este comando, que debemos redireccionar a un fichero, nos mostrará esto:

[...]
/mnt/almacen/Media/Mienteme IN_MOVED_TO /home/nordri/bin/incron/nuevoMedio.py $@ $#
[...]

Que creara un monitor para el directorio, en caso que algún fichero sea movido dentro del directorio, ejecutará el comando nuevoMedio.py con los parametros $@ y $# que son la ruta completa y el nombre del archivo.

Existen, también dos detalles, uno es el directorio históricas donde guardo las series finalizadas, canceladas y discontinuadas. Esto es porque me gusta saber que series he seguido. El otro detalle, es supervisar la raíz de Media para avisar en caso que se añada una nueva serie. Las líneas que hay que añadir al incrontab son las siguientes.

/mnt/almacen/Media/Historicas IN_MOVED_TO /home/nordri/bin/incron/nuevoHistorico.py $# 
/mnt/almacen/Media IN_CREATE /home/nordri/bin/incron/nuevoDirMedio.py $#

Bien, pasemos a la programación, todos los scripts son en el básico Python que manejo, pero que resulta útil para estas cuestiones. El script nuevoMedio.py

import avisoPorEmail
import sys
def getCategoria(t):
  c = t.split("/")
  l = len(c)
  return c[l-1]
categoria  = getCategoria(sys.argv[1])
email = open("/tmp/email", "w")
email.write("En la categoria : " + categoria + "\n\nSe ha compartido un nuevo archivo multimedia: \n\n" + sys.argv[2] + "\n\n enjoy ;)")
email.close()
asunto = "Nuevo archivo multimedia"
direcciones = [ #direcciones# ]
avisoPorEmail.mail('/tmp/email', asunto, direcciones, 'txt')

Simplemente, recibe la ruta del directorio compartido, elimina las barras y nos quedamos el nombre de la serie, que será la categoría, el segundo parámetro es el nombre del archivo, que lleva consigo el episodio, y todo lo que los uploaders le añaden para autopromocionarse. Se escribe la cadena en un archivo y se manda por correo a las direcciones que se le indican en el array. Hay que avisar a los usuarios que miren en spam por si acaso. A mi me pasa.

Los scripts de nuevoDirMedio.py y nuevoHistorico.py son muy similares.

import avisoPorEmail
import sys
import os
categoria  = sys.argv[1]
email = open("/tmp/email", "w")
email.write("Se está siguiendo una nueva serie : " + categoria + "\n\n enjoy ;)")
email.close()
asunto = "Nueva Serie"
direcciones = [ #direcciones# ]
# Dar de alta el nuevo directorio en incron
os.system('/home/nordri/bin/incron/nuevoDirVigilado.sh ' + categoria)
avisoPorEmail.mail('/tmp/email', asunto, direcciones, 'txt')

Prácticamente igual, la peculiaridad aquí está en que si creamos un nuevo directorio, debemos dar de alta el monitor para incron. Para ello utilizo el siguiente script.

echo "/mnt/almacen/Media/$1 IN_MOVED_TO /home/nordri/bin/incron/nuevoMedio.py \$@ \$#" > /tmp/tareaIncron.txt
incrontab -l >> /tmp/tareaIncron.txt
incrontab /tmp/tareaIncron.txt

Simple, no? Se escribe la tarea en un archivo auxiliar, se anexa el contenido del incrontab actual y se recarga usando el archivo auxiliar como base.

El último es para cuando una serie se pasa al histórico, así,

import avisoPorEmail
import sys
def getCategoria(t):
  c = t.split("/")
  l = len(c)
  return c[l-1]
categoria  = getCategoria(sys.argv[1])
email = open("/tmp/email", "w")
email.write("La serie : " + categoria + "\n\nHa sido cancelada o discontinuada. Perdone las molestias")
email.close()
asunto = "Serie abandonada"
direcciones = [ #Direcciones# ]
avisoPorEmail.mail('/tmp/email', asunto, direcciones, 'txt')

Pues bien, cada vez que algo se mueva del incoming del Torrent a su directorio se avisará a los usuarios con un correo electrónico.

Supervisando el Sistema de Ficheros

Existe una utilidad que nos permite un control del sistema de ficheros llamada inotify cron (incron). Su funcionamiento es similar a Cron salvo que en lugar de actuar a una fecha, actúa a un cambio en un directorio. Con este demonio podremos:

  1. Reproducir un sonido cuando un fichero cambie.
  2. Recargar un servicio cuando el fichero de configuración cambie.
  3. Notificar a un usuario de que tiene nuevo correo.
  4. Vigilar cambios en ficheros críticos.
  5. Procesar los archivos subidos (ftp, …)

Para su funcionamiento tiene un archivo de acciones similar al de Cron con los siguientes eventos:

  • IN_ACCESS: Vigila si el archivo es accedido.
  • IN_MODIFY: Vigila si el archivo es modificado.
  • IN_ATTRIB: Vigila los cambios en los metadatos (permisos, atributos, fechas, …)
  • IN_CLOSE_WRITE: Cerrado un fichero de lectura/escritura.
  • IN_CLOSE_NOWRITE: Cerrado un archivo de sólo lectura.
  • IN_OPEN: Archivo abierto.
  • IN_MOVED_FROM: Se ha movido el fichero fuera del directorio vigilado.
  • IN_MOVED_TO: Se ha movido el fichero dentro del directorio vigilado.
  • IN_CREATE: Nuevo fichero/directorio creado dentro del directorio vigilado.
  • IN_DELETE: Fichero/directorio eliminado del directorio vigilado.
  • IN_DELETE_SELF: Supervisa si se elimina el directorio vigilado.
  • IN_CLOSE: Superivsa los eventos IN_CLOSE_WRITE e IN_CLOSE_NOWRITE
  • IN_MOVE: Supervisa los eventos IN_MOVED_FROM e IN_MOVED_TO
  • IN_ALL_EVENTS: Supervisa todos los eventos listados.
  • IN_DONT_FOLLOW: No sigue enlaces simbólicos.
  • IN_ONLYDIR: Supervisa el path únicamente si es un directorio.
  • IN_MOVE_SELF: Supervisa el borrado del fichero/directorio.

Para instalarlo:

# apt-get install incron

La configuración que viene por defecto es válida, sólo está comentada y tenemos que descomentarla, uno de los puntos es que editor usar para añadir las líneas de acciones. El fichero se encuentra en /etc/incron.conf. Para crear las tablas de acciones de usuario, hacemos al igual que cron y vamos a /var/spool/incron y creamos los fichero root y usuario, para que tanto root como nuestro usuario tengan acceso a incron. Después editamos el archivo /etc/incron.allow para añadir a root y a usuario.

La sintaxis del archivo de acciones es similar al de cron. Accedemos mediante.

$ incrontab -e

Y nos recibirá nuestro editor favorito. Escribimos las acciones que queremos de la forma:

/path/to/dir   EVENTO    ACCION

De forma que cada vez que ocurra el evento en la ruta responda con esa acción. Por ejemplo:

/home/ftp    IN_CREATE    enviarEmail.py
/etc/apache/httpd.conf    IN_MODIFY    apache2graceful

En la primera línea decimos que si alguien sube algo nuevo a nuestro servidor de ftp no avise con un correo electrónico. La segunda recarga nuestro servidor web cuando hemos hecho alguna modificación al archivo de configuración.

Otra cosa que tiene incron son los caracteres comodines, se utilizan para referenciar al path y/o al archivo/directorio que estamos tratando. Son los siguientes:

$$ - Símbolo del dólar.
$@ - El path del sistema de ficheros supervisado, esto es, el nombre del directorio si estamos vigilando un directorio.
$# - El nombre de fichero relativo al evento, esto es, el nombre relativo del ficheros si estamos vigilando un directorio. El path absoluto se obtiene con  $@/$#
$% - La señal de evento en texto.
$& - La señal de evento en número.

Fuentes:

http://www.linux.com/feature/144666

http://dailypackage.fedorabook.com/index.php?/archives/102-Productive-Monday-Incron-Execute-commands-based-on-filesystem-activity.html