Gestión de claves para OpenSSH, Parte 3 Daniel Robbins Javier Vecino En este tercer artículo de la serie, Daniel Robbins muesta como mejorar las conexiones del agente OpenSSH. Así como las recientes mejoras en el script de consola keychain. 1.2 2005-10-21 Agente intermedio y mejoras de keychain

Muchos de nosotros usamos las excelencias de OpenSSH como sustituto cifrado y seguro de los venerables comandos telnet y rsh. Una de las características que más intrigan de OpenSSH es la habilidad para validar usuarios usando los protocolos RSA y DSA, que se basan en un par de claves numéricas complementarias. Uno de los principales atractivos de la validación RSA y DSA es la promesa de ser capaz de establecer conexiones a sistemas remotos sin suministrar una contraseña. Para más antecedentes, vea las entregas anteriores de esta serie sobre gestión de claves para OpenSSH, que cubren validación RSA/DSA (Parte 1) y ssh-agent y keychain (Parte 2), respectivamente.

Desde la Parte 2 publicada en developWorks en Septiembre de 2001, y más tarde referenciados en Slashdot y Freshmeat (vease Recursos más adelante en este artículo los enlaces a estos sitios), muchas personas han empezado a usar keychain, y le han realizado algunos cambios, he recibido sobre unos 20 parches de gran calidad de desarrolladores de todo el mundo. He incorporado muchos de estos parches al código de keychain, que está ahora en la versión 1.8 (vea Recursos). Envío mis más sincero agradecimiento a todos aquellos que enviaron parches, informes de errores, y notas de agradecimiento.

Ajustando la seguridad de ssh

En mi último artículo, he pasado algún tiempo comentando los beneficios de seguridad y las ventajas del funcionamiento de ssh-agent. Pocos días después de que el segundo artículo apareciera en developerWorks, recibí un e-mail de Charles Karney de Sharnof Corporation, que amablemente me informó de las nuevas habilidades del nuevo agente intermedio de validación de OpenSSH, le echaremos un vistazo en breve. Además, Charles hizo incapié que la ejecución de ssh-agent en máquinas poco fiables es muy peligroso: si alguien logra acceso como root en el sistema, entonces sus claves descifradas puedes ser extraidas desde ssh-agent. Aunque la extracción de las claves sería algo difícil, está dentro de las habilidades de los crackers profesionales. Y ante mero hecho de que el robo de la clave privada sea medianamente posible deberemos tomar medidas en primer lugar para evitar que suceda.

Para formular una estrategia para proteger nuestras claves privadas, debemos primero poner las máquinas a las que accedemos dentro de una de dos categorías. Si un host particular está bien asegurado o aislado -- siendo poco probable el acceso con éxito del root por medio de una vulnerabilidad -- entonces la máquina debe ser considerada un host de confianza. Sin embargo, si una máquina es usada por muchas personas o si tiene alguna duda acerca de la seguridad del sistema, entonces la máquina debería ser considerada como un host no confiable. De esta manera, incluso si la seguridad del sistema se ve comprometida, en primer lugar no habrá un ssh-agent a mano del intruso para extraer las claves.

Sin embargo, esto crea un problema, si no se puede ejecutar ssh-agent en hosts no confiables, entonces ¿como podemos establecer conexiónes ssh, sin contraseñas y seguras en estos sistemas? La respuesta es usar solamente ssh-agent y keychain en hosts confiables, y usar la nueva capacidad de OpenSSH de validación intermedia para extender la validación sin contraseña en los hosts inseguros. En pocas palabras, el trabajo de la validación intermedia es permitir a las sesiones ssh remotas contactar con un ssh-agent que esté ejecutándose en un sistema confiable.

Agente de vaiidación intermedia

Para tener una idea de cómo funciona la validación intermedia, primero deje que veamos una hipotética situación donde el usuario drobbins tiene un ordenador portátil llamado lappy, un servidor de confianza llamado trustbox, y otros dos sistemas inseguros a los que debe tener acceso, llamados notrust1 y notrust2, respectivamente. Actualmente, utiliza ssh-agent junto con keychain en las cuatro máquinas, de la siguiente manera:

El problema con este enfoque es que si alguien consigue acceso de root en notrust1 o notrust2, entonces, por supuesto, es posible que estas personas extraigan las claves desde el ahora vulnerable proceso ssh-agent. Para solucionar este problema, drobbins para la ejecución de ssh-agent y keychain en los inseguros hosts notrust1 y notrust2. De hecho, es incluso más cuidadoso, drobbins decide usar solamente ssh-agent y keychain en lappy. Esto limita la exposición de sus claves privadas descifradas, protegiéndolas contra el robo.

