Ansible: include dinámico por host

per Victor Carceler darrera modificació 2021-01-24T11:04:47+01:00

Ansible_logo.svg.pngEn el centro se utiliza Ansible para administrar los equipos informáticos. Sin esta herramienta sería imposible mantener a todo el parque informático en un estado coherente y adaptado a la función que debe cumplir. Gracias a esta herramienta es posible definir el estado en el que se deben encontrar las máquinas y ella aplicará los cambios necesarios en el conjunto de máquinas especificado de manera automática.

Se pueden consultar los playbooks que utilizamos este curso en el repositorio: https://github.com/vcarceler/playbooks-ubuntu2004

La idea básica es que en el fichero inventario se declaran conjuntos de equipos. Y en el playbook se declaran tareas que se deben aplicar en esos equipos. Cada tarea utiliza un módulo idempotente que únicamente provoca cambios en el ordenador si es necesario.

Durante la ejecución del playbook Ansible se encarga de conectar con todos los equipos y orquestar en ellos los cambios necesarios para llevarlos hasta el estado deseado. En los playbooks se pueden utilizar variables, bucles, condiciones, handlers y otros elementos que aportan una gran flexibilidad.

Caso práctico: configurar una VPN en muchos ordenadores portátiles

Durante este curso se espera una dotación de ordenadores portátiles para los alumnos y profesores. Estos equipos se utilizarán tanto en el centro como fuera así que se necesita una manera de conectar con ellos para administrarlos con Ansible y monitorizarlos con Prometheus que no dependa del lugar en el que se encuentran.

La solución es utilizar una VPN para que todos los portátiles tengan una interfaz de red con una IP conocida independientemente de donde se encuentren. Afortunadamente utilizar WireGuard resulta muy sencillo y este curso se han implementado diferentes VLANs:

  • Una VLAN para los portátiles que se utilizarán en el centro y fuera del centro.
  • Una VLAN por cada grupo clase de informática para que las máquinas virtuales que utilizan los alumnos mantengan la conectividad estén en el aula, en su casa o en un proveedor de la nube.

Se puede leer sobre WireGuard y las VPNs utilizadas en el centro en los siguientes artículos:

Así la configuración de una VPN con WireGuard en uno de los portátiles del centro incluye:

  1. Instalación del paquete wireguard-tools.
  2. Configuración en el fichero /etc/wireguard/wg0.conf que incluye los siguientes parámetros particulares para cada portátil:
    • Clave privada del equipo.
    • IP a utilizar en la VPN.
  3. Activado y encendido del servicio wg-quick@wg0.

Todas estas tareas se van a realizar automáticamente utilizando Ansible y en este caso particular hay un detalle interesante: por razones de seguridad no se desea que los datos de la VPN de cada equipo (clave privada e IP) acaben en el repositorio de control de versiones. Ni siquiera en su forma cifrada, pues Ansible Vault permite cifrar estos datos.

Hack: include dinámico por host

Es decir se desea tener un playbook para todos los portátiles pero que configure la VPN de una manera particular en cada uno de ellos. Y que los datos de esta configuración estén en otro fichero que se incluya en el playbook para cada uno de los equipos.

Así será posible:

  • Añadir al fichero .gitignore el directorio vpn/ para que su contenido no acabe en el repositorio público.
  • Escribir los ficheros particulares para cada portátil a medida que se añadan máquinas a este sistema.

Y la solución es tan sencilla como utilizar la variable ansible_host para componer dinámicamente el nombre del fichero particular a incluir en el playbook.

---
- hosts: equipos
  user: root

  tasks:
    - name: Instala wireguard-tools
      apt:
        update_cache: yes
        pkg:
        - wireguard-tools

    - include: "vpn/{{ ansible_host }}.yml" 

La variable especial de Ansible ansible_host contiene la IP del equipo concreto y permite, para ese equipo, hacer un include concreto —como por ejemplo vpn/192.168.1.201.yml— que tendrá las tareas particulares para configurar la VPN en ese equipo.

Se puede comprobar en la ejecución del playbook que el include particular se realiza para cada host.

PLAY [equipos] ********************************************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************************************
ok: [192.168.1.201]
ok: [192.168.1.202]

TASK [Instala wireguard-tools] ****************************************************************************************************************************************************************************
ok: [192.168.1.202]
ok: [192.168.1.201]

