LetsEncrypt + NGINX

Como imagino que la mayoría sabréis, obtener un certificado HTTPS puede ser bastante caro, a pesar de que hay alternativas como StartSSL que ofrecen certificados gratuitos (solo durante 1 año).

Pues bien, gracias al Internet Security Research Group (ISRG), el cuál está formado por la unión de varias personas de empresas destacadas (EFF, Cisco, Mozilla...), nace LetsEncrypt, una Entidad Certificadora abierta y libre, la cual permite la emisión de certificados de forma gratuita.

Cliente

Nada más abrirse la beta pública de LetsEncrypt empezaron a aparecer clientes para hacer más fácil el proceso de obtención (y renovación, ya hablaremos de eso más adelante) de certificados. Por mencionar algunos, están:

Sin embargo, yo me decanté por ACME, ya que aunque en el fondo todos hacen prácticamente lo mismo, fue el único con el que logré obtener un certificado y que además funcionara con Nginx.

Instalación de ACME

NOTA

Como estamos tratando con certificados (información sensible), recomiendo compilar el cliente desde el código fuente nosotros mismos, aunque existen binarios precompilados por si alguien decide confiar en ellos.

Para instalar ACME necesitaremos tener instalado GO, como mínimo en su versión 1.5 (ya que ACME depende de ciertas llamadas a funciones nativas no disponibles en versiones anteriores).

Si ya tenéis instalado GO 1.5 (o superior) podéis saltaros este paso.

GO 1.5

Pondré los pasos para realizar la instalación en un servidor Ubuntu 14.04, aunque debería ser bastante similar en otros sistemas Linux.

# Creamos un directorio temporal para la descarga
mkdir go_install
cd go_install

# Descargamos el tarball
wget https://storage.googleapis.com/golang/go1.5.2.linux-amd64.tar.gz

# Descomprimimos el tarball en la ruta correspondiente
tar -C /usr/local -xzf go1.5.2.linux-amd64.tar.gz

# Declaramos las variables de entorno
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin

Sería conveniente, además, poner las dos últimas líneas en vuestro fichero .bashrc o .zshrc, de modo que se exporten siempre al iniciar una sesión.

NOTA

No soy un experto en GO ni mucho menos, pero según leo el paso de la variable de entorno de GOROOT podría no ser necesario. Por si acaso, lo he puesto.

Ahora al ejecutar el comando go version os debería aparecer la versión de Go que hemos instalado (en este caso la 1.5.2).

ACME

Ahora que tenemos Go disponible, podemos pasar a descargar, compilar e instalar ACME.

Para ello, primero debemos asegurarnos de tener la libreria libcap disponible. Un simple apt-get install libcap-dev debería bastar.

# Descargamos el codigo fuente del cliente
git clone https://github.com/hlandau/acme

# Entramos en la carpeta
cd acme

# Compilamos e instalamos
make && sudo make install

Y ya está, no tiene más misterio. Ahora podemos pasar a configurar el cliente.

Configuración de ACME

Una vez tengamos disponible ACME, deberemos utilizar el asistente para configurarlo correctamente.

Se inicia con el comando sudo acmetool quickstart, y durante el asistente se nos pedirán cosas como una dirección de e-mail, y que elijamos el método de renovación automático.
Para esto último, yo recomiendo la opción 2, PROXY (más adelante lo configuraremos).

Una vez finalizado el asistente, toca pedir los certificados. Esto se hace mediante el comando sudo acmetool want undominio.com otro.dominio.com. Puedes concatenar tantos dominios como necesites.

Sin embargo, antes de hacerlo, deberemos parar nginx, ya que acmetool necesitará escuchar en el puerto 80 mientras se generan los certificados.

El proceso, por tanto, será:

sudo service nginx stop
sudo acmetool want TU.DOMINIO.COM
sudo service nginx start

Si todo ha ido bien, solo falta configurar nginx para que utilice los nuevos certificados.

