Automatizando los filtros iptables con Ansible

Dentro de nuestra colección de playbooks de Ansible  para automatizar la gestión de la infraestructura IT del centro,uno muy importante e interesante es el que nos permite configurar los filtros de red que aplicamos a todos los elementos de nuestra infraestructura. En nuestro caso, independientemente de qué filtros se realicen en el equipamiento de red, en nuestros servidores se filtra desde dónde se puede enviar y recibir paquetes (tanto desde qué redes/equipos como puertos TCP/UDP). Estos filtros los implementamos con iptables:  utilidad integrada en el núcleo de Linux que nos permite realizar filtros de red con este sistema operativo.

El playbook iptables.yml es el que se encarga de la configuración de las reglas de IPtables para todos los servidores y, como ya hemos comentado en el resto de entradas, de manera centralizada, realizando las configuraciones y cambios una sola vez, en lugar de realizarlos para cada servidor (aunque también es posible modificar uno solo o un grupo utilizando el parámetro –limit más nombreServidor o nombreGrupo).  Como siempre, antes de ejecutar el playbook en cada servidor, se comprueba la sintaxis y, en caso de error, se deshacen los cambios y continua ejecutando la configuración anterior.

Todos los cambios se realizan modificando variables que (a través de un template) terminarán generando el fichero de configuración con las reglas de filtrado de Iptables para cada servidor (junto con el script de arranque /etc/init.d/iptables.sh) Estas variables se declaran, principalmente, en el fichero general group_vars/all/iptables.

La ejecución del playbook se realiza con la orden:  ansible-playbook iptables.yml que afectará a todos los servidores excepto a los que estén incluidos en el grupo noiptables.

Las variables mencionadas anteriormente son:

  • ipTablesCleaning Para el borrado de reglas. Es una lista simple con los parámetros de iptables para hacer limpieza y empezar desde cero. Se ejecuta al principio del script. Un ejemplo es:
ipTablesCleaning:
- "-F"
- "-X"
- "-Z"
  • ipTablesPolicy Para establecer políticas por defecto. Es una lista simple con los parámetros de las iptables para asignar las políticas por defecto a utilizar. Por ejemplo:
ipTablesPolicy:
- "-P OUTPUT DROP"
- "-P INPUT DROP"
- "-P FORWARD DROP"
  • ipTablesRulesGlobal Se utiliza para establecer reglas generales para todos los servidores. Es una lista con un atributo para cada uno de los principales parámetros que podemos indicarle a una orden de iptables:
    • label: valor Etiqueta clave de la regla. Esto quiere decir que si más adelante queremos hacer referencia a una regla (para eliminarla o sobreescribirla), lo haremos por este atributo (este atributo es obligatorio)
    • chain: [INPUT, OUPUT, FORWARD, … , IO]  Establece la cadena de iptables. La cadena IO permite crear reglas para 2 cadenas: una para INPUT y otra para OUTPUT y es, además, la cadena que se aplicará por defecto.
    • table: [filter, mangle, nat, raw, security]  Establece a cuál de las tablas de iptables  se le aplicará la cadena de reglas.
    • oper: [A, I, D, R] Indicará la acción a tomar. El valor será uno de los aceptados por iptables. Por defecto, A
    • protocol: [ tcp, udp, icmp, all,…] Establece el protocolo al que se le aplica el filtro
    • interfaceIN: valor  Estable la interfaz de entrada de la regla. El valor puede ser ethX, lo…
    • interfaceOUT: valor Estable la interfaz de salida. El valor puede ser ethX, lo…
    • state: [ valor1, … , valorN ]  Como en  una orden de iptables: NEW, ESTABLISHED
    • saddr: [ valor1, … , valorN ] Para indicar el origen: X.X.X.X/netmask
    • sport: [ valor1, … , valorN ]  Para indicar el puerto de origen
    • daddr: [ valor1, … , valorN ] Para indicar el destino: X.X.X.X/netmask
    • dport: [ valor1, … , valorN ] Para indicar el puerto de destino
    • target: [ valor1, … , valorN ] El valor puede ser cualquiera de los aceptados por iptables: ACCEPT, DROP, LOG… El valor por defecto será ACCEPT
    • free: valor   Este atributo nos permite indicar una orden de iptables entera, sin necesidad del resto de campos (solo el comment)
    • comment: valor Para indicar un comentario a la regla

Si en la infraestructura de ejemplo quisiéramos aceptar todos los paquetes que lleguen del servidor Ansible:

ipTablesRulesGlobal:
- label: Acceso SSH por Ansible_server
  chain: IO
  table: filter
  oper: I
  protocol: tcp
  interfaceIN: eth0
  interfaceOUT: eth0
  state: [ NEW, ESTABLISHED ]
  saddr: [ 172.20.1.10/32 ]  
  daddr: [ 0.0.0.0/32 ]
  dport: [ 22 ]
  target: [ ACCEPT ]
  comment: "Acceso SSH por Ansible_server"
  • ipTablesRulesGroup Se utiliza para establecer reglas para un grupo específico. Estas reglas se añadirán después de las generales. Está compuesta por dos atributos:
    • el primer atributo es un campo de nombre group, donde indicaremos el nombre del grupo (definido en el inventario)
    • el segundo atributo es un campo de nombre rules, que es en realidad una lista con una estructura idéntica a ipTablesRulesGlobal. En esta lista definiremos las reglas de iptables que se aplicarán a los servidores que pertenezcan al grupo.

Esta variable es la permite sobrescribir o eliminar las reglas globales para lo que deberemos:

    • Para sobreescribir, declaramos el atributo label con el mismo texto que la regla global, y definimos el resto de atributos con los valores que queramos para este caso concreto.
    • Para eliminar reglas globales, declaramos el atributo label con el mismo texto que la regla global, y no declaramos el resto de atributos (no con valor nulo, sino que en la regla sólo existirá el atributo label)

El resto de reglas que se escriban (y no coincida el label con ninguna ‘global’) serán añadidas a las globales.

Por ejemplo, si queremos que los servidores DNS de la infraestructura de ejemplo accedan a los servidores DNS de Internet, les aplicaremos la configuración:

ipTablesRulesGroup:
- group: DNS
  rules:
  - label: Acceso TCP DNS Internet
    chain: IO
    protocol: tcp
    dport: [ 53 ]
    comment: Acceso a puerto tcp DNS (53) desde Internet
  - label: Acceso UDP DNS Internet
    chain: IO
    protocol: udp
    dport: [ 53 ]
    comment: Acceso a puerto udp DNS (53) desde Internet
  - ...
  • iptablesRulesHost Para establecer reglas para un servidor específico. Estas reglas se añaden después de las de grupo. Se trata de una lista con una estructura idéntica a ipTablesRulesGroup, excepto en que en lugar de campo group (donde se indicaba el grupo) existe un campo host, en el que indicaremos el nombre del servidor al que se le aplicarán las reglas. El resto (el campo rules y la lista de reglas que incluirá) es y funciona exactamente igual. También, de la misma forma que con ipTablesRulesGroup podemos sobreescribir o eliminar reglas globales o de grupo. Al igual que en la regla anterior, las reglas que se escriban (y no coincida el label con ninguna global o de grupo) se añadiran a las globales y a las de grupo.

Un ejemplo de configuración de esta variable podría ser la siguiente. Con ella le damos acceso, a un equipo, concreto a los repositorios externos de la distro.

ipTablesRulesHost:
- host: wordpress
  rules:
  - label: Acceso repositorios externos
    chain: IO
    protocol: tcp
    saddr: [ 150.214.5.134, 150.214.40.67, 178.33.193.139, 185.4.94.24, 193.50.27.70 ]
    sport: [ 80 ]
    comment: Acceso a repositorios externos
  - ...

Para ver más ejemplos de configuración de las variables, en https://github.com/EPSAlicante/ansibleEPS/blob/master/group_vars/all/ipTables  está configuración que dejamos de ejemplo para este playbook.

Un detalle más es que, puesto que la configuración de las iptables puede hacer inaccesible e inutilizable un host, la primera regla que se escribe con este playbook en cada host (aunque no la hayamos definido en las variables) es siempre el permiso de acceso por SSH al servidor Ansible, para asegurarnos que, ante cualquier error, siempre podremos acceder y solucionar el problema.

Con este playbook simplificamos enormemente la gestión y mantenimiento de filtros en nuestros servidores (e, incluso, tenemos el conocimiento de qué se está aplicando):

  • Mediante la variable ipTablesRulesGlobal se puede establecer las reglas que afectan a todos los servidores, presten el servicio que presten. Por ejemplo, la política por defecto, si disponemos de una red de administración, permitir el acceso a ciertos puertos solo desde esta red, etc
  • Posteriormente, podemos ampliar las reglas genéricas para los grupos en los que lo necesitemos. Con la variable ipTablesRulesGroup podemos habilitar el acceso al puerto 53 de los servidores DNS, al 443 para los servidores web, etc
  • Y sin algún servidor tiene, por el motivo que sea, alguna condición especial, se puede añadir las reglas necesarias con la variable iptablesRulesHost

Además, el uso de este playbook permite mejorar la seguridad de nuestros servidores ya que estas configuraciones se lanzan periódicamente, con lo que nos garantizamos que los filtros que están operativos son los que nosotros deseamos (podemos, incluso, detectar qué cambia y que nos avise).

¡Esperamos que os sea útil!

Deja un comentario