24x7 Linux Página personal y profesional HTML 4.01 válido CSS válido
Funcionamiento y uso básico de OpenSSH

Como ha podido leer en la página principal de la sección, OpenSSH es una implementación libre de los protocolos SSH versión 1 y 2, que consta de diversos programas mediante los cuales podemos llevar a cabo administración remota de sistemas, copias de archivos, ejecución remota de comandos, etc. todo ello de manera segura puesto que los usuarios, contraseñas y los propios datos de cada sesión viajan cifrados entre los interlocutores.

Por los motivos de seguridad ya expuestos en este artículo vamos a trabajar siempre usando la versión 2 del protocolo SSH, y de hecho algunas de las configuraciones que llevemos a cabo no podrán efectuarse en caso de usar la versión 1. En concreto, vamos a ver cómo ingresar en un sistema remoto para administrarlo, cómo ejecutar comandos en una máquina remota, cómo copiar archivos, y métodos para restringir qué usuarios y máquinas tiene acceso a nuestro servidor.

Pero antes, un poco de teoría. La definición completa de cómo funciona el protocolo SSH se puede consultar en este documento. Como puede ver, no se trata (aún) de un estándar, sino de un Internet Draft, que es uno de los estadios previos en los trabajos de estandarización que lleva a cabo el IETF (Internet Engineering Task Force). Lo que nos interesa principalmente de este documento es conocer cómo funciona el mecanismo de identificación entre los extremos de una conexión SSH.

El cliente inicia la conexión hacia un puerto predeterminado del servidor, en este caso el puerto reservado número 22. Establecida la conexión TCP los extremos se envían, en texto plano, sus identificadores de versión, para ver si la comunicación es posible entre ellos. De ser compatibles entre sí cambian a un modo de transferencia binaria durante el cual se identifican los interlocutores y se negocian los protocolos de cifrado y firma que se usarán.

Esta negociación de protocolos se lleva a cabo de tal manera que la información intercambiada entre cliente y servidor sólo puede ser descifrada por el destinatario legítimo de la misma, y sólo por él. Para ello al principio de esta fase el servidor envía al cliente su clave pública, para que el cliente la use para cifrar todos los mensajes siguientes hacia el servidor, haciéndolos sólo legibles para el propio servidor. Entonces el cliente genera una clave de cifrado para la sesión en curso, que envía cifrada al servidor junto con el algoritmo seleccionado, y a partir de este momento todo el tráfico entre cliente y servidor viaja cifrado y seguro.

Llegados a este punto el cliente y el servidor se han puesto de acuerdo en un cierto algoritmo de cifrado, han acordado una clave para su sesión, y el servidor se ha identificado ante el cliente con su clave pública RSA (u opcionalmente DSA, en la versión 2 del protocolo). Pero aún falta que el cliente se identifique ante el servidor, que deberá decidir entonces si deja o no aceder al cliente remoto. La identificación del cliente podrá ser mediante pareja de usuario y contraseña, rhost, rhost más identificación de máquina mediante clave RSA o simplemente mediante claves RSA o DSA.

De todos los mecanismos sólo nos interesan el primero y el último. En el primero, el servidor nos dará acceso siempre y cuando la pareja de usuario y contraseña introducida por el cliente sean los de un usuario válido en el sistema operativo del servidor, salvo restricciones adicionales. En la identificación por claves el cliente dispone, al igual que el servidor, de su propia pareja de claves, que envía al servidor durante la identificación. Si el servidor confía en el usuario cuya clave ha recibido, entonces le dará permiso de acceso.

A grandes rasgos el mecanismo de conexión y de identificación del protocolo SSH es sencillo, aunque conviene destacar un par de aspectos relevantes de cara al uso y configuración de los programas relacionados:

  • Las conexiones las inicia siempre el cliente hacia el puerto TCP número 22 del servidor.
  • Las versiones de los protocolos soportados por cliente y servidor deben coincidir, o la conexión no se llevará a cabo.
  • El servidor siempre necesita disponer de una pareja de claves, puesto que usa la clave pública para establecer la conexión segura con el cliente. El tipo de clave tiene que estar soportado en el cliente.
  • El cliente y el servidor deben tener algún algortimo de cifrado en común, o la conexión no se establecerá.
  • Dependiendo de los mecanismos de identificación soportados por ambas partes, o exigidos por el servidor, la identificación podrá o no fallar.
  • Aún cuando haya un mecanismo de identificación soportado por ambas partes, el cliente deberá proporcionar credenciales válidas al servidor, bien sea una pareja de usuario y contraseña, bien una clave pública.

