Administre secretos en sus playbooks de Ansible

Finalmente ha sucedido. Fuiste con todo adentro con Ansible. Ha leído todos los excelentes artículos, visto los casos de uso y está emocionado de comenzar a construir una infraestructura repetible y administrar su configuración como código. Solo hay un problema: tiene un archivo de configuración o una tarea que requiere una contraseña u otra información crítica. Sabe que no debe almacenar la contraseña en sus archivos en texto sin formato, por lo que no sabe exactamente dónde debe ir.

No se preocupe, este artículo lo guía a través de las diferentes opciones para manejar información confidencial en sus libros de jugadas. Ya sea que esté buscando soluciones simples, como pedirle a un administrador que ingrese una contraseña, u opciones más complejas, como la integración con un entorno de administración de secretos existente, Ansible lo tiene cubierto.

Índice

Instrucciones

Si recién está comenzando su viaje a Ansible y está ejecutando todos sus libros de jugadas manualmente, entonces use un invitación interactiva directamente a su libro de jugadas es una solución simple. A hace que Ansible solicite al usuario las variables deseadas y las almacene cada vez que se ejecuta un libro de jugadas. Considere el siguiente libro de jugadas, que garantiza que exista una clave API en un archivo de configuración:

---

- hosts: all
  gather_facts: false
  vars_prompt:
    - name: api_key
      prompt: Enter the API key
  tasks:
    - name: Ensure API key is present in config file
      ansible.builtin.lineinfile:
        path: /etc/app/configuration.ini
        line: "API_KEY={{ api_key }}"

Cuando ejecuto este libro de jugadas, Ansible me solicita en la línea de comando usando el mensaje en el parámetro de solicitud:

# ansible-playbook -i inventory.ini main.yml
Enter the API key:

La entrada proporcionada en la línea de comando se almacenará en el Clave API variable, que luego se puede utilizar en la habitación como cualquier variable normal.

Si bien las indicaciones variables son fáciles de implementar, las superará si invierte en el uso de Ansible para la administración completa de la configuración. A medida que madure su gestión de configuración, comenzará a ejecutar playbooks de forma no interactiva y no habrá nadie en la terminal para ingresar contraseñas. Aquí es donde entra en juego Ansible Vault.

Bóveda de Ansible

Una de mis habilidades favoritas de Ansible es la Bóveda de Ansible, que proporciona capacidades de cifrado de contenido nativo. Ansible Vault puede cifrar y descifrar variables y archivos arbitrarios, lo que significa que puede usarlo para proteger archivos variables que contienen secretos o incluso cifrar archivos de configuración confidenciales completos. Las cajas fuertes de Ansible tienen muchas características avanzadas, pero este artículo se centrará en lo básico.

Los archivos YAML estándar que contienen secretos de texto sin formato se pueden cifrar fácilmente con el ansible-vault encrypt pedido:

# Plaintext YAML file
$ cat secrets_file.enc
api_key: SuperSecretPassword

# Encrypt the file with ansible-vault
$ ansible-vault encrypt secrets_file.enc
New Vault password:
Confirm New Vault password:
Encryption successful

# Confirm that the file now contains encrypted content
$ cat secrets_file.enc
$ANSIBLE_VAULT;1.1;AES256
38396162626134393935663839666463306231653861336630613938303662633538633836656465
3637353766613339663032363538626430316135623665340a653961303730353962386134393162
62343936366265353935346336643865643833353737613962643539373230616239346133653464
6435353361373263640a376632613336366430663761363339333737386637383961363833303830
34336535623736313031313162353831666139343662653665366134633832646661

Cuando ejecuto mi libro de jugadas, puedo pasar el archivo de variables cifradas y decirle a Ansible que me solicite la contraseña. Ansible descifrará el archivo y usará las variables que definí, como si hubiera pasado un archivo de variables normal:

$ cat main.yml
---

- hosts: all
  gather_facts: false
  tasks:
    - name: Ensure API key is present in config file
      ansible.builtin.lineinfile:
        path: /etc/app/configuration.ini
        line: "API_KEY={{ api_key }}"


$ ansible-playbook -i inventory.ini -e @secrets_file.enc --ask-vault-pass main.yml
Vault password:

PLAY [all] ***********************************************************************************

TASK [Ensure API key is present in config file] **********************************************
changed: [localhost]

PLAY RECAP ***********************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

$ cat /etc/app/configuration.ini
API_KEY=SuperSecretPassword

Ya he explicado por qué usar un vars_prompt no es ideal en un entorno automatizado ya que requiere la intervención manual del usuario. Entonces, ¿en qué se diferencia una caja fuerte Ansible? Ansible Vault le permite especificar un archivo de contraseña que contiene la contraseña de descifrado para la bóveda:

$ cat password_file 
password

$ ansible-playbook -i inventory.ini -e @secrets_file.enc --vault-password-file password_file main.yml

PLAY [all] ***********************************************************************************

TASK [Ensure API key is present in config file] **********************************************
changed: [localhost]

PLAY RECAP ***********************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

Asegúrese de establecer los permisos correctos en el archivo de contraseña de descifrado para que solo el usuario que ejecuta el libro de jugadas pueda acceder a él. También puede considerar usar un guión para acceder a la contraseña cuando se ejecuta desde un sistema de almacenamiento de contraseña externo.

Ahora que mi archivo de variables está encriptado, necesito una forma de modificarlo. Hay dos formas de modificar una bóveda cifrada de Ansible. Puede editar el archivo en su lugar o descifrarlo por completo, editarlo y luego volver a cifrarlo. Ambos métodos se muestran a continuación.

# The edit command will launch a text editor, such as vim
$ ansible-vault edit secrets_file.enc 
Vault password: 

# The decrypt command will fully decrypt the file, allowing you to manipulate it how you see fit.
$ ansible-vault decrypt secrets_file.enc 
Vault password: 
Decryption successful

# Notice that the file has been decrypted
$ cat secrets_file.enc 
api_key: SuperSecretPassword

# Don't forget to re-encrypt the file when you're done!
$ ansible-vault encrypt secrets_file.enc 
New Vault password: 
Confirm New Vault password: 
Encryption successful
$ cat secrets_file.enc 
$ANSIBLE_VAULT;1.1;AES256
33373832393864613335393836616538373639353538306462366464303939303838316337336662
6235303936636465366363643761383462356335336239640a356161653166643134663762323136
34616431303434646338343265666135666263633162383662323164396266616638313936303863
3337626365313666630a326465663239653731613637303437666164346531636361653837326166
34396232623138616364393130303036653564643435636639316264636531336161

El uso de Ansible Vault for Secrets es uno de mis métodos favoritos para almacenar datos confidenciales. La ventaja de este enfoque es que en realidad puede almacenar sus datos confidenciales en el control de código fuente, junto con sus libros de jugadas habituales. Dado que estos archivos están encriptados, hay poco riesgo en este enfoque siempre que elija una contraseña segura. Como con cualquier secreto compartido, es una buena idea cambiar la contraseña de cifrado con frecuencia. Ansible también ofrece varias funciones avanzadas para bóvedas, como la capacidad de tener diferentes contraseñas para diferentes bóvedas. Asegúrese de consultar la documentación para conocer algunas formas excelentes de proteger sus secretos mediante las funciones nativas de Ansible.

Usar un administrador de contraseñas existente

Los dos enfoques anteriores son enfoques puramente de Ansible para abordar la administración de secretos. Sin embargo, muchas organizaciones ya cuentan con herramientas, como Cofre HashiCorp Donde Servidor secreto Thycotic. La comunidad de Ansible ha escrito una serie de módulos personalizados para interactuar con este tipo de sistemas.

El siguiente libro de jugadas utiliza un buscar para obtener un secreto de HashiCorp Vault, luego use ese secreto en una tarea:


---

- hosts: all
  gather_facts: false
  tasks:
    - name: Ensure API key is present in config file
      ansible.builtin.lineinfile:
        path: /etc/app/configuration.ini
        line: "API_KEY={{ lookup('hashi_vault', 'secret=config-secrets/data/app/api-key:data token=s.FOmpGEHjzSdxGixLNi0AkdA7 url=http://localhost:8201')['key'] }}"

