jueves, 7 de julio de 2011

Bondades del SSH: acceso a Internet, transferencia de información, túneles,...

Si nos encontramos en alguna red privada, es probable que la configuración de red (entre proxy y firewalls) tenga restringido los servicios de acceso a redes externas, particularmente se va a tratar el servicio SSH en este artículo. El servicio SSH por defecto sale por el puerto 22, uno de los puertos que generalmente se encuentran bloqueados, precisamente por las libertades de acceso y funcionalidades que un servicio SSH ofrece, algunas de estas libertades resultan ser:
  1. Tengo acceso total a una máquina externa, la cual puedo controlar a mi antojo (asumiendo usuarios con permisos suficientes desde luego).
  2. A esta máquina externa le puedo enviar y recibir información de ella, una de las principales preocupaciones de las redes privadas, en especial las corporativas.
  3. Como si fuera poco lo anteriormente mencionado, puedo incluso crear un proxy que me permita salir a otra red por medio de esta máquina. Esta red puede ser cualquiera a la que la máquina externa tenga acceso, generalmente se aprovecha para acceder a la red de redes, Internet.
Teniendo en cuenta todas estas bondades (entre otras), uno se puede ver tentado a crear un acceso a este servicio para evitar restricciones y saltarse un poco las reglas. Una forma de lograrlo es creando un túnel (en inglés conocido como tunneling).

Como primera medida es posible cambiar el puerto de salida desde nuestro servidor, raramente encontraremos el puerto 22 abierto en una red restringida, pero es probable que algunos puertos de común uso no presente este tipo de restricciones, uno que muy probablemente se encontrará libre para acceso es el puerto 443, que generalmente se usa para conexiones HTTP seguras, más conocidas como HTTPS. Restringir este puerto implicaría limitar el acceso a sitios Web que usen esta configuración de acceso, por esta razón es un buen candidato para el túnel de acceso.

Existen diferentes formas de lograr este cambio de puerto, una de ellas es cambiando la configuración del mismo servicio SSH en el servidor para que salga por un puerto determinado, en Linux distribución Debian específicamente (por lo tanto debería funcionar con distribuciones de uso común como Ubuntu), esto puede lograrse configurando el archivo /etc/ssh/sshd_config. Para revisar el valor actualmente configurado puede usarse

cat /etc/ssh/sshd_config | grep Port
Port 22

(Azul el comando, negro la salida)

Esta configuración puede modificarse y reiniciar el servicio SSH

/etc/init.d/ssh restart
Restarting OpenBSD Secure Shell server: sshd.

Y con esa configuración es generalmente suficiente para tener un servicio SSH corriendo por ese puerto. Otra alternativa, mi preferida en comparación a esta, es la configuración por hardware que puede lograrse directamente en el router que realiza la conexión a internet (uno que generalmente proporciona tu ISP "Internet Service Provider"). Naturalmente es necesario que tengas acceso a dicho dispositivo el cual generalmente viene con acceso protegido con usuario y contraseña. Por medio de un router, es posible direccionar las peticiones con puertos y protocolos específicos, para el caso del presente artículo necesitaríamos redireccionar las conexiones recibidas por el puerto 443 a una máquina local con una IP específica, que tiene a su vez el servicio SSH saliendo por el puerto 22. Cada dispositivo dependiendo de su fabricante, marca y/o modelo, cuenta con su propia manera de configurarlo, en mi caso personal dispongo de un clásico D-Link DI-524, donde puede configurarlo por medio de una característica llamada Virtual Server que se puede acceder desde la pestaña Advanced, botón Virtual Server.
La configuración esencial consiste en darle una IP de la red local donde se encuentra el servidor, en la configuración de ejemplo tengo la 192.168.0.120; además debe indicarse un puerto público y uno privado, el privado será el puerto donde se encuentra publicado el servicio dentro de la red local, para el caso será el puerto 22 donde está el servicio SSH; el puerto público será el de acceso exterior, para el caso será el 443. En resúmen, todo lo que entre por el puerto 443 será redireccionado a la máquina con IP 192.168.0.120 por el puerto 22.

Con esta segunda alternativa de configuración el servicio SSH sigue disponible en el puerto 22, lo que nos da ventajas y comodidades, por ejemplo podemos ingresar a una sesión SSH sin indicar explícitamente puertos desde máquinas configuradas en la misma red local.

Para el uso del servicio SSH en redes externas, la red restringida para el caso, será necesario ingresar por el puerto 443; en el cliente SSH esto se indica por medio de la opción "-p", para probar el acceso simplemente podemos escribir

ssh ip-remota@usuario -p 443

Si logramos este acceso ya tenemos transferencia de información tanto envío y recepción, entre la máquina remota y la máquina local dentro de la red "restringida". Pero en algunos casos, y muy seguramente en una red restringida, no será tan sencillo, porque hay seguramente un proxy que requiere configurarse para tener acceso; pero hay solución al respecto.

Un aliado de SSH llamado Corkscrew, permite realizar conexiones SSH sobre conexiones HTTP y HTTPS, que son regularmente permitidas por servidores proxy. De otra manera sería necesario abrir un socket, lo cual seguramente estaría restringido por el proxy y no sería posible conectarse aún con el servicio activo en un puerto permitido, como el 443 mencionado anteriormente.

Es necesario por lo tanto tener instalado el Corkscrew, en Debian y familiares como Ubuntu, puede instalarse ejecutando como super usuario:

apt-get install corkscrew