Al margen de todo lo anterior, el servidor del protocolo SSH, sshd podrá implementar restricciones adicionales de acceso, así como el propio sistema operativo donde ejecuta. Por ejemplo, no es infrecuente que sshd esté compilado con soporte de TCP-wrappers, que esté configurado para aceptar o rechazar el acceso a determinados usuarios o máquinas cliente, o simplemente que algún firewall intermedio no permita establecer la conexión.

Dejemos la teoría y pasemos a la práctica. En un primer lugar vamos a centrarnos en saber cómo podemos ingresar en una máquina remota con servidor SSH previamente configurado. Para ello usaremos el comando cliente del protocolo SSH, ssh:

usuario@cliente:/tmp$ ssh -l usuario servidor
The authenticity of host 'servidor (192.168.1.101)' can't be established.
RSA key fingerprint is e9:df:72:7e:28:2c:eb:1d:bf:b2:3a:38:96:2a:3b:6b.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'servidor,192.168.1.101' (RSA) to the list of known hosts.
usuario@servidor's password: 
Last login: Fri Jan  3 15:24:33 2003 from usuario
usuario@servidor:~$ 

El comando ssh -l usuario servidor intenta conectar al servidor remoto como usuario. Si no indicamos la opción -l y el usuario, ssh intentará identificarse como el usuario que ejecuta el comando (que puede o no existir en la máquina en la que deseamos ingresar). Como se ve en la salida del comando se nos solicita la contraseña en servidor. Si tecleamos la contraseña correspondiente, y no hay restricciones adicionales, obtendremos un prompt del sistema remoto, donde podremos operar como si estuviésemos sentados en la propia consola de servidor.

Sin embargo, lo más importante de esta conexión es el mensaje acerca de que la identidad de servidor no se puede determinar con total seguridad. Si recuerda el diálogo entre cliente y servidor descrito al principio, el servidor al inicio de la conexión envía al cliente, en texto claro, su clave pública. Pero debido a que el programa cliente no puede determinar si esa clave es realmente la del servidor remoto, o la de un impostor que ha interceptado la conexión, el programa pide al usuario que confirme si el fingerprint (o huella digital) de dicha clave se corresponde con el de la clave del servidor real. Y la única manera de confirmarlo es obtener una copia de esta huella por medios seguros alternativos: correo electrónico cifrado, por teléfono, correo postal, en persona, etc. En cualquier caso, si confiamos en la clave ésta se añadirá al archivo $HOME/.ssh/known_hosts.

La idea, al igual que se comentó en este artículo acerca de slrn y GnuPG, es no confiar inicialmente en la identidad de un impostor creyendo que estamos contactando con el interlocutor real. Si confiamos inicialmente en el impostor, siempre creeremos estar conectando con el servidor real. Si confiamos en el servidor real, podremos detectar a los posibles impostores que suplanten la identidad del servidor real. En cualquiera de los dos casos, si ssh intenta conectar con un servidor remoto y su clave pública ha variado desde la ocasión donde confiamos inicialmente en ella, ssh comparará la nueva clave con la asociada al servidor y almacenada en $HOME/.ssh/known_hosts, verá que difieren y nos notificará el hecho de manera bastante explícita:

usuario@cliente:/tmp$ ssh -l usuario servidor
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
3e:21:2a:2e:ab:6c:d6:a9:d5:f0:06:35:5c:36:5d:89.
Please contact your system administrator.
Add correct host key in /home/usuario/.ssh/known_hosts to get rid of this message.
Offending key in /home/usuario/.ssh/known_hosts:5
RSA host key for servidor has changed and you have requested strict checking.
Host key verification failed.
usuario@cliente:/tmp$ 

Otra posibilidad consiste en ejecutar comandos en la máquina remota sin llegar a obtener una shell. Para ello basta añadir a continuación de cualquiera de los comandos anteriores el que deseamos ejecutar en el servidor remoto una vez nos hayamos identificado ante él:

usuario@cliente:/tmp$ ssh -l usuario servidor "cat /etc/hostname"
usuario@servidor's password: 
servidor
usuario@cliente:/tmp$

Dese cuenta que posiblemente necesitará citar (usando comillas dobles como en el fragmento de código mostrado) el comando que ejecutar en el servidor remoto en el caso de que contenga caracteres especiales de la shell local, cuya interpretación debamos evitar en la máquina cliente.

Con una sintaxis similar a la que hemos estado viendo hasta ahora también podremos copiar archivos y directorios entre máquinas con cliente y servidor SSH instalado. Podrá copiar archivos desde la máquina local a la remota, y desde la máquina remota a la local, y en ambos casos en el extremo remoto deberá estar funcionando y con el servidor sshd correctamente configurado.