Puede encontrar una variedad de complementos para diferentes herramientas de administración de secretos en el Galaxia Ansible. Al igual que con cualquier proyecto respaldado por la comunidad, es una buena idea auditar el código para comprender cómo maneja sus datos y secretos. Es posible que incluso desee escribir el suyo propio.

El uso de un complemento o módulo de búsqueda es ideal para organizaciones que ya cuentan con una herramienta de gestión de secretos madura y simplemente quieren que Ansible consuma los secretos de ese sistema existente. La compensación obvia es la reducción de la simplicidad: las ejecuciones de Playbook ahora dependen de la disponibilidad de un sistema externo, y depender de un módulo compatible con la comunidad (o escribir uno propio) puede llevar mucho tiempo.

Nota sobre el registro

Es importante recordar que cifrar los datos en reposo (por ejemplo, en un almacén de Ansible o en un sistema secreto externo) no significa que los datos estén protegidos contra la salida accidental a un archivo de registro de Ansible. Si el módulo al que está llamando registra su secreto durante las operaciones normales o cuando ocurre un error, ese secreto puede quedar expuesto en sus archivos de registro. Ya sea que almacene estos registros en un sistema central o simplemente use la vista de salida estándar predeterminada, es importante proteger sus secretos de la exposición accidental.

El siguiente resultado es del mismo libro de jugadas de Ansible que usé para este tutorial. Sin embargo, aumenté el nivel de depuración de Ansible con -vvv. Fíjate en mi secreto (API_KEY = Contraseña supersecreta) se expone directamente en la salida de depuración. He limpiado un poco este fragmento, así que no se preocupe si intenta esto, y su salida se ve un poco diferente.

TASK [Ensure API key is present in config file] ***********************************************************************************************************************************************
fatal: [localhost]: FAILED! => changed=false 
  ansible_facts:
    discovered_interpreter_python: /usr/bin/python
  invocation:
    module_args:
      attributes: null
      backrefs: false
      backup: false
      content: null
      create: false
      delimiter: null
      directory_mode: null
      firstmatch: false
      follow: false
      force: null
      group: null
      insertafter: null
      insertbefore: null
      line: API_KEY=SuperSecretPassword
      mode: null
      owner: null
      path: /etc/app/configuration.ini
      regexp: null
      remote_src: null
      selevel: null
      serole: null
      setype: null
      seuser: null
      src: null
      state: present
      unsafe_writes: null
      validate: null
  msg: Destination /etc/app/configuration.ini does not exist !
  rc: 257

Ciertamente no es el ideal: mi secreto está ahí, a la vista. Afortunadamente, Ansible ofrece una no_log configuración para tareas que protegen datos confidenciales:

---

- hosts: all
  gather_facts: false
  tasks:
    - name: Ensure API key is present in config file
      ansible.builtin.lineinfile:
        path: /etc/app/configuration.ini
        line: "API_KEY={{ api_key }}"
      no_log: True

Al agregar este parámetro a la tarea que interactúa con datos confidenciales, se suprime la salida de la tarea fallida y se preserva la confidencialidad de mi secreto:

TASK [Ensure API key is present in config file] ***********************************************************************************************************************************************
fatal: [localhost]: FAILED! => changed=false 
  censored: 'the output has been hidden due to the fact that ''no_log: true'' was specified for this result'

Es una buena idea usar no_log en cualquier tarea que interactúe con datos confidenciales. También debe tener en cuenta sus limitaciones: no evitará el registro si la depuración de Ansible está habilitada.

Pensamientos finales

Mantener los secretos adecuados es un desafío inicial común al que se enfrentan muchos administradores de sistemas cuando trabajan en la implementación de la automatización. En este artículo, describí y demostré tres métodos diferentes que puede usar para proteger datos confidenciales cuando usa Ansible en su entorno. Este artículo solo ha arañado la superficie de las posibilidades, así que asegúrese de consultar la documentación que he vinculado a lo largo de esta discusión. La seguridad es cosa de todos. Puede hacer su parte como administrador del sistema asegurándose de tratar los datos privados con la sensibilidad que merece en sus procesos de automatización.

Artículos de interés

Subir