Por supuesto, el problema con este enfoque es que drobbins solamente puede establecer conexiones sin contraseña desde lappy. Veamos como habilitar la validacion intermedia y solucionar este problema.

Suponiendo que todas las máquinas están ejecutando la versión más reciente de OpenSSH, podemos solucionar este problema mediante el uso de la validación intermedia. la validación intermedia permite a los procesos ssh remotos contactar con el ssh-agent que esté en ejecución en su máquina local segura -- en lugar de requerir que una versión de ssh-agent sea ejecutada en la misma máquina desde la que salga por ssh. Esto por lo general le permite ejecutar ssh-agent (y keychain) en una sola máquina, y significa que todas las conexiones ssh que procedan (ya sea directa o indirectamente) desde esta máquina van a utilizar su ssh-agent local.

Para habilitar la validación intermedia, añadimos la siguiente línea en /etc/ssh/ssh_config de lappy y trustbox. Tenga en cuenta que este es el archivo de configuración de ssh (ssh_config), no del servicio sshd (sshd_config):

ForwardAgent Yes

Ahora, para sacar ventaja de la validación intermedia, drobbins se puede conectar desde lappy a trustbox, y a continuación desde trustbox hacia notrust1 sin suministrar contraseña desde ninguna conexión. Ambos procesos ssh "llaman" a ssh-agent ejecutado en lappy:

$ ssh drobbins@trustbox
Last login: Wed Sep 26 13:42:08 2001 from lappy

Welcome to trustbox!
$ ssh drobbins@notrust1
Last login: Tue Sep 25 12:03:40 2001 from trustbox

Welcome to notrust1!
$

Si intenta una configuración similar y se da cuenta que la transmisión no funciona, pruebe utilizando ssh -A en lugar del anterior ssh a secas para habilitar explícitamente la validación intermedia. Aquí hay un driagrama sobre lo que sucede detrás del escenario cuando accedemos a trustbox y notrust1 usando la validación intermedia:

Como puede ver, cuando ssh conecta a trustbox, mantuiene una conexión con el ssh-agent ejecutado en lappy. Cuando una conexión ssh se realiza desde trustbox hacia notrust1, este nuevo proceso ssh mantenie la conexión validada por el anterior ssh, extendiendo la cadena de manera efectiva. Si esta cadena de validacion puede ser extendida más allá de notrust1 a otros hosts depende de como /etc/ssh/ssh_config en notrust1 esté configurado. Mientas el agente intermedio este habilitado, todas las partes de la cadena serán capaces de validarse usando el ssh-agent ejecutado en el seguro lappy,

Ventajas del agente de conexión intermedia

La validación intermedia ofrece una serie de ventajas de seguridad no mostradas. Para convencerme de la importancia del agente de conexión intermedia, Charles Karney ha compartido conmigo estas tres conceptos de seguridad:

  1. La clave privada se guarda sólo en la máquina de confianza. Esto evita que usuarios malintencionados recolecten su clave encriptada desde el disco y traten de romper la encriptación.
  2. ssh-agent sólo se ejecuta en la máquina de confianza. Esto evita que un intruso reealice un volcado de memoria de un proceso ssh-agent remoto y entonces extraiga su clave descifrada desde el volcado.
  3. Dado que sólo tendrá que teclear su contraseña desde su máquina de confianza, evita cualquier furtivo logeador de pulsaciones del teclado que pueda capturar su contraseña cuando es introducida.

El incoveniente de confiar en el agente de validación intermedio es que no resuelve el problema que permita a los cron jobs aprovechar la validación RSA/DSA. Una solución a este problema es crear todos los cron jobs que necesiten la validación RSA/DSA para que sean ejecutados en una máquina de confianza de su LAN. Si es necesario, estos cron jobs pueden utilizar ssh para conectarse a sistemas remotos para automatizar copias de seguridad, sincronizar ficheros, y así sucesivamente.

Ahora que hemos visto la conexión del agente de validación intermedia, es el turno de las mejoras recientemente introducidas en el propio script keychain.

Mejoras en el funcionamiento de keychain

Gracias a los parches enviados por los usuarios, muchas mejoras importantes se han introducido en el código fuente de keychain. Varios de los parches enviados por usuarios fueron en relación a la funcionalidad. Por ejemplo, recordará que keychain ha creado un fichero ~/.ssh-agent; el nombre de este fichero ha cambiado a ~/.ssh-agent-[hostname] de modo que keychain puede trabajar con los directorios home montados por NFS para que se puedan acceder desde varios hosts físicos. Además del fichero ~/.ssh-agent-[hostname], ahora hay un fichero ~/.ssh-agent-csh-[hostname] que puede ser leído por consolas csh y compatibles. Po último, una nueva opción --nocolor ha sido añadida a fin de que la característica coloración pueda ser desactivada en caso de que se utilice un terminal no compatilble vt100.

