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
Desde la Parte 2 publicada en developWorks en Septiembre de 2001, y más tarde
referenciados en Slashdot y Freshmeat (vease
En mi
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.
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
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
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
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:
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.
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
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
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.
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
if [ -z "`echo -e`" ] then E="-e" fi
Arriba, E se establece en
echo $E Usage: ${CYAN}${0}${OFF} [ ${GREEN}options${OFF} ] ${CYAN}sshkey${OFF} ...
Mediante el uso de
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
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
La tubería ps también contiene una verdadera joya grep, que me fue amablemente
enviada por Hans Peter Verne. Observe que
mypids=`ps $psopts 2>/dev/null | grep "[s]sh-agent" | awk '{print $2}'` > /dev/null 2>&1
¿Perplejo?. Si ha decidido que un
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"