Utilizando SSH
Agente SSH
Una de las ventajas de emplear llaves públicas frente al uso de
contraseñas de usuario es que no tenemos que recordar nada más
que una única frase, la frase con la que hemos cifrado nuestra
llave privada. Eso es un avance, pero OpenSSH dispone de una
herramienta que nos puede evitar el trámite de tener que andar
escribiendo dicha frase cada vez que establezcamos una nueva
conexión. Esta herramienta se llama ssh-agent y tiene
la capacidad de recordar las llaves privadas que tenemos.
Al ejecutar el agente ssh (ssh-agent), este crea un socket UNIX
y establece la variable de entorno SSH_AUTH_SOCK con
el nombre del socket. Por razones de seguridad los permisos del
socket son ajustados para que tan sólo el usuario actual pueda
acceder al socket. Además, el agente también crea la variable
de entorno SSH_AGENT_PID y establece su valor con
su PID (identificador de programa).
Cuando el cliente de SSH necesita autenticar a un usuario, lo
primero que hace es mirar si existe la variable de entorno
SSH_AUTH_SOCK, de ser así, la usa para establecer
una conexión con el agente, el agente no le pasa la llave
privada al cliente de SSH, sino que es el propio agente el
que se encarga de realizar la autenticación, de forma que la
llave privada nunca sea expuesta a los clientes.
Iniciando el agente
Actualmente hay varias formas de cargar el agente, una de ellas
es poniendo simplemente ssh-agent en la línea de
comandos:
[hell@local] $ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-nfxOw32113/agent.32113; export SSH_AUTH_SOCK;
SSH_AGENT_PID=32114; export SSH_AGENT_PID;
echo Agent pid 32114;
[hell@local] $
La salida anterior se corresponde a una shell Bourne y no es más que
los comandos que habría que ejecutar para establecer las variables
de entorno SSH_AUTH_SOCK y SSH_AGENT_PID, en
el caso de emplear una shell derivada del csh la salida sería
ligeramente distinta. El programa ssh-agent intenta
averiguar el tipo de shell que se está usando, no obstante, se le
puede pasar la opción -c para indicar una shell derivada
de csh, o la opción -s para que muestre los comandos
apropiados para una shell derivada del Bourne. Por ejemplo, para
especificarle que estamos usando una shell csh:
[hell@local] $ ssh-agent -c
setenv SSH_AUTH_SOCK /tmp/ssh-hLBzi32149/agent.32149;
setenv SSH_AGENT_PID 32150;
echo Agent pid 32150;
[hell@local] $
El inconveniente de invocar el comando ssh-agent de la
anterior manera está en que tenemos que establecer las variables de
entorno manualmente, copiando y pegando, o escribiéndolas a mano,
pero para hacerlo más cómodo, podemos emplear el comando
eval de la siguiente forma:
[hell@local] $ eval `ssh-agent`
Agent pid 32171
[hell@local] $
De esa manera, logramos que la salida de ssh-agent se
evalúe, quedando las variables de entorno establecidas. Podemos
verificarlo así:
[hell@local] $ set | grep SSH
SSH_AGENT_PID=32174
SSH_AUTH_SOCK=/tmp/ssh-QUpBw32173/agent.32173
[hell@local] $
La otra manera de iniciar el agente es especificándole un programa
a ejecutar, de forma que el agente establecerá las variables de
entorno y ejecutará el programa, por ejemplo, para iniciar una
shell csh con el agente:
[hell@local] $ ssh-agent bash
local:hell {1} env | grep SSH
SSH_AUTH_SOCK=/tmp/ssh-DBKNN10192/agent.10192
SSH_AGENT_PID=1921
local:hell {2} exit
[hell@local] $
También, por ejemplo, se puede ejecutar con el agente una terminal
xterm:
[hell@local] $ ssh-agent xterm
Añadir llaves al agente
El agente nada más iniciarse no contiene ninguna llave, para
agregarlas se emplea la herramienta ssh-add,
si se ejecuta sin argumentos intenta añadir los archivos
~/.ssh/id_rsa, ~/.ssh/id_dsa y
~/.ssh/identity:
[hell@local] $ ssh-add
Enter passphrase for /home/hell/.ssh/id_rsa:
Identity added: /home/hell/.ssh/id_rsa (/home/hell/.ssh/id_rsa)
Identity added: /home/hell/.ssh/id_dsa (/home/hell/.ssh/id_dsa)
[hell@local] $
En el caso anterior ssh-add primero encuentra el
archivo ~/.ssh/id_rsa y pide la frase con la que
está cifrada la llave que contiene, entonces añade dicha
llave al agente, después, localiza el archivo
~/.ssh/id_dsa que contiene otra llave, pero al
estar cifrada con la misma frase que la primera llave,
no vuelve a pedir la frase, si no que la descifra y la
añade directamente al agente.
Si lo que queremos es añadir otra llave distinta a las tres
que busca por defecto, lo único que tenemos que hacer es
pasar el archivo con la llave como parámetro de
ssh-add. Por ejemplo, para añadir una llave
llamada millave.key:
[hell@local] $ ssh-add millave.key
Enter passphrase for millave.key:
Identity added: millave.key (millave.key)
[hell@local] $
Listar las llaves que hay en el agente
En caso de querer ver que llaves contiene el agente, podemos
pasarle la opción -l a la herramienta ssh-add,
y obtendremos una salida como la siguiente:
[hell@local] $ ssh-add -l
2048 1f:2b:61:83:c7:f8:27:73:4d:03:0a:92:40:da:b8:bb /home/hell/.ssh/id_rsa (RSA)
1024 78:f2:ac:c0:0b:80:57:e7:fb:98:05:cd:1e:36:69:84 /home/hell/.ssh/id_dsa (DSA)
2048 d1:a7:cb:de:1b:bf:ef:04:04:2f:33:fc:31:f3:80:b3 millave.key (DSA)
[hell@local] $
Cada línea representa una llave. La primera columna muestra el
tamaño en bits de la llave, la segunda se corresponde con la
huella dactilar de la llave, la tercera columna es el archivo
que contiene la llave y la última columna, indica entre paréntesis
el tipo de llave que es, RSA o DSA.
También se puede emplear la opción -L que mostrará
las llaves públicas, lo cual puede ser útil para añadirlas al
archivo authorized_keys de una máquina remota.
[hell@local] $ ssh-add -L
ssh-rsa AAAAB3NzaC1...XnFBZjRf8cqDbugZRVeHYGvRqrNdv/9w== /home/hell/.ssh/id_rsa
ssh-dss AAAAB3NzaC1...6GRYhh2JxuPcr9kwlD7ZUjFrgElDFrng== /home/hell/.ssh/id_dsa
ssh-dss AAAAB3NzaC1...ZumWVFBO5E+fhyaZegOb8Rsz08J8LxOQ== millave.key
[hell@local] $
Nota: Cada llave aparece en una línea, pero como cada línea es
muy larga, he eliminado parte del contenido de la llave pública,
sustituyéndolo por puntos suspensivos para que así quepa bien
en la pantalla.
Eliminar llaves del agente
También podemos eliminar llaves del agente empleando la herramienta
ssh-add con la opción -d seguida de la llave
pública que le corresponde a la llave privada que queremos que el
agente borre de su memoria. Por ejemplo, para que olvide nuestra
llave RSA:
[hell@local] $ ssh-add -d .ssh/id_rsa
Identity removed: .ssh/id_rsa (.ssh/id_rsa.pub)
[hell@local] $
No obstante, también podemos eliminar todas las llaves que tenga
el agente empleando la opción -D, ejemplo:
[hell@local] $ ssh-add -D
All identities removed.
[hell@local] $
Bloquear el agente
Cuando no necesitamos usar el agente, pero no queremos que se
olvide de nuestras llaves, como en el caso de dejar el equipo
en el que estamos trabajando para ir a tomar un cafetín,
podemos optar por bloquear el agente, para ello tan sólo hace
falta pasarle la opción -x a la herramienta
ssh-add, la cual nos pedirá una contraseña con la
que luego podremos desbloquear el agente:
[hell@local] $ ssh-add -x
Enter lock password:
Again:
Agent locked.
[hell@local] $
Luego, cuando necesitemos volver a usar el agente, podemos
desbloquearlo ejecutando la herramienta ssh-add
con la opción -X, ssh-add entonces
nos pedirá la contraseña para desbloquear el agente:
[hell@local] $ ssh-add -X
Enter lock password:
Agent unlocked.
[hell@local] $
Reenviar el agente
Probablemente más de una vez tengamos que acceder a un equipo
remoto que se encuentra dentro de una red privada (LAN), pero
tendremos que hacerlo a través de un router, primero conectando
al router, y después conectando al equipo remoto. Estableciendo
así dos conexiones, una entre el equipo local y el router, y la
otra entre el router y el equipo remoto dentro de la LAN.
El agente ssh será capaz de autenticarnos en la primera conexión
sin ningún problema, pero para que nos autentique en la segunda,
este deberá ser reenviado a través de la primera conexión.
Por defecto, OpenSSH no está configurado para reenviar el agente,
con lo que habrá que ejecutar el cliente con la opción -A,
por ejemplo, para conectar con el router y reenviarle el agente:
[hell@local] $ ssh -A router.ejemplo.com
[hell@router] $
Ahora, si miramos las variables de entorno, podemos apreciar
como en la sesión que hemos abierto en el router, efectivamente
el agente se ha reenviado:
[hell@router] $ echo $SSH_AUTH_SOCK
/tmp/ssh-sQwzt23661/agent.23661
[hell@router] $
¿Cómo funciona esto realmente? Bien, al iniciar la sesión interactiva
en el router, el cliente de SSH crea un socket UNIX y establece la
variable SSH_AUTH_SOCK apuntando hacia el socket. Cuando
se ejecuta otro cliente de SSH para conectar desde el router a otro
equipo, el nuevo cliente de SSH se conecta a ese socket, y el primer
cliente de SSH reenvia todo lo que recibe por ese socket hacia el
agente que se esta ejecutando en nuestro equipo local.
Gracias a este mecanismo, podemos hacer que nuestro agente
nos siga autenticando en el resto de equipos a los que nos
conectemos, de alguna manera, es como si el agente pudiese
viajar con nosotros. Por ejemplo, si desde la sesión interactiva
del router queremos conectarnos a un equipo dentro de la LAN
llamado remoto.ejemplo.com, podremos poner algo
como lo siguiente:
[hell@router] $ ssh -A remoto.ejemplo.com
[hell@remoto] $ echo $SSH_AUTH_SOCK
/tmp/ssh-OsvWx13346/agent.13346
[hell@remoto] $
Se puede apreciar, que se ha creado un nuevo socket UNIX y se
ha establecido la variable SSH_AUTH_SOCK apuntando
hacia el, lo cual permitirá que nuestro agente, que esta
ejecutandose en nuestro equipo local, pueda seguir autenticandonos
en las conexiones que realicemos desde
remoto.ejemplo.com.
Tiempo de vida para llaves
Se puede hacer que el agente recuerde las llaves durante un
tiempo determinado, a partir del cual, el agente las olvidará.
Por defecto, el agente no olvida las llaves núnca (mientras
se siga ejecutando, o no se le fuerce a olvidarlas),
pero se le puede especificar la opción -t seguida
del tiempo de vida que se quiere que tengan las llaves:
[hell@local] $ ssh-agent -t 60 bash
[hell@local] $
El comando anterior inicia un nuevo shell bash con el agente,
y establece el límite de vida de las llaves por defecto en 60
segundos. Pero también se puede establecer un tiempo límite
de vida para cada llave al añadirla con el comando
ssh-add, la forma de hacerlo, es la misma, especificando
la opción -t seguida del tiempo límite de vida
que se quiere que tenga la llave, por ejemplo:
[hell@local] $ ssh-add -t 120 remote
Enter passphrase for remote:
Identity added: remote (remote)
Lifetime set to 120 seconds
[hell@local] $
De esta forma, pasados dos minutos, el agente eliminará la
llave remote de su memoria y no podremos seguir
empleandola para autenticarnos a no ser que se la añadamos
de nuevo al agente.