Verificar contenido DNS con DNSSEC y Python
Ahora que tengo todas las piezas DNSSEC en su lugar (dominios protegidos por DNSSEC y clientes que verifican DNSSEC), queda explicar cómo lo estoy utilizando.
Conviene leer el contexto primero.
Teniendo DNSSEC, tenemos una base de datos masivamente distribuída y masivamente cacheada. Podemos atender millones de dispositivos de forma segura sin apenas carga en nuestros servidores DNS. Algunos ejemplos de uso:
- DNS-based Authentication of Named Entities (DANE), para decir adiós a la infraestructura de certificados X.509 actuales.
- DNS Certification Authority Authorization para limitar las entidades de certificación X.509 que tienen permiso para emitir certificados para nuestro dominio. Por ejemplo, no tiene sentido que la oficina postal de Hong Kong o Google puedan emitir certificados para nuestra organización.
- Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints permite distribuir esa información de forma segura si se utiliza DNSSEC.
En mi caso, estoy utilizando esta tecnología para controlar y actualizar una miriada de dispositivos IoT y sistemas empotrados que tengo distribuídos por todo el mundo. El DNS me permite hacerlo de una forma escalable y redundante (gracias al cacheo de la red mundial de servidores DNS) y DNSSEC me permite hacerlo de una forma segura.
¿Cómo? Veamos el siguiente código Python. Utiliza la biblioteca dnspython:
Las líneas 42-48 hacen una serie de peticiones DNS a varios dominios. La función en las líneas 30-39 realiza las peticiones en sí y nos muestra los resultados y los errores. La función en las líneas 15-27 se encarga de la comunicación real con el servidor DNS y analiza la respuesta.
Para la consulta estamos usando el servidor DNS en la misma máquina [1] (la dirección IP 127.0.0.1) y le estamos delegando toda la verificación DNSSEC. Podemos usar dnsmasq, BIND o similares, tal y como se ha explicado con anterioridad en artículos como Activar verificación de DNSSEC en "dnsmasq".
Técnicamente la biblioteca dnspython tiene todo lo necesario para realizar la verificación DNSSEC completa, extremo a extremo y sin depender de otro proceso local, pero dado que ya estamos usando un resolver local con verificación DNSSEC para el resto actividades del sistema operativo, no parece valer la pena complicarse más la vida.
[1] |
(1, 2) Esto tiene una importancia fundamental. Este programa podría delegar la petición DNS a un servidor externo con capacidad DNSSEC, como el 8.8.8.8 de Google, pero eso tiene dos problemas:
Si la verificación se realiza en nuestro propio ordenador, no es necesario confiar en nadie más. El sistema local detectará cualquier manipulación, venga de quien venga. |
Si ejecutamos este código en una máquina con un resolver local capaz de gestionar DNSSEC, obtendremos un resultado similar al siguiente:
www.jcea.es: www.jcea.es. 12238 IN RRSIG A 7 3 28800 20180321112035 ... www.jcea.es: www.jcea.es. 12238 IN A 176.9.11.11 www.elpais.es: La respuesta no está autenticada este_dominio_no_existe.jpeg: La respuesta DNS indica un error NXDOMAIN dnssec-failed.org: La respuesta DNS indica un error SERVFAIL
Vemos lo siguiente:
-
El dominio www.jcea.es está protegido por DNSSEC y la verificación es correcta. Obtenemos la dirección que hemos pedido y una firma digital que no nos interesa porque hemos delegado su verificación en el resolver local [1].
Estamos seguros de que esa información es correcta y de confianza.
-
El dominio www.elpais.es nos da una respuesta, pero no está protegida por DNSSEC y, por tanto, no sabemos si podemos fiarnos o no de ella. En mi sistema IoT, sencillamente, no me fío.
-
El dominio inventado este_dominio_no_existe.jpeg claramente no existe, así se nos indica con un error NXDOMAIN. De hecho ese error lleva una firma DNSSEC que se puede comprobar, de forma que sabemos seguro que esa "no existencia" del dominio es demostrable y nadie la está falsificando:
$ dig este_dominio_no_existe.jpeg ; <<>> DiG 9.10.3-P4-Raspbian <<>> este_dominio_no_existe.jpeg ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 64399 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1
El error status: NXDOMAIN nos indica que el dominio no existe, y el flag ad (authenticated data) nos indica que este mensaje está firmado correctamente con DNSSEC, certificando su autenticidad.
-
Por último, el dominio dnssec-failed.org es un dominio DNSSEC cuya verificación siempre falla y que se emplea para comprobar el correcto funcionamiento de la seguridad. Ofrezco algunos detalles en Activar verificación de DNSSEC en "dnsmasq".
Naturalmente, el código Python real es bastante más sofisticado, solo he mostrado un ejemplo de uso.