|
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
|