Ansible falla al autenticar Sudo incluso cuando se otorga Sudo Pass

9

Problema

Utilizando la última versión estable de Ansible, tengo un problema extraño en el que mi libro de jugadas se cuelga en un servidor durante "Gathering_Facts" pero funciona bien en otros servidores similares cuando uso Sudo. En el servidor Ansible, ejecuto como mi usuario (usuario NIS) y uso sudo (como root) en el servidor remoto para realizar cambios. Si elimino Sudo de esta configuración, todo funciona bien.

Preparar

Versiones de software

  • OS : RHEL 6.4
  • Versión ansible: ansible 1.8.2
  • Versión de sudo :
    Sudo versión 1.8.6p3
    Complemento de políticas de Sudoers versión 1.8.6p3
    Sudoers file grammar version 42
    Sudoers I / O plugin versión 1.8.6p3
    
  • Versión SSH : OpenSSH_5.3p1, OpenSSL 1.0.0-fips 29 de marzo de 2010

Mapa del servidor

                   -------- Usuario1 @ Servidor1: sudo -H -S -p (Se bloquea en Gathering_Facts)
                  / /
Usuario1 @ Ansible ----
                  \
                   -------- Usuario1 @ Servidor2: sudo -H -S -p (Funciona bien)

Los usuarios

  • Usuario1: usuario accesible NIS en Servidor1 y Servidor2.
  • root: usuario raíz local para cada servidor.

Configuración Ansible

Partes relevantes de mi ansible.cfg .

ansible.cfg

sudo           = true
sudo_user      = root
ask_sudo_pass  = True
ask_pass       = True
...
gathering = smart
....
# change this for alternative sudo implementations
sudo_exe = sudo

# what flags to pass to sudo
#sudo_flags = -H
...
# remote_user = ansible

Aquí hay un libro de jugadas de prueba simple para tocar un archivo vacío y luego eliminarlo. Realmente, solo quiero probar si puedo obtener Ansible para usar correctamente sudo en el servidor remoto. Si el libro de jugadas funciona, estoy en buena forma.

TEST.yml

---
- hosts: Server1:Server2
  vars:
  - test_file: '/tmp/ansible_test_file.txt'
  sudo: yes
  tasks:
  - name: create empty file to test connectivity and sudo access
    file: dest={{ test_file }}
          state=touch
          owner=root group=root mode=0600
    notify:
    - clean
  handlers:
  - name: clean
    file: dest={{ test_file }}
          state=absent

Configuración de sudo

/ etc / sudoers

Host_Alias SRV     = Server1, Server2
User_Alias SUPPORT = User1, User2, User3
SUPPORT SRV=(root) ALL

Esta configuración de sudo funciona bien en AMBOS servidores. No hay problemas con sudo en sí.

Cómo lo ejecuto todo

Muy simple:

$ ansible-playbook test.yml
Contraseña SSH: 
contraseña de sudo [predeterminada a la contraseña SSH]:

PLAY [Servidor1: Servidor2] ******************************************** ** ** 

HECHOS DE REUNIÓN ************************************************ *************** 
ok: [Servidor2]
error: [Servidor1] => {"error": verdadero, "analizado": falso}

Perdón intente de nuevo.
[sudo via ansible, clave = mxxiqyvztlfnbctwixzmgvhwfdarumtq] contraseña: 
sudo: 1 intento de contraseña incorrecta


TAREA: [cree un archivo vacío para probar la conectividad y el acceso a sudo] **************** 
cambiado: [Servidor2]

NOTIFICADO: [limpio] ********************************************* **************** 
cambiado: [Servidor2]

PLAY RECAP ************************************************ ******************** 
           para volver a intentar, use: --limit @ / home / User1 / test.retry

Servidor1: ok = 0 cambiado = 0 inalcanzable = 0 fallido = 1   
Servidor2: ok = 3 cambiado = 2 inalcanzable = 0 fallido = 0

Falla independientemente de si ingreso explícitamente tanto las contraseñas SSH / Sudo como implícitamente (dejando que sudo pase por defecto a SSH).

Registros remotos del servidor

Servidor1 (falla)

/ var / log / secure

