Introducción a Knot Resolver

per Victor Carceler darrera modificació 2023-10-14T11:26:44+02:00

knot-resolver.webp

Knot Resolver es un servidor DNS recursivo que puede servir como resolver para un equipo o toda una organización.

Es un servidor libre GPL3 desarrollado por CZ.NIC (los administradores del TLD .cz) con un gran rendimiento (no en vano es la herramienta que Cloudflare utilizó en su conocido DNS 1.1.1.1) y que, aunque ofrece muchas funciones, tiene un manejo extremadamente simple.

Un servidor DNS recursivo es un servidor que atiende las consultas de los clientes y para ello puede:

  1. Acudir a la jerarquía de DNS autoritativos para realizar una búsqueda recursiva. Comenzando por los root servers o por alguno de los DNS con autoridad que se hayan descubierto en consultas anteriores.
  2. Contestar con datos que guarda en su cache.

Las distribuciones actuales de GNU/Linux suelen utilizar systemd-resolvedpara proporcionar resolución de nombres a las aplicaciones locales. systemd-resolved es un stub que reenvía las consultas que no puede contestar con su propia cache a un servidor DNS recursivo.

Pero cuando se trata de ofrecer un servidor recursivo podemos acudir a una opción tradicional como bind9 (que combina las funciones de DNS recursivo y autoritativo) o a Knot Resolver. Para montar un DNS autoritativo hay que utilizar otra herramienta: Knot DNS.

Este mismo esquema en el que hay dos herramientas diferenciadas para montar los servidores recursivos y autoritativos lo encontramos en las herramientas de NLnet Labs: Unbound es el DNS recursivo y NSD es el DNS autoritativo.

Instalación y configuración por defecto

Como la herramienta forma parte de la colección de paquetes de las principales distribuciones su instalación no puede ser más sencilla:

apt update
apt install knot-resolver

Una vez instalada encontraremos su fichero de configuración en /etc/knot-resolver/kresd.conf

-- SPDX-License-Identifier: CC0-1.0
-- vim:syntax=lua:set ts=4 sw=4:
-- Refer to manual: https://knot-resolver.readthedocs.org/en/stable/

-- Network interface configuration
net.listen('127.0.0.1', 53, { kind = 'dns' })
net.listen('127.0.0.1', 853, { kind = 'tls' })
--net.listen('127.0.0.1', 443, { kind = 'doh2' })
net.listen('::1', 53, { kind = 'dns', freebind = true })
net.listen('::1', 853, { kind = 'tls', freebind = true })
--net.listen('::1', 443, { kind = 'doh2' })

-- Load useful modules
modules = {
	'hints > iterate',  -- Allow loading /etc/hosts or custom root hints
	'stats',            -- Track internal statistics
	'predict',          -- Prefetch expiring/frequent records
}

-- Cache size
cache.size = 100 * MB

Este fichero de configuración está escrito en Lua (los comentarios son dos guíones: --) y por defecto:

  • Escucha en los puertos 53 (tcp y udp) de 127.0.0.1 y ::1 para recibir consultas DNS de los clientes.
  • Escucha en el puerto 853 (tcp) de 127.0.0.1 y ::1 para recibir consultas DNS over TLS de los clientes.
  • Se incluyen líneas comentadas que muestran cómo se podría activar DNS over HTTPS para atender a los clientes.
  • En la lista de módulos activos encontramos:
    • 'hints > iterate': Carga los datos de /etc/hosts y hace que tengan precedencia sobre la cache.
    • 'stats': Recoge estadísticas de funcionamiento que se pueden exportar a Prometheus u otras bases de datos de series temporales.
    • 'predict': Refresca los registros de cache que están apunto de expirar cuando se vuelve a acceder a ellos.
  • Se define una cache de 100MiB.

Activar y controlar instancias de kresd con systemctl

Desde el punto de vista de systemd Knot resolver es un servicio instanciado. Esto quiere decir que encontraremos una plantilla del servicio (kresd) que nos permitirá lanzar/controlar diferentes instancias.

Por ejemplo, para lanzar una instancia de kresd en cada arranque podremos ejecutar:

systemctl enable kresd@1

Pero si quisiéramos lanzar 4 instancias podríamos haber hecho:

systemctl enable kresd@1
systemctl enable kresd@2
systemctl enable kresd@3
systemctl enable kresd@4

En máquinas con varios núcleos se recomienda lanzar una instancia por cada núcleo. Las instancias son servicios independientes (se pueden encender o parar por separado), utilizan la misma configuración y se reparten las consultas de los clientes compartiendo la misma cache.

Reenvío de consultas para una zona interna

Cuando una organización mantiene algunos servidores en la red interna puede ocurrir que se desee obtener una dirección diferente al preguntar por su nombre dependiendo de si el cliente está en el interior de la propia organización o fuera.

Sin ir más lejos en el instituto se mantienen varios servicios internos pero a los que también se puede acceder desde el exterior. Algunos de ellos pueden ser: https://cloud.elpuig.xeill.net, http://meteo.elpuig.xeill.net, http://valinor.iespuigcastellar.xeill.net.

Al preguntar consultar por estos nombres de dominio se debe obtener:

  1. La IP pública de nuestro router si el cliente está fuera del centro.
  2. La IP interna del servicio si el cliente está en el interior del centro.

Esto se consigue montando un servidor DNS autoritativo interno y configurando el DNS recursivo para que reenvíe las consultas correspondientes a xeill.net al servidor DNS autoritativo interno en lugar de resolverla con los DNS autoritativos de Internet que contestarán con la IP pública del router.

En el caso de utilizar Knot Resolver se podría utilizar la siguiente configuración:

-- SPDX-License-Identifier: CC0-1.0
-- vim:syntax=lua:set ts=4 sw=4:
-- Refer to manual: https://knot-resolver.readthedocs.org/en/stable/

-- Network interface configuration
net.listen('127.0.0.1', 53, { kind = 'dns' })
net.listen('192.168.0.11', 53, { kind = 'dns' })
--net.listen('127.0.0.1', 853, { kind = 'tls' })
--net.listen('127.0.0.1', 443, { kind = 'doh2' })
--net.listen('::1', 53, { kind = 'dns', freebind = true })
--net.listen('::1', 853, { kind = 'tls', freebind = true })
--net.listen('::1', 443, { kind = 'doh2' })

-- Load useful modules
modules = {
	'hints > iterate',  -- Allow loading /etc/hosts or custom root hints
	'stats',            -- Track internal statistics
	'predict',          -- Prefetch expiring/frequent records
}

-- Cache size
cache.size = 512 * MB

-- define list of internal-only domains
internalDomains = policy.todnames({'xeill.net'})

-- forward all queries below 'internalDomains' to '192.168.0.10'
policy.add(policy.suffix(policy.FORWARD({'192.168.0.10'}), internalDomains))

Más información: