Automatizando la gestión de los certificados HTTPS en un entorno Dockerizado con Let’s Encrypt

Hola, soy Pablo, amigo de Jesús Amieiro y socio de TramitApp, un programa de control horario y gestión de recursos humanos online que te permite gestionar los trámites de los empleados (solicitud de vacaciones, bajas médicas, notas de gastos, etc.) con tu empresa de una manera rápida y sencilla.

Hace ya muchos años que soy amigo de Jesús y de vez en cuando nos juntamos para hablar de “frikadas”.

En una de nuestras charlas me habló acerca de Let’s Encrypt y lo fácil que era de implementar con WordPress, con lo que seguí su consejo y lo implanté gratuitamente para los WordPress que ya gestionaba.

Una vez se acercó la caducidad del certificado que utilizamos para nuestro servicio en la nube (una wildcard de COMODO que habíamos contratado hace un año), pensé en si podríamos utilizar Let’s Encrypt, no sólo por ahorrar en el certificado que costaba unos 120€, sino por automatizar el proceso y “olvidarnos” de tener que renovarlo y realizar los pasos manuales de cambio de certificado.

Nuestra arquitectura de sistemas está desplegada con docker, donde tenemos un servidor http (nginx), una base de datos y el servidor backend.

Para tratar de afectar lo menos posible a todo lo ya implantado busqué una solución que lo único que hiciese fuese generar los certificados y que fuesen accesibles desde nuestro servidor http.

La solución que mejor se ajusta a estos requisitos es la de Pierre Prinetti, disponible en el siguiente enlace https://github.com/pierreprinetti/certbot

Básicamente consiste en crear un volumen docker donde se guardarán los certificados y generarlos ejecutando su contenedor. Por lo tanto, si generamos certificados en un volumen compartido por el contenedor de los certificados y el de nginx, no necesito tocar ninguno de los otros contenedores, solamente configurar dónde están los certificados generados por let’s encrypt.

Pasos a seguir:

1.- Creamos un volumen con el nombre “certs”

docker volume create --name certs

2.- Paramos momentáneamente el contenedor del servidor http en el que tenemos desplegado nuestra aplicación web para poder generar los certificados.

docker stop http-server

3.- Ejecutamos el contenedor con el certificado o certificados que queremos generar

docker run \
-v certs:/etc/letsencrypt \
-e http_proxy=$http_proxy \
-e domains="my.domain.com" \
-e email="firstName.lastName@domain.com" \
-p 80:80 \
-p 443:443 \
--rm pierreprinetti/certbot:latest

4.- Volvemos a levantar el servidor http.

docker start http-server

Podemos comprobar que los certificados están bien generados inspeccionando el volumen “certs”

docker volume inspect certs

Una vez generados, sólo tenemos que montar el volumen de los certificados en nuestro servidor http, en nuestro caso, orquestamos todos los contenedores con docker-compose y cambiar la configuración del servidor para que utilice los nuevos certificados.

En nuestro caso, con nginx,

listen 443 http2;
listen [::]:443 http2;
server_name example.com;

ssl on;
ssl_certificate /etc/nginx/certs/live/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/live/example.com/privkey.pem;

Una vez cambiado, sólo queda relanzar docker-compose con los cambios que hemos hecho en el contenedor del servidor http.

docker-compose up -d

El último paso después de comprobar que todo está funcionando correctamente es automatizar el proceso de renovación.

Pierre proporciona un script “certbot-renew.sh” para renovar los certificados. Actualmente no está disponible en GitHub, por lo que dejo aquí el código fuente:

#!/usr/bin/env bash

while getopts ":s:" opt; do
  case $opt in
  s)
  webserver_container=$OPTARG
  ;;
  \?)
  echo "Invalid option: -$OPTARG" >&2
  exit 1
  ;;
  :)
  echo "Option -$OPTARG requires an argument." >&2
  exit 1
  ;;
  esac
done

docker pull pierreprinetti/certbot:latest

if [ -z ${webserver_container+x} ]; then :; else
    webserver_is_running=$(docker inspect -f {{.State.Running}} $webserver_container)
  if $webserver_is_running; then docker stop $webserver_container; fi
fi

docker run \
  -v nginx-certs:/etc/letsencrypt \
  -e http_proxy=$http_proxy \
  -e renew=true \
  -p 80:80 \
  -p 443:443 \
  --rm pierreprinetti/certbot:latest

if [ -z ${webserver_container+x} ]; then :; else
  if $webserver_is_running; then docker start $webserver_container; fi
fi

que debemos de ejecutar pasando como parámetro el nombre del contenedor del servidor http para que el propio script lo pare de modo que se puedan generar correctamente los certificados. En nuestro caso sería así:

./certbot-renew -s http-server

Solamente queda meter el script en el cron. Ejecutamos:

crontab -e

Añadimos la siguiente línea para ejecutar el script 2 veces al día tal y como indicaba Jesús en su Post y listo.

15 2,14 * * * /home/user/letsencrypt/certbot-renew.sh -s nginx > /home/user/certbot-renew.log

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.