La configuración puede hacerse de diferentes formas, pero una recomendada es creando dos archivos de configuración que tendrán los siguientes propósitos:
  1. Un archivo de autenticación con las credenciales necesarias para acceder al proxy
  2. Configuración proxy, donde se indicará un comando de proxy que ejecutará el Corkscrew con sus parámetros asociados.
El primer archivo con las credenciales lo llamé Credenciales, lo ubiqué en ${HOME}/.ssh, y le agregé el siguiente contenido:

usuario:password

Con esa simple línea indicamos el usuario de acceso al proxy, y la contraseña asociada. El segundo archivo con el comando para Corkscrew también tiene una sola línea, lo llamé ConexionProxy y lo ubiqué en ${HOME}/.ssh, pero al igual que el anterior, pueden ubicarse donde se deseé.

ProxyCommand /usr/bin/corkscrew proxy 8080 %h %p $HOME/.ssh/Credenciales

Puede notarse la siguiente información relevante:
  • Puerto donde está configurado el proxy (8080 en el ejemplo).
  • Ruta del archivo con las credenciales de acceso al proxy
Indicada la información anterior, se puede considerar listo el sistema para conectarse. Un acceso SSH por ejemplo, se podría hacer de la siguiente manera

ssh -p 443 -F ${HOME}.ssh/ConexionProxy usuario@ip-remota

La información relevante del comando anterior sería:
  • Puerto por donde saldrá la conexión SSH, 443 en el ejemplo.
  • Archivo con la configuración del comando para Corkscrew.
  • Usuario y dirección IP de la máquina remota donde se conectará al servicio SSH.
Otra tarea importante que quizá querramos realizar es definitivamente la transferencia de archivos, esto lo podemos hacer con el comando "scp", ejemplo:

scp -P 443 -F ${HOME}.ssh/ConexionProxy usuario@ip-remota:/home/usuario/ejemplo.zip .

La instrucción anterior descargaría el archivo /home/usuario/ejemplo.zip, ubicado en la máquina remota al directorio actual en el sistema local. Nótese que el puerto está indicado con "P" mayúscula, a diferencia de la "p" minúscula usada en el comando "ssh".

Finalmente, como si fuera poco ya tener transferencia de información y acceso a un equipo externo donde podremos realizar todo lo que nuestros conocimientos y alcance en materia de línea de comando nos permita; también podremos crear un segundo proxy propio, que nos permita salir a Internet u otra red a la que el equipo externo tenga acceso. Realizando esta configuración no tendremos las restricciones de la red privada, sino que estaremos limitados a lo que pueda acceder la máquina remota, que si es nuestro equipo casero muy seguramente serán ínfimas las restricciones. La instrucción para levantar este servicio de proxy sería algo como lo siguiente:

ssh -p 443 -F ${HOME}.ssh/ConexionProxy -N -D localhost:9999 -l usuario ip-remota

Este comando crea un proxy que estará configurado y disponible en la dirección localhost en el puerto 9999. Ahora podemos dirigirnos a las opciones de nuestro navegador preferido y configurar el acceso usando este proxy virtual, o la configuración global del sistema operativo.

La imágen anterior muestra la configuración en un Firefox para OSX.

En la siguiente ruta http://www.filesavr.com/EVLLBBHI382E6WH, subí un paquete con los archivos de configuración indicados en el ejemplo que fácilmente se pueden modificar para crear un túnel en cualquier red restringida.

Referencias y lecturas recomendadas asociadas al tema

Richfaces: verificación de errores de validación en el cliente

En algunas circunstancias es útil verificar en el cliente (generalmente un navegador) si ocurrieron errores de validación mientras se enviaba información al servidor. Un caso típico es un cuadro de diálogo modalPanel, donde probablemente se tiene un commandButton para ejecutar una acción que requiere validaciones en la información de un form; este tipo de validaciones generalmente son de tipo campos requeridos (required), formatos válidos para el tipo de dato y otros.  Se desea que el cuadro de diálogo no se cierre hasta que la operación sea exitosa o el usuario cancele el cuadro de diálogo


Para llevar a cabo esta verificación, se puede revisar los mensajes de error del contexto faces y dependiendo de los resultados obtenidos se pueden dar los siguientes casos:
  1. Si no hay mensajes de error en el contexto faces, se asume la información se ha enviado y la operación asociada se ha ejecutado satisfactoriamente.
  2. En caso contrario, se asume un error de validación, o de alguna regla de negocio que no permite ejecutar la operación requerida.
Lo anterior es sencillo y del lado del servidor puede verificarse de la siguiente manera:

FacesContext fc = FacesContext.getCurrentInstance();
boolean tieneMensajesError = fc.getMessages(null).hasNext();

if (tieneMensajesError) {
   // Realizar alguna acción si hay mensajes de error
} else {
   // Realizar alguna acción si no hay mensajes de error


¿Cómo hacer esta verificación en el cliente? JavaScript es el mecanísmo más común, usando la API de Richfaces la implementación quedaría de la siguiente forma:

<rich:modalpanel id="ejericioDlg">
 

<a4j:commandButton id="btnGuardar" action="#{algunBean.guardar}" value="Guardar" oncomplete="if (#{facesContext.maximumSeverity==null}) #{rich:component('ejercicioDlg')}.hide();"/>

<a4j:commandButton id="btnCancelar" value="Cancelar" immediate="true"> 

   <rich:componentControl for="ejercicioDlg" operation="hide" event="oncomplete" /> 
</a4j:commandButton>

</rich:modalpanel>

Con lo anterior logramos que el cuadro de diálogo solo se cierre en caso de no presentar errores de validación, que es un comportamiento comunmente deseado.