Correcciones de compatibilidas de la consola

Si bien la funcionalidad de las mejoras han sido importantes, la gran mayoría de las correciones se han ocupado de los problemas de compatibilidad de la consola. Verá, mientras keychain 1.0 requiere de bash, versiones posteriores fueron cambiadas para trabajar con cualquier consola compatible con sh. Este cambio permite a keychain trabajar "de forma poco común" en casi cualquier sistema UNIX, incluyendo Linux, BSD, Solaris, IRIX, y AIX así como de otras plataformas UNIX. Si bien la transición a sh y a la compatibilidad general UNIX ha sido un camino lleno de baches, también ha sido una tremenda experiencia de aprendizaje. La creación de un único script que funcione en todas estas plataformas ha sido muy difícil, principalmanete por mi, ¡sencillamente no tengo acceso a la mayoría de estos sistemas operativos!. Afortunadamente, lo hicieron los usuarios de keychain de todo mundo, y muchos han proporcionado gran ayuda en la identificación de problemas de compatibilidad y presentando parches para solucionarlos.

Realmente hay dos tipos de problemas de compatibilidad que tenían que ser solucionados. Primeramente, necesitaba asegurar que keychain, integraba expresiones y operadores que estuvieran plenamente soportados bajos todas las implementaciones de sh, incluyendo todas las consolas sh populares libres y las comerciales UNIX, zsh (en modo compatible sh), y las versiones 1 y 2 de bash. Estos son algunas de las correciones de compatilibilidad de consola enviadas por los usuarios que fueron aplicadas al código funete de keychain:

Dado que las antiguas consolas sh no soportan el convenio ~ para referirse al directorio home del usuario, las líneas que usaban ~ fueron cambiadas para usar $HOME en su lugar:

hostname=`uname -n`
pidf=${HOME}/.ssh-agent-${hostname}
cshpidf=${HOME}/.ssh-agent-csh-${hostname}

A continuaciòn, todas las referencias al código fuente fueron cambiadas por un . para asegurar la compatibilidad con la purista /bin/sh de NetBSD, que no es compatible con el comando source:

if [ -f $pidf ]
then
    . $pidf
else
SSH_AGENT_PID="NULL"
fi

A lo largo del camino, también he ido aplicando algunos parches relacionados con el rendimiento. Un experimentado programador de scripts de consola me informó que en lugar de "crear" un fichero tecleando touch foo, tu puedes hacer esto en su lugar:

> foo

Sobre la base de usar la sintaxis interna de la consola en lugar de un binario externo, un fork() es evitado y el script se convierte en algo más eficiente. > foo debería trabajar con cualquier consola compatible sh; sin embargo, no parece estar soportado por ash. Esto no debería ser un problema para la mayoría de la gente, ya que ash es una consola para un disco de recuperación en lugar de algo que la gente use en el día a día básico.

Preguntando a la plataforma por los ejecutables

Obtener un script que trabaje en múltiples sistemas operativos UNIX requiere algo más que ceñirse a la sintaxix pura de sh. Recuerde que, la mayoría de los scripts también hacen llamadas a comandos externos, tales como grep, awk, ps, y otros, y estos comandos deben ser llamados de una manera compatible en la medida de lo posible. Por ejemplo, mientras el comando echo incluido en la mayoría de los UNIX reconoce la opción -e, Solaris no -- simplemente imprime -e hacia stdout cuando se utiliza. Por lo tanto ,con el fin de tratar con Solaris, keychain ahora autodetecta si echo -e funciona:

if [ -z "`echo -e`" ]
then
    E="-e"
fi

Arriba, E se establece en -e si -e es soportada, entonces, echo se puede llamar de la siguiente manera:

echo $E Usage: ${CYAN}${0}${OFF} [ ${GREEN}options${OFF} ] ${CYAN}sshkey${OFF} ...

Mediante el uso de echo $E en lugar de echo -e, la opción -e puede ser dinámicamente activada o desactivada según sea necesario.

pidof, ps

Probablemente, la más significativa revisión de compatibilidad ha implicado cambios en como keychain detecta los actuales procesos ssh-agent en ejecución. Anteriormente yo estaba usando el comando pidof para hacerlo, pero he tenido que eliminarlo desde que varios sistemas no tienen un pidof. Realmente, pidof no es la mejor solución de todos modos, ya que lista todos los procesos ssh-agent ejecutándose en el sistema, independientemente del usuario, cuando realmente estamos interesados en todos los procesos ssh-agent propiedad del actual UID.

Así que, en lugar de depender de pidof, pasamos sobre las tuberias de la salida ps a grep y awk con el fin de extraer el proceso ids necesario. Esto es un arreglo enviado por un usuario:

