Certificados SSL gratis

Hoy vengo a contaros que se pueden conseguir certificados SSL válidos en todos los navegadores, y sistemas operativos, por cero Euros. Aunque realmente lo que quiero contar es como ponerlo en marcha rápidamente, que es lo que espero no sepáis para que terminéis de leer esto 🙂

Let’s Encrypt lleva unos meses funcionando y posiblemente para cuando termine de escribir esta entrada ya habrán generado más de dos millones de certificados. Su objetivo no es acabar con la mafia y el timo de (la mayoría de) los certificados SSL de pago. Su propósito es el que tráfico de Internet viaje cifrado, para que al menos les cueste algo más de presupuesto espiarnos a todos aquellos que lo hacen.

No voy a entrar en detalle sobre el protocolo que desarrollaron (ACME), sólo comentaré las piezas que necesitamos conocer y espero que con un poco de copiar y pegar estéis funcionando en minutos.

Software

Existen varias implementaciones del “cliente ACME” necesario para gestionar la petición de certificados. De entre ellos yo elegí acme-tiny porque su código es sencillo de entender y no tiene apenas dependencias, básicamente Python y openssl. En su repositorio de github tenéis sus instrucciones de uso, muy fáciles de seguir, pero si lo queréis algo un poco más fácil he creado un pequeño script en shell, usa acme-tiny para funcionar y sólo requiere como argumento el nombre, o nombres, de dominio para los que solicitar el certificado.

También necesitaremos un servidor web, aunque el certificado SSL sea para un servidor de correo, XMPP, o lo que se os ocurra. Es necesario porque Let’s Encrypt hará una petición a nuestro servidor web para validar que el dominio del certificado solicitado es realmente nuestro. El funcionamiento a grandes rasgos es: El “cliente ACME” solicita un certificado a Let’s Encrypt, estos piden al cliente que ponga un fichero que pruebe que el dominio es nuestro (challenge) en el servidor web y posteriormente Let’s Encrypt pide al servidor web del dominio en cuestión el fichero acordado. Si el fichero está allí y es correcto, se entiende que el dominio está bajo nuestro control y el certificado se emite.

Configuración

Lo primero que necesitaremos (además de tener acme-tiny  (enlace para “wget-ear”) y opcionalmente mi script (enlace para “wget-ear”)) es un directorio para guardar claves privadas y certificados. Para no ejecutar acme_tiny como root crearemos ese directorio con permisos de escritura para un usuario no privilegiado (no es buena idea usar alguno bajo el que se ejecute Apache/nginx o algún servicio), podemos usar nuestro propio usuario (en mi caso será “agi“). Ya que, en Debian, tenemos los directorios /etc/ssl/(private|certs), yo elegí /etc/ssl/letsencrypt:

# mkdir /etc/ssl/letsencrypt
# chown agi:ssl-cert /etc/ssl/letsencrypt
# chmod 750 /etc/ssl/letsencrypt

Bajaremos el certificado intermedio de Let’s Encrypt para que no de problemas la cadena de validación de nuestro certificado SSL. Lo dejaremos en el directorio creado:

# cd /etc/ssl/letsencrypt
# wget https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem

Crearemos una clave privada para establecer la comunicación con Let’s Encrypt:

# cd /etc/ssl/letsencrypt
# openssl genrsa 4096 > account.key
# chmod 600 account.key
# chown agi account.key

Ahora crearemos el directorio donde el “cliente ACME ” dejará los ficheros de validación (challenge) y que el servidor web tendrá que devolver cuando Let’s Encrypt lo solicite:

# mkdir /var/www/letsencryptchallenges
# chown agi /var/www/letsencryptchallenges

Y configuraremos el servidor web para que sirva el contenido de ese directorio. Si tenemos un sólo dominio lo podemos hacer en la configuración es éste, y si tenemos varios configurar el directorio de forma global. A forma de ejemplo, esta sería la configuración para un servidor Apache:

Alias /.well-known/acme-challenge/ /var/www/letsencryptchallenges/
<Directory /var/www/letsencryptchallenges>
  Options None
  AllowOverride None
  Order allow,deny
  Allow from all
</Directory>

Y esta para un servidor nginx:

location /.well-known/acme-challenge/ {
  alias /var/www/letsencryptchallenges/;
  try_files $uri =404;
}

