Archivo de la etiqueta: cifrado

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

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

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

Ingredientes

Para ofrecer DNSSEC en las respuestas de nuestro dominio, garantizando a los usuarios/aplicaciones la validez de éstas, necesitamos:

– Acceso al panel de control del registrador del dominio. Donde informaremos el registro DS (Delegation Signer) que indicará cual es nuestra clave (KSK) en la zona padre de nuestro dominio (normalmente .com.net.org, etc…). Aunque en los dominios .es ya hay soporte para DNSSEC, en el registrador que yo uso en la actualidad (abril 2015) no hay posibilidad de informar el DS, y por tanto no hay DNSSEC para mis .es.

– Control de tu DNS primario. Supongo que los que usen su registrador, o su empresa de hosting, para gestionar su dominio tendrán la posibilidad de usar DNSSEC. Este artículo está dirigido a aquellos que administran su servidor DNS (bajo GNU/Linux claro, y usando Bind).

Pasos a seguir

Aunque existen utilidades (dnssec-tools) que facilitan todos los pasos que seguiremos, he optado (como suelo hacer) por usar las herramientas más «básicas», ya que mi objetivo siempre es aprender como funcionan todas las piezas y no depender de «mágia» que lo haga todo por mi. Así que lo único que necesitaremos es tener instalado Bind en nuestro servidor DNS, lo que posiblemente ya tenemos salvo que usemos otro software. En ese caso este tutorial sólo será orientativo.

Antes de nada, deberemos asegurarnos que nuestro Bind está configurado para soportar y validar DNSSEC.

Después crearemos las claves KSK y ZSK. Compuestas, respectivamente, de una clave privada que usaremos para firmar la clave ZSK y la zona, y de una clave pública que irá informada en el fichero de zona de nuestro dominio.

Posteriormente firmaremos la zona de nuestro dominio con las claves creadas y publicaremos, mediante el panel de control de nuestro registrador, el registro DS correspondiente a nuestra KSK. Ésto permitirá a otros servidores DNS validar la información proveniente de nuestros servidores DNS.

En el siguiente post veremos como comprobar que todo está correcto y nuestra zona está siendo servida con DNSSEC además de establecer un mecanismo de firmado periódico de la zona, ya que ésta sólo se firma con una validez finita (normalmente un mes).

Configuración de Bind para soportar DNSSEC

Dos son las opciones principales que controlan el soporte DNSSEC en Bind:

// dnssec-enable, indica soporte DNSSEC como servidor autoritativo
// de un dominio (en los DNS primario/secundario)
dnssec-enable yes;
// dnssec-validation, que activará la validación de respuestas DNSSEC
// como servidor recursivo (en el DNS que usan nuestras máquinas como resolver)
dnssec-validation yes;

Si bien se supone que el valor por defecto de estas opciones es «yes» desde la versión 9.5 de Bind, en versiones 9.7, como la que viene en Debian Squeeze, he tenido que añadir explicitamente dichas opciones a la sección «options» en /etc/bind/named.conf.options, junto con un «include» del fichero /etc/bind/bind.keys. Resultando en un named.conf.options como el siguiente:

options {
          // opciones que vienen de serie...
          .....
          // añadido lo siguiente:
          dnssec-enable yes;
          dnssec-validation yes;
};
// fuera de "options" también añadido:
include "/etc/bind/bind.keys";

En Debian Wheezy y posteriores no es necesario tocar nada de la configuración de Bind, funciona «out-of-the-box».

Para comprobar que nuestro servidor DNS recursivo (el que usa nuestro portátil/servidor/… para resolver nombres) tiene soporte DNSSEC podemos hacer alguna de las siguientes pruebas:

  •  Usar dig para obtener una respuesta clara y sencilla:
$ dig +short test.dnssec-or-not.net TXT | tail -1
"No, you are not using DNSSEC"
## ^^ En este caso no tenemos soporte DNSSEC
  • Usar dig para comprobar los flags en la respuesta del DNS recursivo:
## La opción +dnssec hace que dig incluya en la query el flag
## 'do' (DNSSEC OK)
## El flag 'ad' (Authenticated Answer) debe estar presente en la respuesta
$ dig +dnssec . SOA | grep flags
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 13, ADDITIONAL: 25
##        ^^^^^^^^^ En este caso no hay soporte DNSSEC

## En el siguiente si lo hay:
$ dig +dnssec . SOA | grep flags
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 13, ADDITIONAL: 1
  •  Preguntar por el dominio www.dnssec-failed.org. Que es un dominio con una configuración DNSSEC inválida. Este es un ejemplo interesante, ya que con un DNS con soporte DNSSEC correcto, no deberíamos obtener una respuesta válida, y por tanto no ser capaces de ver la web. Mientras que si nuestro DNS no está validando correctamente, obtendremos una IP y podremos ver la web.
# Si nuestro DNS recursivo no usa (correctamente) DNSSEC:
$ host www.dnssec-failed.org
www.dnssec-failed.org has address 69.252.193.191
www.dnssec-failed.org has address 68.87.109.242

