Actualmente se habla mucho del sistema de nombres de dominio (DNS), ya que se están proponiendo cambios de gran calado para este protocolo de 36 años de antigüedad. El servicio de nombres de Internet, cuyos orígenes se remontan a ARPANET, nunca ha sufrido desde su creación ninguna ruptura de la compatibilidad con versiones anteriores. Sin embargo, las nuevas propuestas para cambiar el mecanismo de transporte del DNS podrían estar a punto de alterar esta situación.
En este artículo analizo dos tecnologías emergentes para proteger el DNS, DNS sobre TLS (DoT) y DNS sobre HTTPS (DoH), y muestro cómo implementarlas mediante NGINX Open Source y NGINX Plus.
[Editor – Esta entrada es una de las varias que analizan casos de uso del módulo JavaScript de NGINX. Para consultar la lista completa, véase Casos de uso del módulo JavaScript de NGINX.
El código de esta publicación se ha actualizado para usar la directiva js_import , que sustituye a la directiva obsoleta js_include en NGINX Plus R23<.htmla> y versiones posteriores. Para obtener más información, consulte la documentación de referencia del módulo JavaScript de NGINX; en la sección Configuración de ejemplo se muestra la sintaxis correcta para los archivos de configuración de NGINX y JavaScript.]
La historia del DNS
DNS fue el segundo intento de crear un servicio de nombres para la incipiente Internet de la Agencia de Proyectos de Investigación Avanzada (ARPA); el primero fue el protocolo Internet Name Server, publicado por John Postel en 1979 como IEN-116. DNS se diseñó como un sistema jerárquico que proporcionara una estructura para descentralizar los nombres de host en zonas gestionadas por numerosas autoridades independientes. Los primeros RFC de DNS se publicaron en 1983 (RFC 882 y 883) y, aunque se han ampliado varias veces a lo largo de los años, un cliente desarrollado conforme a los estándares definidos entonces seguiría funcionando hoy en día.
Entonces, ¿por qué cambiar el protocolo ahora? Está claro que funciona según lo previsto, lo que justifica la confianza de sus autores, quienes estaban tan seguros de haber acertado que ni siquiera incluyeron un número de versión en el paquete DNS; no se me ocurren muchos otros protocolos que puedan presumir de lo mismo. DNS se concibió en una época más inocente, cuando la mayoría de los protocolos transmitían los datos sin cifrar y solían utilizar ASCII de 7 bits, pero la Internet actual es un lugar mucho más peligroso que la ARPANET de la década de 1980. Hoy en día, la mayoría de los protocolos han adoptado la seguridad de la capa de transporte (TLS) con fines de cifrado y verificación. Los críticos de DNS sostienen que hace ya mucho tiempo que debería contar con medidas de seguridad adicionales.
Así pues, del mismo modo que DNS fue el segundo intento de proporcionar un protocolo de servicio de nombres para Internet, DNS sobre TLS (DoT) y DNS sobre HTTPS (DoH) están surgiendo como segundos intentos de proteger el protocolo DNS. El primer intento fue una extensión conocida como DNSSEC y, aunque la mayoría de los dominios de nivel superior (TLD) utilizan DNSSEC, nunca se diseñó para cifrar los datos transportados en los paquetes DNS; solo permite verificar que los datos no hayan sido manipulados. DoT y DoH son extensiones de protocolo que encapsulan DNS dentro de un túnel TLS y, si se adoptan, pondrán fin a 36 años de compatibilidad con versiones anteriores.
DoT y DoH con más detalle
Creo que DoT se considera, en general, una extensión razonable. La Autoridad de Números Asignados de Internet (IANA) ya le ha asignado su propio número de puerto (TCP/853), y simplemente encapsula los paquetes DNS sobre TCP en un túnel cifrado mediante TLS. Muchos protocolos ya han hecho esto antes: HTTPS es HTTP dentro de un túnel TLS, y SMTPS, IMAPS y LDAPS son versiones seguras de esos protocolos. DNS siempre ha utilizado UDP (o TCP en determinados casos) como protocolo de transporte, por lo que añadir la capa TLS no supone un cambio drástico.
DoH, por otro lado, es algo más controvertido. DoH toma un paquete DNS y lo encapsula en una solicitud HTTP GET o POST, que después se envía mediante HTTP/2 o una versión posterior a través de una conexión HTTPS. A todos los efectos, parece una conexión HTTPS como cualquier otra, por lo que a las empresas o los proveedores de servicios les resulta imposible ver qué solicitudes se están realizando. Mozilla y otros defensores afirman que esta práctica aumenta la privacidad de los usuarios al mantener los sitios que visitan lejos de miradas indiscretas.
Pero eso no es del todo cierto. Los detractores de DoH señalan que no es exactamente el equivalente a Tor para DNS, ya que, cuando el navegador finalmente se conecta al host que ha localizado mediante DoH, es casi seguro que la solicitud utilice la extensión TLS Indicación del nombre del servidor (SNI), que incluye el nombre del host y se envía en texto sin cifrar. Además, si el navegador intenta validar el certificado del servidor mediante el Protocolo de estado de certificados en línea (OSCP), lo más probable es que ese proceso también se realice en texto sin cifrar. Por tanto, cualquiera que pueda supervisar las consultas DNS también podrá leer el SNI de la conexión o el nombre del certificado durante la validación OCSP.
Para muchas personas, el mayor problema de DoH es que los proveedores de navegadores eligen los servidores DNS a los que se envían de forma predeterminada las solicitudes DoH de sus usuarios (en el caso de los usuarios de Firefox en Estados Unidos, por ejemplo, los servidores DNS pertenecen a Cloudflare). El operador de los servidores DNS puede ver la dirección IP del usuario y los nombres de dominio de los sitios a los que envía solicitudes. Puede que no parezca gran cosa, pero investigadores de la Universidad de Illinois descubrieron que es posible deducir qué sitios web ha visitado una persona únicamente a partir de las direcciones de destino de las solicitudes de elementos de una página web, lo que se conoce como huella digital de carga de página. Esa información puede utilizarse después para «crear un perfil del usuario y dirigirle publicidad».
¿Cómo puede ayudar NGINX?
DoT y DoH no son intrínsecamente perjudiciales, y hay casos de uso en los que aumentan la privacidad de los usuarios. Sin embargo, existe un consenso cada vez mayor en que un servicio DoH público y centralizado es perjudicial para la privacidad de los usuarios, por lo que recomendamos evitar su uso a toda costa.
En cualquier caso, es probable que gestione sus propias zonas DNS para sus sitios y aplicaciones: algunas públicas, otras privadas y otras con horizonte dividido. En algún momento, quizá decida ejecutar su propio servicio DoT o DoH. Aquí es donde NGINX puede ayudar.
Las mejoras de privacidad que ofrece DoT aportan grandes ventajas para la seguridad del DNS, pero ¿qué ocurre si su servidor DNS actual no es compatible con DoT? NGINX puede ayudar proporcionando una pasarela entre DoT y el DNS estándar.
O quizá le interese la capacidad de DoH para sortear cortafuegos en aquellos casos en los que el puerto de DoT pueda estar bloqueado. Una vez más, NGINX puede ayudar proporcionando una puerta de enlace de DoH a DoT/DNS.
Implementación de una puerta de enlace DoT-DNS sencilla
El módulo Stream (TCP/UDP) de NGINX admite la terminación SSL, por lo que configurar un servicio DoT es realmente sencillo. Puede crear una sencilla puerta de enlace DoT con tan solo unas líneas de configuración de NGINX.
Necesitas un bloque upstream para tus servidores DNS y un bloque server para la terminación TLS:
Cargando ejemplo de código…
Por supuesto, también podemos hacerlo a la inversa y reenviar las solicitudes DNS entrantes a un servidor DoT ascendente. Sin embargo, esto resulta menos útil porque la mayor parte del tráfico DNS utiliza UDP y NGINX solo puede realizar la conversión entre DoT y otros servicios TCP, como DNS basado en TCP.
Cargando ejemplo de código…
Una puerta de enlace DoH-DNS sencilla
En comparación con una puerta de enlace DoT, la configuración de una puerta de enlace DoH sencilla es un poco más compleja. Necesitamos tanto un servicio HTTPS como un servicio Stream, y utilizar código JavaScript y el módulo JavaScript de NGINX<.htmla> (njs) para realizar la conversión entre ambos protocolos. La configuración más sencilla es la siguiente:
Cargando ejemplo de código…
El script de JavaScript utilizado en esta configuración (nginx_stream.js) incluye varios archivos de módulos de la biblioteca DNS. Dentro del módulo dns.js, la variable dns_decode_level establece cuánto procesamiento se realiza en los paquetes DNS. Evidentemente, procesar paquetes DNS perjudica el rendimiento. Si utiliza una configuración como la anterior, establezca dns_decode_level en 0.
Una puerta de enlace DoH más avanzada
Aquí, en NGINX, se nos da realmente bien HTTP, por lo que creemos que utilizar NGINX únicamente como una simple puerta de enlace DoH es desaprovechar una oportunidad.
El código JavaScript utilizado aquí se puede configurar para realizar una decodificación total o parcial de los paquetes DNS. Esto nos permite crear una caché de contenido HTTP para las consultas DoH con las cabeceras Expires y Cache-Control configuradas en función de los TTL mínimos de las respuestas DNS.
A continuación se muestra un ejemplo más completo, con optimizaciones adicionales de la conexión y compatibilidad con el almacenamiento en caché de contenido y el registro:
Cargando ejemplo de código…
Un filtro DNS avanzado que utiliza las funciones de NGINX Plus
Si dispone de una suscripción a NGINX Plus, puede combinar los ejemplos anteriores con algunas de las funciones más avanzadas de NGINX Plus, como las comprobaciones de estado activas y la alta disponibilidad, o incluso utilizar la API de purga de caché para gestionar las respuestas DoH almacenadas en caché.
También es posible utilizar el almacén de clave-valor de NGINX Plus para crear un sistema de filtrado de DNS que proteja a los usuarios frente a dominios maliciosos devolviendo una respuesta DNS que impida de forma efectiva acceder a ellos. El contenido del almacén de clave-valor se gestiona dinámicamente mediante la API RESTful de NGINX Plus.
Definimos dos categorías de dominios maliciosos: «bloqueados» y «sumidero». El sistema de filtrado DNS gestiona las consultas DNS sobre un dominio de forma diferente según su categoría:
- Para los dominios bloqueados, devuelve la respuesta
NXDOMAIN(que indica que el dominio no existe) - Para los dominios blackhole, devuelve un único registro DNS
Acon el valor 0.0.0.0 en respuesta a las solicitudes de registrosA, o un único registroAAAAcon el valor :: en respuesta a las solicitudes de registrosAAAA(IPv6); para otros tipos de registro, devuelve una respuesta sin resultados
Cuando nuestro filtro DNS recibe una solicitud, primero busca una clave que coincida exactamente con el FQDN consultado. Si encuentra una, «depura» la solicitud (la bloquea o la redirige a un agujero negro) según lo especificado por el valor asociado. Si no hay ninguna coincidencia exacta, busca el nombre de dominio en dos listas —una de dominios bloqueados y otra de dominios redirigidos a un agujero negro— y depura la solicitud de la forma correspondiente si encuentra una coincidencia.
Si el dominio consultado no es malicioso, el sistema reenvía la solicitud a un servidor DNS de Google para que la procese con normalidad.
Configuración del almacén de clave-valor
Definimos los dominios que consideramos maliciosos mediante dos tipos de entradas en el almacén de clave-valor de NGINX Plus:
- Entradas individuales para nombres de dominio completos (FQDN), cada una con el valor
blockedoblackhole - Dos listas de dominios, con claves denominadas
blocked_domainsyblackholed_domains, cada una asociada a una lista de dominios en formato de valores separados por comas (CSV)
La siguiente configuración del almacén de clave-valor se incluye en el contexto stream. La directiva keyval_zone asigna un bloque de memoria al almacén de clave-valor, denominado dns_config. La primera directiva keyval carga cualquier par de clave-valor de FQDN coincidente que se haya configurado, mientras que la segunda y la tercera definen las dos listas de dominios:
Cargando ejemplo de código…
A continuación, podemos usar la API de NGINX Plus para asignar el valor blocked o blackhole a cualquier clave de FQDN que queramos filtrar explícitamente, o modificar la lista en formato CSV asociada a la clave blocked_domains o blackhole_domains. Podemos modificar o eliminar FQDN exactos, o modificar el conjunto de dominios de cualquiera de las listas, en cualquier momento, y el filtro DNS se actualizará al instante con los cambios.
Elegir el servidor ascendente que gestionará la solicitud
La siguiente configuración carga la variable $dns_response, cuyo valor se establece mediante una directiva js_preread en los bloques server definidos más adelante en Configuración de los servidores que escuchan consultas DNS. Cuando el código JavaScript invocado determina que es necesario filtrar la solicitud, establece la variable en blocked o blackhole, según corresponda.
Usamos una directiva map para asignar el valor de la variable $dns_response a la variable $upstream_pool, lo que permite controlar cuál de los grupos de servidores ascendentes descritos en Definición de los servidores ascendentes más adelante gestiona la solicitud. Las consultas de dominios no maliciosos se reenvían al servidor DNS de Google predeterminado, mientras que las consultas de dominios bloqueados o descartados las gestiona el servidor ascendente correspondiente a esas categorías.
Cargando ejemplo de código…
Definición de los servidores ascendentes
Esta configuración define los grupos de servidores ascendentes blocked, blackhole y google, que gestionan las solicitudes de dominios bloqueados, de tipo agujero negro y no maliciosos, respectivamente.
Cargando ejemplo de código…
Configuración de los servidores que escuchan consultas DNS
Aquí definimos los servidores del contexto stream que escuchan las solicitudes DNS entrantes. La directiva js_preread invoca el código JavaScript que descodifica el paquete DNS, obtiene el nombre de dominio del campo NAME del paquete y busca el dominio en el almacén de clave-valor. Si el nombre de dominio coincide con una clave FQDN o se encuentra dentro de la zona de uno de los dominios incluidos en la lista blocked_domains o blackhole_domains, se descarta. De lo contrario, se envía al servidor DNS de Google para su resolución.
Cargando ejemplo de código…
Ya casi está: solo tenemos que añadir nuestro último bloque «server», que responde a las consultas con la respuesta de tipo agujero negro o de bloqueo.
Cargando ejemplo de código…
Tenga en cuenta que los servidores de este bloque son casi idénticos a los servidores DNS reales situados justo encima. La principal diferencia es que estos servidores envían un paquete de respuesta al cliente en lugar de reenviar la solicitud a un grupo de servidores DNS ascendentes. Nuestro código JavaScript detecta cuándo se ejecuta desde el puerto 9953 o 9853 y, en lugar de establecer una marca para indicar que el paquete debe bloquearse, rellena $dns_response con el paquete de respuesta real. Eso es todo.
Por supuesto, también podríamos haber aplicado este filtrado a los servicios DoT y DoH, pero utilizamos DNS estándar para simplificar las cosas. La integración del filtrado DNS con la puerta de enlace DoH se deja como ejercicio para el lector.
Prueba del filtro
Anteriormente, hemos utilizado la API de NGINX Plus para añadir entradas correspondientes a dos FQDN y algunos dominios a las dos listas de dominios:
$ curl -s http://localhost:8080/api/5/stream/keyvals/dns_config | jq{
"www.some.bad.host": "blocked",
"www.some.other.host": "blackhole",
"blocked_domains": "bar.com,baz.com",
"blackhole_domains": "foo.com,nginx.com"
}Así, cuando solicitamos la resolución de www.foo.com (un host dentro del dominio bloqueado foo.com), obtenemos un registro A con la dirección IP 0.0.0.0:
$ dig @localhost www.foo.com
; <<>> DiG 9.11.3-1ubuntu1.9-Ubuntu <<>> @localhost www.foo.com
; (1 server found)
;; global options: +mcd
;; Got answer:
;; ->>HEADER,,- opcode: QUERY, status: NOERROR, id: 58558
;; flags: qr aa rd ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
; www.foo.com. IN A
;; ANSWER SECTION:
www.foo.com. 300 IN A 0.0.0.0
;; Query time: 0 msec
;; SERVER: 172.0.0.1#53(126.0.0.1)
;; WHEN: Mon Dec 2 14:31:35 UTC 2019
;; MSG SIZEW rcvd: 45Acceso al código
Los archivos de DOH y DOT están disponibles en mi repositorio de GitHub:
- El código JavaScript de la carpeta njs.d/dns folder
- La configuración de NGINX en la carpeta «examples»
Para probar NGINX Plus, empiece hoy mismo su prueba gratuita de 30 días o póngase en contacto con nosotros para hablar sobre sus casos de uso.
Sobre el autor
Artículos relacionados del blog
Únete a F5 NGINX en AppWorld 2024
Descubre las numerosas razones para asistir al evento insignia de F5, que incluirá un emocionante anuncio sobre la cartera de productos NGINX.
Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source
La función de diccionario compartido permite configurar NGINX sin necesidad de reiniciarlo al renovar los certificados SSL/TLS.
Exposición de APIs en Kubernetes
La unidad 2 de Microservices March 2022 se centra en cómo exponer tus API en Kubernetes. Aquí tienes enlaces a los recursos que necesitas: la retransmisión en directo del 14 de marzo, artículos de blog y otros recursos de preparación, y el laboratorio práctico «Protege las API de Kubernetes mediante la limitación de solicitudes».
NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto
Hoy, en NGINX Sprint 2.0, anunciamos cómo respaldaremos nuestra visión del futuro de las aplicaciones modernas con una mayor presencia en la comunidad de código abierto, incluida la participación en el proyecto comunitario Kubernetes, nuevos proyectos de software de código abierto y la arquitectura de referencia para aplicaciones modernas.
Presentamos NGINX Plus R22
NGINX Plus R22 incorpora nuevas funcionalidades, como la compatibilidad con OCSP para verificar la revocación de certificados en TLS mutuo, el uso de varios proveedores de identidad OIDC en una configuración, el seguimiento en tiempo real de la limitación de solicitudes y conexiones en el panel de NGINX Plus y mejoras en NGINX JavaScript.
Presentamos NGINX Plus R17
NGINX Plus R17 incorpora compatibilidad con la limitación de velocidad en dos fases y con TLS 1.3, la versión más reciente del protocolo Transport Layer Security. La configuración de OpenID Connect es más sencilla y NGINX ModSecurity WAF es el doble de rápido que antes. También se ha actualizado el módulo JavaScript de NGINX.