Estos días estuve peleando (sin éxito) por tratar de evitar darle a Ansible root access irrestricto en mis targets Linux. He aquí mi experiencia y conclusión.
Mi plan original era que Ansible no se autentique ni corra como root en los targets. Esto apunta a no tener que habilitar el login como root a través de SSH (por seguridad). Pero además a lograr una configuración de mínimo privilegio utilizando sudo. Esto es, permitir a Ansible ejecutar como root sólo aquellos comandos necesarios para el funcionamiento de los módulos a utilizar, para evitar la clásica y terrible configuración de sudo
:
pepe ALL=(ALL) NOPASSWD: ALL
La cual básicamente hace que "pepe" se convierta automáticamente en root (mala idea).
Para comenzar, se me ocurrió probar con el módulo apt
, el cual se encarga de gestionar paquetes utilizando el gestor APT sobre hosts Debian/Ubuntu. La configuración previa de sudoers para el usuario con el que autentica Ansible en los targets ("sysadmin"), permite que éste ejecute apt-get update
, entre otros comandos, como root (sudo
). Luego, para pasar a root utilizo la opción -b
(become), una especie de wrapper a los diferentes mecanismos de elevación de privilegios disponibles en cada sistema operativo soportado (sudo
, su
, doas
, ksu
, etc.).
Sin embargo, al momento de ejecutar el módulo, este falla indicando que se requiere password para sudo (lo cual significa que la configuración de sudoers sin contraseña no hizo efecto):
[root@hal9000 /opt/ansible]# ansible devel-debian8 -m apt -a "update_cache=yes" -b www-devel.linuxito.com | FAILED! => { "changed": false, "failed": true, "module_stderr": "Shared connection to www-devel.linuxito.com closed.\r\n", "module_stdout": "sudo: a password is required\r\n", "msg": "MODULE FAILURE", "rc": 1 }
La raíz del problema está en la implementación misma de Ansible. En su funcionamiento normal, luego de abrir una sesión SSH, abre una subshell como root, desde la cual luego se lanza el intérprete de Python (obviamente como usuario root), que se encarga de correr el script que implementa el módulo utilizado (en este caso "apt"). Es decir, luego de abrir la sesión SSH se pasa inmediatamente a root, y todo se hace como root. Esto se puede verificar ejecutando ps
en el target:
root 26347 0.0 0.5 86812 5976 ? Ss 09:45 0:00 sshd: sysadmin [priv] sysadmin 26349 0.0 0.4 86812 4992 ? S 09:45 0:00 sshd: sysadmin@pts/1 sysadmin 26362 0.0 0.0 4336 724 pts/1 Ss+ 09:45 0:00 /bin/sh -c sudo -H -S -n -u root /bin/sh -c 'echo BECOM root 26363 0.0 0.3 44656 3580 pts/1 S+ 09:45 0:00 sudo -H -S -n -u root /bin/sh -c echo BECOME-SUCCESS-zo root 26364 0.0 0.0 4336 768 pts/1 S+ 09:45 0:00 /bin/sh -c echo BECOME-SUCCESS-zoevhwomjrmnkoysqlzidksk root 26365 0.0 1.0 33228 10484 pts/1 S+ 09:45 0:00 /usr/bin/python /home/sysadmin/.ansible/tmp/ansible-tmp root 26366 1.0 4.8 94556 49456 pts/1 S+ 09:45 0:00 /usr/bin/python /tmp/ansible_za0xsv/ansible_module_apt.
El primer comando es un simple echo
para verificar que pudo pasar a root, luego se inicia el intérprete de Python con un script temporal que finalmente será borrado.
Probablemente sea imposible tomar esta captura con ps
cuando la autenticación con sudo falla (hay que correr ps
justo en el instante que falla la autenticación, algo imposible). Sin embargo, al fallar la autenticación, root recibe un mail con la advertencia de seguridad. Si hemos configurado el servidor para reenviar todo mail a root a nuestra casilla, recibiremos un correo como el siguiente:

Se observa claramente la secuencia de comandos en el target. Ahora bien, como el comando enviado a sudo
abre directamente una subshell no queda otra opción que filtrar, como mucho, el comienzo del mismo.
Editar el archivo de configuración de sudoers correspondiente:
root@www-devel:~# nano /etc/sudoers.d/sysadmin
Siendo que la cadena del comando comienza siempre con "/bin/sh -c echo BECOME-SUCCESS" y luego sigue con un número aleatorio, como mínimo privilegio se deberá permitir lo siguiente:
User_Alias USR_ADMIN = sysadmin # ansible commands Cmnd_Alias CMD_ANSIBLE = /bin/sh -c echo BECOME-SUCCESS* # ansible nopasswd USR_ADMIN SERVIDOR = NOPASSWD:CMD_ANSIBLE
La consecuencia de esta configuración es que cualquiera (logueado como "sysadmin") puede abrir una shell como "root" ejecutando la siguiente secuencia:
sysadmin@www-devel:~$ sudo /bin/sh -c 'echo BECOME-SUCCESS; su - root' BECOME-SUCCESS root@www-devel:~#
Es una cagada, ya lo sé, pero al menos es un poco mejor que permitir:
sysadmin ALL=(ALL) NOPASSWD: ALL
Al menos sirve como triste consuelo.
Con esta configuración de sudo
, ansible puede elevar privilegios correctamente y el módulo funciona:
[root@hal9000 /opt/ansible]# ansible desa-debian8 -m apt -a "update_cache=yes" -b www-devel.linuxito.com | SUCCESS => { "cache_update_time": 1504182750, "cache_updated": true, "changed": true, "failed": false }
Lamentablemente Ansible no tiene bien documentada la configuración de permisos en los targets y asume que la autenticación es como root, o puede se ejecutar cualquier comando como root elevando privilegios, o bien uno debe resignarse a tipear la contraseña de root para cada host (algo ridículo cuando justamente estamos tratando de lograr automatización de tareas. Falta de buena documentación e implementaciones con diseño "fuck security" son cosas que me molestan mucho, pero lamentablemente uno debe resignar (por no contar con tiempo suficiente para reimplementar los módulos) para poder aprovechar esta muy buena herramienta.
Para lograr el mejor balance entre automatización (no solicitar contraseña) y seguridad (mínimo privilegio), lo mejor sería tener la posibilidad de permitir (para cada módulo de Ansible) ejecutar como superusuario sólo una lista de comandos estrictamente necesarios. Desconozco cómo será la configuración de seguridad del resto de las soluciones, pero al menos éste es un punto flojo de Ansible. Sin embargo es de esperarse con este tipo de soluciones donde la facilidad (máxima automatización posible) suele estar por encima de la seguridad.
Referencias
- Ansible - GitHub
- Become (Privilege Escalation) - Ansible Documentation
- apt - Manages apt-packages
- Running Ad Hoc Commands - Ansible Tips and Tricks
- 15 Things You Should Know About Ansible - CodeHeaven