31 de diciembre 15:21:10 Servidor1 sshd [27093]: Contraseña aceptada para Usuario1 del puerto xxxx 51446 ssh2
31 de diciembre 15:21:10 Servidor1 sshd [27093]: pam_unix (sshd: sesión): sesión abierta para el usuario Usuario1 por (uid = 0)
31 de diciembre 15:21:11 Servidor1 sshd [27095]: solicitud del subsistema para sftp
31 de diciembre 15:21:11 Servidor1 sudo: pam_unix (sudo: auth): error de autenticación; logname = Usuario1 uid = 187 euid = 0 tty = / dev / pts / 1 ruser = Usuario1 rhost = usuario = Usuario1
31 de diciembre 15:26:13 Server1 sudo: pam_unix (sudo: auth): conversación fallida
31 de diciembre 15:26:13 Servidor1 sudo: pam_unix (sudo: auth): auth no pudo identificar la contraseña para [Usuario1]
31 de diciembre 15:26:13 Sudo servidor1: Usuario1: 1 intento de contraseña incorrecta; TTY = pts / 1; PWD = / inicio / Usuario1; USUARIO = raíz; COMANDO = / bin / sh -c echo SUDO-SUCCESS-mxxiqyvztlfnbctwixzmgvhwfdarumtq; LANG = C LC_CTYPE = C / usr / bin / python /tmp/.ansible/tmp/ansible-tmp-1420039272.66-164754043073536/setup; rm -rf /tmp/.ansible/tmp/ansible-tmp-1420039272.66-164754043073536/> / dev / null 2> & 1
31 de diciembre 15:26:13 Servidor1 sshd [27093]: pam_unix (sshd: sesión): sesión cerrada para el usuario Usuario1 

Servidor2 (funciona bien)

/ var / log / secure

31 de diciembre 15:21:12 Servidor2 sshd [31447]: Contraseña aceptada para Usuario1 desde el puerto xxxx 60346 ssh2
31 de diciembre 15:21:12 Servidor2 sshd [31447]: pam_unix (sshd: sesión): sesión abierta para el usuario Usuario1 por (uid = 0)
31 de diciembre 15:21:12 Server2 sshd [31449]: solicitud del subsistema para sftp
31 de diciembre 15:21:12 sudo Servidor2: Usuario1: TTY = pts / 2; PWD = / inicio / Usuario1; USUARIO = raíz; COMANDO = / bin / sh -c echo SUDO-SUCCESS-vjaypzeocvrdlqalxflgcrcoezhnbibs; LANG = C LC_CTYPE = C / usr / bin / python /tmp/.ansible/tmp/ansible-tmp-1420039272.68-243930711246149/setup; rm -rf /tmp/.ansible/tmp/ansible-tmp-1420039272.68-243930711246149/> / dev / null 2> & 1
31 de diciembre 15:21:14 Servidor2 sshd [31447]: pam_unix (sshd: sesión): sesión cerrada para el usuario Usuario1 

Salida de almacenamiento

Aquí está la salida de strace cuando se dirige al comando ansible del usuario raíz. Mando:

while [[ -z $(ps -fu root|grep [a]nsible|awk '{print $2}') ]]; do
    continue
