De HTTP a HTTPS con Let’s Encrypt en modo manual (Debian)

por | noviembre 19, 2017

Lenta pero inexorablemente el protocolo HTTPS se está convirtiendo en un nuevo estándar mínimo a cumplir para la mayoría de webs. Los principales navegadores y buscadores como Firefox o Chrome ya vienen anunciándolo hace tiempo. Se trata de una evolución que no sólo afectará a webs de medios de pago, sino también a blogs, periódicos online, etc…

Por suerte, actualmente contamos con Let’s Encrypt como Autoridad Certificadora abierta, gratuita y fruto del trabajo conjunto de organizaciones como Mozilla, Cisco o la EFF.

El proceso de certificación SSL con Let’s Encrypt constará principalmente de dos fases:

  1. Validación (challenge) contra la CA de Let’s Encrypt para verificar que somos los propietarios del dominio del que se va a generar un certificado SSL.
  2. Instalación del certificado en el servidor web Apache.

Posteriormente, también debemos tener en cuenta cómo renovar nuestro certificado.

Instalación de Certbot

La gestión de certificados de Let’s Encrypt se realiza con un programa llamado certbot. Dada la gran cantidad de distribuciones y casuísticas posibles, os recomiendo visitar la web de certbot y seguir los pasos de instalación correspondientes en cada caso.

Para el caso que nos ocupa, en Debian o derivada instalaremos con:

apt-get install python-certbot-apache 

Validación y generación del certificado

A la hora de generar el certificado SSL es importante tener en cuenta el nombre de nuestro dominio con y sin www. Por motivos de seguridad tendremos que aceptar que la IP desde donde se está realizando el proceso de generación del certificado sea registrada. Procederemos a crear nuestro certificado con:

[root@jota-pc ~]# certbot certonly -d www.example.com -d example.com --manual 
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for www.example.com
http-01 challenge for example.com

-------------------------------------------------------------------------------
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
-------------------------------------------------------------------------------
(Y)es/(N)o: y

-------------------------------------------------------------------------------

Una vez aceptado, tendremos que proceder a preparar nuestro entorno para la validación (challenge) que hará Let’s Encrypt de nuestro sitio web. El objetivo es probar que la persona que está intentando generar el certificado del dominio es la propietaria del sitio web.

Ya que estamos generando el certificado para nuestro dominio con y sin www, tendremos que realizar una verificación en cada caso (dos en total). Tendremos que crear un determinado FICHERO con el contenido STRING en la carpeta .well-known/acme-challenge de nuestro DocumentRoot (ej.: para un DocumentRoot /var/www/example, sería /var/www/example/.well-known/acme-challenge). Si no existe dicho directorio, lo creamos. En condiciones reales FICHERO y STRING son una serie de cadenas alfanuméricas:

Make sure your web server displays the following content at
http://www.example.com/.well-known/acme-challenge/FICHERO before continuing:

STRING
...

-------------------------------------------------------------------------------
Press Enter to Continue

Después, la siguiente verificación para el dominio sin www. Tendremos que crear otro FICHERO con el contenido STRING distintos del anterior:

Make sure your web server displays the following content at
http://example.com/.well-known/acme-challenge/FICHERO before continuing:

STRING
...
-------------------------------------------------------------------------------
Press Enter to Continue

Una vez listo y presionado Enter por segunda vez, se procede a la verificación:

Waiting for verification...

Si todo va bien deberías ver dos líneas en tu log de acceso access_log donde Let’s Encrypt intenta validar. Haciendo un grep "acme" /var/log/apache2/access_log:

outbound1.letsencrypt.org - - [09/Sep/2017:23:05:17 +0200] "GET /.well-known/acme-challenge/FICHERO HTTP/1.1" 200 430 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
outbound1.letsencrypt.org - - [09/Sep/2017:23:05:17 +0200] "GET /.well-known/acme-challenge/FICHERO HTTP/1.1" 200 430 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"

Pasada la verificación, tendremos los ficheros de certificado, clave privada y cadena en /etc/letsencrypt/live/www.example.com

[root@jota-pc www.example.com]# pwd
/etc/letsencrypt/live/www.example.com
[root@jota-pc www.example.com]# ls -l
total 4
lrwxrwxrwx 1 root root  45 Nov 19 01:02 cert.pem -> ../../archive/www.example.com/cert2.pem
lrwxrwxrwx 1 root root  46 Nov 19 01:02 chain.pem -> ../../archive/www.example.com/chain2.pem
lrwxrwxrwx 1 root root  50 Nov 19 01:02 fullchain.pem -> ../../archive/www.example.com/fullchain2.pem
lrwxrwxrwx 1 root root  48 Nov 19 01:02 privkey.pem -> ../../archive/www.example.com/privkey2.pem
-rw-r--r-- 1 root root 543 Sep  9 23:04 README

Copiamos los ficheros en el directorio de Apache que corresponda y configuramos las directivas correspondientes en nuestro VirtualHost HTTPS, por ejemplo:

SSLCertificateFile      /opt/apache/certs/example.com/cert.pem
SSLCertificateKeyFile   /opt/apache/certs/example.com/privkey.pem
SSLCertificateChainFile /opt/apache/certs/example.com/chain.pem

Securizamos el directorio donde guardamos certificados:

chmod -R 400 /opt/apache/certs

Reiniciamos Apache:

systemctl restart apache2.service

Configuraciones adicionales

