Creative Commons License
Excepto donde se indique otra cosa, todo el contenido de este lugar está bajo una licencia de Creative Commons.
Taquiones > sysadmin > Anotaciones sobre iptables

Anotaciones sobre iptables

Cadenas de reglas personalizadas

El escenario consiste en una red con un sistema de copias de seguridad bacula funcionando sobre él y que necesita que máquinas situadas en el exterior accedan a su almacenamiento a través de Internet; la dificultad estriba en que esas máquinas remotas tienen en su mayor parte direcciones IP dinámicas.

Vamos pues a crear una cadena de reglas que nos permita abrir el cortafuegos para que servidores de archivos remotos accedan a nuestro servidor de almacenamiento. Este último servidor está en la misma máquina que encara el acceso exterior, aunque también tengo el caso de un servidor de almacenamiento en el interior de la red y lo comentaré más abajo.

Para crear la cadena de reglas se emplea:

# iptables -t filter -N bacula-storage

y para comprobar que ha funcionado se usa:

# iptables -t filter -L 
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain bacula-storage (0 references)
target     prot opt source               destination
#

donde podemos comprobar que no está asignada a ninguna de las cadenas predefinidas de la tabla de filtrado, así que para asignarla a alguna usaremos:

# iptables -t filter -A INPUT -j bacula-storage
# iptables -t filter -L 
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
bacula-storage  all  --  anywhere             anywhere

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain bacula-storage (1 references)
target     prot opt source               destination
#

Ahora llega el momento de añadirle las reglas que necesitemos:

# iptables -t filter -A bacula-storage -s NN.NN.NN.NN/32 -p tcp -m tcp --dport 9103 -j ACCEPT
# iptables -t filter -A bacula-storage -s XX.XX.XX.XX/24 -p tcp -m tcp --dport 9103 -j ACCEPT

e inspeccionar los resultados

# iptables -t filter -L bacula-storage 
Chain bacula-storage (1 references)
target     prot opt source               destination
ACCEPT     tcp  --  remote1.mynet.com    anywhere            tcp dpt:bacula-sd
ACCEPT     tcp  --  remote2.mynet.com    anywhere            tcp dpt:bacula-sd
#

Cadenas de quita y pon

Ahora bien, ¿ qué ocurre si las direcciones IP son dinámicas y pueden ser distintas de tiempo en tiempo ? Lo peligroso desde mi punto de vista es dejar el puerto de acceso al servidor de almacenamiento (ya sea el estándar ó uno en particular) abierto a todo el mundo contínuamente, y puesto que una manera de rebajar el riesgo es abrirlo sólo durante el tiempo que dura la copia, se puede construir un mecanismo de apertura y cierre definiendo la cadena de reglas si no lo está, y conectándola y desconectándola del cortafuegos según se emplee.

Un programa como el siguiente puede emplearse para controlar el acceso al servidor de almacenamiento; obviamente es mejorable y lo pongo aquí a modo de ejemplo.

    1 #!/bin/bash
    2 
    3 # comprobamos si ya existe la cadena
    4 iptables -t filter -L bacula-storage 2>&1 >/dev/null
    5 
    6 if [ $? -ne 0 ]; then
    7     # No existe, pasamos a crearla
    8     iptables -t filter -N bacula-storage
    9     iptables -t filter -A bacula-storage -p tcp -m tcp --dport 9103 -j ACCEPT
   10 fi
   11 
   12 # según lo que se nos pida
   13 case $1 in
   14 start)
   15     # añadimos nuestra cadena a la cadena de entrada de la tabla de filtrado
   16     iptables -t filter -A INPUT -j bacula-storage
   17     ;;
   18 stop)
   19     # retiramos nuestra cadena pero NO la borramos
   20     iptables -t filter -D INPUT -j bacula-storage
   21     ;;
   22 *)
   23     echo "uso: bacula-sd-iptables.sh {start|stop}" >&2
   24     exit 1
   25     ;;
   26 esac
   27 
   28 exit $?

Nota: el lugar adecuado para ejecutar el programa anterior sería justo antes de iniciar el trabajo de copia e inmediatamente tras finalizarlo. Para ello se emplean las claúsulas Run before job y Run after job en la definición de la copia de seguridad.

Bien pensado esta solución es un tanto burda, dado que en el fondo no consiste en otra cosa más que en abrir un puerto a todo el mundo durante un tiempo, las más de las veces medido en horas (dado el atraso tecnológico que suponen las líneas ADSL que nos vemos obligados a emplear). Eso significa que sigue siendo posible a terceros intentar conexiones al servidor en momentos concretos del día.

Otra solución más acertada sería abrir el acceso únicamente a los servidores de archivos de las máquinas a respaldar. Si cualquiera de esas máquinas sólo dispone de direcciones IP dinámicas será necesario instalar en ellas algún servicio de DNS dinámico como no-ip ó dhis y esperar que no se presenten cambios durante la transferencia de archivos.

Podría quedar como el siguiente programa:

    1 #!/bin/bash
    2 
    3 function usage () {
    4     echo "uso: bacula-sd-iptables.sh {start|stop} host" >&2
    5     exit 1
    6 }
    7 
    8 # salvamos parámetros
    9 operation=$1
   10 target=$2
   11 
   12 # y verificamos que existan
   13 if [ -z $operation -a -z $target ]; then
   14     usage
   15 fi
   16 
   17 # comprobamos si ya existe la cadena
   18 iptables -t filter -L bacula-storage 2>&1 >/dev/null
   19 
   20 # Si no existe ...
   21 if [ $? -ne 0 ]; then
   22     # Creamos la cadena
   23     iptables -t filter -N bacula-storage
   24 
   25     # le añadimos como regla inicial la denegación de acceso al puerto para
   26     # todas las conexiones nuevas (quizás habría que definir también el
   27     # interfaz de red o algo similar para no dispararnos en el pié).
   28     iptables -t filter -A bacula-storage -p tcp --syn --dport 9103 -j REJECT
   29 
   30     # y la añadimos al filtrado de paquetes entrantes
   31     iptables -t filter -A INPUT -j bacula-storage
   32 fi
   33 
   34 # según lo que se nos pida
   35 case $operation in
   36 start)
   37     # Insertamos la regla para que encaje antes de la denegación global que
   38     # establecimos al principio
   39     iptables -t filter -I bacula-storage 1 -s $target -p tcp -m tcp --dport 9103 -j ACCEPT
   40     ;;
   41 stop)
   42     # Borramos la regla buscando por coincidencia en lugar de por número
   43     iptables -t filter -D bacula-storage -s $target -p tcp -m tcp --dport 9103 -j ACCEPT
   44     ;;
   45 *)
   46     usage
   47     ;;
   48 esac
   49 
   50 exit $?

Cambiando reglas en la cadena

Las reglas están numeradas por orden de aparición, que también es el orden de proceso de paquetes, por lo que si necesitamos borrar una de ellas tenemos:

# iptables -t filter -D bacula-storage 2
# iptables -t filter -L bacula-storage 
Chain bacula-storage (1 references)
target     prot opt source               destination
ACCEPT     tcp  --  remote1.mynet.com    anywhere            tcp dpt:bacula-sd
#

aunque también podemos reemplazarla de esta manera:

# iptables -t filter -R bacula-storage 2 -s XX.XX.XX.XX/32 -p tcp -m tcp --dport 9103 -j ACCEPT
#

Referencias y enlaces