done
strace -vfp $(ps -fu root|grep [a]nsible|awk '{print $2}') -o /root/strace.out`

Servidor 1

23650 select (0, NULL, NULL, NULL, {1, 508055}) = 0 (Tiempo de espera)
Conector 23650 (PF_NETLINK, SOCK_RAW, 9) = 10
23650 fcntl (10, F_SETFD, FD_CLOEXEC) = 0
23650 readlink ("/ proc / self / exe", "/ usr / bin / sudo", 4096) = 13
23650 sendto (10, "| \ 0 \ 0 \ 0L \ 4 \ 5 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0op = PAM: auténtico" ..., 124, 0, {sa_family = AF_NETLINK, pid = 0, grupos = 00000000}, 12) = 124
23650 encuesta ([{fd = 10, eventos = POLLIN}], 1, 500) = 1 ([{fd = 10, revents = POLLIN}])
23650 recvfrom (10, "$ \ 0 \ 0 \ 0 \ 2 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0b \\\ 0 \ 0 \ 0 \ 0 \ 0 \ 0 | \ 0 \ 0 \ 0L \ 4 \ 5 \ 0 \ 1 \ 0 \ 0 \ 0 "..., 8988, MSG_PEEK | MSG_DONTWAIT, {sa_family = AF_NETLINK, pid = 0, grupos = 00000000}, [12]) = 36
23650 recvfrom (10, "$ \ 0 \ 0 \ 0 \ 2 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0b \\\ 0 \ 0 \ 0 \ 0 \ 0 \ 0 | \ 0 \ 0 \ 0L \ 4 \ 5 \ 0 \ 1 \ 0 \ 0 \ 0 "..., 8988, MSG_DONTWAIT, {sa_family = AF_NETLINK, pid = 0, grupos = 00000000}, [12]) = 36
23650 cerrar (10) = 0
23650 escribir (2, "Lo siento, inténtalo de nuevo. \ N", 18) = 18
23650 gettimeofday ({1420050850, 238344}, NULL) = 0
Conector 23650 (PF_FILE, SOCK_STREAM, 0) = 10
23650 connect (10, {sa_family = AF_FILE, ruta = "/ var / run / dbus / system_bus_socket"}, 33) = 0

Servidor2

6625 select (8, [5 7], [], NULL, NULL) =? ERESTARTNOHAND (Para reiniciar)
6625 --- SIGCHLD (Niño salido) @ 0 (0) ---
6625 escritura (8, "\ 21", 1) = 1
6625 rt_sigreturn (0x8) = -1 EINTR (llamada de sistema interrumpida)
6625 select (8, [5 7], [], NULL, NULL) = 1 (en [7])
6625 lectura (7, "\ 21", 1) = 1
6625 wait4 (6636, [{WIFEXITED (s) && WEXITSTATUS (s) == 0}], WNOHANG | WSTOPPED, NULL) = 6636
6625 rt_sigprocmask (SIG_BLOCK, NULL, [], 8) = 0
6625 socket (PF_NETLINK, SOCK_RAW, 9) = 6
6625 fcntl (6, F_SETFD, FD_CLOEXEC) = 0
6625 readlink ("/ proc / self / exe", "/ usr / bin / sudo", 4096) = 13
6625 sendto (6, "x \ 0 \ 0 \ 0R \ 4 \ 5 \ 0 \ 6 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0op = PAM: session_c" ..., 120, 0, {sa_family = AF_NETLINK, pid = 0, grupos = 00000000}, 12) = 120
6625 encuesta ([{fd = 6, eventos = POLLIN}], 1, 500) = 1 ([{fd = 6, revents = POLLIN}])
6625 recvfrom (6, "$ \ 0 \ 0 \ 0 \ 2 \ 0 \ 0 \ 0 \ 6 \ 0 \ 0 \ 0 \ 330 \ 355 \ 377 \ 377 \ 0 \ 0 \ 0 \ 0x \ 0 \ 0 \ 0R \ 4 \ 5 \ 0 \ 6 \ 0 \ 0 \ 0 "..., 8988, MSG_PEEK | MSG_DONTWAIT, {sa_family = AF_NETLINK, pid = 0, grupos = 00000000}, [12]) = 36
6625 recvfrom (6, "$ \ 0 \ 0 \ 0 \ 2 \ 0 \ 0 \ 0 \ 6 \ 0 \ 0 \ 0 \ 330 \ 355 \ 377 \ 377 \ 0 \ 0 \ 0 \ 0x \ 0 \ 0 \ 0R \ 4 \ 5 \ 0 \ 6 \ 0 \ 0 \ 0 "..., 8988, MSG_DONTWAIT, {sa_family = AF_NETLINK, pid = 0, grupos = 00000000}, [12]) = 36
6625 cerrar (6) = 0
6625 abierto ("/ etc / security / pam_env.conf", O_RDONLY) = 6
6625 fstat (6, {st_dev = makedev (253, 1), st_ino = 521434, st_mode = S_IFREG | 0644, st_nlink = 1, st_uid = 0, st_gid = 0, st_blksize = 4096, st_blocks = 8, st_size = 2980, st_atime = 2014/12 / 31-16: 10: 01, st_mtime = 2012/10 / 15-08: 23: 52, st_ctime = 2014/06 / 16-15: 45: 35}) = 0
6625 mmap (NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) = 0x7fbc3a59a000
6625 read (6, "# \ n # Esta es la configuración fi" ..., 4096) = 2980
6625 lectura (6, "", 4096) = 0
6625 cerrar (6) = 0
6625 munmap (0x7fbc3a59a000, 4096) = 0
6625 abierto ("/ etc / environment", O_RDONLY) = 6

Mi conjetura

El Servidor1 no está obteniendo la contraseña correctamente o está pidiendo / esperando incorrectamente una contraseña. Esto no parece un problema de Sudo o Ansible (solo, ambos funcionan bien), pero el Servidor1 no parece recibir las credenciales (o adherirse a ellas) de manera similar a Servidor2. Los servidores 1 y 2 tienen diferentes propósitos, por lo que es posible que tengan algunas diferencias de autenticación o de versión del paquete, pero ambos se crearon desde el mismo repositorio; por lo tanto, no deberían ser tan diferentes.

Autenticación PAM

Pensé que tal vez los sistemas tenían diferentes configuraciones de PAM, lo que hacía que las contraseñas se manejaran de manera un poco diferente. Comparé los archivos /etc/pam.d/ (usando md5sum [file]), y son los mismos entre los dos sistemas.

Pruebas

Sudo STDIN

Probé otro problema en el que sudo no leía una contraseña de STDIN, pero funcionaba bien en ambos servidores.

Prueba Sudo Ad-Hoc

-bash-4.1 $ ansible Server1 -m archivo -a "dest = / tmp / ansible_test.txt state = touch" -sK
Contraseña SSH: 
contraseña de sudo [predeterminada a la contraseña SSH]: 
Servidor1 | éxito >> {
    "cambiado": verdadero, 
    "dest": "/tmp/ansible_test.txt", 
    "gid": 0, 
    "group": "root", 
    "modo": "0644", 
    "propietario": "raíz", 
    "tamaño": 0, 
    "estado": "archivo", 
    "uid": 0
}

¡Éxito! ¡¿Pero por qué?!

TL; DR

  1. El Servidor1 parece estar esperando la solicitud de contraseña de sudo mientras el Servidor2 funciona bien.
  2. Ejecutar ansible"ad-hoc" en el Servidor1 funciona bien. Ejecutarlo como un libro de jugadas falla.

Pregunta (s)

  • ¿Qué podría causar que mi configuración de Ansible Sudo funcione bien en un servidor y sea rechazada en otro?
  • ¿Ansible realiza la contraseña "pasar" de máquina local a remota de manera diferente cuando se ejecuta ad-hoc versus playbook? Asumí que serían lo mismo.

Estoy pensando que esto se está acercando a simplemente enviar un informe de error a la página de GitHub simplemente por el hecho de que el acceso a sudo tiene diferentes resultados dependiendo de si estoy ejecutando ad-hoc o no.

BrM13
fuente

Respuestas:

4

Lo que haría es usar

strace -vfp `pidof sshd`

y ver dónde está fallando.

Verifique también la cuenta, tal vez esté restringida o algo así, pero mi apuesta es que algo está mal con su archivo / etc / hosts o si cambia en el proceso.

Iulian
fuente
Gracias lulian. He aplicado algunas ediciones a la pregunta, una sección es la salida STrace. Está claro que hay una diferencia entre los dos servidores en cómo proceden después de que el proceso ansible se inicia en el servidor remoto. Las ejecuciones posteriores y las capturas de rastreo fueron consistentes.
BrM13
Creo que necesita más de esa secuencia -vfp, hágalo manualmente en el proceso sshd superior y siga la salida. No creo que después de leer la contraseña solo cierre el canal así antes de pasar por PAM, etc. En ese momento, eche un vistazo al archivo sshd_config y hosts.deny ... vea si puede encontrar algo allí.
Iulian
Había intentado su sugerencia antes, pero debo haber pasado por alto el elemento crucial allí (de ahí por qué opté por ver el proceso ansible en la STrace inicial). Después de otro intento, encontré una variable vacía {{contraseña}} que se pasaba en lugar de la contraseña real. Optamos por enviar otra "Respuesta" por separado, ya que su respuesta finalmente me llevó por el camino correcto.
BrM13
4

Usando @lulian como punto de apoyo en esta respuesta, el problema se redujo a un pícaro ansible_sudo_pass:definido en group_vars que anulaba la contraseña ingresada --ask-sudo-pass.

Usando lo siguiente:

while [[ -z $(ps -eaf|grep 'sshd: [U]ser1@pts/1') ]]; do
    continue
done
strace -ff -vfp $(ps -eaf|grep 'sshd: [U]ser1@pts/1'|awk '{print $2}') -o /root/strace_sshd1_2.out

Pude encontrar que write(4, "{{ password }}\n", 15)se estaba pasando en lugar de la contraseña ingresada. Después de una búsqueda rápida, de hecho encontré ansible_sudo_passdefinido en mi group_vars que anulaba mi contraseña ingresada.

Como un FYI para todos los demás, la ansible_sudo_pass:definición parece tener prioridad sobre lo --ask-sudo-passque, al principio, parecía contrario a la intuición. Al final, esto es un error del usuario, pero la metodología de @ lulian para depurar la interacción SSH, así como el descubrimiento de la relación entre ansible_sudo_passy --ask-sudo-passdebería ser muy útil para otros. (¡Ojalá!)

BrM13
fuente
1
Yo diría que Ansible que da prioridad a las variables definidas por el archivo sobre las opciones de la línea de comandos es contraintuitivo y tiene un mal comportamiento. Curiosamente, reconoce que esto se rompe cuando pasa opciones con -e, y es posible que pueda evitar esto pasando una opción adecuada con -e.
Christopher Cashell