Si en nuestro playbook realizamos una escalada de privilegios en los nodos target y tenemos a su vez handlers que se ejecutan en local con "delegate_to: localhost"
, éstos intentarán a su vez realizar escalada de privilegios en el nodo de control. Hay dos problemas: por un lado el handler no tiene por qué requerir una escalada y por otro las credenciales de become
pueden no coincidir en la máquina de control, por lo que la ejecución del handler fallará.
Para ilustrarlo con un ejemplo, este es un playbook sencillo que simplemente reinicia el servidor Apache y manda un correo notificando en cuántos servidores se ha realizado la tarea:
--- - hosts: "{{ server | default('none') }}" become: yes tasks: - name: Restart Apache Web Server service: name: apache2 state: restarted notify: email-notification handlers: - name: email-notification mail: host: smtp.gmail.com port: 587 username: mail@example.com password: ******* from: SYSTEM to: example <mail@example.com> subject: Ansible-Report Apache Web Server restarted body: |- Apache Web Server restarted in: {% for item in ansible_play_hosts_all %} - Server: {{ item }} {% endfor %} delegate_to: localhost run_once: true
La instrucción "become: yes"
aplica a todas las acciones -tasks y handlers- del playbook. El handler en concreto delega a localhost (máquina control) e intenta realizar una escalada de privilegios con la contraseña que introdujimos con become. Si nuestro nodo de control no tiene la misma contraseña de root, fallará la ejecución del handler.
ansible-playbook main.yml -e "server=jota-node01" --ask-become -vv
Aquí el error de ejecución:
RUNNING HANDLER [email-notification] ************************************************************************************************************************************************************************* task path: /ansible/playbooks/apache/restart-httpd/main.yml:13 fatal: [jota-node01 -> localhost]: FAILED! => {"changed": false, "module_stderr": "su: Authentication failure\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}
Tenemos varias opciones:
ansible_become_pass
para el host en el inventario.localhost ansible_connection=local ansible_become_pass=test123
Y en el handler poner la opción "delegate_facts: True"
handlers: - name: email-notification mail: host: smtp.gmail.com port: 587 username: mail@example.com password: ******* from: SYSTEM to: example <mail@example.com> subject: Ansible-Report Apache Web Server restarted body: |- Apache Web Server restarted in: {% for item in ansible_play_hosts_all %} - Server: {{ item }} {% endfor %} delegate_to: localhost delegate_facts: True run_once: true
"become: no"
handlers: - name: email-notification become: no mail: host: smtp.gmail.com port: 587 username: mail@example.com password: ******* from: SYSTEM to: example <mail@example.com> subject: Ansible-Report Apache Web Server restarted body: |- Apache Web Server restarted in: {% for item in ansible_play_hosts_all %} - Server: {{ item }} {% endfor %} delegate_to: localhost run_once: true
Lanzamos:
ansible-playbook main.yml -e "server=jota-node01" --ask-become -vv
Ahora la ejecución finaliza sin errores:
RUNNING HANDLER [email-notification] ******************************************************************** task path: /ansible/playbooks/apache/restart-httpd/main.yml:13 ok: [jota-node01 -> localhost] => {"changed": false, "msg": "Mail sent successfully", "result": {}}