mypids=`ps uxw | grep ssh-agent | grep -v grep | awk '{print $2}'`

La tubería establecerá la variable mypids a los valores de todos los procesos ssh-agent propiedad del actual usuario. El comando grep -v grep es parte de la tubería para garantizar que el procesos grep ssh-agent no se convierta en parte de nuestra lista de PID.

Si bien este enfoque es un buen concepto, usando ps se abrió toda una nueva lata de gusanos ya que las opciones de ps no están estandarizadas a través de las diferentes BSD y System V derivados de UNIX. He aquí un ejemplo: mientras ps uxw trabaja bajo Linux, no funciona bajo IRIX. Y mientras ps -u username -f trabaja bajo Linux, IRIX y Solaris, no funciona bajo BSD, que sólo entiende el estilo BSD de opciones ps. Para evitar este problema, keychain autodetecta si el ps del sistema actual trabaja con la sintaxis BSD o System V antes de ejecutar la tubería ps:

psopts="FAIL"
ps uxw >/dev/null 2>&1
if [ $? -eq 0 ]
then
psopts="uxw"
else
ps -u `whoami` -f >/dev/null 2>&1
if [ $? -eq 0 ]
then
psopts="-u `whoami` -f"
fi
fi
if [ "$psopts" = "FAIL" ]
then
echo $0: unable to use \"ps\" to scan for ssh-agent processes.
Report KeyChain version and echo system configuration to drobbins@gentoo.org.
exit 1
fi

mypids=`ps $psopts 2>/dev/null | grep "[s]sh-agent" | awk '{print $2}'` > /dev/null 2>&1

Para asegurar que el comando ps trabaja con ambos System V y estilo BSD, el script hace un "simulacro" de ps uxw, mostrando alguna salida, si el código de error de este comando es igual a cero, sabemos que ps uxw trabaja y ajustamos el valor psopts adecuadamente. Sin embargo, si ps uxw devuelve un código de error distinto de cero (lo que indica que tenemos que usar las opciones del estilo BSD), haremos una prueba de ps -u `whoami` -f, nuevamente mostrando toda la salida. En este punto, esperamos haber encontrado una variante BSD o System V de ps que podamos usar. Si no, mostramos un error y salimos. Pero es muy probable que uno de los dos comandos ps trabaje, en cuyo caso se ejecutará la línea final del anterior fragmento de código, nuestra tubería ps. Mediante el uso de la variable $psopts expandida inmediatamente después de ps, estamos en condiciones de pasar las opciones correctas al comando ps.

La tubería ps también contiene una verdadera joya grep, que me fue amablemente enviada por Hans Peter Verne. Observe que grep -v grep ya no es parte de la tubería; en su lugar, se ha eliminado y grep "ssh-agent" ha sido cambiado por grep "[s]sh-agent". Este único comando grep termina haciendo lo mismo que grep ssh-agent | grep -v grep; ¿se puede figurar por qué?.

mypids=`ps $psopts 2>/dev/null | grep "[s]sh-agent" | awk '{print $2}'` > /dev/null 2>&1

¿Perplejo?. Si ha decidido que un grep "ssh-agent" y grep "[s]sh-agent" deberían coincidir con las mismas líneas de texto, está en lo cierto. ¿Entonces porqué hacer que generen resultados diferentes cuando la salida de ps es entubada hacia ellos? He aquí como funciona: cuando se utiliza grep "[s]sh-agent", cambia la forma en que el comando grep aparece en el listado de procesos ps. De esta manera, prevenimos que grep coincida consigo mismo, ya que la cadena [s]sh-agent no coincide con la expresión regular [s]sh-agent. ¿No es brillante? Si todavía no lo pilla, juegue con grep un poco más y lo logrará pronto.

Conclusión

Con este artículo concluye mi covertura de OpenSSH. Afortunadamente, ha aprendido lo suficiente para empezar a utilizar OpenSSH para asegurar sus sistemas. El próximo mes continuaré con la serie de articulos con la "Guía avanzada de implementación de ficheros"

Recursos
  • Lea los otros dos artículos de Daniel de esta serie, Gestión de claves para OpenSSH, Parte 1 y Gestión de claves para OpenSSH, Parte 2
  • La versión más reciente de keychain está disponible en la página deGentoo Linux Keychain
  • Asegúrese de visitar elsitio de desarrollo de OpenSSH, y revise lasFAQ de OpenSSH
  • PuTTY es un excelente cliente ssh para máquinas Windows
  • El libro "SSH, The Secure Shell: The Definitive Guide" (O'Reilly & Associates, 2001) puede ayudarle. El sitio de los autores contiene información acerca del libro, FAQ, noticias, y actualizaciones.