usuario@cliente:/tmp$ scp root@servidor:/boot/kernel-2.4.20 /tmp/
root@servidor's password:
kernel-2.4.20 100% |*****************************|   837 KB    00:02    
usuario@cliente:/tmp$ 

El comando que usar en este caso es scp (Secure CoPy). Puede consultar la sintaxis y opciones completas en su página de manual, pero lo importante es saber cómo especificar los archivos remotos que copiar. La cadena root@servidor:/boot/kernel-2.4.20 indica "conecta a la máquina servidor como usuario root, y copia el archivo /boot/kernel-2.4.20". El último parámetro del comando mostrado indica el directorio o nombre del archivo en el destino. Si en lugar de copiar un archivo remoto a la máquina local queremos copiar un archivo local a una máquina remota:

usuario@cliente:/tmp$ scp /etc/services root@servidor:/tmp/
usuario@servidor's password:
services             100% |*****************************| 16651       00:00    
usuario@cliente:/tmp$ 

Sólo dos aclaraciones: la primera, que aunque estemos conectando como usuario root al servidor remoto, el peligro no es alto, puesto que la contraseña viaja cifrada por la red. Segundo, que la opción -r permite realizar copias recursivas de archivos, pudiendo servir para realizar copias de seguridad remotas seguras de manera sencilla (pero poco eficiente). Para finalizar esta parte del artículo conviene indicar que hay ciertas opciones de configuración de ssh que podemos predeterminar mediante archivos de configuración. En el archivo /etc/ssh/ssh_config podemos poner opciones globales (aplicables a todos los usuarios locales del sistema), mientras que en los archivos $HOME/.ssh/config las personalizaciones de cada usuario.

Hasta ahora hemos visto cómo usar varios de los comandos incluidos con el paquete OpenSSH con identificación mediante usuario y contraseña, pero sabemos que además podemos identificarnos ante el servidor mediante claves públicas, haciendo posible la identificación automática de usuarios. Para configurar este tipo de acceso (especialmente útil para la ejecución remota de comandos, como copias de seguridad) necesitamos saber cómo configurar el servidor al que deseamos acceder, y generar las claves correspondientes al cliente.

Empezaremos generando las claves que identifican al usuario ante los servidores, al margen del posible uso (adicional o alternativo) de contraseñas. Para generar las parejas de claves de los usuarios, o la del servidor, se dispone del comando ssh-keygen. Como ya hemos dicho las claves se generan por parejas, puesto que son claves de tipo RSA o DSA, en las cuales lo que una de ellas cifra, sólo puede descifrarlo la otra, y viceversa. Aparte del tipo, las claves tienen una longitud (en bits, cuanto mayor, más invulnerable será la información cifrada con ella) y, opcionalmente, una contraseña que protege el acceso a la clave privada aunque se disponga de acceso físico al archivo que la contiene.

usuario@cliente:~$ ssh-keygen -b 1024 -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/home/usuario/.ssh/id_dsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/usuario/.ssh/id_dsa.
Your public key has been saved in /home/usuario/.ssh/id_dsa.pub.
The key fingerprint is:
b2:51:fe:77:85:82:c1:21:27:10:0b:58:a8:dc:31:df accesoinet@dardhal
usuario@cliente:~$ 

Con el comando anterior hemos generado una clave de tipo DSA con una longitud de clave de 1024 bits (suficiente de momento para la mayoría de las situaciones) y hemos dejado que ssh-keygen almacene las claves pública y privada en las localizaciones predeterminadas de nuestro usuario. La clave pública será la que el cliente envíe al servidor cuando configuremos la identificación mediante claves. Dese cuenta en la penúltima línea del fingerprint de la clave pública recién generada. Si con posterioridad desea averiguar la huella de una determinada clave pública podrá usar ssh-keygen como se muestra a continuación:

usuario@servidor:~$ ssh-keygen -l -f /etc/ssh/ssh_host_dsa_key.pub 
1024 03:dd:a1:a9:ae:81:12:18:b7:d5:9e:eb:65:61:c4:a1 /etc/ssh/ssh_host_dsa_key.pub
usuario@servidor:~$ 

Con el comando anterior hemos averiguado la huella de la clave pública indicada en la línea de comandos, en principio la del servidor sshd. La localización de los archivos con las claves, y el nombre de dichos archivos es indiferente siempre que el servidor pueda encontrarlos por sí mismo, o indicándoselo en su archivo de configuración, típicamente /etc/ssh/sshd_config.

Es precisamente el archivo anterior el que debemos modificar en caso necesario para activar en el servidor sshd el soporte para identificación mediante claves públicas. En concreto, debemos tener (entre otras opciones no relacionadas) las siguientes opciones en el archivo /etc/ssh/sshd_config:

# Activa la identificación por clave pública para la versión 2 de SSH
PubkeyAuthentication yes
# Podemos poner a "no" la identificación mediante contraseñas en caso necesario
PasswordAuthentication yes

Una vez reiniciado el servidor ya sería posible entrar al servidor usando claves públicas por parte del cliente, siempre y cuando le digamos al servidor qué claves públicas son de confianza, y a qué usuarios locales deben dar acceso. Para que en el servidor un usuario remoto identificado por clave pueda acceder como otro usuario al propio servidor es neceario editar (y crear en caso necesario) el archivo $HOME/.ssh/authorized_keys. Por ejemplo, para permitir el acceso como root al servidor a un usuario concreto, deberíamos editar el archivo /root/.ssh/authorized_keys.

Este archivo debe contener, todo en la misma línea, el contenido literal del archivo con la clave pública del usuario remoto al que deseamos dar permiso de acceso como root. Por ejemplo:

usuario@cliente:~$ cat /home/cliente/.ssh/id_dsa.pub 
ssh-dss AAAAB3NzaC1kc3MAAACBAIbJthkQwq4A8lhhbtARv . . . . . . Jl/FZrfRzxf5c9Q== usuario@cliente
usuario@cliente:~$ 

servidor:/tmp# cat /root/.ssh/authorized_keys
ssh-dss AAAAB3NzaC1kc3MAAACBAIbJthkQwq4A8lhhbtARv . . . . . . Jl/FZrfRzxf5c9Q== usuario@cliente
servidor:/tmp# 

A partir de este momento usuario en la máquina cliente podrá ingresar como root en la máquina servidor sin necesidad de suministrar contraseña alguna (salvo si usuario protegió su clave privada).

usuario@cliente:/tmp$ ssh -l root servidor
Enter passphrase for key '/home/usuario/.ssh/id_dsa': 
Last login: Fri Jan  3 19:17:36 2003 from usuario
servidor:~# 

Es evidente que proteger nuestra clave privada con contraseña mejora la seguridad, a costa de impedir automatizar la entrada en las máquinas remotas, especialmente interesante para ejecución automática y desatendida de comandos. Además, en la configuración del cliente ssh podemos indicar de manera explícita mediante qué métodos nos queremos identificar ante cada servidor remoto, y en qué orden intentar cada método. Consulte las páginas de manual de ssh y ssh_config, y busque la directiva PreferredAuthentications para más información.

Por último, y al respecto de la identificación mediante claves públicas, decir que es posible restringir desde qué máquinas resulta válida cada clave, para evitar en la medida de lo posible que la sustracción de la misma dé acceso al servidor remoto (bien es cierto que las claves privadas hay que protegerlas con extremo cuidado, y siempre que sea posible protegidas por contraseña). Para ello basta con editar el archivo $HOME/.ssh/authorized_keys correspondiente, y anteponer a la clave en cuestión la opción from="nombre_máquina":

servidor:/tmp# cat /root/.ssh/authorized_keys
from="cliente" ssh-dss AAAAB3NzaC1kc3MAAACBA . . . . . . Jl/FZrfRzxf5c9Q== usuario@cliente
servidor:/tmp#

De esta manera, y aunque alguien consiga robar el archivo que contiene la clave privada del usuario legítimo, para entrar al servidor además debería convencerle de que su dirección IP coincide con la de la máquina cliente, lo que obligaría al atacante a tomar el control del servidor DNS que da servicio al dominio en que se encuentra cliente. Si además la citada clave privada está protegida por una frase de paso no predecible, las garantías de seguridad son considerablemente elevadas.

Existen algunas opciones de interés además de la ya mencionada para el archivo authorized_keys, que están documentadas en la página de manual de sshd. Por ejemplo, se puede limitar qué único comando se puede usar si nos identificamos con una determinada clave, o a qué único puerto y dirección IP podemos establecer un túnel de aplicación.

Última modificación: 11-January-2003 13:23:18 -0500

© 2002-2007 José Luis Domingo López. Todos los derechos reservados.
Contacte con el webmaster para informarle de fallos, incorrecciones o sugerencias.
Esta página cumple con los estándares HTML 4.01 y CSS2 del World Wide Web Consortium.