¿Se envía la contraseña de sudo en texto plano?
No por la red. Todo lo que hay en una sesión SSH va cifrado, incluido lo que escribes en el prompt de sudo. La contraseña se descifra en el servidor, como tiene que ser para que sudo la compruebe, así que un servidor comprometido puede capturarla. Es una razón más para no reutilizar esa contraseña en ningún otro sitio.
La forma normal: ssh -t
sudo necesita un terminal para pedir una contraseña, y un comando remoto no recibe ninguno por defecto:
$ ssh user@server 'sudo whoami'
sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
sudo: a password is required
El error sugiere -S, y ahí es donde la gente se equivoca. La solución sencilla es -t, que pide un terminal:
ssh -t user@server 'sudo whoami'
Obtienes el prompt normal, no se muestra nada de lo que escribes y no queda nada en una línea de comandos.
Qué se filtra con sudo -S
Este es el patrón que debes evitar:
ssh user@server 'echo mypassword | sudo -S systemctl restart myapp'
La contraseña pasa a ser parte de un comando, así que aparece en tres sitios:
- La lista de procesos del servidor. Mientras se ejecutaba el comando, otra cuenta sin privilegios del servidor de pruebas vio esto con un simple
ps:user bash -c echo mypassword | sudo -S -p "" sleep 3. - La lista de procesos de tu máquina. Todo el texto es un argumento de
ssh, así quepstambién lo muestra ahí. - El historial de tu shell local, si lo escribiste, y cualquier script o registro de CI que lo contenga.
Si de verdad tienes que dar la contraseña a sudo desde un script
Lee la contraseña sin eco y envíala por la entrada estándar, para que nunca sea un argumento:
read -rs SUDO_PW
printf '%s\n' "$SUDO_PW" | ssh user@server 'sudo -S -p "" whoami'
unset SUDO_PW
printf es un comando interno del shell, así que no inicia ningún proceso que ps pueda mostrar. En la misma prueba la otra cuenta solo vio sudo -S -p sleep 3. -p "" quita el prompt de la salida. sudo usa aquí la entrada estándar para la contraseña, así que el comando remoto no debería necesitar entrada propia.
Para automatizar: NOPASSWD para un único comando exacto
Un script no debería guardar ninguna contraseña. Permite solo lo que necesita:
# /tmp/deploy.sudoers
deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp, /usr/bin/journalctl -u myapp -n 100 --no-pager
Comprueba el archivo antes de instalarlo, porque un archivo sudoers roto puede dejarte sin sudo:
sudo visudo -cf /tmp/deploy.sudoers
sudo install -m 0440 -o root -g root /tmp/deploy.sudoers /etc/sudoers.d/deploy
sudo visudo -c
Después úsalo, con -n para que una discrepancia falle al instante y no se quede esperando una contraseña:
$ ssh deploy@server 'sudo -n /usr/bin/systemctl restart myapp'
$ ssh deploy@server 'sudo -n /usr/bin/systemctl stop myapp'
sudo: a password is required
Lista lo que una cuenta puede ejecutar con sudo -l. Cuatro trampas:
- Un comando sin argumentos en la regla permite cualquier argumento. Con solo
/usr/bin/iden la regla, tantosudo -n /usr/bin/idcomosudo -n /usr/bin/id -use ejecutaron en nuestra prueba. Escribe los argumentos que quieres. - Los nombres de archivo con un punto se ignoran.
/etc/sudoers.d/deploy.confno tuvo ningún efecto en nuestra prueba, mientras que el mismo archivo llamadodeployfuncionó. - Algunos comandos pueden iniciar un shell. Los editores, los paginadores,
find -execytarpueden ejecutar otros programas como root, así que permitir uno de ellos puede equivaler a permitirlo todo. Por eso la regla de arriba lleva--no-pager: sin ello, journalctl puede abrir un paginador. - Los comodines también coinciden con los espacios. El manual de sudoers advierte de que los comodines en los argumentos coinciden con cualquier carácter, incluidos los espacios en blanco, así que pueden coincidir con más argumentos de los que habías previsto.
Cuánto tiempo te recuerda sudo
Por defecto sudo no vuelve a preguntar durante 5 minutos, y el registro es independiente para cada terminal. En la prueba, una sesión con ssh -t ejecutó un segundo sudo -n whoami sin ningún prompt. Para terminarlo antes, ejecuta sudo -k. Para que pregunte siempre en una cuenta, añade esto con visudo:
Defaults:deploy timestamp_timeout=0
Una shell de root abierta es donde ocurren la mayoría de los despistes. Consulta cómo evitar errores en servidores de producción.
sudo -A y askpass
Con -A, sudo ejecuta el programa indicado en SUDO_ASKPASS y lee la contraseña de su salida:
SUDO_ASKPASS=$HOME/bin/askpass sudo -A whoami
Mantiene la contraseña fuera de la lista de procesos, pero el programa auxiliar tiene que sacarla de algún sitio. En un servidor sin sesión gráfica, eso suele significar un archivo, como en nuestra prueba, de modo que el secreto queda en el disco. Una regla NOPASSWD acotada suele ser la mejor respuesta.
Ansible
Usa --ask-become-pass (-K) para que te la pida, o guarda ansible_become_password en un archivo cifrado con Ansible Vault. No la pases con -e en la línea de comandos, porque así queda en la lista de procesos y en tu historial como cualquier otro argumento.
Una regla NOPASSWD para unos pocos comandos no funciona con Ansible. Su documentación dice que la elevación de privilegios no se puede limitar a ciertos comandos, porque los módulos se ejecutan desde un archivo temporal cuyo nombre cambia cada vez. Sugiere usar una cuenta aparte sin esta restricción.
Preguntas
¿La contraseña de sudo viaja en claro por SSH?
No. Todo lo que pasa por una sesión SSH va cifrado, también lo que escribes en el aviso de sudo. Se descifra en el servidor, que tiene que comprobarla.
¿Por qué ssh dice que hace falta un terminal para leer la contraseña?
Un comando remoto no recibe terminal por defecto, y sudo lo necesita para pedir la contraseña. Añade -t, como en ssh -t user@server 'sudo command'.
¿Es seguro NOPASSWD para scripts?
Es más seguro que una contraseña guardada si la regla indica un único comando exacto con sus argumentos. Evita editores, paginadores y otros programas que pueden abrir una shell.
Fuentes
- Manual de sudo(8)
- Manual de sudoers(5)
- Manual de visudo(8)
- Understanding privilege escalation, documentación de Ansible