Esta pagina se ve mejor con JavaScript habilitado

Gestión de reglas iptables con Ansible

 ·  🎃 kr0m

Si tenemos un amplio grupo de servidores es importante poder gestionar de forma centralizada las reglas de firewall, Ansible es una buena opción ya que podemos crear grupos y aplicar unas reglas determinadas sobre un grupo u otro.

Instalamos las userlands de iptables:

emerge -av net-firewall/iptables

Cargamos las reglas de iptables en el arranque:

rc-update add iptables default

Instalamos las librerías de python necesarias:

emerge -av dev-python/python-iptables

Creamos los directorios necesarios para Ansible:

mkdir /etc/ansible/
chown -R root:kr0m /etc/ansible/
chmod 775 /etc/ansible/

Creamos un grupo de servidores llamado test:

vi /etc/ansible/hosts

[test]
kr0mtest

Escribimos el playbook:

vi kr0mtest.yml

- hosts: test
  vars:
    allowed_hosts:
      - 1.1.1.1
      - 2.2.2.2
      - 3.3.3.3

  tasks:
  - name: Firewall rule - allow INPUT custom hosts
    iptables:
      action: insert
      chain: INPUT
      rule_num: 1
      source: "{{ item }}"
      jump: ACCEPT

    with_items:
      - "{{ allowed_hosts }}"

  - name: Firewall rule - allow OUTPUT custom hosts
    iptables:
      action: insert
      chain: OUTPUT
      rule_num: 1
      destination: "{{ item }}"
      jump: ACCEPT

    with_items:
      - "{{ allowed_hosts }}"

  - name: Save iptables rules
    shell: /etc/init.d/iptables save

Lo ejecutamos:

ansible-playbook ./kr0mtest.yml

Comprobamos que ha aplicado la config correctamente:

kr0mtest ~ # iptables -L -n

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  3.3.3.3              0.0.0.0/0           
ACCEPT     all  --  2.2.2.2              0.0.0.0/0           
ACCEPT     all  --  1.1.1.1              0.0.0.0/0           

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  0.0.0.0/0            3.3.3.3             
ACCEPT     all  --  0.0.0.0/0            2.2.2.2             
ACCEPT     all  --  0.0.0.0/0            1.1.1.1      

NOTA: No he sido capaz de aplicar un DROP por defecto, la única manera con la que he podio hacerlo es dejando el DROP como última ACL tanto en INPUT como OUTPUT.

Algo muy común es tener unas reglas genéricas y otras mas específicas, para poder reutilizar playbooks haremos uso de los includes:

vi genericFw.yml

- hosts: "{{hostlist}}"
  vars:
    input_ports:
      ssh32002:
        port: 32002
        protocol: tcp
      ssh22:
        port: 22
        protocol: tcp
      http:
        port: 80
        protocol: tcp
      https:
        port: 443
        protocol: tcp
      dns:
        port: 53
        protocol: tcp
      dnsUdp:
        port: 53
        protocol: udp
        
    output_ports:
      http:
        port: 80
        protocol: tcp
      https:
        port: 443
        protocol: tcp
      dns:
        port: 53
        protocol: tcp
      dnsUdp:
        port: 53
        protocol: udp

    traffic_direction:
      - INPUT
      - OUTPUT

  tasks:
  - name: Firewall rule - flush previous INPUT ACLs
    iptables:
      flush: true
      chain: "{{ item }}"

    with_items: "{{ traffic_direction }}"

  - name: Firewall rule - allow all INPUT loopback traffic
    iptables:
      action: append
      chain: INPUT
      in_interface: lo
      jump: ACCEPT

  - name: Firewall rule - allow all OUTPUT loopback traffic
    iptables:
      action: append
      chain: OUTPUT
      out_interface: lo
      jump: ACCEPT

  - name: Firewall rule - allow INPUT ICMP traffic
    iptables:
      action: append
      chain: "{{ item }}"
      protocol: icmp
      jump: ACCEPT

    with_items: "{{ traffic_direction }}"

  - name: Firewall rule - allow INPUT ports
    iptables:
      action: append
      chain: INPUT
      protocol: "{{ item.value.protocol }}"
      destination_port: "{{ item.value.port }}"
      jump: ACCEPT

    loop: "{{ lookup('dict', input_ports) }}"

  - name: Firewall rule - allow INPUT2 ports
    iptables:
      action: append
      chain: OUTPUT
      protocol: "{{ item.value.protocol }}"
      source_port: "{{ item.value.port }}"
      jump: ACCEPT

    loop: "{{ lookup('dict', input_ports) }}"

  - name: Firewall rule - allow OUTPUT ports
    iptables:
      action: append
      chain: INPUT
      protocol: "{{ item.value.protocol }}"
      source_port: "{{ item.value.port }}"
      jump: ACCEPT

    loop: "{{ lookup('dict', output_ports) }}"

  - name: Firewall rule - allow OUTPUT2 ports
    iptables:
      action: append
      chain: OUTPUT
      protocol: "{{ item.value.protocol }}"
      destination_port: "{{ item.value.port }}"
      jump: ACCEPT

    loop: "{{ lookup('dict', output_ports) }}"

  - name: Firewall rule - deny all traffic
    iptables:
      action: append
      chain: "{{ item }}"
      jump: DROP

    with_items: "{{ traffic_direction }}"

Ahora incluimos el playbook genérico en nuestro playbook:

vi kr0mtest.yml

- name: Load generic fw rules
  import_playbook: genericFw.yml hostlist=test

- hosts: test
  vars:
    allowed_hosts:
      - 1.1.1.1
      - 2.2.2.2
      - 3.3.3.3

  tasks:
  - name: Firewall rule - allow INPUT custom hosts
    iptables:
      action: insert
      chain: INPUT
      rule_num: 1
      source: "{{ item }}"
      jump: ACCEPT

    with_items:
      - "{{ allowed_hosts }}"

  - name: Firewall rule - allow OUTPUT custom hosts
    iptables:
      action: insert
      chain: OUTPUT
      rule_num: 1
      destination: "{{ item }}"
      jump: ACCEPT

    with_items:
      - "{{ allowed_hosts }}"

  - name: Save iptables rules
    shell: /etc/init.d/iptables save

Ejecutamos el playbook:

ansible-playbook ./kr0mtest.yml

Comprobamos que haya funcionado:

kr0mtest ~ # iptables -L -n --line-numbers

Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     all  --  3.3.3.3              0.0.0.0/0           
2    ACCEPT     all  --  2.2.2.2              0.0.0.0/0           
3    ACCEPT     all  --  1.1.1.1              0.0.0.0/0           
4    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
5    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           
6    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:32002
7    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80
8    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:53
9    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22
10   ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:443
11   ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:53
12   ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp spt:53
13   ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp spt:80
14   ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp spt:53
15   ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp spt:443
16   DROP       all  --  0.0.0.0/0            0.0.0.0/0           

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     all  --  0.0.0.0/0            3.3.3.3             
2    ACCEPT     all  --  0.0.0.0/0            2.2.2.2             
3    ACCEPT     all  --  0.0.0.0/0            1.1.1.1             
4    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
5    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           
6    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp spt:32002
7    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp spt:80
8    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp spt:53
9    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp spt:22
10   ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp spt:443
11   ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp spt:53
12   ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:53
13   ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80
14   ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:53
15   ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:443
16   DROP       all  --  0.0.0.0/0            0.0.0.0/0           

Efectivamente están los puertos del playbook genérico y las ips del playbook normal.

Si te ha gustado el artículo puedes invitarme a un RedBull aquí