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
    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> 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   

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.

  • Actually, the best explanation of this known limitation is done IMHO by ansible's official doc itself: privilege escalation must be general – Zeitounator Mar 15 '20 at 13:04
  • Thanks for the reply. In your first link, to the bug report, bcoca says, "...since configuration management normally requires root or full root privileges, it makes sense for ansible, most users either login as root or have sudo ALL privs..." If Ansible starts with that premise, then it's no wonder it doesn't work right. This flies in the face of Ansible Tower's RBAC capabilities, which give lie to that premise. I think it's broken, and have submitted an enhancement request to the Tower team. How many more system admins are going to bump into this problem over the years? – Mike S Mar 17 '20 at 13:18
  • You could ensure that the only copy of the ssh private key of the Ansible user is with whatever runs your playbook (Tower/AWX, some other automation). Please suggest how you would restrict privilege for Ansible's generated-on-the-fly scripts. It ultimately is running user provided input privileged, which means letting a user do anything as the become user. – John Mahowald Mar 17 '20 at 20:57
  • I wouldn't restrict privilege for generated-on-the-fly scripts. I would say, "The user wants to run a command with elevated privileges. Therefore, I'm going to have my generated-on-the-fly scripts run that command with elevated privileges." I don't care about the privileges of the generated-on-the-fly scripts, really, because that's not my goal. My goal is to run my command. The mechanics of which I expect to be taken care of by some sort of programming magic. – Mike S Mar 19 '20 at 17:23
  • Ansible's become implementation is backwards to what you are expecting, on its generated scripts, not each underlying command modules run. Modules usually don't specify what user they run as, that is abstracted to the wrapper script. If this is unacceptable for you, you won't be able to use the become feature. – John Mahowald Mar 19 '20 at 17:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.