Llegado este punto, si no vas a usar mi script deberías seguir las instrucciones de acme-tiny para crear un CSR (Petición de firma de certificado), instalar el certificado y programar una tarea periódica que lo renueve.

Si vas a usar mi script deberías repasar las variables de configuración que tiene al principio para ajustar los ficheros y directorios que usará (los comentados anteriormente). Si estás siguiendo literalmente los pasos que llevamos hasta ahora posiblemente sólo tendrás que cambiar el path al script acme_tiny.py (apuntar la variable ACME_TINY al path donde lo dejaras y no olvidar dar permisos de ejecución a ambos). Si ya has recargado la configuración del servidor web, para que se sirva correctamente el directorio de los ficheros challenge, sólo queda una cosa que hacer, pedir el nuevo certificado:

## Ejecutar con el usuario no privilegiado, no como root!
## Lo más sencillo es dejar new_cert en el PATH (p.e. /usr/local/bin)

$ new_cert midominio.com

## Si queremos un certificado para varios (sub)dominios podemos pasar todos
## como argumentos al script.
## IMPORTANTE: En este caso, asegúrate de que el servidor web devolverá
## el fichero challenge correctamente en todos ellos
$ new_cert midominio.com www.midominio.com eldominiodemiprimo.com

Si todo fue bien, deberíamos tener varios ficheros, que toman su nombre del primer dominio especificado en la llamada a new_cert, en el directorio de trabajo que usemos. Los importantes son: DOMINIO.key, la clave privada, y DOMINIO_chained.crt, el certificado solicitado con el certificado intermedio de Let’s Encrypt. Con ellos tendremos que configurar el virtual HTTPS de nuestros dominios. Ejemplo en Apache:

<VirtualHost *:443>
  ServerName DOMINIO
  SSLEngine on
  SSLCertificateFile /etc/ssl/letsencrypt/DOMINIO_chained.crt
  SSLCertificateKeyFile /etc/ssl/letsencrypt/DOMINIO.key
......
</VirtualHost>

Ejemplo en nginx:

server {
  listen 443;
  server_name DOMINIO;
  ssl on;
  ssl_certificate /etc/ssl/letsencrypt/DOMINIO_chained.crt;
  ssl_certificate_key /etc/ssl/letsencrypt/DOMINIO.key;
...
}

Existen varios parámetros (tanto en los servidores web mencionados como en otro tipo de servidores) para mejorar la seguridad y compatibilidad SSL. En el sitio github de acme_tiny mencionan algunos (para deshabilitar SSLv3 e inferiores y corregir posibles ataques conocidos), aunque yo soy fan de la herramienta de análisis (y  recomendaciones) de Qualys SSL Labs. Así que no os quedéis en instalar el certificado y darle un repaso a parametrización del SSL.

Mantenimiento

Los certificados de Let’s Encrypt, al menos en el momento que escribo estas líneas, tienen una vigencia de tres meses. Por ello es importante que preparemos un mecanismo automático de renovación. Que consiste en algo tan simple como una entrada de cron que solicite un nuevo certificado antes de que caduque el actual. En mi caso tengo un fichero en /etc/cron.d llamado local-letsencrypt (el prefijo local- me permite diferenciar los ficheros instalados por el gestor de paquetes de los creados por mi) con el siguiente contenido (y donde digo nginx, digo apache2, postfix, prosody, dovecot, …):

# Ajustar el nombre de usuario, el path a new_cert y el comando para recargar
# la configuración del servidor correspondiente (salvo que uséis nginx y systemd)
0 6 1 * * agi /usr/local/bin/new_cert DOMINIO(s) && sudo systemctl reload nginx

# Y no olvidéis permitir a vuestro usuario ejecutar ese comando con sudo en
# el /etc/sudoers:
agi ALL=NOPASSWD: /bin/systemctl reload nginx

## O sin depender de sudo (recomendación de Tincho):
0 6 1 * * root su -u agi -c "/usr/local/bin/new_cert DOMINIO(s)" && systemctl reload nginx

Cuidado con las cosas gratis

Para los que no se fían de las cosas gratis, les recomiendo que sigan los pasos anteriormente descritos y luego envíen 200 EUR a mi cuenta de Paypal. Una vez al año, por la renovación, claro. Les garantizo que sus  datos viajarán cifrados igualmente que con el certificado más caro de [insertar entidad certificadora aquí].

