lunes, abril 27, 2015

Cómo usar Latch para proteger el acceso a sitios web de la Intranet publicados en Internet en un servidor Nginx

La idea de esto surge de la necesidad de poder habilitar bajo demanda la publicación de sitios en mi red hogareña a Internet de una forma simple. De esta forma se evita tener habilitados todo el tiempo en Internet servicios que se usan de forma esporádica, bajando la probabilidad de cualquier intento de acceso no autorizado. Una idea muy sencilla que pretende reducir la superficie de exposición temporal de un servicio en Internet para fortificar un servidor GNU/Linux.

Figura 1: Cómo proteger sitios en NGINX con Latch

¿Qué sale un 0day para las tecnologías que soportan el sitio? Solo tengo que esperar a parchearlo y volver a abrir el acceso. Para hacer esto utilizo Latch y un script que actualiza la configuración del webserver usado como Proxy Reverso que en mi caso es un servidor Nginx. Este es el proceso completo y los scripts que yo he utilizado para montar esta arquitectura de seguridad que publica y des-publica los sitios usando Latch.

Prerrequisitos para instalar el entorno
• Python
• Una cuenta de desarrollador del Latch
• Una cuenta de usuario del Latch
• La librería con el SDK de Latch para Python
• WebServer de su elección: Puede ser Apache Http Server, Nginx, o cualquier otro,  configurado para actuar como Proxy Reverso.
• Un rato de tu tiempo
Antes de ponernos con el paso a paso, les cuento que no tengo experiencia en Python por lo que seguramente encontraran formas más elegantes o eficientes de hacer esto, son bienvenidos los comentarios constructivos.

Integrar Latch con el Servidor Nginx: Creación de la app Latch
Generar una cuenta de desarrollador de Latch: Una vez que tengas una cuenta de Latch, puedes convertirla en una cuenta de desarrollador. Si quieres proteger esta cuenta, ya sabes que también puedes por Latch a los accesos a Latch.
Figura 2: Poner Latch a la cuenta de desarrollador de Latch
• Generar una aplicación: El proceso es sencillo. Esto se hace primero haciendo clic en Mis Aplicaciones.  Luego en el botón Añadir una nueva aplicación. En esa parte del proceso 
Figura 3: Creación de una aplicación en Latch
• Le asignamos un nombre. En mi ejemplo: Mis Webs y luego tomamos nota de los campos remarcados (Id de aplicación y Secreto). Los necesitaremos para aparear nuestra aplicación cliente (en el celular) y el script.
Figura 4: Creación de la aplicación en Latch

Integrar Latch con el Servidor Nginx: Pareo de la aplicación con cuenta Latch

Para parear la aplicación se puede utilizar este pequeño usamos este script de Python que hace el proceso con un sencillo menú para interactuar.
pair.py:

import os
import json
import latch
appid = input("Ingrese el Applicatrion ID:")
seckey = input("Ingrese Secret Key:")

api = latch.Latch(appid,seckey)
pair_code = input("Ingrese el Pairing Code entregado por su celular:")
response = api.pair(pair_code)
responseData = response.get_data()
responseError = response.get_error()

print "Respuesta: ", responseData
if responseError != "" :
 print "Error:" , responseError

try:
    salida=json.dumps(responseData)
except (TypeError, ValueError) as err:
    print 'ERROR:', err

Antes de ejecutar el script debemos iniciar sesión en la aplicación de Latch en el terminal móvil con nuestra cuenta de usuario de Latch y marcar en la opción inferior, Añadir nuevo servicio y luego en el botón: Generar nuevo código. Cuando tengamos el código temporal de pareado visible lanzamos el script pair.py:
~/latch $ python pair.py
Ingrese el Applicatrion ID:"*****hqH3usJ4"Ingrese Secret Key:"j32mbXBfhHUyn*******************CrF"Ingrese el Pairing Code entregado por su celular:"*****Asqg"Respuesta: {u'accountId': u'7z3ZTP6p2H3***************************cHaYwmRQyHrk6JYae'}
Si este paso no muestra errores, la aplicación del terminal móvil indicará que hay un nuevo servicio asociado a nuestra cuenta de Latch. De esta forma hemos apareado la aplicación (nuestro script), con nuestra cuenta de Latch. Tomen nota del accountID, lo necesitaremos más adelante.

Figura 5: Cuenta de Latch pareada con la aplicación

Integrar Latch con el Servidor Nginx: Reverse Proxy permitido o denegado

Llegados a este punto, ya estamos listos para configurar el script que se ejecutara de forma recurrente, cada n minutos por crontab en mi caso, pero que cada uno puede configurar a su gusto. La lógica de este script es:
• Consultar el estado del Latch
• En base al cambio de estado del Latch manipularemos los archivos de configuración de Nginx para que opere como un Proxy Reverso o simplemente como una web con autenticación http.
El primer paso para hacer funcionar este script es reemplazar las tres variables appid, seckey (provistas por la web de latch) y el accid (accountId que anotamos al momento del pareo de la aplicación).
• Luego debemos generar un archivo de configuración de Nginx que bloquee el acceso y otro que lo habilite.
 A continuación un archivo de configuración que configura el Proxy Reverso:
/etc/nginx $ more rproxy.conf.allow
server {
    listen            80;
    return 301 https://$host$request_uri;
}
server {
    listen            443;
    server_name       xxxx.com;

    # Change this to the location you installed lirc_web
    root              /home/nginx/web;
    index             index.html index.htm;

    access_log        /var/log/SSL-rproxy.nginx.log;
    ssl on;
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_session_cache  builtin:1000  shared:SSL:10m;
    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    ssl_prefer_server_ciphers on;
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass https://sitiointerno:443/;
        proxy_redirect off;
    }
    error_page 401 /401.html;
    location ~ (401.html)$ {
      alias /usr/share/nginx/www/$1;
    }

}
Y este que lo inhabilita forzando a autenticarse :
/etc/nginx $ more rproxy.conf.deny
server {
    listen            80;
    return 301 https://$host$request_uri;
}
server {
    listen            443;
    server_name       x.x.x.x;

    # Change this to the location you installed lirc_web
    root              /home/pi/web;
    index             index.html index.htm;

    access_log        /var/log/SSL-rproxy.nginx.log;
    ssl on;
    ssl_certificate /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_session_cache  builtin:1000  shared:SSL:10m;
    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
    ssl_prefer_server_ciphers on;
    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass https://sitiointerno:443/;
        proxy_redirect off;
        auth_basic "Restricted";
        auth_basic_user_file /etc/nginx/.htpasswd;
    }
error_page 401 /401.html;

location ~ (401.html)$ {
    alias /usr/share/nginx/www/$1;
    }
}
Integrar Latch con el Servidor Nginx: Consulta del estado de Latch

Con todos los puntos anteriores revisados y preparados, ya podemos correr este script para confirmar el estado de nuestro Latch:
estado.py:
import os
import json
import latch

appid = "[reemplazar por application id]"
seckey = "[reemplazar por Secret Key];"
accid = "[Reemplazar por account id]"

api = latch.Latch(appid,seckey)
response = api.status(accid)
responseData = response.get_data()
responseError = response.get_error()

#print "Respuesta: ", responseData
if responseError != "" :
 print "Error:" , responseError

try:
    salida=json.dumps(responseData)
except (TypeError, ValueError) as err:
    print 'ERROR:', err

response = json.loads(salida)

estado = response['operations'][appid]['status']
print "Estado:", estado
if estado == "on" :
 print "Ejecutando script de habilitación de Proxy Reverso"
# os.system("diff /etc/nginx/conf.d/rproxy.conf /etc/nginx/rproxy.conf.deny -q >/dev/null && sudo cp -p /etc/nginx/ rproxy.conf.allow /etc/nginx/conf.d/ rproxy.conf && sudo service nginx reload")
else:
 print "Ejecutando script de bloqueo de Proxy Reverso"
# os.system("diff /etc/nginx/conf.d/rproxy.conf /etc/nginx/rproxy.conf.allow -q >/dev/null && sudo cp -p /etc/nginx/rproxy.conf.deny /etc/nginx/conf.d/rproxy.conf && sudo service nginx restart")
Si el resultado es el esperado:
$ python /home/pi/latch/estado.py
Estado: off
Ejecutando script de bloqueo de Proxy Reverso
En este punto sólo es cuestión de descomentar las líneas del script estado.py donde se toma acción sobre el servidor Nginx:
print "Ejecutando script de habilitación de Proxy Reverso"# os.system("diff /etc/nginx/conf.d/rproxy.conf /etc/nginx/rproxy.conf.deny -q >/dev/null && sudo cp -p /etc/nginx/ rproxy.conf.allow /etc/nginx/conf.d/ rproxy.conf && sudo service nginx reload")

else:

 print "Ejecutando script de bloqueo de Proxy Reverso"# os.system("diff /etc/nginx/conf.d/rproxy.conf /etc/nginx/rproxy.conf.allow -q >/dev/null && sudo cp -p /etc/nginx/rproxy.conf.deny /etc/nginx/conf.d/rproxy.conf && sudo service nginx restart")
Por último, ya solo quedaría agregar nuestro script al crontab o similar. En este punto hay algo  que debes tener en cuenta. Si llegaste a este punto puede que te estés dando cuenta de algo muy molesto está pasando con tu terminal. Cada consulta sobre el estado del Latch (cuando este está bloqueado) nos dispara una notificación de intento de acceso no autorizado. Esto es parte de la seguridad de Latch y por ahora no existe la forma de “silenciar” las alertas de un Latch en particular. Como workaround en la configuración de Android he deshabilitado las notificaciones de la aplicación.

Autor: Gonzalo Trancillo

Nota de Chema Alonso: Desde Eleven Paths estamos trabajando en un sistema de silenciado de alertas para evitar el flooding de notificaciones en escenarios como éste. En la modalidad Silver de Latch - que puedes tener gratis si eres una Pyme Española, está disponible vía API la consulta silenciosa.

3 comentarios:

  1. Lo voy a probar, interesante :)

    ResponderEliminar
  2. Y dale con el "desde Eleven Paths", con lo sencillo que sería "en Eleven Paths estamos trabajando"...

    ResponderEliminar
  3. Crontab me pareció una buena idea y la facilidad de las solicitudes silenciosas es bienvenida!

    ResponderEliminar