(Note: I wrote this up quickly and without a lot of research, so there are probably inaccuracies. However, I wanted to put this out there in case it helps someone else hitting this issue. Github gists like this unfortunately don't have comment notifications, so if you want me to send me a comment, use my email email@example.com and not the comments.)
Problem: Can't use sudo command-limiting in Ansible
The ability to limit sudo users to only be able to execute certain commands doesn't work with Ansible (without a workaround).
This isn't a problem if you're running Ansible as a super-user like
root, but if you are allowing others to run Ansible on your systems in order to do things like application deploys, then you need a way to limit their access to the system for basic security.
For example, a line in
/etc/sudoers like this:
some-non-root-user ALL=(ALL) NOPASSWD: /some/root/command
That line allows
some-not-root-user to run the
/some/root/command as if they were a super-user like
Why doesn't it work?
Ansible sends Python code to be executed on the targeted servers. Since Ansible is running Python code and generally not executing system commands directly, you can't limit system commands with sudo and expect them to work with Ansible. While you could theoretically limit the sudo user to be able to run Python as root, that would defeat the purpose of command-limiting the user since Python can run arbitrary system commands.
A workaround that sometimes works, is to use Ansible's
raw module which passes a command through to the system without the Python wrapper (I'm probably describing that wrong, but that's conceptually what happens). However, the
raw module can not be used reliably since Ansible added functionality to track the success of a
raw command when it is successful.
From what I understand, the
raw module used to run a command directly on the system like
/bin/command --options, but now it prepends a "success tracking" echo command to the command like
echo BECOME-SUCCESS-sjsscfneygqfcntttkcomefpxnbkzumb; /bin/command --options.
Some versions of
sudo might allow this to work if you also add
echo to the commands the sudo user can run, but other versions of
sudo apparently don't allow multiple commands to be run like this.
Use another shell to invoke your
This essentially isolates your
sudo command from getting altered in a breaking way by Ansible for non-root sudo command-limited users.
- shell: sh -c "sudo /some/root/command" become: yes become_user: some-non-root-user become_method: sudo
Since this is a bit of a hack, you'll need to test it for your particular scenario. In my testing, I could still get adequate exit codes, standard output, etc with this method, but your mileage may vary.
Note that you can set
become_method = sudo in your Ansible config file so it is used by default and then be unnecessary in the task declaration.
Alternative non-sudo method
Another pattern you can use to bypass the "sudo command limiting" issue is to use
cron to monitor something like the existance of a file, then execute a workflow when the monitoring is triggered.
For example, you could set up a
crontab entry to execute this deploy script every minute.
The deploy script would do something like:
# psuedocode if /tmp/deploy.txt exists if deploy playbook is already running exit end delete /tmp/deploy.txt run deploy playbook else exit end
This allows non-privileged users to safely trigger deploys.