$ exit

Televisión Española, A la carta, sin Flash.

(Nota: este post posiblemente es de interés a gente que reside (tiene una IP) en España. Para el resto… VPNs)

Esta vez me toca salirme un poco de mi temática de administración de sistemas, pero todo sea por retomar el Blog (y repartir un poco de leña 😛 )

Resulta que en Radio Televisión Española (RTVE), ente que pagamos todos con nuestros impuestos, creen que para ver los vídeos que ponen on-line (A la carta) necesito instalar una aberración (propietaria y vulnerable como pocas) como es Flash Player. No voy a entrar en si ésta fue la mejor opción en un pasado (Youtube la ha usado como opción principal hasta no hace mucho), pero hoy es un gran error tener Flash instalado y mayor que tu sitio web dependa de ello.

Pero ahí no acaba la cosa, “lo peor de todo” es que ¡tienen una versión sin Flash!, pero aunque tu navegador no tenga el plugin dichoso instalado… ¡no te la ofrecen! Hay que hacer trampas para poder ver vídeos que ya tienen preparados y que cualquier navegador actual puede ver sin problemas.

¿Cómo veo A la carta sin Flash?

Paso 1. Instala un plugin/addon/accesorio/complemento/extensión que te permita cambiar el User-Agent de tu navegador. Ésto es la cadena de identificación que envía tu navegador al servidor web, en principio con un fin meramente informativo aunque que en muchos casos el servidor lo usa para determinar como servirte la web (por ejemplo si estás usando un teléfono móvil servirte una versión “reducida” de la web).  Yo uso User Agent Switcher para Firefox y Chromium.

Paso 2. (puede que sea opcional según el plugin que uses). Con los dos plugins mencionados he tenido que bajar adicionalmente una lista con decenas de User-Agents existentes para poder elegir posteriormente. Ambos plugins tienen un enlace al sitio donde obtener la lista.

Paso 3. Elegir un User-Agent que convenza al RTVE de que ni tienes ni puedes tener Flash y que por tanto te tiene que ofrecer la versión que no lo requiere. Esta parte, al menos en mi caso, es un poco “retorcida” porque cuando lo he hecho con la página ya abierta (el programa que quería ver listo para ser reproducido) no he tenido éxito. Así que lo más sencillo para mi es abrir un navegado de incógnito (navegación privada) y antes de ir a la web de RTVE cambiar el User-Agent con el plugin correspondiente. ¿Cuál elegir? Yo he tenido éxito con el iPad con iOS 6. En concreto la cadena entera es: “Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25

Paso 4. Ve a RTVE A la carta y disfruta de la enorme colección de vídeo, documentales y programas para todos los gustos y colores, que de esta forma podrán ser disfrutados por más usuarios.

Adenda. Cachitos de Hierro y Cromo

No me voy a quedar en una crítica a la elección de Flash y voy a recomendaros algo que hacer con la posibilidad de disfrutar de los programas de TVE A la carta.

Cachitos de Hierro y Cromo es un programa de los de quitarse el sombrero. Un programa de música, que cuenta, que enseña, que entretiene y que, al menos da la impresión, está hecho por gente vocacional. Su elección de fragmentos musicales, su guión y su presentación es un gusto para todos los sentidos (involucrados). Da igual que el género que traten ese día no sea de tus preferidos, el programa te hará disfrutar de él toda su duración. Desde aquí mis felicidades y agradecimiento a todos los que lo realizáis. Me gusta esa televisión pública.

$ exit

 

DNSSEC. Asegurando las respuestas de nuestro dominio. La práctica (II)

Validar tu implementación de DNSSEC

Si seguiste las indicaciones de mi post anterior, ahora tendrás tu dominio bajo DNSSEC. En este momento es vital que te asegures de que todo está correcto, ya que si tu dominio “anuncia” (mediante la publicación de un registro DS en el registrador) que soportas DNSSEC, pero la implementación es errónea (claves no corresponden al DS, firmas caducadas, …) tu dominio dejará de ser visible para aquellos que usen DNSSEC (que cada día serán más). Es decir, adiós a tu correo, tu web, etc…

