У меня есть плейбук Ansible, который я использую для управления нашими файлами sudoers в нашей среде. Нам нравится хранить минимальный файл sudoers в / etc / sudoers, тогда все, что мы хотим добавить, помещается в отдельные файлы в /etc/sudoers.d.
Моя книга воспроизведения Ansible содержит следующую задачу для отправки этих файлов:
- 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
Задача содержит предложение проверки, чтобы убедиться, что файл действителен перед фиксацией файла, и это обычно работает хорошо. Однако сегодня я столкнулся с проблемой, когда обновление сломало sudo. Файл прошел этап проверки, но содержал User_Alias с тем же именем, что и User_Alias в основном файле / etc / sudoers. Любая попытка запустить sudo после этого приводила к ошибке синтаксического анализа.
У меня такой вопрос - как мне протестировать обновления моих файлов sudoers из Ansible, которые могут обнаруживать подобные ошибки? Как только файл будет на месте, ошибку можно будет обнаружить, запустив visudo -c
, но вставить это в качестве этапа проверки не получится. Ansible требует %s
заполнитель, и даже если это не так, проверка выполняется перед копированием файла на место, чтобы visudo -c
не поймает.
Вы пробовали это:
- copy:
src: '{{ item }}'
dest: '/etc/sudoers.d/{{ item }}'
owner: root
group: root
mode: 0440
validate: 'bash -c "cat /etc/sudoers %s | visudo -cf-"'
Меня устраивает.
У меня все заработало. Вот что я сделал. Сначала я добавил набор задач Ansible, чтобы создать промежуточный каталог в /etc/sudoers.stage.d и скопировать в него содержимое /etc/sudoers.d. Затем я загружаю файлы в эту промежуточную область и, если какие-либо из них изменяются, запускаю собственный сценарий для их активации.
Вот как теперь выглядит логика в playbook
- name: delete staging area
file:
path: "/etc/sudoers.stage.d"
state: absent
changed_when: false
- name: copy /etc/sudoers.d to staging area
shell: "cp -rp /etc/sudoers.d /etc/sudoers.stage.d"
changed_when: false
- name: stage sudoers files
copy:
src: "{{item}}"
dest: "/etc/sudoers.stage.d/{{item}}"
backup: yes
owner: root
group: root
mode: 0440
validate: /usr/sbin/visudo -cf %s
with_items:
- admins
- apache
- monitor
register: sudoers_d
- block:
- name: push out activate script
copy:
src: activate_sudoers.sh
dest: /usr/local/bin/activate_sudoers.sh
owner: root
group: root
mode: 0700
- name: activate change
shell: /bin/sh /usr/local/bin/activate_sudoers.sh /etc/sudoers.stage.d
when: sudoers_d.changed
а вот как выглядит скрипт activate_sudoers.sh.
#!/bin/sh
function usage {
echo "Usage: $0 <stage directory>" >&2
exit 1
}
function abort {
echo "*** Error detected" >&2
[ "$#" -gt 0 ] && echo "***" $@ >&2
exit 1
}
PATH=/usr/bin:/bin:/usr/sbin:/sbin
export PATH
test $# -eq 1 || usage
test -d "$1" || abort "Stage directory $1: missing or not a directory"
test -d /etc/sudoers.old.d && rm -rf /etc/sudoers.old.d
test -d /etc/sudoers.old.d && abort "Failed to remove /etc/sudoers.old.d"
mv /etc/sudoers.d /etc/sudoers.old.d \
&& mv "$1" /etc/sudoers.d \
&& visudo -c
if [ $? -eq 0 ]; then
# Success - clean up
rm -rf /etc/sudoers.old.d
exit 0
else
# Failure - roll back
rm -rf /etc/sudoers.d
mv /etc/sudoers.old.d /etc/sudoers.d
abort "sudoers update failed"
fi
Это немного длиннее и сложнее, чем я ожидал, но свою работу выполняет. Надеюсь, это будет полезно всем, кто столкнется с той же проблемой.