Volver
Una configuración problemática con systemd-resolved

Una configuración problemática con systemd-resolved

- - Artículos

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

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.

  1. 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.
  2. La aplicacion busca las direcciones IPv6 (registros Tipo AAAA) de keyserver.ubuntu.com. Mismo comportamiento que antes.
  3. La aplicación busca un registro Tipo 0 (Clase 7168) y el resolver local de systemd-resolved contesta con un Error de Formato.
  4. 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:

  1. 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.
  2. 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.
  3. 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 😀

¡No te pierdas ningún post! Subscríbete al Blog de Moss