Servidor DHCP y DNS
En este apunte describo cómo he evolucionado mi servidor DHCP y DNS casero. Hasta ahora tenía un PiHole dedicado en la red a DHCP, DNS y Sumidero de la publicidad. He decidido migrar a una configuración distinta, mover ambos servicios DNS y DHCP al router (linux) de casa.
Me he dado cuenta que ante una caída de PiHole el resto de servicios de la casa entraban en barrena, a pesar de tener el router e internet funcionando, así que dejo PiHole exclusivamente como sumidero de la publicidad.
Introducción
Entre las diferentes opciones para servidor DNS (bind, unbound, powerDns, dnsmasq) y para el servidor DHCP (ISC, Kea, DHCPD, dnsmasq) he optado por dnsnmasq, que es ligero y simple, combina servicios DNS y DHCP. Es ideal para redes pequeñas, además migrar la parte de DHCP desde mi PiHole es casi inmediato, ya que usa también dnsmasq
por debajo.
Para entender los ejemplos del apunte, en casa sirvo dos dominios locales: .parchis.org
y .home.arpa
(RFC 8375), al que estoy migrando poco a poco. Los dos servidores importantes en este ejemplo son:
cortafuegix.parchis.org
: Es el router de internet, corre en linux en una VM con Ubuntu, utiliza PPPoE para conectar a internet y tiene la dirección192.168.100.1
en la intranet.pihole.parchis.org
: Actualmente es el DNS y DHCP basado en el software PiHole, ejecutándose tambien en una VM con Ubuntu, su dirección es la192.168.100.224
en la intranet.
Nota: cortafuegix resolverá ambos dominios (parchis.org, home.arp ), y el resto de consultas las redirigirá al PiHole que además hace de sumidero de la publicidad; si PiHole cae, el router pasará en orden estricto a resolver a través del siguiente forwarder upstream en internet (OpenDNS en mi caso), y cuando vuelva PiHole pues lo empezará a usar de forma automática. |
PiHole lo reconfiguro para que solo atienda peticiones DNS (desactivo DHCP). Su forwarder serán el mismo par de servidores DNS upstream (OpenDNS). Si dentro de mi red alguien consulta a PiHole sobre mis dominios locales, lo he configurado para que consultará a su vez al router cortafuegix
(muy útil durante la migración).
Advertencia
Este tipo de instalación funciona excepcionalmente bien, sin embargo, tras un par de meses en producción me he dado cuenta que tiene un problema. El problema se presenta al hacer troubleshooting desde el PiHole, por ejemplo para identificar de dónde vienen las peticiones DNS en tu red casera. Todo viene desde el router, así que no es posible.
En ese caso hay que irse al router y usar comandos CLI. No mola. Este es el aspecto que tengo en mi PiHole. Dejo este apunte como referencia, pero me he puesto ya a trabajar en una nueva versión: Router con PiHole
Instalación
Actualizo e instalo dnsmasq
en mi router (cortafuegix
)
apt update -y && apt upgrade -y && apt full-upgrade -y
apt install -y dnsmasq dnsutils ldnsutils
Intentará arrancar el servicio y fallará. De momento ignóralo, se debe a que ya hay un servicio escuchando en el puerto 53, systemd-resolved
, lo resuelvo más tarde.
Configuración
A continuación muestro los archivos de configuración con ejemplos
Empiezo por el /etc/dnsmasq.conf
, dejo solo una línea para indicar que cargue cualquier fichero que encuentre bajo /etc/dnsmasq.d
$ cat /etc/dnsmasq.conf
conf-dir=/etc/dnsmasq.d
Creo archivos bajo /etc/dnsmasq.d
. Notar que tengo un par de vlan’s, la 100 y la 205, pido que escuche en las direcciones IP’s concretas. Indico que su primer forwarder es la IP de pihole
(que será el único que use, por el modo stric-order
, solo si cae pasará a los siguientes, los de OpenDNS)
Fichero: /etc/dnsmasq.d/000.dnsmasq.conf
# Configuración principal para dnsmasq
# Nunca reenviar nombres sin un punto o una parte de dominio.
domain-needed
# Configuración de los DNS Forwarders: Primero PiHole, luego upstreams en caso de fallo.
strict-order
server=192.168.100.224
server=208.67.222.222
server=208.67.220.220
# Añadir dominios locales. Las consultas a estos dominios se responderán
# únicamente desde /etc/hosts.* o DHCP.
local=/parchis.org/
local=/home.arpa/
# Especifico en qué IP's voy a escuchar. Mejor esta opción que usar interface= en
# el caso de tener interfaces dinámicas (que se activan después de dnsmasq)
listen-address=192.168.100.1
listen-address=192.168.205.2
listen-address=127.0.0.1
# En el caso de usar interface= en vez de listen-address= activaría la siguiente opción
# para que dnsmasq realmente se vincule solo a las interfaces configuradas. No lo uso
# porque como decía, una de mis interfaces (donde tengo la .205.2) es dinámica.
#bind-interfaces
# Expande los nombres simples del archivo de hosts agregando el dominio automáticamente.
expand-hosts
# Configuración del dominio. Esto permite:
# 1) Dar nombres de dominio totalmente cualificados a los hosts DHCP, siempre
# que el dominio coincida con esta configuración.
# 2) Configurar la opción "domain" en DHCP, estableciendo el dominio para todos
# los sistemas configurados por DHCP.
# 3) Proporcionar la parte del dominio para "expand-hosts".
domain=parchis.org
# Todavía en pruebas migrando a home.arpa...
#domain=home.arpa
# Configuración de DHCP
# Establecer el servidor DHCP en modo autoritativo para evitar largos tiempos de espera
# cuando un dispositivo se conecta a una nueva red.
dhcp-authoritative
# Especificar la ubicación del archivo de log.
log-facility=/var/log/dnsmasq.log
# Solo al hacer DEBUG: registrar cada consulta DNS.
#log-queries
# Solo al hacer DEBUG: información adicional sobre las transacciones DHCP.
#log-dhcp
# Permitir que dnsmasq continúe funcionando sin ser bloqueado por syslog.
log-async
# Tamaño máximo de la caché.
cache-size=10000
# No resolver nada que este en /etc/hosts. Ver addn-hosts
no-hosts
# Utilizar estos archivos adicionales para resolver nombres.
addn-hosts=/etc/hosts.home.arpa
addn-hosts=/etc/hosts.parchis.org
# Configuración para paquetes EDNS.
edns-packet-max=1232
# Configuración basada en la RFC 6761 para dominios especiales
# Según la RFC 6761, los servidores de caché DNS no deben intentar resolver
# los nombres "test", "localhost" e "invalid" en servidores DNS autoritativos.
# Estas entradas se configuran para que dnsmasq no intente buscar registros NS
# ni realizar consultas innecesarias para estos dominios.
server=/test/
server=/localhost/
server=/invalid/
# La misma RFC establece que ciertas direcciones inversas de red no deben
# ser resueltas, como las subredes privadas IPv4 indicadas en 10.in-addr.arpa
# y rangos específicos en 172.in-addr.arpa y 192.168.in-addr.arpa.
# Nunca reenviar direcciones en los espacios de direcciones no enrutadas.
bogus-priv
# OpenWRT implementa reglas adicionales para bloquear los dominios "bind"
# y "onion", usados en contextos específicos, como redes locales o Tor.
# Más información en los enlaces de OpenWRT e IANA:
# - https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob_plain;f=package/network/services/dnsmasq/files/rfc6761.conf;hb=HEAD
# - https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
# Nota: No se incluye explícitamente ".local", dado que podría causar conflictos.
server=/bind/
server=/onion/
Creo el fichero /etc/dnsmasq.d/100-vlan.conf
que serán cargados e interpretados, para configurar DHCP. A continuacion muestro algunas entradas a modo de ejemplo:
$ cat /etc/dnsmasq.d/100-vlan.conf
:
####
#### Ejemplo para Access Points
#### Nota: como TAG se puede usar cualquier cosa, aquí uso "capwap"
dhcp-option=set:capwap,option:router,192.168.100.1
dhcp-option=set:capwap,option:dns-server,192.168.100.1
dhcp-option=set:capwap,option:netmask,255.255.252.0
dhcp-option=set:capwap,43,192.168.252.238
dhcp-host=set:capwap,12:34:56:78:16:10,ap-paso.parchis.org,192.168.100.220
dhcp-host=set:capwap,12:34:56:78:57:48,ap-buhardilla.parchis.org,192.168.100.221
dhcp-host=set:capwap,12:34:56:78:35:F8,ap-cuartos.parchis.org,192.168.100.222
####
#### Ejemplo de Rango dinámicas
####
#### Nota: como TAG se puede usar cualquier cosa, aquí uso "vlan100"
dhcp-range=set:vlan100,192.168.100.100,192.168.100.199,1h
dhcp-option=set:vlan100,option:netmask,255.255.252.0
dhcp-option=set:vlan100,option:router,192.168.100.1
dhcp-option=set:vlan100,option:dns-server,192.168.100.1
dhcp-option=set:vlan100,option:ntp-server,192.168.100.1
####
#### Ejemplo de asignaciones estáticas
####
dhcp-host=set:vlan100,12:34:56:77:0E:A1,192.168.100.2,panoramix.parchis.org
dhcp-host=set:vlan100,12:34:56:70:49:ED,192.168.100.3,idefix.parchis.org
dhcp-host=set:vlan100,12:34:56:75:0d:20,192.168.100.4,idefix-wifi.parchis.org
dhcp-host=set:vlan100,12:34:56:75:df:41,192.168.100.5,kymera.parchis.org
:
Para todos aquellos equipos que reciban una IP vía DHCP (estática por la MAC o dinámica) no necesitas darlos de alta en /etc/hosts.*
. El servidor resolverá su nombre desde la informacion DHCP. Por lo tanto, solo necesitas dar de alta en /etc/hosts.*
aquellos equipos que se configuran manualmente (por ejemplo servidores, routers, switches, etc).
Todos los equipos de la red van a tener un nombre DNS y se podrá hacer una resolución directa (por nombre) o inversa (por su IP) y funcionará perfectamete.
Veamos algunos ejemplos de entradas DNS específicas, aquellos que no van a recibir su nombre/dirección vía DHCP.
$ sudo cat /etc/hosts.home.arpa
192.168.100.1 cortafuegix.home.arpa
192.168.100.12 asterix.home.arpa
:
$ sudo cat /etc/hosts.parchis.org
# IP's especiales
130.206.13.20 rediris
# Nombres que se configuran manualmente sin DHCP
192.168.100.1 cortafuegix.parchis.org
192.168.100.1 pnpntpserver.parchis.org
192.168.100.1 pbx.parchis.org
192.168.100.2 panoramix.parchis.org
:
Importante, configuro que las consultas DNS de cortafuegix
se las haga a sí mismo (127.0.0.1
, a su proceso dnsmasq
), modifico el fichero netplan para que su nameserver sea él mismo, además deshabilito que se instale el dominio de búsqueda que me llega por la vlan3 vía dhcp.
$ cat /etc/netplan/netplan.yaml
:
# Movistar VoIP
vlan3:
:
dhcp4-overrides:
use-routes: false
use-dns: false
use-domains: false
:
# Vlan principal
vlan100:
id: 100
link: eth0
macaddress: "62:54:55:44:01:30"
addresses:
- 192.168.100.1/21
nameservers:
addresses:
- 127.0.0.1
search:
- parchis.org
:
Reconfiguro systemd-resolved
para que no haga un bind al puerto 53. En principio no hace falta (porque este escucha en 127.0.0.53
), pero así evito que cuando cortafuegix
necesite resolver haga consultas dobles a 127.0.0.53:53
y 127.0.0.1:53
.
$ cat /etc/systemd/resolved.conf
[Resolve]
DNSStubListener=no
$ systemctl restart systemd-resolved
Ya puedo arrancar el servicio y comprobar el estado. Nota: justo antes de este paso he cambiado la configuracion de mi PiHole para que deje de servir DHCP en la red y eliminado todas las entradas locales DNS que tenía. Recuerda que no debes tener dos dhcp servers en la misma red.
systemctl start dnsmasq.service
systemctl status dnsmasq.service
● dnsmasq.service - dnsmasq - A lightweight DHCP and caching DNS server
Loaded: loaded (/lib/systemd/system/dnsmasq.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2024-12-24 16:40:01 CET; 2min 13s ago
Process: 5591 ExecStartPre=/etc/init.d/dnsmasq checkconfig (code=exited, status=0/SUCCESS)
Process: 5599 ExecStart=/etc/init.d/dnsmasq systemd-exec (code=exited, status=0/SUCCESS)
Process: 5608 ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf (code=exited, status=0/SUCCESS)
:
Verifico que dnsmasq
está escuchando en el puerto 53 en la vlan100 y loopback (lo). Compruebo que el servicio systemd-resolved
ha dejado de escuchar 127.0.0.53:53
.
# netstat -tulpn |grep 53
[root]@cortafuegix:~# netstat -tulpn | grep 53
tcp 0 0 127.0.0.1:53 0.0.0.0:* LISTEN 41744/dnsmasq
tcp 0 0 192.168.100.1:53 0.0.0.0:* LISTEN 41744/dnsmasq
udp 0 0 127.0.0.1:53 0.0.0.0:* 41744/dnsmasq
udp 0 0 192.168.100.1:53 0.0.0.0:* 41744/dnsmasq
Verifico que las consultas propias se las va a hacer a si mismo:
[root]@cortafuegix:/etc/dnsmasq.d# resolvectl
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: foreign
Current DNS Server: 127.0.0.1
DNS Servers: 127.0.0.1
DNS Domain: parchis.org
:
Para cualquier otra consulta la derivará a su forwarder, server=192.168.100.224
definido en /etc/dnsmasq.d/000.dnsmasq.conf
, por lo que todo seguirá funcionando.
$ nslookup ibm.com
Server: 127.0.0.1
Address: 127.0.0.1#53
Non-authoritative answer:
Name: ibm.com
Address: 23.217.255.169
:
Compruebo desde un ordenador de la red que la resolución DNS e inversa son correctas. Como puedes observar no necesito añadir el nombre del dominio, que se autocompleta solo. Comprobado con clientes Windows, Mac, Linux y dispositivos móviles.
luis@kymeraw:~ ❯ nslookup.exe luix-wifi
Server: cortafuegix.parchis.org
Address: 192.168.100.1
Name: luix-wifi.parchis.org
Address: 192.168.100.15
luis@kymeraw:~ ❯
luis@kymeraw:~ ❯
luis@kymeraw:~ ❯ nslookup.exe 192.168.100.15
Server: cortafuegix.parchis.org
Address: 192.168.100.1
Name: luix-wifi.parchis.org
Address: 192.168.100.15
Rotación del log
El log de dnsmasq
es extremadamente útil, pero por desgracia no ofrece rotacion. Hay un paquete, dnsmasq-logrotate
que garantizará que los registros se creen y luego se gestionen mediante logrotate
.
Intalo el paquete dnsmasq-logrotate en Ubuntu.
add-apt-repository ppa:m-grant-prg/utils
apt update
apt-get install dnsmasq-logrotate
Una vez instalado queda ya configurado por defecto y la rotación empezará a funcionar, sin necesidad de hacer nada más.
Comandos útiles
Activa log-queries
en el fichero /etc/dnsmasq.d/000.dnsmasq.conf
cuando quieres hacer debug de las consultas DNS. Activa log-dhcp
para tener mucha más informacion sobre el servicio de DHCP.
Con esas opciones activas, lo que más utilizo cuando estoy haciendo troubleshooting es:
Monitorizar consultas DNS
sudo tail -n 1000 -f /var/log/dnsmasq.log | grep -i query
Monitorizar DHCP
sudo tail -n 1000 -f /var/log/dnsmasq.log | grep -i dhcp
El programa dnsmasq
mantiene una cache en memoria y es posible pedirle al proceso que haga un volcado de la misma en el fichero de log, basta con mandarle la señal USR1. En una sesión puedes estar observando el log y en otra envías la señal.
sudo tail -f /var/log/dnsmasq.log
proceso=$(ps --no-headers -C dnsmasq -o pid | sed 's/ //g') && kill -s SIGUSR1 $proceso
# otra forma es usar la herramienta que instalé para hacer logrotate
dnsmasq-postrotate -l -v -p
Otros comandos curiosos para consultar la cache a través de peticiones dns de clase CHAOS.
[root]@cortafuegix:~# dig +short chaos txt cachesize.bind @127.0.0.1
"10000"
[root]@cortafuegix:~# dig +short chaos txt hits.bind @127.0.0.1
"6621"
[root]@cortafuegix:~# dig +short chaos txt misses.bind @127.0.0.1
"4472"
Tolerancia a fallos
Dada la criticidad del servicio dnsmasq
y lo sensible que es a fallos en la configuración o porque una interface no esté todavía disponible, a mi me gusta modificar el fichero dnsmasq.service
para que rearranque el servicio si ha ocurrido un fallo. Eso no significa que vaya a resolver los fallos pero en ciertos casos puede ser útil.
Esta es una copia de mi fichero /etc/systemd/system/dnsmasq.service
[Unit]
Description=dnsmasq - A lightweight DHCP and caching DNS server
Requires=network.target
Wants=nss-lookup.target
Before=nss-lookup.target
After=network.target
[Service]
Type=forking
PIDFile=/run/dnsmasq/dnsmasq.pid
ExecStartPre=/etc/init.d/dnsmasq checkconfig
ExecStart=/etc/init.d/dnsmasq systemd-exec
ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf
ExecStop=/etc/init.d/dnsmasq systemd-stop-resolvconf
ExecReload=/bin/kill -HUP $MAINPID
# Dos lineas para rearrancar en caso de fallo
Restart=on-failure
RestartSec=15
[Install]
WantedBy=multi-user.target
El original lo tienes en /etc/systemd/system/multi-user.target.wants/dnsmasq.service