Проверка файлов sudoers.d с помощью Ansible

Проверка файлов sudoers.d с помощью Ansible

У меня есть Ansible playbook, который я использую для управления файлами 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не поймает ее.

решение1

Пробовали ли вы это:

- copy:
    src: '{{ item }}'
    dest: '/etc/sudoers.d/{{ item }}'
    owner: root
    group: root
    mode: 0440
    validate: 'bash -c "cat /etc/sudoers %s | visudo -cf-"'

Меня устраивает.

решение2

У меня это заработало. Вот что я сделал. Сначала я добавил набор задач Ansible для создания промежуточного каталога в /etc/sudoers.stage.d и скопировал в него содержимое /etc/sudoers.d. Затем я загружаю файлы в эту промежуточную область и, если какие-либо из них были изменены, запускаю пользовательский скрипт для их активации.

Вот как сейчас выглядит логика в сценарии

- 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

Это немного длиннее и сложнее, чем я надеялся, но это делает работу. Надеюсь, это будет полезно для тех, кто сталкивается с той же проблемой.

решение3

То же самое делаем только с модулями Ansible.

- name: Creating assemble directory
  file:
    path: /etc/sudoers.stage.d
    state: directory
    mode: 0600

- name: Applying sudoers.d files on assemble directory
  template:
    src: "{{ item }}.j2"
    dest: /etc/sudoers.stage.d/{{ item }}
    mode: 0400
  loop:
    - 10-wheel
    - 20-sys
  register: check

- name: Copying original sudoers to the assemble directory
  copy:
    remote_src: yes
    src: /etc/sudoers
    dest: /etc/sudoers.stage.d/99-sudoers
  when: check.changed

- name: Removing include line from 99-sudoers
  lineinfile:
    path: /etc/sudoers.stage.d/99-sudoers
    state: absent
    regex: ^#includedir /etc/sudoers.d
  when: check.changed

- name: Assembling unique config for validation
  assemble:
    src: /etc/sudoers.stage.d
    dest: /etc/sudoers.stage
    mode: 0600
    validate: visudo -cf %s
  when: check.changed

- name: Applying sudoers configurations
  template:
    src: "{{ item }}.j2"
    dest: /etc/sudoers.d/{{ item }}
    mode: 0440
  loop:
    - 10-wheel
    - 20-sys
  when: check.changed

Связанный контент