Nginx + Let’s Encrypt. Monta un reverse proxy con Docker en tu Raspberry

A raíz del correo de un lector (gracias Javier) hemos decidido sacar este artículo, que se complementa con el que publicamos sobre «Cómo instalar Docker en una Raspberry«, para saber cómo podemos montar un revesre proxy con Docker usando Nginx + Let’s Encrypt y tener nuestro propio certificado ssl para poder realizar conexiones HTTPS a nuestra Raspi. Pero antes, veamos un breve resumen de qué es un Reverse Proxy y ponernos un poco en contexto.

Peticiones web: lo básico

Si sabéis más o menos cómo funcionan las peticiones vía web, sabréis que cuando accedemos a una url en realidad estamos accediendo a un servidor para pedir cierto recurso/servicio/aplicación.

Por defecto, todas estas solicitudes se hacen a través del puerto 80 o 443, dependiendo de si son peticiones no seguras (http) o sí (https) respectivamente, por lo que esos puertos no es necesario explicitarlos en la url ya que vienen implícitos en ella. Así, si ponemos google.es:80 o google.es:443 nos llevará a google.es, sin ningún puerto.

En cambio, es posible que nuestra aplicación o servicio esté usando un puerto diferente y para ello deberíamos explicitar el puerto al que queremos hacer la petición. Por ejemplo, las aplicaciones o servicios basadas en node o python suelen usar los puertos 3000 o 5000. En estos casos, si queremos acceder a una aplicación que está corriendo en un puerto diferente al 80/443, deberíamos poner en el navegador «laurlquequeremos.com:XXXX», siendo XXXX el puerto en cuestión.

Pero como os podéis imaginar, tener que explicitar cada vez el puerto es, por una parte, poco práctico, y por otra, difícil de recordar qué puerto usa cada aplicación. Pero para eso tenemos alternativas como usar un Reverse Proxy, así que veamos cómo funciona y cómo configurarlo.

Reverse Proxy: lo básico

Para que os hagáis una idea sencilla, un Reverse Proxy es como si fuera un «organizador» que se encarga de decir dónde está cada recurso que se pide vía navegador. De esta manera, nosotros tan solo debemos escribir la url a la que queremos acceder y será el reverse proxy el encargado de redirigirnos a ese recurso. Ese nginx sería el que se despliega en los puertos 80/443.

Como se ve en la imagen, el usuario haría la petición a nginx (aunque esto no tiene por qué saberlo) y sería nginx el que redirige el tráfico al recurso solicitado. De esta forma podemos tener varios servicios o aplicaciones en un mismo servidor físico, y acceder a ellos dependiendo de la url que pongamos.

Imaginad que tenemos nuestra web (RealDroid en este caso), y en el mismo servidor tenemos un foro que está en el puerto 8080, un portfolio que usa el 8042 y una aplicación de registro de horas trabajadas (que últimamente en nuestro caso se usaría poco) en el puerto 5000. Todas estas aplicaciones están en el mismo servidor físico, y sabiendo que la url base es realdroid.es, podríamos acceder a esos diferentes servicios poniendo realdroid.es:8080, realdroid.es:8042 o realdroid.es:5000. Pero eso, como veis, es algo feo y poco práctico.

Para solucionar esto podemos crear subdominios de ese dominio principal (que son gratis si ya tenemos un dominio), o crear dominios nuevos si van a ser aplicaciones o servicios totalmente diferentes. Podemos crear foro.realdroid.es, portfolio.realdroid.es y reporte.realdroid.es. Todas esas url acceden al puerto 80/443 a la vista del usuario (recordemos que es donde desplegaremos nginx), pero por debajo nginx dirige el tráfico a los puertos 8080, 8042 y 5000 respectivamente.

Por tanto, si el usuario escribe X, verá lo que hay en Y:

  • foro.realdroid.es –> realdroid.es:8080
  • portfolio.realdroid.es –> realdroid.es:8042
  • reporte.realdroid.es –> realdroid.es:5000

Otra forma de hacerlo es, si no queremos crear subdominios, crear rutas relativas a ese dominio, del tipo realdroid.es/foro, realdroid.es/portfolio o realdroid.es/reporte, aunque esto, dependiendo de qué servicio o aplicación usemos puede no irnos bien siempre.

