I have an Ansible playbook I use to manage our sudoers files across our environment. We like to keep a minimal sudoers file at /etc/sudoers, then anything we want to add gets put into separate files under /etc/sudoers.d.
My Ansible playbook contains the following task for pushing these files:
- name: copy sudoers files
copy:
src: "{{ item }}"
dest: "/etc/sudoers.d/{{ item }}"
backup: yes
owner: root
group: root
mode: 0440
validate: /usr/sbin/visudo -cf %s
with_items:
- admins
- apache
- monitor
The task contains a validate clause to make sure the file is valid before committing the file, and this has generally worked well. However, today I ran into a problem where an update broke sudo. The file passed the validation step, but contained a User_Alias with the same name as a User_Alias in the main /etc/sudoers file. Any attempt to run sudo after that resulted in a parse error.
My question is this - how do I test updates to my sudoers files from Ansible that can catch errors like this? Once the file is in place, the error can be caught by running visudo -c
, but putting this in as the validation step doesn't work. Ansible requires the %s
placeholder, and even if it didn't, the validation is done before copying the file into place so that visudo -c
wouldn't catch it.
{{ item }}
and%s
as parameters; it can dump/etc/sudoers
and all files from/etc/sudoers.d/*
except{{ item }}
(old file), add content of%s
(new file) and feed tovisudo
.