Registro centralizado de IPs peligrosas

Enviado por lamigo el Lun, 30/04/2018 - 18:29
Diagrama de flujo de API

Todos sabemos que internet es un patio de lobos y que nuestros servidores son una pequeña caperucita que pasea por el bosque ignorando los peligros que le acechan.

Fail2ban es una herramienta muy útil para vigilar los ficheros de registro y detectar posibles amenazas, basicamente notify vigila los ficheros de registro en busca de cadenas que nosotros le indiquemos, como fallos de acceso, y cuando se produce un número predeterminado de repeticiones realiza una acción predefinida (habitualmente un bloqueo en el firewall).

El resumen es muy sencillo, si repites la/las cadena/cadenas de mi filtro X veces te aplico la acción definida durante el tiempo previsto.

Es una herramienta que te quita gran parte de los dolores de cabeza si se configura de la forma adecuada, pero tiene un pequeño pero... es una herramienta local.

Hay una alternativa muy útil (Net2Ban) que permite hacer esto de forma centralizada con un repositorio de logs común, funciona bien y es bastante recomendable, pero (desde mi punto de vista) el enfoque es muy rígido, da poco margen para experimentar y, sobre todo, es un proyecto que actualmente no está mantenido (o eso parece, 4 años sin un commit en Github)

Existen diversos servicios gratuitos que permiten centralizar los logs o servicios de reporte de IPs como badips o Dshield para usar sin complicarse la vida, pero aquí nos gusta complicarnos la vida, así que vamos a hacer un pequeño API con Restler para guardar IPs mediante una sencilla llamada. De paso, vemos como funciona Restler, que es una pequeña maravilla que permite hacer este tipo de proyectos en un pequeño ratillo.

Restler es un framework para crear APIs RESTful en PHP de forma sencilla y robusta. Restler se encarga de todo el trabajo duro, nosotros solo tenemos que definir las clases que queremos exponer, los parámetros que reciben, opcionalmente las rutas, la seguridad y el throttling de las conexiones.

Restler se instala como dependencia con Composer, por lo que nos vamos a centrar en el uso básico para nuestro ejemplo, usaremos como backend una base de Datos PostgreSQL, para quien no lo sepa, incluye tipos de datos muy potentes para este tipo de tareas, en particular los tipos de datos de direcciones (inet) y rangos (cidr).

Para guardar las IPs sancionadas, vamos a crear una tabla de intercambio (en la que haremos las entradas y que más adelante procesaremos):

tables.sql


CREATE TABLE abuse_input (
entryid SERIAL PRIMARY KEY,
submit_date TIMESTAMP DEFAULT NOW(),
address inet NOT NULL,
action varchar(64),
source inet
);

Para crear nuestro API crearemos una clase que será la encargada de procesar las peticiones, para hacerlo más sencillo voy a simplificar las comprobaciones de seguridad y las excepciones, el código de nuestra clase será:

abuseAddress.php


class abuseAddress
{

    /**
     * Adds a new IP to the the database
     *
     * @url    POST add
     * @access public
     * @throws \Exception
     * @return string
     */
    protected function post($request_data=null)
    {
        $database = new Database();
            if (!$database->addAdress($request_data)) {
                header("HTTP/1.1 500 Internal Server Error", true, 500);
                return "wrong parameters";
            } else {
           return "data added";
         }
    }
}

Restler usa las anotaciones para recibir parámetros, de esta forma:

@url le indica la dirección de la ruta y el tipo de petición

@access nos permitirá proteger la llamada a la API (lo veremos en otro artículo)

@param nos permitirá filtrar los parámetros que recibe la clase

El hecho de especificar un único parámetro: $request_data indica que queremos recibir todos los parametros de la llamada "en crudo"

No me voy a extender en el contenido de la clase Database, pero básicamente comprueba los parámetros y hace la inserción en la base de datos.

Para decirle a Restler que queremos publicar esa clase, solo necesitamos esto en nuestro index.php (otra vez simplificado):

index.php


require_once 'vendor/autoload.php';
require_once 'abuseAddress.php';

use Luracast\Restler\Restler;

try {
    $restler = new Restler();
    //this creates resources.json at API Root
    $restler->addAPIClass('Luracast\\Restler\\Resources');
} catch (\Exception $e) {
    header("HTTP/1.1 500 Internal Server Error", true, 500);
    exit(0);
}
try {
    $restler->addAPIClass('AbuserLog\\abuseAddress', '');
    $restler->handle();
} catch (\Exception $e) {
    header("HTTP/1.1 500 Internal Server Error", true, 500);
    exit(0);
}

Con esto y habilitando mod_rewrite para que envíe todo a index.php tenemos un API montado.

Ya solo nos queda crear un nuevo action en nuestra instalación de fail2ban:

abuserlog.conf

# Fail2ban reporting to Abuserlog
#
# Note: This reports an IP only and does not actually ban traffic. Use
# another action in the same jail if you want bans to occur.
#

[Definition]

actionban = curl --fail -X POST d -d '{"address":"<ip>","action":"<name>"}' <serverurl> --header "Content-Type:application/json"

[Init]

# Default name of the action
#
name = 

Y añadimos la nueva acción en nuestra jail.local:

jail.local

[ssh]
enabled  = true
port = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 3
action = iptables-allports
        abuserlog[name=ssh,serverurl=miservidor/add]

Con esto ya veremos los baneos de nuestro fail2ban registrados en la base de datos:

     144 | 2018-04-30 17:47:06.823119 | 91.121.77.171   | ssh       | 127.0.0.1
     145 | 2018-04-30 17:57:57.636322 | 104.210.7.172   | ssh       | 127.0.0.1

Más adelante podremos procesar los datos, buscar los datos de acceso de esas IPs en un rsyslog y buscar patrones en los ataques que nos sirvan para anticiparlos en la medida de lo posible.