Selfhosted SaaS seguro con reverse proxy y TLS

En el último post preparamos nuestra red doméstica local para tener una mayor seguridad implementada utilizando OpenWRT en nuestro router. Ahora ya estamos listos para que los servicios de nuestro servidor pasen a estar accesibles desde internet, de forma que podamos acceder a estos servicios desde cualquier parte del mundo.

El esquema de SaaS que os voy a proponer es el siguiente:

Esquema

Si quieres navegar de forma rápida, este es el TOC:

  1. DDNS
  2. Dominio propio
  3. Port Forwarding en OpenWRT
  4. Proxy reverso
  5. SaaS seguro con TSL

DDNS

En internet la forma que tenemos para acceder a los diferentes sitios web es a través del direccionamiento IP. Este sistema de direccionamiento es muy sencillo de interpretar por una máquina, pero no es que sea muy amigable para nosotros los humanos, por lo que lo habitual es que los sitios web se publiquen bajo un nombre de dominio, como por ejemplo firstcommit.dev o www.wikipedia.org.

Existen a nivel mundial servidores dedicados a guardar esta relación entre nombres de dominio e IPs públicas de los servicios web, de manera que cuando en tu navegador introduces un nombre de dominio, este hará una solicitud a un servidor DNS y este le entregará a tu navegador la dirección IP a la que debe llegar. Si no me crees:

  1. Abre el menú de inicio de Windows
  2. Escribe CMD y vuelve a pulsar Enter
  3. En la ventana negra que se acaba de abrir escribre ping www.wikipedia.org y pulsa Enter

Verás que aparece una dirección IPv4 como a donde resuelve la llamada de ping:

Ping

Normalmente estas IPs públicas son fijas, esto es, tu adquieres una IP pública como una matrícula de coche y será tuya exclusivamente. Esto es muy útil, pero como sabrás, el rango de direccionamiento IPv4 está agotado desde 2011, por lo que el precio de estas direcciones ha subido mucho. Nuestro operador de internet, para ahorrar costes, lo que hace es utilizar un pool de IPs fijas que se van asignando a sus clientes de forma dinámica según necesidad, por lo que tu IP pública variará en el tiempo. En algunas operadoras existe la posibilidad de alquilar una de estas IPs públicas, pero con un coste. A nivel mundial, el protocolo IPv6 se está implementando como nuevo estándar con, teóricamente, direcciones IP ilimitadas, pero en España (y en otros países) todavía es algo que no está muy extendido.

¿Cómo vamos entonces a conectar un dominio a nuestra casa? Usando un sistema dinámico de DNS o DDNS: un servicio local dentro de nuestra casa llamará cada poco tiempo a un servicio de DNS externo, y éste actualizará nuestra IP en la tabla de relación de nombre de dominio <-> IP pública cada vez que ésta cambie.

Existe un servicio gratuito que podemos instalar de forma muy sencilla en unraid llamado DuckDNS. Lo primero que vamos a hacer es entrar a su web y crearnos una cuenta utilizando alguna de sus posibles formas de inicio de sesión (github, google, twitter…)

Una vez creada la cuenta, nos aparecerá la siguiente ventana. En ella lo que debemos hacer es añadir un nombre de subdominio que queramos (podemos crear hasta 5 de forma gratuita), de forma que el nombre web que tendrá nuestra casa sea algo como firstcommit.duckdns.org. Nos quedamos con la página abierta ya que necesitaremos el token en el siguiente paso.

Duckdns 1

Ahora vamos a unraid y buscamos la app DuckDNS:

Duckdns 2

En la pantalla de configuración del docker rellenamos el subdominio que hemos elegido antes y el token que nos ha aparecido en DuckDNS:

Duckdns 3

Aceptamos, y una vez arrancado vamos hacemos click en el el logo del contenedor (en este caso, el pato) y vamos al log para ver si el servicio se comunica con los servidores de DuckDNS y actualiza nuestra IP pública:

Duckdns 4 Duckdns 5

Ahora, si accedemos desde el navegador a nuestro subdominio de duckdns la solicitud llegará hasta nuestro firewall, que como tenemos bien configurado bloqueará la solicitud y no podremos ver nada.


Dominio propio

Es posible que no te guste mucho tener que acceder a tus servicios utilizando un subdominio de duckdns. Es algo un poco feo, pero gratis. Si estás dispuesto a gastar unos pocos euros al año, de forma opcional podemos hacer que este acceso sea a través de un dominio propio, sin el .duckdns.org detrás. Si de ta igual, puedes saltarte este paso e ir directamente al siguiente.

Para comprar un dominio, puedes hacerlo en cualquier revendedor conocido como Dinahosting, Arsys, Godaddy o incluso Google, pero mi recomendación por calidad/precio es Namecheap. Los pasos para comprar y configurar un dominio serán diferentes en cada plataforma, por lo que en caso de no hacerlo con Namecheap y tengas dudas no dudes en dejar un comentario.