TASK [include] ********************************************************************************************************************************************************************************************
included: /home/usuario/playbooks/vpn/192.168.1.201.yml for 192.168.1.201
included: /home/usuario/playbooks/vpn/192.168.1.202.yml for 192.168.1.202

TASK [Prueba de hostname] *********************************************************************************************************************************************************************************
ok: [192.168.1.201]

TASK [Prueba de hostname] *********************************************************************************************************************************************************************************
changed: [192.168.1.202]

PLAY RECAP ************************************************************************************************************************************************************************************************
192.168.1.201              : ok=4    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.202              : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

Relación de direcciones LAN-VPN

Cuando se preparan equipos nuevos la ejecución del playbookes especialmente larga debido a la gran cantidad de software que se instala. Esta tarea conviene realizarla, si es posible, con el equipo conectado a la red cableada del centro. Así será necesario especificar la relación entre la IP que tiene el equipo en la LAN del centro y la IP que tendrá el equipo en la VPN.

Una de las maneras de especificar esta relación puede ser utilizando variables en el fichero inventory:

[equipos]
192.168.1.201 vpn=10.0.0.101
192.168.1.202 vpn=10.0.0.102
192.168.1.203 vpn=10.0.0.103

A las que se accederá en el playbook de la siguiente manera:

---
- hosts: equipos
  user: root

  vars:


  tasks:
    - name: Instala wireguard-tools
      apt:
        update_cache: yes
        pkg:
        - wireguard-tools

    - include: "vpn/{{ vpn }}.yml" 

Produciendo la siguiente ejecución:

PLAY [equipos] ********************************************************************************************************************************************************************************************

TASK [Gathering Facts] ************************************************************************************************************************************************************************************
ok: [192.168.1.201]
ok: [192.168.1.202]
fatal: [192.168.1.203]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.1.203 port 22: No route to host", "unreachable": true}

TASK [Instala wireguard-tools] ****************************************************************************************************************************************************************************
ok: [192.168.1.202]
changed: [192.168.1.201]

TASK [include] ********************************************************************************************************************************************************************************************
included: /home/usuario/playbooks/vpn/10.0.0.101.yml for 192.168.1.201
included: /home/usuario/playbooks/vpn/10.0.0.102.yml for 192.168.1.202

TASK [Prepara /etc/wireguard/wg0.conf] ********************************************************************************************************************************************************************
ok: [192.168.1.201]

TASK [Activa la VPN wg0 (enable y start)] *****************************************************************************************************************************************************************
changed: [192.168.1.201]

TASK [Prepara /etc/wireguard/wg0.conf] ********************************************************************************************************************************************************************
ok: [192.168.1.202]

TASK [Activa la VPN wg0 (enable y start)] *****************************************************************************************************************************************************************
ok: [192.168.1.202]

PLAY RECAP ************************************************************************************************************************************************************************************************
192.168.1.201              : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.202              : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
192.168.1.203              : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0

Tareas para configurar la VPN

En el interior del directorio vpn/ se encuentran las tareas que configuran la VPN para cada equipo. Aquí unos scripts habrán dejado un fichero .yml para cada equipo. Por ejemplo para el equipo 10.0.0.101.yml el fichero será:

- name: Prepara /etc/wireguard/wg0.conf
  vars:
          privatekey: CLAVEPRIVADADELEQUIPO
          address: 10.0.0.101/20
  template:
    src: templates/vpn-control.j2
    dest: /etc/wireguard/wg0.conf
    owner: root
    group: root
    mode: 0400

- name: Activa la VPN wg0 (enable y start)
  systemd:
    name: wg-quick@wg0
    enabled: yes
    state: started

La primera tarea prepara el fichero de configuración /etc/wireguard/wg0.conf para el equipo a partir del template vpn/templates/vpn-control.j2 que contiene:

[Interface]
PrivateKey = {{ privatekey }}
Address = {{ address }}

[Peer]
Endpoint = ENDPOINT:PORT
PublicKey = CLAVEPÚBLICADELSERVIDOR
AllowedIPs = 10.0.0.0/20
PersistentKeepalive = 25

La segunda tarea, Activa la VPN wg0 (enable y start), como su nombre indica utiliza el módulo systemd para activar y enciender la VPN en el equipo. Esta segunda tarea podría estar en el playbookinicial, fuera del include, ejecutándose en paralelo en los diferentes equipos.

Más información: