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