Entramos en Namecheap y buscamos por el dominio que más nos guste:

Namecheap 1

Como puedes ver, el precio anual varía mucho, por lo que aquí ya queda en tus manos cuál quieres comprar.

Namecheap 2

Una vez adquirido el dominio, vamos a Manage -> Advanced DNS y creamos un registro HOST de tipo CNAME como este (con tu subdominio de duckdns):

Namecheap 3

Esto hará que cualquier intento de acceso a nuestro dominio o subdominios que crearemos después vaya al servidor DNS de duckdns y este a su vez lo redirija a nuestro firewall.


Port Forwarding en OpenWRT

Ya tenemos nuestro dominio propio o subdominio duckdns apuntando a nuestra casa y actualizado siempre gracias a DuckDNS. Ahora lo que debemos hacer es redirigir los accesos necesarios desde nuestro router a nuestro servidor.

La idea es que todos nuestros servicios sean de tipo web y seguros con un cifrado TLS. Este tipo de navegación es el conocido https y siempre será a través del puerto 443. En caso de que queramos publicar servicios no seguros sin TLS, la navegación será http por el puerto 80, pero no os lo recomiendo ya que toda la información viajaría sin cifrado y cualquier persona podría ver la información. En cualquier caso, lo vamos a necesitar igualmente por lo que vamos a nuestro router/firewall OpenWRT, accedemos con nuestro usuario root/contraseña y vamos Network > Firewall > Port Forwards y añadimos dos reglas con la siguientes configuraciones:

Firewall 1 Firewall 2

Debemos usar un puerto interno diferente al 80 y 443 ya que estos puertos lo usa el propio portal de administración de unraid. Por ejemplo, el 1880 y 18443 nos podrían servir.

Lo que estamos haciendo con esta regla es redirigir todas las peticiones http y https que lleguen a nuestro router vía DuckDNS por el puerto 80/443 a la IP local y un puerto específico de nuestro servidor. Este puerto no será directamente el de un servicio concreto (como el 8123 de Home Assistant) ya que nos deberá servir para acceder a cualquiera de los servicios que queramos. ¿Cómo? ¿Es posible navegar a diferentes puertos de nuestro servidor utilizando como entrada y salida en el firewall solo los puertos http y https?


Proxy reverso

Para poder hacer este puente entre diferentes subdominios o sub-subdominios a diferentes puertos de cada servicio utilizaremos un proxy inverso o reverse proxy. Un proxy es un servicio que redirige las solicitudes de acceso de un cliente a un servidor. Normalmente se usan para hacer de puente entre una red de tipo local sin acceso a internet y un servicio de internet. En nuestro caso, le damos la vuelta a la tortilla y hacemos que sea el servicio que se aloja en nuestro servidor y que no es accesible desde internet, se convierta un servicio accesible a través de nuestro dominio:

Esquema

Para hacer esto existen diferentes plataformas, las más utilizadas son Traefic y Ngix Proxy Manager. En mi caso, me decanto por la segunda ya que la primera, además de ser más compleja de configurar está muy ligada a docker-compose, un sistema docker que nuestro unraid no tiene de forma nativa. Lo primero que vamos a hacer es instalar la App en unraid:

Proxy 2

En la configuración, añadimos los puertos 1880 y 18443 que hemos configurado en nuestro OpenWRT:

Proxy 3

Una vez arrancado vamos al webserver del propio reverse proxy y entramos con el siguiente user/pass:

Proxy 4

Lo primero que vamos a hacer es cambiar el email de usuario y la contraseña a algo seguro. Lo lógico de todas formas es no exponer este panel de administración al exterior, pero en cualquier caso como siempre es mejor tener nuestros servicios securizados y sin contraseñas por defecto.

Proxy 5

SaaS seguro con TSL

Ya tenemos todo listo, solo nos queda publicar un servicio y securizar su acceso a https con SSL. En este momento debo volver a recordar que en el momento en el que tu servicio esté público a internet, cualquier persona podrá acceder a él. Para no tener sustos vamos a listar las buenas prácticas que deberías seguir siempre con servicios publicados al exterior:

  • Nunca publiques servicios críticos como el propio panel de unraid, el portal de administración del firewall o el propio portal de Nginx Proxy Manager
  • Nunca publiques servicios que no tengan un portal de identificación con al menos usuario y contraseña
  • Utiliza siempre securización SSL (navegación con https)
  • Utiliza siempre contraseñas seguras y no las reutilices en diferentes servicios
  • Si es posible, activa el OTP para tener una capa de seguridad mayor
  • Mantén tus sistemas actualizados, tanto el firewall, el sistema de unraid como los propios docker de tus servicios.