Para nuestra primera puesta en marcha de HTTPS tendremos que hacer modificaciones adicionales:

  • Para nuestro blog de WordPress, en el fichero wp-config.php modificamos las variables WP_SITEURL y WP_HOME:
    define('WP_SITEURL', 'https://www.example.com');
    define('WP_HOME', 'https://www.example.com');  
    
  • Si queremos que todo el tráfico HTTP se redirija a HTTPS, ponemos en nuestro VirtualHost HTTP la siguiente directiva:
    Redirect permanent / https://www.example.com/
    
  • Opcionalmente, podemos incluir también HSTS sólo en caso de que toda nuestra web funcione sin problemas por HTTPS:
    # HSTS (mod_headers is required) (604800 seconds = 1 week)
    Header always set Strict-Transport-Security "max-age=604800"
    
  • Debemos actualizar la sección de nuestro robots.txt donde se especifica la localización del sitemap. Sustituimos:
    Sitemap: http://www.example.com/sitemap.xml
    

    Por:

    Sitemap: https://www.example.com/sitemap.xml
    
  • Posiblemente tengamos links que sigan apuntando a direcciones HTTP. Las imágenes suelen ser un clásico en este sentido. Tendremos que sustituir dichos links antiguos por los nuevos HTTPS. Un plugin que te será de gran ayuda es Better Search Replace. Tendrás que sustituir, por ejemplo, todas las ocurrencias de http://www.example.com por https://www.example.com
  • Si utilizas Google Webmaster Tools tendrás que añadir dos nuevas propiedades (una para https://example.com y otra para https://www.example.com) para que Google sepa de la existencia de tu sitio con protocolo HTTPS.

Renovación del certificado

Actualmente el certificado de Let’s Encrypt tiene una validez de 90 días. Es recomendable renovarlo unos días antes de que caduque en caso de que pudieran presentarse imprevistos de último momento.

Podemos comprobar el período de validez de nuestros certificados con certbot certificates:

[root@jota-pc ~]# certbot certificates
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Found the following certs:
  Certificate Name: www.example.com
    Domains: www.example.com example.com
    Expiry Date: 2017-12-08 20:05:00+00:00 (VALID: 19 days)
    Certificate Path: /etc/letsencrypt/live/www.example.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/www.example.com/privkey.pem
-------------------------------------------------------------------------------

Cuando se aproxime la fecha de caducidad, podemos renovar con:

[root@jota-pc ~]# certbot certonly -d www.example.com -d example.com --manual
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Cert is due for renewal, auto-renewing...
Renewing an existing certificate
Performing the following challenges:
http-01 challenge for www.example.com
http-01 challenge for example.com

-------------------------------------------------------------------------------
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
-------------------------------------------------------------------------------
(Y)es/(N)o: 

Seguimos los mismos pasos que al generar el certificado SSL por primera vez.

Si todo va bien:

Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0001_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0001_csr-certbot.pem

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/www.example.com/fullchain.pem. Your
   cert will expire on 2018-02-16. To obtain a new or tweaked version
   of this certificate in the future, simply run certbot again. To
   non-interactively renew *all* of your certificates, run "certbot
   renew"
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

El certificado estará en la misma localización que antes (/etc/letsencrypt/live/www.example.com) y podremos actualizarlo en nuestro Apache.

Si comprobamos con certbot certificates veremos la nueva fecha de expiración:

[root@jota-pc ~]# certbot certificates
Saving debug log to /var/log/letsencrypt/letsencrypt.log

-------------------------------------------------------------------------------
Found the following certs:
  Certificate Name: www.example.com
    Domains: www.example.com example.com
    Expiry Date: 2018-02-16 23:03:16+00:00 (VALID: 89 days)
    Certificate Path: /etc/letsencrypt/live/www.example.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/www.example.com/privkey.pem
-------------------------------------------------------------------------------

Recordad que para configuraciones TLS/SSL es muy útil la herramienta Mozilla SSL Configuration Generator

Para todo lo demás, la ayuda de certbot es nuestra amiga:

[root@jota-pc ~]# certbot --help

  certbot [SUBCOMMAND] [options] [-d DOMAIN] [-d DOMAIN] ...

Certbot can obtain and install HTTPS/TLS/SSL certificates.  By default,
it will attempt to use a webserver both for obtaining and installing the
cert. The most common SUBCOMMANDS and flags are:

obtain, install, and renew certificates:
    (default) run   Obtain & install a cert in your current webserver
    certonly        Obtain or renew a cert, but do not install it
    renew           Renew all previously obtained certs that are near expiry
   -d DOMAINS       Comma-separated list of domains to obtain a cert for

  (the cerbot apache plugin is not installed)
  --standalone      Run a standalone webserver for authentication
  (the certbot nginx plugin is not installed)
  --webroot         Place files in a server's webroot folder for authentication
  --manual          Obtain certs interactively, or using shell script hooks

   -n               Run non-interactively
  --test-cert       Obtain a test cert from a staging server
  --dry-run         Test "renew" or "certonly" without saving any certs to disk

manage certificates:
    certificates    Display information about certs you have from Certbot
    revoke          Revoke a certificate (supply --cert-path)
    delete          Delete a certificate

manage your account with Let's Encrypt:
    register        Create a Let's Encrypt ACME account
  --agree-tos       Agree to the ACME server's Subscriber Agreement
   -m EMAIL         Email address for important account notifications

More detailed help:

  -h, --help [TOPIC]    print this message, or detailed help on a topic;
                        the available TOPICS are:

   all, automation, commands, paths, security, testing, or any of the
   subcommands or plugins (certonly, renew, install, register, nginx,
   apache, standalone, webroot, etc.)

😉