Configurando NGINX

Una vez generados los certificados, que a no ser que hayáis seleccionado otra opción, estarán alojados en /var/lib/acme/live, podemos pasar a configurar nuestros scripts de NGINX para que ahora se ejecuten por HTTPS.

Además, sería interesante hacer que la versión HTTP de nuestro dominio apunte a la versión HTTPS del mismo.

El script quedaría de esta forma:

server {
    listen 80;
    server_name mi_dominio.com www.mi_dominio.com;
    
    # Si viene por HTTP, lo redireccionamos a HTTPS
    return 301 https://www.mi_dominio.com$request_uri;
}

server {
    listen 443;
    ssl on;

    server_name www.mi_dominio.com;

    # Cambiar <www.mi_dominio.com> por tu dominio
    ssl_certificate /var/lib/acme/live/<www.mi_dominio.com>/fullchain;
    ssl_certificate_key /var/lib/acme/live/<www.mi_dominio.com>/privkey;

    # Si utilizamos una app mediante reverse proxying, como por ejemplo
    # Ghost, pondremos estos headers
    location / {
        proxy_pass http://<app>;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;        
    }
}

Y ya está, con esto deberíamos ser capaces de ver nuestro sitio publicado mediante HTTPS.

Renovación automática

Como ya he dicho antes, los certificados emitidos por LetsEncrypt tienen una validez de 3 meses, por tanto deberemos encargarnos de irlos renovando periódicamente. Pero una de las gracias de ACME es que permite la auto-renovación de certificados.

Si a la hora de ejecutar el asistente hemos seleccionado la opción 2 (PROXY), podemos hacer que ACME renueve automáticamente estos certificados. Para ello deberemos crear el siguiente upstream en el fichero /etc/nginx/nginx.conf

http {

    # Resto de la configuracion...

    # Declaramos el upstream
    upstream acmetool {
        server 127.0.0.1:402;
    }
}

Y en cada uno de los archivos de nginx en los que estemos utilizando un certificado, deberemos poner las siguientes líneas:

server {

    listen 80;

    # Resto de la configuracion...

    # Le pasamos la peticion a acmetool para que responda a los challenges
    location /.well-known/acme-challenge {
        proxy_pass http://acmetool;
    }
}

Y finalmente, crearemos una entrada en el crontab para que cada mes (concretamente haremos que lo haga el dia 1 de cada mes a las 00:00) lance las rutinas de renovación. Abrimos el crontab mediante sudo crontab -e y añadimos la siguiente linea al final:

0 0 1 * * acmetool reconcile --batch

Otros problemas encontrados

Cloudflare

En mi caso utilizo Cloudflare como gestor de DNS, ya que además ofrece protección contra ataques DoS, y permite otras opciones de configuración que considero interesantes (como por ejemplo cacheo de archivos js).

Si lo utilizáis en su servicio gratuito, y habilitáis el "boost" (en el apartado DNS, la nube en naranja) para uno de los sitios en los que utilizáis un certificado HTTPS, no podréis acceder al sitio y probablemente obtengáis un error ERR_TOO_MANY_REDIRECTS.

Por tanto, si queréis utilizar Cloudflare como gestor de DNS, aseguráos de:

  1. Deshabilitar el boost en los dominios/subdominios en los que utilicéis un certificado HTTPS.
  2. En el apartado de Crypto, la opción de SSL deberéis tenerla a OFF (ya que si no, Cloudflare utilizaría sus propios certificados).

Archivos externos

Otra cosa a comentar es que, si en vuestra web servís contenido que ha sido cargado por HTTP en lugar de por HTTPS, cabe la posibilidad de que el "candadito" no aparezca en verde, sino en gris.

Al menos en Chrome, si abrís la consola de desarrollador (⌥+ ⌘ +j en OS X, F12 en Windows), os indicará con un warning qué recurso se ha cargado por HTTP, y podréis intentar solucionarlo cargándolo por HTTPS.