BALUG (8950 bytes)

SSH (Secure SHell)

Este es el machete que usé para dar la charla. Es bastante completo aunque, obviamente, no tiene ciertas aclaraciones se dieron en la charla en respuesta a las preguntas que surgieron.

Es un programa que permite logearse en una máquina remota tal como lo haríamos con telnet, de hecho reemplaza a rlogin y rsh (remote login y remote shell). A diferencia de estos protocolos SSH utiliza un canal de comunicación encriptado y mecanismos de validación de usuarios bastante sofisticados.
Otro uso es el de crear un túnel para hacer seguro un protocolo antiguo que no posee encripción. Eso lo vamos a ver con más detalle después.
Un par de herramientas que trae sirven para copiar archivos y para evitar tener que validarse repetidamente.

Toda la comunicación se lleva a cabo utilizando un canal encriptado. El canal se establece de la siguiente manera:

  1. Cada server tiene una clave RSA de 1024 bits. Esta clave es específica del servidor.
  2. Cada vez que se levanta el sshd o cada hora si es que fue usado (aún cuando haya sido solo para rechazar una conexión!) genera una clave RSA de 768 bits. Esta jamás se guarda a disco.
En este punto se explicó un poco el mecanismo de public keys.
Algo de esto se explica en la validación de usuarios.
Cuando un cliente se conecta con el server este le pasa sus claves públicas, [el server manda (1)-pub y (2)-pub] el cliente compara la clave del host con una base de datos (un archivo de texto) para ver si es correcta. Las claves se guardan en /etc/ssh_known_hosts (global) y en ~/.ssh/known_hosts. Si la clave no coincide o jamás se ha establecido una conexión contra ese server el cliente pide confirmación al usuario antes de continuar.

Esto se des/habilita con CheckHostIP yes en /etc/ssh/sshd_config.

A continuación el cliente genera un número aleatorio de 256 bits y lo encripta con las claves (1) y (2) y se lo envía al servidor. La única manera de leer este número es conociendo la clave privada del server ((1)-priv) y la clave privada del sshd ((2)-priv) que jamás se guarda a disco y que es descartada una vez por hora.
Desde este momento en adelante todo diálogo se hace encriptando las cosas con el número de 256 bits. Se usa 3DES o Blowfish (IDEA en versiones comerciales, pero está cubierta por patentes). El server ofrece las encripciones que soporta y el cliente elige una. OpenSSH usa 3DES por default.
A continuación se pasa a validar el usuario, SSH usa varios mecanismos y por lo tanto hay varias maneras de usarlo, en esta charla voy a enfocar el uso de la manera que yo lo uso. Algunos de los métodos son simplemente para guardar compatibilidad con rlogin y rsh y de hecho no son recomendados. El sistema que yo uso es basado en claves públicas y privadas.
Algo de OpenSSH

OpenSSH se basa en free SSH pero no contiene ningún código patentado o con licensias. Para las cosas que lo necesitan usa librerías externas. Hasta el 21/9 RSA estaba patentado en USA por lo que el uso en USA tenía algunos recaudos, pero ya venció. Algo similar pasa con la encripción strong que usa, por eso el desarrollo se hace mayormente fuera de USA.

Según "SSH Communications Security" SSH2 es free solo para aplicaciones no comerciales.

SSH no puede ser exportado desde USA.

Las patentes en USA de RSA expiraron.

Las patentes de IDEA siguen y OpenSSH no lo soporta.

SSH1 es más vulnerable que SSH2.

Este método usa el mecanismo RSA que hasta hace unos días estaba cubierto por patentes en USA y por lo tanto generaba problemas de uso. De hecho el paquete OpenSSH de Debian es parte de la distribución "non-us".
La idea básica de este mecanismo es que cada usuario crea dos medias claves, una es la pública que puede darse a conocer sin problemas y la otra es la privada que tiene que ser protegida a toda costa. Esta clave se genera usando el comando ssh-keygen. Esto genera las passwords en forma aleatoria y guarda la privada en ~/.ssh/identity y la pública en ~/.ssh/identity.pub. La pública se guarda en texto plano ya que es la que daremos a conocer y la privada se encripta y guarda en forma binaria. El largo de la clave es 1024 bits por defecto lo cual es bastante seguro hoy día y por varios años. La clave se encripta usando 3DES, Blowfish u otro mecanismo de encriptado que sea en una sola dirección. OpenSSH por defecto encripta con 3DES, para proteger la clave privada se usa una passphrase de entre 10 y 30 caracteres que no sea simple de deducir o inferir. Esa frase se pasa por una hash para obtener la cantidad de bits necesarios y se usa para encriptar la mitad privada.
Para darle acceso a alguien a una cuenta a través de ssh se debe copiar la mitad pública de la clave de esa persona en ~/.ssh/authorized_keys de la cuenta a la que se le quiere dar acceso. Como ven el uso de la mitad pública es darle acceso a su dueño y no sirve para lograr acceso a su máquina.
A la hora de conectarnos con otra máquina usando ssh usamos:

ssh usuario@maquina SSH establece la conexión y luego pasa a validarnos para ello utiliza RSA. Basicamente la idea es (lease: la implementación no es exactamente esta pero se basa en este mecanismo) que el sistema remoto encripta algo secreto con la mitad pública de la clave y nos pasa el resultado, solo podemos obtener la respuesta correcta si poseemos la mitad privada de la clave. Por lo que nuestro lado (cliente) debe acceder a la clave privada. Para esto ssh nos pide la passphrase con la cual desencriptará la media clave. De esta manera aunque alguien lograra acceso a nuestra máquina y nos robara nuestro ~/.ssh/identity no le serviría de nada ya que no posee la passphrase para desencriptarla.
Como este no es el único mecanismo que soporta ssh, también soporta passwords regulares de UNIX enviadas sobre el canal encriptado, lo más aconsejable es limitarlo a esto, para eso hay que editar el archivo /etc/ssh/sshd_config para que diga:

PasswordAuthentication no De esta manera solo aceptará validación por RSA.
Una vez que el usuario fue validado se realiza una negociación para seleccionar detalles del tipo de conexión. Finalmente se ejecuta un shell u opcionalmente un comando pasado por la línea de comandos.

Este es el uso más simple y que nos permite controlar una máquina remota con un grado razonable de seguridad. Otro uso muy común es de copiar archivos. Hay una versión comercial que incluye un ftp sobre este mecanismo y que la gente de OpenSSH estuvo desculando para incluirlo, pero lo que figura en el standard es un comando que se llama scp, el mismo es un reemplazo para rcp. La sintaxis es:

scp usuario@host:path/file usuario@host:path/file
En pocas palabras

  1. Instalar.
  2. Configurar para forzar RSA.
  3. Generara la clave con ssh-keygen.
  4. Protegerla con una passphrase.
  5. Copiar la mitad publica a la máquina que queremos acceder.
  6. ssh usuario@maquina
Un problema que aparece con ssh es que tenemos que ingresar nuestra passphrase cada vez que queremos ser validados, esto puede volverse bastante tedioso si estamos compilando un programa en forma local, transfiriéndolo a otra máquina y ejecutándolo en forma remota (con una sesión ssh obviamente). Esto me ha pasado con un programa de control que corre sobre una 486 DX2 66 donde sería tortuoso compilar el programa, es más simple hacerlo en una Pentium II 233 y luego de debugearlo enviarlo. Para evitar el ingreso repetido de la passphrase existe una utilidad que se llama ssh-agent.
La idea es que uno ejecuta ssh-agent diciéndole que shell vamos a correr o disparando X. Es decir: la idea es que el X o el shell que normalmente usamos sea un proceso hijo de ssh-agent. Para el uso que yo le doy este esquema es incómodo, pero veamos primero esto que es el uso que recomienda la man page.
El ssh-agent crea un socket con nombre (o pipe). A través de este socket se hacen las sub-siguientes comunicaciones. El socket se crea en /tmp y los permisos se setean para que sea accesible unicamente por el usuario que lo creó (el root puede abusar obviamente). Para que otros procesos puedan comunicarse el agent define una variable de entorno SSH_AUTH_SOCK.
Inicialmente el agent no conoce ninguna clave, para que las memorice se usa el comando ssh-add usuario@host luego se nos pide la passphrase y si es válida ssh-add obtiene la clave privada y se la pasa al agent a través del socket. Con ssh-add -l se puede obtener una lista.
El agent puede memorizar varias claves y cada vez que iniciamos una conexión con ssh primero se consulta al agent y si el mismo posee la clave no es necesario que ingresemos la passphrase.
Las claves nunca se transmiten a través de la red. Si es posible hacer ssh a una máquina donde tengamos claves que necesitamos para a su vez saltar a otra. O pedir un túnel para esto.
Una vez que el proceso que disparó el agent muere también lo hace el agent desapareciendo las claves.
Otra forma de usarlo es: una vez que ya estamos logeados y sin necesidad de crear otra instancia del shell simplemente correr ssh-agent sin especificar nada más. En este caso ssh-agent queda corriendo en background y nos imprime en pantalla las variables de entorno que necesitamos tipear para que otros procesos se comuniquen con él. Son dos variables de entorno, una es para conocer el socket y otra para saber el PID del agent. Esto último es necesario para matarlo, una vez que no lo queremos más simplemente corremos ssh-agent -k y el mismo se mata (usando SSH_AGENT_PID). El resto de uso es exactamente igual.
El único problema es que necesitamos tipear (cut & paste de hecho) las variables de entorno, para evitarlo se pueden usar estos scripts:

