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

DNSSEC. Asegurando las respuestas de nuestro dominio. La teoría

¿Qué es?

DNSSEC es una extensión al sistema de resolución de nombres (DNS) que permite aumentar la seguridad de éste, evitando ataques como el envenenamiento de cache (respuestas falsas con el fin de dirigir el tráfico a un destino diferente al real).

Básicamente añade autenticación a las respuestas DNS mediante firma digital, permitiendo a los clientes validar la veracidad de dichas respuestas. DNSSEC no cifra el tráfico entre clientes y/o servidores, que sigue siendo “en claro”. Ni evita ataques de denegación de servicio.

¿Cómo funciona DNSSEC?

El funcionamiento se basa en una delegación de confianza entre las zonas padre e hijas y el uso de criptografía asimétrica (clave pública/privada). La zona raíz (.) está firmada con una clave privada y su pública es de conocimiento global (normalmente viene incluida en los servidores DNS). En dicha zona (.) se encuentra un registro DS (Delegation Signer) por cada zona hija (.com, .net, .org, .es,…) que contiene un hash de la clave pública correspondiente a cada una de ellas.

Es decir, en la zona raíz (.) hay un registro DS que indica cual es la clave pública de la zona .net. En la zona .net hay un registro DS para cada dominio (que soporte DNSSEC) que cuelga de ella. Por ejemplo, en la zona .net, además de los servidores DNS (NS) que sirven el dominio inittab.net, también hay un registro DS que indica con que clave (por su hash) hay que verificar las respuestas de esa zona.

Cuando usamos DNSSEC, las respuestas a una pregunta DNS (sea un registro A, MX, DS, CNAME, …) vendrán acompañadas de un registro “extra” de tipo RRSIG (Resource Record SIGnature) que consiste en la firma digital (con la clave privada de la zona) del registro que se solicitó. Este registro RRSIG puede ser verificado con la clave pública de inittab.net, que además puede ser validada al ser un registro DS en la zona padre (.net), que a su vez está firmado por la clave privada de ésta,….

De forma que si solicitamos el registro MX del dominio inittab.net, el proceso (que seguiría normalmente el servidor DNS que nos presta el servicio de resolución de nombres) sería:

  • Solicitar a los servidores raíz la lista de DNS (registros NS) que sirven el dominio .net. La respuesta llevaría dichos registros (NS), además de los correspondientes RRSIG para validarlos. Además habría que solicitar el registro DS (hash de la clave pública) del dominio .net, para poder validar la respuesta que nos den los siguientes servidores DNS. Por supuesto este registro (DS) será verificable con su correspondiente registro RRSIG (firma creada con la clave privada del dominio raíz).
  • Nuestro servidor DNS validaría con la clave pública de la zona raíz (que ya tiene, por ejemplo en /etc/bind/bind.keys) que la firma (RRSIG) de los registros NS y DS es correcta y por tanto la información válida.
  • Lo siguiente sería pedir a alguno de los servidores encargados de .net su clave pública (registro DNSKEY) para poder validar las siguientes respuestas que nos dé. Este registro debe contener una clave cuyo hash coincida con el que venía en el registro DS que nos entregó la zona raíz y que ya validamos. (Aquí he simplificado un poco el proceso, luego hablaremos de los dos tipos de claves de firmado, KSK y ZSK)
  • Ahora, con la clave pública (ya validada) del dominio .net, podemos pedir a sus DNS los registros NS del dominio inittab.net y el registro DS con el hash de su clave pública. Por supuesto las respuestas a estas preguntas vendrán acompañadas de los correspondientes registros RRSIG que permitirán comprobar la validez de las mismas con la clave pública de .net.
  • El proceso se repetiría con los DNS de inittab.net. Es decir, pedimos la clave pública de inittab.net (DNSKEY) verificamos que su hash coincide con el registro validado DS que nos entregaron los servidores de la zona padre (.net) y con ella validaremos el resto de peticiones que hagamos del dominio y que vendrán acompañadas de sus correspondientes RRSIG.
Validación de queries DNS con DNSEC

Validación de queries DNS con DNSSEC

Claves de firma de clave y claves de firma de zona (KSK vs ZSK)

Como se puede observar del proceso anteriormente descrito, las zonas padre deben contener una referencia (DS) a la clave con la que las zonas hijas firman sus registros. En el caso de un dominio tradicional (p.e. inittab.net), eso requiere de informar en el registrador, junto con los DNS que lo servirán, el valor de dicho registro DS para el domino. Esto es un proceso manual y poco automatizable.

Además la información que se firma de los registros de un dominio es muy pequeña (una IP, un nombre de máquina, un registro SPF, etc..), lo que “facilita” el criptoanálisis de la clave con la que están firmados. Por ello es conveniente cambiar la clave con la que se firman de forma periódica.

De hecho los registros RRSIG tienen una validez temporal (independiente del TTL del dominio) y tienen que ser recreados (firmados) de forma periódica. El motivo es evitar ataques de repetición (Replay Attacks) que permitirían reutilizar una respuesta vieja (firmada hace meses o años) por un atacante para engañar a un cliente.

Por estos motivos, y para facilitar el cambio de la clave con la que se firman los registros de la zona, se recomienda el uso de dos claves: KSK y ZSK. La clave KSK (Key Signing Key) es aquella que estará “informada” como DS en la zona superior. Su función es la de firmar la clave de zona (ZSK) con la que están firmados el resto de registros del dominio. Es decir, la “exposición” de la KSK es mínima.

Por el contrario la ZSK (Zone Signing Key) es la que firma todos los registros de la zona. Puede ser algo más “pequeña/corta” que la KSK ya que su rotación periódica es más sencilla que la de una sola clave o la de la KSK. Es decir, no hay que modificar la zona padre (cambios en el registrador) cuando se quiera cambiar de clave de firmado de zona (ZSK), basta con generar una nueva y firmarla con la KSK, únicamente informando los cambios en nuestra zona DNS.

Desde el punto de vista técnico no hay diferencias (importantes) entre un tipo de clave y otro. Sólo es la forma en la que se usan lo que las diferencia. De hecho puede usarse una única clave, informando a la zona padre de ésta, y firmando todo con ella. El problema es que el trabajo de cambio de clave será más complejo. Mientras que el de cambio de una ZSK usando una KSK está prácticamente automatizado con las herramientas DNSSEC que usaremos y no
requiere de cambios en el registrador del dominio.

Así que aunque parezca una solución más complicada inicialmente, el uso de una clave para cada función a la larga es más seguro y más sencillo de gestionar.

 

En la próxima entrada veremos todos los pasos necesarios para implementar DNSSEC en nuestro dominio.

$ exit