Acceder a servicio en ip publica mapeada a tu propia maquina con iptables

La semana pasada tuve que mapear un puerto de una ip del firewall para que llegase a una máquina de una subred interna que tenía como puerta de enlace una ip de la subred interna del firewall. Accediendo desde afuera de nuestra red el puerto funcionaba sin problemas haciendo la redirección con rinetd o con las siguientes reglas en iptables.

iptables -A INPUT -i INTERFAZ_PUBLICO_FIREWALL -p tcp –dport PUERTO_PUBLICO_FIREWALL -j ACCEPT
iptables -A PREROUTING -i INTERFAZ_PUBLICO_FIREWALL -t nat -p tcp –dport PUERTO_PUBLICO_FIREWALL -j DNAT –to IP_SERVICIO_RED_INTERNA:PUERTO_SERVICIO_RED_INTERNA
iptables -A FORWARD -i INTERFAZ_PUBLICO_FIREWALL -p tcp -d IP_SERVICIO_RED_INTERNA –dport PUERTO_SERVICIO_RED_INTERNA -j ACCEPT

De esta manera todos los paquetes que vengan de Internet al puerto publico de mi firewall irán al puerto del servicio que está corriendo en la ip de la máquina de la subred interna. El problema es cuando te intentas conectar desde un host de la misma red local a la que pertenece el servidor interno (conectándote a la ip pública del firewall) . No se puede. Es un problema de enrutado: comenzaremos estudiando lo que ocurre en el caso normal. Veamos que ocurre paso a paso:

  • El paquete deja la máquina de la red interna para dirigirse a ip pública del firewall.
  • El paquete llega al cortafuegos.
  • El paquete sufre la traducción DNAT y todas las acciones necesarias se toman en consecuencia, si bien al paquete no se le efectúa ninguna traducción SNAT y mantiene la misma dirección IP de origen (es decir una ip de la red interna)
  • El paquete sale del cortafuegos y alcanza la máquina que está corriendo el servicio
  • .

  • El servidor interno intenta responder al paquete y observa en las tablas de enrutado que el
    paquete viene de una máquina local de la misma red, por lo que intenta enviar el paquete
    directamente a la dirección IP de origen (que a partir de ese momento se convierte en la dirección IP
    de destino).

  • El paquete llega al cliente, que no sabe qué hacer puesto que el paquete devuelto no proviene del
    host al que envió la petición original. Por éllo el cliente desecha el paquete y continúa esperando la
    respuesta válida.
  • Solución

    Una solución es hacer una traducción SNAT a todos los paquetes que entren al cortafuegos y a los que sabemos que también se les va a aplicar la traducción DNAT. Por ej., vamos a efectuar una traducción SNAT a los paquetes que entren al firewall y estén destinados a la ip interna de nuestro servicio en el puerto correspondiente, de forma que parecerá que provengan de la ip de la subred interna del firewall. Ésto forzará al servidor interno a devolver los paquetes a través del cortafuegos, que invertirá la traducción DNAT y los reenviará al cliente.

    iptables -A INPUT -i INTERFAZ_PUBLICO_FIREWALL -p tcp –dport PUERTO_PUBLICO_FIREWALL -j ACCEPT
    iptables -t nat -A PREROUTING –dst IP_PUBLICA_FIREWALL -p tcp –dport PUERTO_PUBLICO_FIREWALL -j DNAT –to-destination IP_SERVIDOR_INTERNO:PUERTO_SERVICIO_INTERNO
    iptables -t nat -A POSTROUTING -p tcp –dst IP_SERVIDOR_INTERNO –dport IP_SERVICIO_INTERNO -j SNAT –to-source IP_INTERNA_FIREWALL
    iptables -t nat -A OUTPUT –dst IP_PUBLICA_FIREWALL -p tcp –dport PUERTO_PUBLICO_FIREWALL -j DNAT –to-destination IP_SERVIDOR_INTERNO:PUERTO_SERVICIO_INTERNO
    iptables -A FORWARD -i INTERFAZ_PUBLICO_FIREWALL -p tcp -d IP_SERVIDOR_INTERNO –dport PUERTO_SERVICIO_INTERNO -j ACCEPT

    No sé si lo he explicado claro y si hay alguna forma mejor de hacerlo, pero a mi me ha funcionado.

    Iptables para limitar el número de conexiones entrantes.

    Iptables tiene módulos muy útiles que extienden sus funcionalidades .Uno de los más interesantes es el módulo recent que permite monitorizar las conexiones recientes y limitarlas a un número que decidamos.

    Esto puede ser muy útil, si por ejemplo queremos evitar ataques de denegación de servicio, ataques por fuerza bruta,…

    Si normalmente ves en /var/log/auth.log registros de este tipo:

    sshd[x]: Illegal user admin from aa.bb.cc.dd
    sshd[x]: Illegal user test from  aa.bb.cc.dd
    sshd[x]: Illegal user guest from aa.bb.cc.dd

    podrías usar algunas reglas del recent para denegar el acceso temporalmente a clientes remotos que están intentado conectarse demasiadas veces.

    La forma de funcionar del módulo recent es muy simple. Básicamente es añadir ips a una lista con la que posteriormente compararemos las ips de los nuevos intentos de conexión. Esto nos va a permitir limitar el número de conexiones, por intervalo de tiempo o por número de intentos.

    Vamos a utilizar un ejemplo para ilustrarlo. Las siguientes 2 reglas limitarán las conexiones entrantes al puerto 22, a no más de 3 intentos en un minuto, y todo lo demas será descartado por el firewall.

    iptables -I INPUT -p tcp –dport 22 -i eth0 -m state –state NEW -m recent \
    –set
    iptables -I INPUT -p tcp –dport 22 -i eth0 -m state –state NEW -m recent \
    –update –seconds 60 –hitcount 4 -j DROP

    El flag –state toma una lista de los posibles estados de una conexión tcp, en el ejemplo “–state NEW”, hará que solo se monitoricen las conexiones con el flag de new activado.

    El flag –set en la primera línea asegurará que la ip del host que está iniciando las conexiones se añaadirá a la ‘lista reciente‘, donde posteriormente se comparará con las ips que se detecten en la segunda regla de nuestro ejemplo.

    La segunda regla es donde el filtrado ocurrirá realmente. El flag –update comparará si las ips de las nuevas conexiones al puerto 22 ya existen (el parámetro –set habrá hecho que se incluyan en la lista reciente)

    El flag –seconds establece el intervalo de tiempo en segundos. El –hitcount es el número de ocurrencias que deben haber de este tipo de paquetes, para aplicar el target de la regla.

    La segunda regla hará un drop de una conexión entrante si:

    – la ip que inicia la conexión ya está en la lista reciente,
    – la ip ha mandado algún paquete en los últimos 60 segundos,
    – esta ip ha mandado mas de 4 paquetes en total.

    Ya sólo tienes que jugar con los parámetros para ajustarlo al servicio que quieras proteger, ya sea ssh, ftp, web,…
    Con netcat puedes simular intentos de conexión. Aquí va un ejemplo:

    #!/bin/bash

    for i in `seq 1 5` ; do
    echo ‘exit’ | nc 192.168.1.1 22 ;
    done

    Si vas a hacer pruebas deberias flushear reglas y tablas:

    iptables -F
    iptables -X

    Y por último, si no estamos seguros de los parámetros que tenemos que definir para filtrar el tráfico, loguear los paquetes y analizarlos,y asi no interferir en el funcionamiento de los clientes del servicio.