
Una configuración problemática con systemd-resolved
Probablemente sepas que puedes conectar Moss con tu nuevo servidor Ubuntu 18.04 o 16.04 – independientemente del proveedor donde esté alojado dicho servidor. Moss también implementa integraciones nativas con algunos proveedores (Amazon, DigitalOcean, Google y Vultr en el momento de escribir estas líneas), pero puedes usar Moss con cualquier vps, instancia cloud, o incluso servidor físico – el último no es un caso de uso común, pero es factible.
Hace unos días un cliente tuvo problemas intentando conectar con Moss un servidor Ubuntu 18.04 (alojado en el proveedor de su elección), por lo que decidí crear una cuenta en dicho proveedor e investigar el problema. Resulta que la imagen del proveedor tenía algunas configuraciones «extrañas», además de que la solución por defecto para resolución de nombres en Ubuntu 18.04 (bionic) tiene algunos bugs relacionados. Creo que el problema es lo suficientemente interesante como para compartirlo aquí, y además nos va a permitir hablar un poco sobre systemd y, de forma más específica, sobre systemd-resolved.
systemd-resolved
systemd es un proyecto de software libre que proporciona bloques en espacio de usuario para construir un sistema Linux. El componente más conocido es su proceso init, capaz de iniciar múltiples servicios en paralelo y reducir así el tiempo de arranque de tu máquina Linux. Si tienes servidores con Ubuntu (Xenial o Bionic) ya estás usando systemd como proceso init – PID 1.
A pesar de que lo anterior suena muy bien, una buena parte de la comunidad open source ha criticado duramente a systemd durante años. No voy a profundizar en dichas críticas, pero sí que me gustaría resaltar una de ellas: que systemd reinventa muchos subsistemas que no estaban rotos, creando incompatibilidades sutiles con infraestructura existente.
Uno de estos subsistemas es el de resolución de nombres, es decir, cómo tu servidor traduce nombres de dominio en direcciones de red. systemd tiene su propia implementación: systemd-resolved. Ubuntu incluyó systemd-resolved en la versión 16.10 y ahora está presente en la versión LTS actual – 18.04.
systemd-resolved proporciona a las aplicaciones locales una interfaz con el DNS. Además de implementar un resolver añade características adicionales, tales como caché DNS y validación DNSSEC. Las aplicaciones pueden usar systemd-resolved de tres formas distintas:
- Usando su API D-Bus. D-Bus es un bus de mensajes para comunicación entre procesos. Es parte del proyecto freedesktop.org y systemd hace un uso extensivo del mismo. Por lo que en general, los clientes más probables de esta interfaz son las aplicaciones de escritorio y los servicios de systemd.
- Usando su implementación del API de glibc – getaddrinfo(3) y funciones relacionadas. Esta interfaz no soporta actualmente todas las capacidades de systemd-resolved, y además se requiere configuración adicional para que systemd-resolved se encargue de la resolución de nombres en este caso. Si se configura para que lo haga, diría que la mayoría de software que se ejecuta en tu servidor usaría este interfaz con systemd-resolved.
- Usando el DNS local de systemd-resolved que escucha en la interfaz de loopback en la dirección 127.0.0.53. Si una aplicación construye consultas DNS directamente, ésta es la única forma de que systemd-resolved pueda resolver dichas consultas.
Umm, esto empieza a complicarse… ¿verdad? En un mismo servidor, el modo en que la resolución de nombres se comporta realmente depende de la aplicación y de la configuración. Y como veremos las cosas se pueden volver más complejas aún.
Repasemos rápidamente cómo ha funcionado la resolución de nombres en Linux tradicionalmente.
/etc/resolv.conf y amigos
Si alguna vez has hecho algo de configuración de sistemas en Linux, sabrás que el «resolver» de nombres se configura en /etc/resolv.conf. Normalmente consiste en una lista de nameservers que se consultan en orden. Esto es todo. En los viejos tiempos el administrador de sistemas configuraba este fichero y a otra cosa.
Pero entonces llegaron entornos más dinámicos y se convirtieron en el caso común. En particular los escritorios Linux y los proveedores de cloud computing requieren entornos de red dinámicos, por lo que un archivo de configuración estático para la resolución de nombres no era la mejor solución en esos casos. Por tanto, se empezaron a usar (y se siguen usando) aplicaciones como resolvconf para actualizar dinámicamente /etc/resolv.conf en base a información externa. resolvconf no está pensado para usarse a mano, sino desde otro software de configuración como ifup, ifdown, dhclient, o dnsmasq.
¿Cómo se relaciona todo esto con systemd-resolved? Pues bien, volvemos a encontrar más complejidad. systemd-resolved podría ser el proveedor de /etc/resolv.conf o bien un consumidor más de ese fichero. Depende del modo de compatibilidad que el administrador de sistemas haya determinado. Básicamente, puedes delegar en 127.0.0.53 para la resolución de nombres, usar systemd-resolved para que las aplicaciones se salten systemd-resolved, o dejar que otros paquetes administren /etc/resolv.conf.
Ok, supongo que en estos momentos estarás algo confuso… Permíteme que explique el problema que motivó esta entrada en el blog y lo usaré como ejemplo para navegar por algunas de estas configuraciones.
El problema de resolución de nombres
El problema que nuestro cliente estaba teniendo en el servidor Ubuntu 18.04 de su proveedor cloud era éste:
root@systemd-00:~# gpg --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 14AA40EC0831756756D7F66C4F4EA0AAE5267A6C gpg: keyserver receive failed: Invalid argument
gpg
delega en dirmngr
(ambos parte del proyecto GNU Privacy Guard) el manejo de certificados y listas de revocación. Al buscar en los logs apropiados, encontré un fallo de resolución para el nombre de host keyserver.ubuntu.com:
root@systemd-00:~# cat /var/log/syslog | grep dirmngr Jun 5 09:13:44 ubuntu-1804-image dirmngr[3005]: resolving 'keyserver.ubuntu.com' failed: Invalid argument Jun 5 09:13:44 ubuntu-1804-image dirmngr[3005]: can't connect to 'keyserver.ubuntu.com': host not found [redacted]
Ok, el host no se podía encontrar. Pues tratemos de resolver el nombre de dominio:
root@systemd-00:~# host keyserver.ubuntu.com keyserver.ubuntu.com has address 91.189.89.49 keyserver.ubuntu.com has address 91.189.90.55
Umm… funciona – algo extraño está ocurriendo. ¿Qué servidores de nombres se están usando?
root@systemd-00:~# cat /etc/resolv.conf [redacted] nameserver 127.0.0.53 nameserver 1.0.0.1 nameserver 1.1.1.1
- 127.0.0.53: el «resolver» local de systemd-resolved.
- 1.1.1.1 and 1.0.0.1: resolver de Clouflare.
Dado que 127.0.0.53 es el primer servidor de nombres en /etc/resolv.conf, debería ser el que está procesando las consultas DNS en primer lugar. ¿Cómo está configurado systemd-resolved?
root@systemd-00:~# cat /etc/systemd/resolved.conf # This file is part of systemd. # # systemd is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # # Entries in this file show the compile time defaults. # You can change settings by editing this file. # Defaults can be restored by simply deleting this file. # # See resolved.conf(5) for details [Resolve] #DNS= #FallbackDNS= #Domains= #LLMNR=no #MulticastDNS=no #DNSSEC=no #Cache=yes #DNSStubListener=yes
Parece que todo es correcto. Por la última línea sabemos que el «resolver» local (127.0.0.53) está habilitado y debería responder a las peticiones de resolución de nombre. Comprobemos si se está ejecutando realmente:
root@systemd-00:~# systemctl status systemd-resolved.service ● systemd-resolved.service - Network Name Resolution Loaded: loaded (/lib/systemd/system/systemd-resolved.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2018-06-05 08:04:10 UTC; 12min ago [redacted]
root@systemd-00:~# netstat -nlutp Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN 1753/systemd-resolv udp 18432 0 127.0.0.53:53 0.0.0.0:* 1753/systemd-resolv [redacted]
Sí, se está ejecutando y escucha en los puertos apropiados – udp/53 y tcp/53. Pero espera, vimos antes que las aplicaciones también pueden interactuar con systemd-resolved por medio de las API de D-Bus o de la glibc. Si dirmngr
y host
usasen interfaces distintas, podríamos inferir que una de ellas expone un problema pero la otra no.
De hecho, dirmngr
utiliza la llamada getaddrinfo() de la glibc mientras que host
es parte de BIND y maneja las peticiones DNS directamente. Esto lo comprobé desensamblando los binarios con objdump
(paquete binutils), pero podría haber revisado el código fuente en su lugar. Entonces, ¿está atendiendo systemd-resolved las llamadas de dirmngr
de forma directa? Para contestar esto, tenemos que comprobar si la directiva hosts: en /etc/nsswitch.conf contiene la palabra clave «resolve». No es el caso en el servidor objeto de estudio.
root@systemd-00:~# cat /etc/nsswitch.conf # /etc/nsswitch.conf # # Example configuration of GNU Name Service Switch functionality. # If you have the `glibc-doc-reference' and `info' packages installed, try: # `info libc "Name Service Switch"' for information about this file. passwd: compat systemd group: compat systemd shadow: compat gshadow: files hosts: files dns networks: files protocols: db files services: db files ethers: db files rpc: db files netgroup: nis
Así que, aparentemente, podemos asumir quesystemd-resolved procesa las solicitudes de dirmngr
cuando llegan a 127.0.0.53. En caso de que el servidor de nombres local fallase, se le preguntaría a los servidores de Cloudflare en su lugar.
Entonces, ¿por qué el comando gpg
no puede resolver el nombre de host? ¿Qué está pasando realmente por debajo? El tráfico de red nos lo dirá. Capturemos las consultas y respuestas DNS con tcpdump
mientras ejecutamos gpg
.
En esta captura de pantalla podemos observar varias fases diferentes.
- La aplicacion busca las direcciones IPv4 (registros Tipo A) de keyserver.ubuntu.com. La consulta llega al resolver local de systemd-resolved, que emite tres consultas en paralelo – una para Cloudflare y dos para los servidores DNS de Google. La primera respuesta se reenvía a la aplicación.
- La aplicacion busca las direcciones IPv6 (registros Tipo AAAA) de keyserver.ubuntu.com. Mismo comportamiento que antes.
- La aplicación busca un registro Tipo 0 (Clase 7168) y el resolver local de systemd-resolved contesta con un Error de Formato.
- Las consultas expiran 5 segundos más tarde y el proceso se inicia de nuevo.
El registro de la fase 3 resulta ser RRSIG – almacena firmas digitales de recursos que se usan durante el proceso de autenticación de DNSSEC. En estos momentos, systemd-resolved no soporta consultas para estos registros (y otros similares) bajo ciertas condiciones. Podemos comprobarlo de forma sencilla forzando a 127.0.0.53 que resuelva una consulta RRSIG (falla). Si la misma consulta la dirigimos a los servidores de nombres de Cloudflare, ésta funciona sin problema:
root@systemd-00:~# host -t RRSIG keyserver.ubuntu.com 127.0.0.53 Using domain server: Name: 127.0.0.53 Address: 127.0.0.53#53 Aliases: Host keyserver.ubuntu.com not found: 1(FORMERR) root@systemd-00:~# host -t RRSIG keyserver.ubuntu.com 1.1.1.1 Using domain server: Name: 1.1.1.1 Address: 1.1.1.1#53 Aliases: keyserver.ubuntu.com has no RRSIG record
Por fin tenemos algo que explica por qué gpg
fallaba. Sin embargo aún no está claro por qué todas las consultas expiran, ya que los registros A y AAAA se pudieron resolver de forma satisfactoria. Sigamos investigándolo…
¿Poe qué systemd-resolved lanza 3 consultas en paralelo? Comprobemos su estado:
root@systemd-00:~# systemd-resolve --status Global DNS Servers: 1.0.0.1 1.1.1.1 DNSSEC NTA: 10.in-addr.arpa 16.172.in-addr.arpa 168.192.in-addr.arpa 17.172.in-addr.arpa 18.172.in-addr.arpa 19.172.in-addr.arpa 20.172.in-addr.arpa 21.172.in-addr.arpa 22.172.in-addr.arpa 23.172.in-addr.arpa 24.172.in-addr.arpa 25.172.in-addr.arpa 26.172.in-addr.arpa 27.172.in-addr.arpa 28.172.in-addr.arpa 29.172.in-addr.arpa 30.172.in-addr.arpa 31.172.in-addr.arpa corp d.f.ip6.arpa home internal intranet lan local private test Link 3 (eth1) Current Scopes: DNS LLMNR setting: yes MulticastDNS setting: no DNSSEC setting: no DNSSEC supported: no DNS Servers: 8.8.8.8 8.8.4.4 Link 2 (eth0) Current Scopes: DNS LLMNR setting: yes MulticastDNS setting: no DNSSEC setting: no DNSSEC supported: no DNS Servers: 8.8.8.8 8.8.4.4
Tenemos:
- 1.0.0.1 y 1.1.1.1: Los servidores de nombres de Cloudflare como servidores DNS globales. Aparecen en /etc/resolv.conf, tal y como vimos antes.
- 8.8.8.8 y 8.8.4.4: Los servidores de nombres de Google para consultas que salen por la interfaz eth0. Esta lista de servidores se especifica mediante una fuente externa, en este caso a través de un servidor DHCP.
- 8.8.8.8 y 8.8.4.4: Igual que arriba pero para la interfaz eth1.
Las tres consultas en paralelo se corresponden con el comportamiento esperado de systemd-resolved: una para el servidor de nombres global y otra por cada interfaz de red. ¿Quién está estableciendo Cloudflare como proveedor de nombres global? Veamos de nuevo /etc/resolv.conf:
root@systemd-00:~# ls -l /etc/resolv.conf lrwxrwxrwx 1 root root 29 May 7 12:36 /etc/resolv.conf -> ../run/resolvconf/resolv.conf root@systemd-00:~# cat /etc/resolv.conf # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8) # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN # 127.0.0.53 is the systemd-resolved stub resolver. # run "systemd-resolve --status" to see details about the actual nameservers. nameserver 127.0.0.53 nameserver 1.0.0.1 nameserver 1.1.1.1
¡Extraña configuración! ¿No te parece? Fíjate en las siguientes contradicciones:
- Estás usando systemd-resolved, que utiliza para cada interfaz de red la configuración DNS que le indica el servidor DHCP. Y el servidor DHCP proporciona servidores DNS externos – los de Google.
- Pero /etc/resolv.conf lo está gestionando
resolvconf
, no systemd-resolved. Esto debería seignificar que el administrador de sistemas quiere usar el último modo de compatibilidad de systemd-resolved respecto a /etc/resolv.conf. Sin embargo, como 127.0.0.53 aparece listado como servidor de nombres, systemd-resolved acaba tomando el control de la resolución. - Pero
resolvconf
incluye servidores DNS externos adicionales (y diferentes a los anteriores) – los de Cloudflare – para tu configuración de resolución de nombres global.
Esta compleja configuración junto al bug de RRSIG que vimos antes provocan que tu sistema se rompa de una forma muy sutil, difícil de depurar, y difícil de entender. En particular, cuando algunas consultas están esperando una respuesta de algunos de los servidores upstream, pero systemd-resolved recibe una consulta que no soporta (como la del registro RRSIG), el proceso de resolución de nombres falla completamente.
En mi opinión, la configuración que ha realizado el proveedor cloud en este caso es incorrecta (desde el punto de vista de la mantenibilidad) y deberían corregirla. Aunque funcionase, tiene poco sentido y añade complejidad a un asunto que ya es de por sí bastante complejo. El proveedor debería elegir una política clara – bien usar 127.0.0.53 como único servidor de nombres o eliminar por completo systemd-resolved.
Conclusión
El DNS es un componente crítico de Internet, y es más complejo de lo que podría parecer a simple vista. En aras de proporcionar más funcionalidad, systemd-resolved añade más complejidad al asunto. Según el autor de systemd-resolved:
resolved is not supposed to be a DNS server, it’s supposed to be exactly good enough so that libc-like DNS clients can resolve their stuff
Por tanto, no parece muy afortunado que distribuciones como Ubuntu Server (entre otras) y proveedores cloud lo incluyan como solución de resolución de nombres por defecto sin una configuración muy cuidada.
En este artículo he tratado de mostrarte el doloroso camino para seguirle la pista a un problema real en un proveedor real. Si crees que hay algo incorrecto en lo que he expuesto aquí, mándame un mensaje y estaré encantado de corregirlo. ¡Ah!, y no olvides registrate abajo si quieres que te enviemos un email cuando publiquemos nuevos artículos 😀