Para comprobar que todo está OK podemos usar servicios web como:

  • http://dnssec-analyzer.verisignlabs.com/ Que lista todos los pasos para validar el soporte DNSSEC de tu dominio.
  • http://dnsviz.net/ Que visualiza la relación (firmas) entre las claves desde el dominio raíz hasta el tuyo. Además de comprobar que está correcto, ayuda a entender la relación entre los diferentes (sub-)dominios y sus claves.

Si somos más de consola, los siguientes comandos nos permitirán comprobar que nuestro dominio valida correctamente:

## Primero guardamos las claves (KSK y ZSK) de la zona raíz (.)
$ dig . DNSKEY | grep -Ev '^($|;)' > root.keys

## Luego hacemos la validación de la cadena completa (desde la raíz
## hasta nuestro dominio)
$ dig +sigchase +trusted-key=./root.keys inittab.net. SOA

Autopsia de un registro DNSSEC

Una vez seguros de que nuestra configuración de DNSSEC es correcta, nos queda un asunto que tratar… el periodo de validez de la zona firmada! Como comenté en el post anterior, la zona se firma para un periodo de tiempo limitado, por defecto 30 días. Es necesario por tanto volver a firmar antes de que caduque, ya que en caso contrario… adiós web, correo, etc… Veamos un ejemplo de registro firmado:

inittab.net. 21569 IN A 91.121.65.176 
inittab.net. 21569 IN RRSIG A 14 2 21600 20150521144640 20150421144640 34840 inittab.net. uY8......

Primero vemos el RR de tipo A para inittab.net (la IP a la que apunta). Y luego tenemos el RR de tipo RRSIG (Resource Record SIGnature) que le acompaña permitiendo verificar la validez del RR de tipo A. Analicemos los campos del RRSIG:

Después de IN RRSIG, encontramos el tipo de registro al que se refiere. En este caso: A. Luego sigue el algoritmo con el que está firmado 14 (ECDSAP384SHA384). El 2 indica el número de etiquetas (net e inittab). Si fuera para un nombre tipo foo.bar.inittab.net. el valor sería 4. Detrás tenemos el TTL original, 21600. El siguiente campo es el más relevante para tema que nos preocupa, la fecha de caducidad de la firma 2015052114464021 de mayo de 2015, a las 9 de la noche (UTC). Antes de esa fecha deberemos volver a firmar la zona o las respuestas de nuestro DNS serán interpretadas como inválidas. La siguiente fecha indica cuando se realizó la firma del registro 21 de abril de 2015, y con que clave (ZSK en este caso) se hizo, 34840 de inittab.net. Por último estará la firma propiamente dicha.

Automatizando el proceso de firma

Ya sabemos cuando caducan las firmas de nuestra zona, 2015-05-21-14:46:40 en este ejemplo. Sólo queda automatizar el proceso de mantener las firmas vigentes. Para ello podemos usar los comandos del paquete dnssec-tools, o un par de sencillos shell scripts. El primero (check_rrsig), de Hauke Lampe, comprueba la fecha de caducidad de las firmas y el segundo, de un servidor, firma la zona en caso de que sea necesario:

#!/bin/bash
## (c) 2015 Alberto González Iniesta. GPLv2
## Depends: /usr/local/bin/check_rrsig

## Configuración
#DOMINIOS="foo.com bar.net baz.org"
DOMINIOS="inittab.net"

SERVIDOR_DNS=localhost
DIR_ZONAS="/etc/bind"
DIR_CLAVES="${DIR_ZONAS}/keys"
##

SALIDA=0

