Thinger.io es una infraestructura en la nube escalable que permite conectar millones de dispositivos conectados a Internet (Internet of Things). Esto permite controlarlos a través de una consola de administración simple de utilizar, o integrarlos en la lógica de nuestro negocio mediante una API REST.

A su vez, Thinger.io es un proyecto open source que permite instalar el servidor en nuestra propia nube y utilizar las librerías (también open source) para conectar nuestros dispositivos. Thinger.io soporta todo tipo de dispositivos como Arduino, ESP8266, Raspberry Pi, Intel Edison y muchos otros.

Veamos entonces cómo montar nuestro propio servidor Thinger.io en un servidor corriendo Debian Stretch (9).

Ahora bien, luego de esta bonita y breve introducción a Thinger.io que destaca todas sus cualidades, la realidad es que a simple vista el proyecto tiene poco de open source (mas bien "obfuscated source" diría, si es que existe dicho término). Basta con ver la página oficial del proyecto en GitHub:

Al menos al 16 de febrero de 2018 no hay un sólo archivo fuente en el repositorio del servidor. No entiendo cómo se puede llamar a un proyecto open source sin publicar el código fuente, ¿no se trata de justamente eso el openness? En fin, de todas formas vamos a instalarlo.

Por otro lado debo decir que la documentación es escasa e incompleta. Especialmente la sección de instalación y configuración del servidor. Es por ello que decidí escribir este artículo.

El motivo de usar un proxy reverso con Nginx consiste en permitir el uso de Certbot para generar certificados TLS gratuitos y a su vez mejorar la implementación de HTTPS (hardening).

Instalación del servidor Thinger.io

Como requisito para instalar un servidor Thinger.io en Debian, se debe partir de la versión 9 ("Stretch") o superior. Lamentablemente no es posible partir desde una instalación de Devuan (hasta el momento), ya que el servidor Thinger.io se distribuye sólo como paquete snap, y el utilitario para instalar dichos paquetes (snapd) depende de systemd.

El primer paso entonces consiste en actualizar el sistema operativo:

root@iot:~# apt-get -y update && apt-get -y upgrade

Luego instalar el servidor de bases de datos NoSQL MongoDB:

root@iot:~# apt-get -y install mongodb-server

Verificar que MongoDB esté levantado y aceptando peticiones en el puerto 27017:

root@iot:~# netstat -tulpn | grep mongo
tcp        0      0 127.0.0.1:27017         0.0.0.0:*               LISTEN      2490/mongod

A su vez es posible comprobar el estado del demonio a través de systemctl:

root@iot:~# systemctl status mongodb.service
● mongodb.service - An object/document-oriented database
   Loaded: loaded (/lib/systemd/system/mongodb.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2018-02-15 21:05:26 UTC; 4min 48s ago
     Docs: man:mongod(1)
 Main PID: 2490 (mongod)
    Tasks: 16 (limit: 4915)
   CGroup: /system.slice/mongodb.service
           └─2490 /usr/bin/mongod --unixSocketPrefix=/run/mongodb --config /etc/mongodb.conf

Feb 15 21:05:26 iot systemd[1]: Started An object/document-oriented database.

El siguiente paso consiste en instalar snapd:

root@iot:~# apt-get -y install snapd

Ahora es posible instalar el paquete snap "thinger-maker-server", simplemente ejecutando snap install thinger-maker-server:

root@iot:~# snap install thinger-maker-server
thinger-maker-server 1.3.0 from 'thinger' installed

Una vez más, verificar el funcionamiento del servidor Thinger.io mediante systemctl:

root@iot:~# systemctl status snap.thinger-maker-server.thingerd.service
● snap.thinger-maker-server.thingerd.service - Service for snap application thinger-maker-server.thingerd
   Loaded: loaded (/etc/systemd/system/snap.thinger-maker-server.thingerd.service; enabled; vendor preset: enabled)
   Active: active (running) since Thu 2018-02-15 21:08:24 UTC; 1min 13s ago
  Process: 3048 ExecStart=/usr/bin/snap run thinger-maker-server.thingerd (code=exited, status=0/SUCCESS)
 Main PID: 3060 (thingerd)
    Tasks: 2 (limit: 4915)
   CGroup: /system.slice/snap.thinger-maker-server.thingerd.service
           └─3060 thingerd --fork --runpath=/var/snap/thinger-maker-server/common

Feb 15 21:08:20 iot systemd[1]: Starting Service for snap application thinger-maker-server.thingerd...
Feb 15 21:08:20 iot /usr/bin/snap[3048]: cmd.go:105: DEBUG: restarting into "/snap/core/current/usr/bin/snap"
Feb 15 21:08:24 iot systemd[1]: Started Service for snap application thinger-maker-server.thingerd.

Y comprobar que esté aceptando peticiones en los puertos 80, 443, 25200 y 25202:

root@iot:~# netstat -tulpn | grep thinger
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      3060/thingerd
tcp        0      0 0.0.0.0:25200           0.0.0.0:*               LISTEN      3060/thingerd
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      3060/thingerd
tcp        0      0 0.0.0.0:25202           0.0.0.0:*               LISTEN      3060/thingerd

En este punto es posible acceder al servidor desde cualquier navegador Web:

La configuración del servidor se encuentra en el directorio /var/snap/thinger-maker-server/common/, dentro del archivo config.json:

root@iot:~# cd /var/snap/thinger-maker-server/common/
root@iot:/var/snap/thinger-maker-server/common# ll
total 24
drwxr-xr-x 2 root root 4096 Feb 15 21:08 certificates
-rw-r--r-- 1 root root 2446 Feb 15 21:08 config.json
drwxr-xr-x 2 root root 4096 Feb 15 21:08 data
drwxr-xr-x 2 root root 4096 Feb 15 21:08 email
drwxr-xr-x 2 root root 4096 Feb 15 21:08 exports
drwxr-xr-x 2 root root 4096 Feb 15 21:08 logs

Editar el archivo a fin de modificar la configuración por defecto:

root@iot:/var/snap/thinger-maker-server/common# nano config.json

Definir el acceso al un servidor de correo SMTP dentro de nuestra red (necesario para enviar notificaciones por correo electrónico, reseteos de contraseña y confirmación de nuevas cuentas):

"email" : {
    "domain" : "linuxito.com",
    "type" : "smtp",
    "smtp" : {
      "host" : "smtp.linuxito.com",
      "port" : "465",
      "username" : "linuxito",
      "password" : "1234",
      "secure" : true
    }
},

También es útil habilitar el log, especialmente para diagnosticar problemas con el servidor:

  "log" : {
    "enabled" : true,
    "level" : "debug",

    [...]

Cerrar el archivo y guardar los cambios. Reiniciar el servidor Thinger.io para que tome la nueva configuración:

# systemctl restart snap.thinger-maker-server.thingerd.service

Desde el navegador Web, acceder al servidor nuevamente y crear una cuenta de usuario:

Configuración de un proxy reverso con Nginx

Tal como mencioné anteriormente, el objetivo principal de implementar un proxy reverso es poder generar certificados TLS con Certbot. Además, proveer una implementación más segura de HTTPS.

En artículos anteriores expliqué cómo compilar e instalar la última versión de Nginx en Debian con soporte para SSL, y cómo implementar un balanceador de carga con Nginx. Un balanceador de carga es un proxy reverso hacia varias instancias de un mismo servicio, con lo cual la configuración es similar.

Los pasos necesarios para descargar, extraer, compilar e instalar la última versión de Nginx con su script de inicio son los siguientes:

# cd
# wget http://nginx.org/download/nginx-1.12.2.tar.gz
# tar axf nginx-1.12.2.tar.gz
# cd nginx-1.12.2/
# apt-get install gcc make libpcre3-dev zlib1g-dev git libssl-dev
# ./configure --with-http_ssl_module
# make && make install && make clean
# cd
# git clone https://github.com/Fleshgrinder/nginx-sysvinit-script.git
# cd nginx-sysvinit-script/
# make
# ln -s /usr/local/nginx/sbin/nginx /sbin/nginx

Elijo Nginx porque es mi predilecto, aunque podría utilizarse cualquier otro servidor HTTP, como por ejemplo Apache.

A continuación se debe configurar el archivo que almacenará el PID del servidor Nginx para el script de inicio. Editar el archivo de configuración del servidor Nginx:

root@iot:/usr/local/nginx/conf# nano /usr/local/nginx/conf/nginx.conf

Habilitar la siguiente línea:

pid        logs/nginx.pid;

Luego editar el script de inicio e indicar la ubicación de dicho archivo:

root@iot:/usr/local/nginx/conf# nano /etc/init.d/nginx
PIDFILE="/usr/local/nginx/logs/nginx.pid"

Antes de poder iniciar Nginx, es necesario detener el servidor Thinger, pues está ocupando el puerto 80:

# systemctl stop snap.thinger-maker-server.thingerd.service

Finalmente iniciar Nginx:

root@iot:/usr/local/nginx/conf# systemctl start nginx.service

Verificar el puerto 80:

root@iot:~/nginx-sysvinit-script# netstat -tulpn | grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      26495/nginx: master 

El siguiente paso consiste en cambiar la configuración del servidor Thinger para que escuche sólo en el puerto 8080 (HTTP) y no abra ningún puerto para HTTPS:

  "http_server" : {
    "address" : "0.0.0.0",
    "port" : "8080",
    "ssl_port" : "",

    [...]

Cerrar el archivo y guardar los cambios. Reiniciar el servidor Thinger.io para que tome la nueva configuración:

# systemctl start snap.thinger-maker-server.thingerd.service

Llegado este punto está todo listo para poder montar nuestro proxy reverso, sólo falta hacer que Nginx redirija todo el tráfico de un alias específico (por ejemplo "/thinger") hacia el servidor Thinger.

Editar la configuración de Nginx:

root@iot:/usr/local/nginx/conf# nano nginx.conf

Agregar las siguientes directivas location para redirigir el tráfico hacia el servidor Thinger (ahora escuchando en el puerto 8080):

    server {
        listen       80;

        [...]

        location /thinger {
            proxy_pass http://localhost:8080/;
            proxy_set_header Host localhost;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_http_version 1.1;
        }

        location /v1 {
            proxy_pass http://localhost:8080;
            proxy_set_header Host localhost;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_http_version 1.1;
        }

        location /oauth {
            proxy_pass http://localhost:8080;
            proxy_set_header Host localhost;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_http_version 1.1;
        }

        [...]

Notar que además se necesitan redirigir los aliases /v1 y /oauth. Esto se debe a que Thinger arma las URLs desde JavaScript de forma absoluta (en ves de relativas a la URL actual). Pésima práctica de programación, siendo que además no se provee una forma de configurar la ruta base de la aplicación (o al menos no está documentado).

Por otro lado, notar las sutiles diferencias en la configuración del reenvío: en las directivas location para /v1 y /oauth, la configuración proxy_pass no incluye una barra al final (sino que termina en la definición del puerto). Esto significa que la URI del pedido se pasa completa al servidor backend. No así para /thinger, donde se fuerza el acceso al recurso raíz (/).

Reiniciar el servidor Nginx y verificar el acceso a la aplicación desde la nueva URI "/thinger":

root@iot:/usr/local/nginx/conf# systemctl restart nginx.service

En caso de errores consultar los logs de Nginx y Thinger.

Generar un certificado SSL/TLS con Certbot

El artículo Cómo generar un certificado SSL/TLS gratis con certbot explica detalladamente cómo generar un certificado SSL/TLS gratuito con Certbot, utilizando la versión descargada desde el sitio Web oficial de la EFF. Sin embargo también es posible instalarlo desde paquete:

root@iot:/usr/local/nginx/conf# apt-get install certbot

Al igual que antes, generar el certificado de manera manual ejecutando:

root@iot:/usr/local/nginx/conf# certbot certonly

Seguir los pasos tal como explica el mencionado artículo, tanto para generar el certificado como para instalarlo en Nginx:

    # HTTPS server
    #
    server {
        listen       443 ssl;
        server_name  iot.linuxito.com;

        ssl_certificate      /etc/letsencrypt/live/iot.linuxito.com/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/iot.linuxito.com/privkey.pem;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        location / {
            root   html;
            index  index.html index.htm;
        }

        location /thinger {
            proxy_pass http://localhost:8080/;
            proxy_set_header Host localhost;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_http_version 1.1;
        }

        location /v1 {
            proxy_pass http://localhost:8080;
            proxy_set_header Host localhost;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_http_version 1.1;
        }

        location /oauth {
            proxy_pass http://localhost:8080;
            proxy_set_header Host localhost;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_http_version 1.1;
        }

    }

}
Notar que además es necesario replicar la configuración de proxy reverso, y opcionalmente forzar el uso de HTTPS con una redirección permanente (301).

Por último, no olvidar recargar Nginx automáticamente luego de renovar alguno de los certificados de Let's Encrypt.

A partir de ahora es posible acceder al panel de control de Thinger y comenzar a agregar dispositivos:

Siguientes pasos

Esto es todo lo necesario para poner nuestro servidor Thinger seguro en funcionamiento. Sin embargo resta todo el trabajo necesario para endurecer la seguridad y persistencia de los datos:

Referencias


Tal vez pueda interesarte


Compartí este artículo