Comprobando archivos sudoers.d con Ansible

Comprobando archivos sudoers.d con Ansible

Tengo un libro de jugadas de Ansible que uso para administrar nuestros archivos sudoers en nuestro entorno. Nos gusta mantener un archivo sudoers mínimo en /etc/sudoers, luego todo lo que queramos agregar se coloca en archivos separados en /etc/sudoers.d.

Mi manual de Ansible contiene la siguiente tarea para enviar estos archivos:

- 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

La tarea contiene una cláusula de validación para garantizar que el archivo sea válido antes de confirmarlo y, en general, esto ha funcionado bien. Sin embargo, hoy me encontré con un problema donde una actualización rompió sudo. El archivo pasó el paso de validación, pero contenía un User_Alias ​​con el mismo nombre que un User_Alias ​​en el archivo principal /etc/sudoers. Cualquier intento de ejecutar sudo después de eso resultó en un error de análisis.

Mi pregunta es la siguiente: ¿cómo pruebo las actualizaciones de mis archivos sudoers de Ansible que puedan detectar errores como este? Una vez que el archivo está en su lugar, el error se puede detectar ejecutando visudo -c, pero incluirlo como paso de validación no funciona. Ansible requiere el %smarcador de posición, e incluso si no lo hiciera, la validación se realiza antes de copiar el archivo en su lugar para que visudo -cno lo detecte.

Respuesta1

¿Has probado esto?

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

Esto funciona para mi.

Respuesta2

Lo hice funcionar. Esto es lo que hice. Primero agregué un conjunto de tareas de Ansible para crear un directorio provisional en /etc/sudoers.stage.d y copié el contenido de /etc/sudoers.d en él. Luego subo los archivos a esta área de preparación y, si alguno de ellos cambia, ejecuto un script personalizado para activarlos.

Así es como se ve ahora la lógica del libro de jugadas

- 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

y así es como se ve el script enable_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

Es un poco más largo y complejo de lo que esperaba, pero hace el trabajo. Con suerte, esto será útil para cualquiera que tenga el mismo problema.

Respuesta3

Haciendo lo mismo solo con los módulos de 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

información relacionada