renueva_firma() {
  echo -n "Renovando $1 .."
  cd "$DIR_ZONAS"
  SERIE_ACTUAL=$( dig +short @$SERVIDOR_DNS $1 soa | awk '{print $3}' )
  (( SERIE_NUEVO = SERIE_ACTUAL + 1 ))
  rndc freeze # por si la zona es dinámica
  SERIE_EN_FICHERO=$( cut -d';' -f1 $1 | awk 'BEGIN {ORS=" "} /SOA.*\(/,/\)/ ' | tr '\t' ' ' | tr -s ' ' | cut -d'(' -f2 | awk '{print $1}' )
  sed -i "s/$SERIE_EN_FICHERO/$SERIE_NUEVO/" $1
  dnssec-signzone -d "$DIR_ZONAS" -K "$DIR_CLAVES" -S -t -u -3 $( dd if=/dev/random bs=16 count=1 2>/dev/null | hexdump -e \"%08x\" ) -o $1 "${DIR_ZONAS}/$1"
  rndc thaw # por si la zona es dinámica
  if rndc reload > /dev/null 2>&1 ; then
    echo "OK"
  else
    echo "ERROR"
    SALIDA=1
  fi
}

for DOMINIO in $DOMINIOS ; do
  OUTPUT=$( /usr/local/bin/check_rrsig -n $SERVIDOR_DNS -z $DOMINIO -w +10days )
  [ $? -eq 0 ] || renueva_firma $DOMINIO
done

exit $SALIDA

Una entrada en cron para que se ejecute todos los días y listo!. El script supone que los ficheros de zona se llaman como el ORIGIN (dominio), es decir para el dominio inittab.net el fichero se llama inittab.net, y supone que está en /etc/bind. Así como que las claves se encuentran en /etc/bind/keys. Sin duda es mejorable y se aceptan parches. Pero si lo hago de esta manera es porque para aprender siempre es mejor usar el mínimo de magia. Un día migraré a dnssec-tools, que entre otras cosas gestiona el cambio (rotación) de claves (KSK y ZSK), proceso que habría que hacer con cierta periodicidad. Pero si has llegado hasta aquí ya tienes una base de DNSSEC para seguir solito 🙂

NSEC vs NSEC3. Evitando la enumeración de zona

Para tener un sistema seguro de DNS es necesario firmar respuestas negativas, evitando que alguien pueda negar la existencia de un registro por ti. Es decir, con DNSSEC una respuesta que indique que un registro no existe, también tiene que venir firmada.

No se puede firmar siempre lo mismo, es decir no se puede firmar un texto que diga “eso no existe“, ya que una respuesta negativa podría ser usada por un atacante para negar la existencia de registros que si existen (enviando la respuesta firmada “eso no existe” a un cliente). Véase Replay Attack

Tampoco podemos generar una firma por cada registro no existente, por ejemplo firmar “estonoexiste.inittab.net y estotampoco.inittab.net y …. ya que el coste computacional (firmar miles de preguntas sobre registros no existentes) sería enorme, además de requerir la clave (ZSK normalmente) en todos los servidores de la zona (el primario y todos los secundarios). Mucha exposición para la clave.

NSEC al rescate. Se ordenan los registros de la zona en orden alfabético y se crear un registro NSEC por cada dos registros consecutivos. Esto permite devolver un registro NSEC (con su correspondiente RRSIG) por cualquier pregunta al DNS de algo que no exista entre los dos registros que cubre el NSEC. Es decir, si tenemos bar.inittab.net, foo.inittab.net y nada entre ellos, habrá un registro NSEC con esta forma:

bar.inittab.net. NSEC foo.inittab.net. A RRSIG NSEC

Que se devolverá cuando se pregunte por cochino.inittab.net o demente.inittab.net o eclectico.inittab.net. El problema de NSEC es que permite enumerar la zona completa. Es decir, preguntando por entradas no existentes podemos ir obteniendo los registros existentes mediantes las respuestas NSEC. Y podrían desvelarse registros “no públicos”, tipo desarrollo.inittab.net, que aunque el DNS no garantiza que sean secretos, con este sistema sería trivial descubrirlos.

Para evitar este problema se crea NSEC3. La idea es más o menos la misma, pero con un cambio importante. Primero se hace un hash de todas las entradas de la zona y son estos hash los que se ordenan y los que componen una respuesta NSEC3:

LCFQO0AOA1QL3Q7A8E0ONQ9T2PTRLFS3.inittab.net. 86400 IN NSEC3 1 0 10 874201500AC7C3CFDAB802EDD07EEFFD ( VPE6NRVJK9AI2SUFEJG8I0G36C1ANBGS CNAME RRSIG )

NSEC3 no evita, mediante ataques por fuerza bruta/diccionarios, que se termine por enumerar la zona. Pero lo hace bastante más complejo, sobre todo si se usan salts aleatorias (en el ejemplo anterior el campo que empieza por 87420….). De ahí las opción -3 y los dd/hexdump en el comando dnssec-signzone recomendado en éste y el anterior post. De hecho ya hay planes de un NSEC5 que solvente la posible enumeración con NSEC3

$ exit