# Si nuestro DNS usa correctamente DNSSEC:
$ host www.dnssec-failed.org
Host www.dnssec-failed.org not found: 2(SERVFAIL)
  • Existen plugins/add-ons/extensiones/como-lo-quieran-llamar-este-año para los navegadores que te indican si las webs a las que llegas están «verificadas» desde el punto de vista DNSSEC. Por ejemplo DNSSEC/TLSA Validator para Firefox.

Una vez estamos seguros de que tenemos soporte DNSSEC en nuestro DNS recursivo, y habiendo activado la opción ‘dnssec-enable‘ en los servidores autoritativos de nuestros dominios si son más antiguos que la versión 9.5 de Bind, podemos continuar.

Creación de las claves de firmado

Antes de crear las claves, vamos a poner unos permisos en el directorio /etc/bind que permitan que el propio demonio firme las zonas en caso de necesidad (por actualización dinámica de la zona, o caducidad de la misma), y a crear un directorio para guardar las claves:

# chmod 2775 /etc/bind
# mkdir /etc/bind/keys
# chmod 2770 /etc/bind/keys

Como ya comenté en el post anterior, crearemos dos claves. La primera, KSK (Key Signing Key), será usada para firmar la ZSK (Zone Signing Key), permitiendo cambiar esta última de forma periódica de forma sencilla y sin realizar cambios en el registrador del dominio:

$ dnssec-keygen -K /etc/bind/keys/ -a RSASHA256 -b 4096 \
              -n ZONE -3 -f KSK inittab.net

Este comando creará un par de claves en el directorio (-K/etc/bind/keys. El nombre de los ficheros será algo como Kinittab.net.+008+51772.private o .key. Donde el fichero .private contiene la clave privada y el .key la pública. El «+008» indica el algoritmo elegido (-a RSASHA256) y el «+51772» el identificador de la clave (lo usaremos luego en el registrador). Con «-b 4096» elegimos el tamaño de la clave a generar, 4096 es una buena opción para la KSK. «-n ZONE» indica que la clave será usada para trabajar con zonas DNSSEC, «-3» que usaremos NSEC3 (hablaré de ello más adelante) y por último «-f KSK» indica el tipo de clave. Lo único que podríamos querer variar:

  • Añadir «-r /dev/urandom» para que use este dispositivo para la obtención de números pseudo-aleatorios, en vez de /dev/random. Esto hará que la generación de la clave sea mucho más rápida, pero más débil desde el punto de vista criptográfico (mucho que explicar para un sólo post). Para pruebas podéis usar /dev/urandom, para algo «serio» mejor no usarlo.
  • Cambiar el algoritmo «-a«. En la página de manual de dnssec-keygen (8), tenéis las posibilidades. En la wikipedia también. Mi consejo es que no vayáis a lo último, porque podría suceder que algún servidor DNS no tenga implementado el algoritmo (por ejemplo yo elegí ECDSAP384SHA384 para pruebas y preparar este post, en abril de 2015, y me he dado con versiones anteriores de Bind que no lo digieren. Fail!). Mejor evitar MD5 y SHA1.
  • Cambiar el tamaño de la clave «-b«. En la actualizad (2015) 4096 bits para la KSK y 2048 bits para la ZSK son  tamaños recomendables.

Si nos fijamos en el fichero .key, veremos una entrada de zona DNS (DNSKEY en este caso) con la siguiente información:

inittab.net. IN DNSKEY 257 3 8 AwEA......
# 257 indica que se trata de una KSK
# 3 es el protocolo, y siempre tiene ese valor [RFC4034]
# 8 es el algoritmo elegido [lista en la wikipedia]. En este caso RSA/SHA-256
# AwEA.....Y el resto será la clave pública
# La única diferencia con el .key de la ZSK es que el primer campo tendrá
# un valor de 256 (en vez de 257)

Después crearemos la ZSK, con un comando prácticamente igual al anterior, en este caso con un tamaño de clave algo menor (2048 bits) y sin «marcar» como KSK:

$ dnssec-keygen -K /etc/bind/keys/ -a RSASHA256 -b 2048 \
              -n ZONE inittab.net

Una vez más obtendremos dos ficheros (un .private y un .key) del tipo Kinittab.net.+008+50946. Donde, 008 es el algoritmo y 50946 el identificador/etiqueta de la clave.

Firmando el fichero de zona

Ahora ya podemos firmar nuestra zona DNS. En mi caso el fichero se llama inittab.net y se encuentra en /etc/bind. Ese fichero es el que seguiré modificando cuando quiera hacer cambios en mi zona, sólo que después de los cambios habrá que volver a firmar y recargar la zona.

$ dnssec-signzone -d /etc/bind/ -K /etc/bind/keys/ -N "increment" -S \
   -3 $( dd if=/dev/random bs=16 count=1 2>/dev/null | hexdump -e \"%08x\" ) \
   -o inittab.net /etc/bind/inittab.net

El resultado de este comando será un fichero /etc/bind/inittab.net.signed. Veamos el por qué de las opciones que he usado:

  • -d directorio de trabajo, donde dejar la zona firmada
  • -K directorio con las claves generadas anteriormente
  • -N «increment», esta opción indica que debe aumentarse el número de serie de la zona además de firmarla. Es decir, el fichero .signed tendrá un número de serie (en el SOA) mayor al del fichero original. Yo no soy amigo de esta opción. Como hay que seguir editando el fichero original para realizar los cambios en la zona, es allí donde aumento el número de serie. Así que os podéis ahorrar esta opción.
  • -S, mi opción favorita. Se trata de «Smart«, es decir, que él busca las claves (en el directorio indicado), sabe (por el 257/256 que vimos antes) cual es la KSK y la ZSK, las usa correctamente (firmando los RR tipo DNSKEY con la KSK y el resto con la ZSK), las incluye en el nuevo fichero generado, limpia y abrillanta. En el caso de que las claves se generen con periodos de validez (eso para otro post), se encargaría de gestionar la rotación por caducidad de las mismas. ¡Una maravilla, oiga!
  • -3 SALT. Genera los registros NSEC3 de los que hablaré en el próximo post. Éstos son necesarios para que no se pueda enumerar por completo la zona. Como están basados en un hash, la «salt» varía el valor del hash para que no pueda inferirse su valor original, por ejemplo con diccionarios. En este comando uso /dev/random y hexdump para generar una «salt» completamente aleatoria. Si no queremos usar «salt«, podemos especificar un guión (-3 –) o incluso poner una fija (en hexadecimal, 32 carácteres…)
  • -o inittab.net. Indica el ORIGIN de la zona, es decir el dominio (para que no lo tenga que deducir del nombre del fichero de zona, aunque en este ejemplo sea obvio). Así que se trata de una opción importante.
  • /etc/bind/inittab.net. El fichero original de la zona. El destino tendrá el mismo nombre, con la extensión .signed (duh!).

Es importante notar que la firma de los registros tiene caducidad. Por defecto 30 días desde su creación. Este dato es vital, ya que pasados 30 días nuestro dominio dejará de validar correctamente y los DNS que tengan soporte para DNSSEC (hoy casi todos) dejarán de aceptar como válidas nuestras respuestas DNS, es decir, desapareceremos de Internet… LOL! En el próximo post (ahí, creando tensión) explicaré alguna de las posibilidades para mantener nuestras zonas firmadas al día. Si queremos modificar el periodo de validez de la firma, podemos usar la opción -e. Por ejemplo, el valor por defecto es: -e now+30D (30 Días desde ahora). Valga, por ahora, decir que basta ejecutar el mismo comando de firma de zona otra vez para generar las firmas (con su validez de 30 días) una vez más.

Una vez tenemos la zona firmada, lo único que nos queda en el servidor es servir la zona firmada en vez de la original. Es decir, en la configuración del DNS cambiar:

zone "inittab.net" {
 type master;
 file "/etc/bind/inittab.net";
 .....
};

Por:

zone "inittab.net" {
 type master;
 file "/etc/bind/inittab.net.signed";
 .....
};

Y recargarla con: rndc reload inittab.net

Informando la KSK en el registrador

Una vez nuestro servidor DNS está sirviendo la zona firmada, nos queda el último paso. Añadir el «enlace» entre la zona padre (.net en este ejemplo) y la nuestra. Es decir, informar (en el registrador) de cual es nuestra KSK, para que las respuestas de nuestros DNS puedan ser validadas correctamente.

Para ello iremos al panel de control de nuestro dominio y rellenaremos los datos del registro DS (Delegation Signer). En mi caso mi registrador es Joker.com y el formulario correspondiente tiene esta pinta:

En la entrada DS pondremos el hash de nuestra KSK

En la entrada DS pondremos los hash de nuestra KSK

¿De dónde sacamos la información para el registro DS? Si nos fijamos, después del comando de firmado que vimos anteriormente (dnssec-signzone), en nuestro directorio /etc/bind, tendremos un fichero llamado dsset-inittab.net, cuyo contenido es:

inittab.net. IN DS 51772 8 1 6397....5B
inittab.net. IN DS 51772 8 2 581E....D8....

Donde 51772 es el identificador/etiqueta de la clave (campo keytag), el 8 el el algoritmo (alg) correspondiente a la clave (recordad que en este ejemplo era RSA/SHA-256) y el 1 y 2 las funciones hash usadas (digest type) para crear cada resumen de la clave (digest), que es el chorro hexadecimal del final. Es decir, para una misma clave (KSK en este caso) se generan dos registros DS, con diferentes funciones de resumen (1 es SHA-1 y 2 es SHA-256). En el registrador podemos informar sólo una, aunque lo ideal es tener las dos. Si por algún motivo el fichero dsset-dominio.foo no se ha creado, podemos hacerlo partiendo del componente público de la clave KSK con el comando dnssec-dsfromkey:

$ ddnssec-dsfromkey Kinittab.net.+008+51772.key

Espero que, a estas alturas de la (larga) lectura, tengas tu dominio funcionando correctamente con DNSSEC. En el próximo post veremos como comprobarlo, hablaré de NSEC3 y de como automatizar el firmado de las zonas antes de que las firmas caduquen.

$ exit