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:
- Cada server tiene una clave RSA de 1024 bits. Esta clave es
específica del servidor.
- 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
- Instalar.
- Configurar para forzar RSA.
- Generara la clave con ssh-keygen.
- Protegerla con una passphrase.
- Copiar la mitad publica a la máquina que queremos acceder.
- 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:
- El usuario que va a usar el programa dispara el ssh-agent y agrega su
passphrase.
- En la configuración del ssh (/etc/ssh/ssh_config) se agrega:
Host host
LocalForward port_local host:port
- El usuario establece una conexión ssh contra el host.
- 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:
- En el /etc/ssh/sshd_config del servidor.
- 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.
- 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:
- 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.
- 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