De esta forma tenemos todos nuestros servicios mucho más organizados y es más fácil de gestionar a la hora de dar las direcciones de esos servicios, así que vamos a desplegar ese reverse proxy en nuestra Raspberry.

Instalación y configuración

Para poder usar el reverse proxy con nginx + let’s encrypt nos ayudaremos de Docker y Docker Compose, que si no lo tenéis instalado os podéis pasar por esta entrada que explica cómo hacerlo.

Una vez tenemos Docker y Docker Compose listo, nos descargamos este repositorio en nuestra raspi con el siguiente comando:

wget https://gitlab.com/realdroid/nginx-reverse-proxy-with-certbot/-/archive/master/nginx-reverse-proxy-with-certbot-master.zip

Una vez descomprimido, veremos que tenemos lo siguiente

Lo que nos interesa de aquí es el fichero default.conf, que encontraremos dentro de la carpeta conf.d, el cual tiene lo siguiente:

En este archivo, cada «bloque» {} contenido tras la palabra server indica que es una nueva configuración específica para un dominio y debe tener otro sub-bloque con el tag «location /». De esta manera, si sólo tenemos una única dirección que nos interese añadir, dejamos sólo un bloque server.

Nota: el bloque inicial donde el server_name es localhost lo dejamos tal cual, y empezamos a crear nuestros propios bloques debajo.

En cambio, si tenemos más de una dirección añadiremos tantos bloques «server» como direcciones tengamos.

Las directrices location que vemos dentro del bloque server indican las rutas relativas a ese dominio que queremos configurar. Por ejemplo, si tenemos 3 servicios y queremos usar la misma url base (realdroid.es/foro, realdroid.es/portfolio y realdroid.es/reporte), creamos un único bloque server, de nombre le damos realdroid.es, y luego añadimos tantos location como necesitemos (3 en este caso, más la ruta base o raíz), cada uno con su ruta.

En cambio, si lo que queremos es añadir un subdominio para cada servicio, creamos 3 bloques server y dejamos sólo 1 location, el de la /, ya que esa es la raíz (punto de entrada) de dicho dominio.

Y por último modificamos la directriz «proxy_pass» para cada dominio o location, que es la ruta real donde ese servicio/aplicación se está ejecutando.

Generalmente la ruta será la IP de nuestro servidor o la URL base que tenga nuestro DNS (noip, duckdns, etc), y el puerto específico de cada servicio. Así que ponemos ahí la IP/URL:PUERTO de cada uno de los servicios que queremos configurar y guardamos el fichero.

Nota: Las direcciones que añadimos a location deben ser http, ya que el https lo servirá nginx

Generar certificados SSL

Lo único que nos queda ahora es generar los certificados SSL para cada una de las direcciones que hemos creado en ese fichero default.conf, es decir, un certificado ssl por cada bloque server que hayamos definido (excepto el de localhost). Para ello, ejecutamos el siguiente comando estando en la raíz del proyecto que nos hemos descargado para arrancar el servidor nginx

sudo docker-compose up -d

Para comprobar que todo ha ido bien ejecutamos:

sudo docker ps

Y nos fijamos si aparece nuestro contenedor en la lista

Ahora ejecutamos el siguiente comando para acceder al contenedor:

sudo docker exec -it nginxReverseProxy bash

Y una vez dentro:

certbot --nginx

Esto nos lanza el daemon de certbot para generar los certificados de forma automática, y una vez terminados los pasos (son muy guiados y bastante explicativos) reiniciamos el contenedor:

sudo docker-compose restart

Y con esto, ya podemos acceder al navegador a nuestra url usando HTTPS.

Notas

El servicio de Let’s Encrypt es gratuito, pero tiene la restricción de que los certificados caducan a los 3 meses, por lo que debemos ir renovándolos.

Esto lo podemos hacer fácilmente desde el mismo contenedor ejecutando

certbot renew

Aunque lo suyo es meterlo en un cron para que se ejecute cada 3 meses y se renueven automáticamente.

Ahora ya tenéis un dominio con conexión segura, por lo que podéis estar más tranquilos a la hora de desplegar algún servicio en vuestra Raspberry.

¡Sé el primero en compartir este artículo!