Ansible – Create Proper Sudoer Rule for Ansible Handler

ansiblesudo

Is it possible to use become in a handler?

We are running ansible 2.9.3 on a RHEL 7.4 machine. We have a user who is modifying some config files for a daemon (Consul), and we want the service to restart after any configuration change. We have a notify in the tasks, and a handler.

The files are getting modified properly, and the handler is getting notified and it runs. However, the sudo rule is not working correctly as Ansible exits with a failure:

"module_stderr": "sudo: a password is required\n"

When they run it from the command line, the user can run any systemctl commands they want using sudo commands; eg sudo systemctl restart consul and sudo systemctl restart consul.service work famously. Only the Ansible handler demands the password.

Here is our handler:

---

- name: Restart consul
  service:
    name: consul
    state: restarted
  become: yes
  become_user: root
  become_method: sudo

What is the magic inside the sudoers file that Ansible wants? I have looked for clues from the ansible -vvv output but the only thing it tells me is as below. It looks like it wants to run something like

sudo -H -S -n  -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-hltnfuwykrbhkmyixwzzockacxeosntn ; /usr/bin/python /home/consinstall/.ansible/tmp/ansible-tmp-1584133543.53-230020740311861/AnsiballZ_systemd.py'"'"'

which would be impossible to put in our sudoers file. Here's the output from the handler:

RUNNING HANDLER [workdir : Restart consul] *****************************************************************************************************
task path: /home/consinstall/bitbucket/workdir/roles/deploy/handlers/main.yml:3
<testhost01> ESTABLISH LOCAL CONNECTION FOR USER: consinstall
<testhost01> EXEC /bin/sh -c 'echo ~consinstall && sleep 0'
<testhost01> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/consinstall/.ansible/tmp/ansible-tmp-1584133543.53-230020740311861 `" && echo ansible-tmp-1584133543.53-230020740311861="` echo /home/consinstall/.ansible/tmp/ansible-tmp-1584133543.53-230020740311861 `" ) && sleep 0'
Using module file /usr/lib/python2.7/site-packages/ansible/modules/system/systemd.py
<testhost01> PUT /home/consinstall/.ansible/tmp/ansible-local-79139pfL0dr/tmpauDoVw TO /home/consinstall/.ansible/tmp/ansible-tmp-1584133543.53-230020740311861/AnsiballZ_systemd.py
<testhost01> EXEC /bin/sh -c 'chmod u+x /home/consinstall/.ansible/tmp/ansible-tmp-1584133543.53-230020740311861/ /home/consinstall/.ansible/tmp/ansible-tmp-1584133543.53-230020740311861/AnsiballZ_systemd.py && sleep 0'
<testhost01> EXEC /bin/sh -c 'sudo -H -S -n  -u root /bin/sh -c '"'"'echo BECOME-SUCCESS-hltnfuwykrbhkmyixwzzockacxeosntn ; /usr/bin/python /home/consinstall/.ansible/tmp/ansible-tmp-1584133543.53-230020740311861/AnsiballZ_systemd.py'"'"' && sleep 0'
<testhost01> EXEC /bin/sh -c 'rm -f -r /home/consinstall/.ansible/tmp/ansible-tmp-1584133543.53-230020740311861/ > /dev/null 2>&1 && sleep 0'
fatal: [testhost01]: FAILED! => {
    "changed": false, 
    "invocation": {
        "module_args": {
            "daemon_reexec": false, 
            "daemon_reload": false, 
            "enabled": null, 
            "force": null, 
            "masked": null, 
            "name": "consul", 
            "no_block": false, 
            "scope": null, 
            "state": "restarted", 
            "user": null
        }
    }, 
    "msg": "Unable to start service consul: Job for consul.service failed because the control process exited with error code. See \"systemctl status consul.service\" and \"journalctl -xe\" for details.\n"
}
META: ran handlers

NO MORE HOSTS LEFT *******************************************************************************************************************************************

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

Best Answer

Ansible does not simply run systemctl. The service module determines the correct service manager for your system, then transfers an appropriate Python module with proper state reporting and error checking.

The necessary sudo rule is running arbitrary python scripts as the become_user. It is near impossible to limit this, so Ansible users in less restrictive environments tend to give the Ansible user sudo to run all.

An alternative to remote Ansible ssh-ing in and becoming root is to run plays from an already privileged process. Think ansible-pull style clone and run from root's crontab. It is possible to become a less privileged user for tasks that don't require it.

raw module technically doesn't need to run any scripts, but that severely limits Ansible as you can't use modules.

Another write up of this: Can't use sudo command-limiting in Ansible.