<--------- ssh-a

#!/bin/sh ssh-agent > ~/.agent.sh . ~/.agent.sh ssh-add <----------

y

<--------- ssh-k

#!/bin/sh ssh-agent -k > ~/.agent.sh . ~/.agent.sh <----------

Los scripts se corren con . ssh-a y . ssh-k para que sean interpretados por el bash que estamos corriendo y no por una nueva instancia. De esta manera las variables de entorno quedan definidas en el shell que estamos corriendo.

Otro uso muy interesante de ssh es el de agregar seguridad a un protocolo que originalmente no tenía seguridad alguna. En mi caso yo tengo un banco de mezcla de gases que se puede controlar en forma remota. El demonio solo usa TCP-wrappers para validar la máquina que se está conectando.

<-------- /etc/hosts.allow

ALL: 123.45.67.68 in.smtpd: 123.45.67.13 exim: 123.45.67.13 sshd: .inti.gov.ar gextd: 127.0.0.1 123.45.67.18 named: ALL <--------

<-------- /etc/hosts.deny

ALL: ALL <--------

Pero lo que se envía a través de esa conexión está totalmente expuesto. Para evitar que esa información se exponga podemos usar ssh de la siguiente manera:

ssh -L port_local:host:port_remoto usuario@host Esto abre una conexión al host especificado con el usuario pedido. Luego de establecerse la conexión y validarse el usuario ssh dispara un shell del otro lado. La única diferencia es que a partir de ese comento cualquier conexión local al port port_local es forwardeada a través del canal encriptado al host nombrado:port_remoto.
Lo mismo puede especificarse para un port remoto con:

-R port_remoto:host:port Este mecanismo se puede hacer para cualquier port, pero solo el root puede hacerlo para los ports privilegiados.
Para automatizar esto no vi nada del todo simple. Una opción es:

  1. El usuario que va a usar el programa dispara el ssh-agent y agrega su passphrase.
  2. En la configuración del ssh (/etc/ssh/ssh_config) se agrega: Host host LocalForward port_local host:port
  3. El usuario establece una conexión ssh contra el host.
  4. El usuario corre en forma local el proceso que usa dicho protocolo y le indica conectarse contra 127.0.0.1:port_local.
La mayor incomodidad radica en el hecho de que el túnel tiene que establecerse y para eso es necesario que el usuario realice una conexión ssh. No es necesario que del otro lado dispare un shell, pero tiene que disparar algo que retenga el canal abierto.
Si alguien sabe o encuentra una forma más cómoda de establecer el canal bajo demanda que me lo diga ;-). Por supuesto que se puede automatizar con scripts.
Una vez establecido este mecanismo se puede restringir el acceso a nuestro servicio solo a la computadora donde corre el demonio. Esto se debe a que el túnel le hace pensar al demonio que la conexión es local. De esta manera la única manera de usar el servicio en forma remota es a través del túnel. Esto se configura /etc/hosts.allow.

También se puede usar para hacer forward de la conexión a X11. Para ello hay que habilitarlo:

  1. En el /etc/ssh/sshd_config del servidor.
  2. En el /etc/ssh/ssh_config especificando que servidor/es tienen permiso para usar nuestro X:

    Host host ForwardX11 yes Se puede indicar desde la línea de comandos.
  3. Luego al logearnos ssh crea una variable del tipo DISPLAY=local:10.0 (los valores dependen de lo que esté libre) y las aplicaciones que corramos se conectan a este display que no es más que un proxy.
Ojo!!! el root de la máquina remota puede abusar del proxy y acceder a nuestro X. La man page recomienda hacerlo solo si se confía en el administrador de la otr máquina.

Otras cositas:

  1. make-ssh-known-hosts es un script para generar la base de known_hosts, se puede correr luego de instalar ssh para que escanee una o más redes buscando por demonios de ssh y los agregue a la lista.
  2. OpenSSH soporta compresión. El protocolo es sumamente pesado y cuando se usan conexiones muy lenta aunque las máquinas sean rápidas se torna pesado. Para aliviar esto se puede habilitar compresión (con libz) y especificar el nivel de compresión. Todo se encripta, no solo la transferencia de archivos.

URLs:


Ir a: Home page

Texto by SET. Copywrong (w) 2000. Handmade HTML using SETEDIT SourceForge.net Logo