El servicio que vamos a publicar es el de Home Assistant. Aplicamos actualizaciones en caso de que tengamos algo pendiente y mientras se descargan e instalan, en nuestro teléfono nos bajamos una aplicación de TOTP. Os recomiendo Authy para escapar de las garras de Google. Una vez instalada y creada la cuenta en Authy, vamos a Home Assistant y entramos en nuestro perfil de usuario. En Módulos de autenticación multifactor le damos a habilitar y nos saldrá un QR:

Auth

En Authy añadimos un acceso leyendo el QR con la cámara, y el código de 6 dígitos que nos aparece en la pantalla la metemos en Home Assistant. Con esto activado, en caso de acceder a Home Assistant necesitaremos este código a parte de nuestro usuario y contraseña. Como estos códigos caducan cada minuto, es prácticamente imposible que alguien pueda robarnos la cuenta y acceder a nuestro sistema domótico. Además, si alguien con un robot intentase un ataque por fuerza bruta contra Hass, seguiría teniendo siempre este paso intermedio que no podrá llevar a cabo sin tener acceso a nuestra terminal móvil.

Ahora en Hass vamos a configuración > Configuración General y bajamos a la zona donde se definen las URL. Vamos a añadir una URL externa basada en nuestro dominio como por ejemplo:

  • ha.midominio.duckdns.org en caso de que no tengas un dominio propio
  • ha.midominio.com en caso de que tengas un dominio propio

En la URL interna ponemos la IP y puerto al que estamos accediendo ahora mismo en la red local.

Publish 5

Por fin, después de todos estos pasos estamos listos para efectivamente publicar nuestro servicio en internet. Vamos a Nginx Proxy Manager, nos logeamos y vamos a Proxy Host y hacemos click en Add host. Los datos a rellenar en la primera pantalla son estos:

Publish 1 Publish 2
  • Domain Name: el subdominio al que llamaremos, sea con el dominio de duckdns o el nuestro propio, en este caso el mismo que hayamos puesto en la configuración de URL externa de hass.
  • Scheme: el protocolo de acceso desde Nginx al servicio. Normalmente será http ya que una vez que llegamos al Nginx la navegación es en local.
  • Forward hostname o IP: ponemos la ip local de nuestro servidor
  • Forward port: el puerto web del servicio, en este caso el 8123
  • Cache Assets: hará que la los accesos vayan más rápidos
  • Block common exploits: añade una capa de seguridad, como por ejemplo, evitar ataques DDOS.
  • Websocket support: en este caso Hass usa websockets por lo que lo activamos

Ahora vamos a la pestaña de SLL para añadir un certificado y que la navegación entre el cliente y el Nginx sea por https, de forma que nadie pueda espiar nuestras comunicaciones.

Para entenderlo de forma simple, podemos explicar el cifrado TLS o SSL como un sistema de correos: la comunicación entre el cliente y el servidor va cerrada dentro de un sobre (cifrado). Para poder cerrar o abrir el sobre, antes de empezar a comunicarse ambas partes pactan una clave que solo conocen ellos, por lo que nadie de correos podrá abrir esa carta durante el transporte.

Para poder hacer esto, debemos añadir un certificado digital a nuestro servicio y para ello normalmente hay que pagar, ya que la entidad que emite los certificados cobra por sus servicios. Por suerte existe un servicio gratuito y libre de auto-certificación llamado Let’s Encrypt, al cual podemos solicitar un certificado digital para nuestros servicios. Esta funcionalidad ya está integrada en Nginx Proxy Manager por lo que los pasos a seguir son muy sencillos.

Seleccionamos en el desplegable Requets a new SSL Certificate y rellenaremos los siguientes datos:

Publish 3
  • Force SSL: para obligar a que la comunicación sea https
  • HTTP/2 support: activado
  • HSTS: activado
  • HSTS subdomains activado
  • Email: ponemos un email (que sea nuestro) y aceptamos los términos y condiciones de Let’s Encrypt.

Ya podemos guardar el Proxy Host. Tarda un poco en finalizar el proceso ya que automáticamente enviará una petición a Let’s encrypt, que para poder certificar nuestro subdominio, intentará acceder a él haciendo una petición http. (es por esto que debemos pasar el puerto 80 desde el firewall al Nginx). El certificado generado tiene una caducidad de 3 meses, pero Nginx Proxy Manager se encargará de actualizarlos por nosotros (aunque no está de más echarle un vistazo de vez en cuando para actualizarlos manualmente si hace falta).

Finalmente, vamos al navegador y escribimos la url externa de Hass que hayamos configurado y si hemos hecho todo bien, nos llevará a la página de inicio de sesión donde deberemos introducir nuestro usuario, contraseña y el código TOTP que nos dará Authy.

Publish 4

En el siguiente post vamos a dar un paso más en la seguridad de nuestros sistemas publicando un gestor de contraseñas como servicio para nosotros mismos y hacernos la vida un poco más fácil con contraseñas seguras y el TOTP integrado en una única plataforma.


Suscríbete

Nota: algunos de los enlaces a productos o servicios pueden ser enlaces referidos con los que podemos obtener una comisión de venta.