Merge pull request #31951 from bluca/resolve_reload

resolved: support reloading configuration at runtime
This commit is contained in:
Yu Watanabe 2024-03-27 02:37:52 +09:00 committed by GitHub
commit d30d0b04ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 167 additions and 24 deletions

View file

@ -421,6 +421,16 @@ search foobar.com barbar.com
<xi:include href="version-info.xml" xpointer="v235"/></listitem>
</varlistentry>
<varlistentry>
<term><constant>SIGHUP</constant></term>
<listitem><para>Upon reception of the <constant>SIGHUP</constant> process signal
<command>systemd-resolved</command> will flush all caches it maintains, drop all open TCP
connections (if any), and reload its configuration files.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
</variablelist>
</refsect1>

View file

@ -1890,6 +1890,7 @@ static int bus_method_register_service(sd_bus_message *message, void *userdata,
if (r < 0)
return r;
service->originator = euid;
service->config_source = RESOLVE_CONFIG_SOURCE_DBUS;
r = sd_bus_message_read(message, "sssqqq", &name, &name_template, &type,
&service->port, &service->priority,

View file

@ -55,7 +55,7 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons
return 0;
}
return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name);
return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name, RESOLVE_CONFIG_SOURCE_FILE);
}
int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) {

View file

@ -3,6 +3,14 @@
#include "conf-parser.h"
typedef enum ResolveConfigSource {
RESOLVE_CONFIG_SOURCE_FILE,
RESOLVE_CONFIG_SOURCE_NETWORKD,
RESOLVE_CONFIG_SOURCE_DBUS,
_RESOLVE_CONFIG_SOURCE_MAX,
_RESOLVE_CONFIG_SOURCE_INVALID = -EINVAL,
} ResolveConfigSource;
#include "resolved-dns-server.h"
int manager_parse_config_file(Manager *m);

View file

@ -28,7 +28,8 @@ int dns_server_new(
const union in_addr_union *in_addr,
uint16_t port,
int ifindex,
const char *server_name) {
const char *server_name,
ResolveConfigSource config_source) {
_cleanup_free_ char *name = NULL;
DnsServer *s;
@ -67,6 +68,7 @@ int dns_server_new(
.port = port,
.ifindex = ifindex,
.server_name = TAKE_PTR(name),
.config_source = config_source,
};
dns_server_reset_features(s);
@ -794,6 +796,17 @@ void dns_server_unlink_all(DnsServer *first) {
dns_server_unlink_all(next);
}
void dns_server_unlink_on_reload(DnsServer *server) {
while (server) {
DnsServer *next = server->servers_next;
if (server->config_source == RESOLVE_CONFIG_SOURCE_FILE)
dns_server_unlink(server);
server = next;
}
}
bool dns_server_unlink_marked(DnsServer *server) {
bool changed = false;

View file

@ -24,6 +24,8 @@ typedef enum DnsServerType {
_DNS_SERVER_TYPE_INVALID = -EINVAL,
} DnsServerType;
#include "resolved-conf.h"
const char* dns_server_type_to_string(DnsServerType i) _const_;
DnsServerType dns_server_type_from_string(const char *s) _pure_;
@ -100,6 +102,9 @@ struct DnsServer {
/* If linked is set, then this server appears in the servers linked list */
bool linked:1;
LIST_FIELDS(DnsServer, servers);
/* Servers registered via D-Bus are not removed on reload */
ResolveConfigSource config_source;
};
int dns_server_new(
@ -111,7 +116,8 @@ int dns_server_new(
const union in_addr_union *address,
uint16_t port,
int ifindex,
const char *server_string);
const char *server_string,
ResolveConfigSource config_source);
DnsServer* dns_server_ref(DnsServer *s);
DnsServer* dns_server_unref(DnsServer *s);
@ -145,6 +151,7 @@ void dns_server_warn_downgrade(DnsServer *server);
DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, uint16_t port, int ifindex, const char *name);
void dns_server_unlink_all(DnsServer *first);
void dns_server_unlink_on_reload(DnsServer *server);
bool dns_server_unlink_marked(DnsServer *first);
void dns_server_mark_all(DnsServer *first);

View file

@ -57,6 +57,16 @@ DnssdService *dnssd_service_free(DnssdService *service) {
return mfree(service);
}
void dnssd_service_clear_on_reload(Hashmap *services) {
DnssdService *service;
HASHMAP_FOREACH(service, services)
if (service->config_source == RESOLVE_CONFIG_SOURCE_FILE) {
hashmap_remove(services, service->name);
dnssd_service_free(service);
}
}
static int dnssd_service_load(Manager *manager, const char *filename) {
_cleanup_(dnssd_service_freep) DnssdService *service = NULL;
_cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;

View file

@ -3,6 +3,7 @@
#pragma once
#include "list.h"
#include "resolved-conf.h"
typedef struct DnssdService DnssdService;
typedef struct DnssdTxtData DnssdTxtData;
@ -44,6 +45,9 @@ struct DnssdService {
Manager *manager;
/* Services registered via D-Bus are not removed on reload */
ResolveConfigSource config_source;
bool withdrawn:1;
uid_t originator;
};
@ -51,6 +55,7 @@ struct DnssdService {
DnssdService *dnssd_service_free(DnssdService *service);
DnssdTxtData *dnssd_txtdata_free(DnssdTxtData *txt_data);
DnssdTxtData *dnssd_txtdata_free_all(DnssdTxtData *txt_data);
void dnssd_service_clear_on_reload(Hashmap *services);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnssdService*, dnssd_service_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnssdTxtData*, dnssd_txtdata_free);

View file

@ -274,7 +274,7 @@ static int bus_link_method_set_dns_servers_internal(sd_bus_message *message, voi
if (s)
dns_server_move_back_and_unmark(s);
else {
r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name);
r = dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, dns[i]->family, &dns[i]->address, dns[i]->port, 0, dns[i]->server_name, RESOLVE_CONFIG_SOURCE_DBUS);
if (r < 0) {
dns_server_unlink_all(l->dns_servers);
goto finalize;

View file

@ -273,7 +273,7 @@ static int link_update_dns_server_one(Link *l, const char *str) {
return 0;
}
return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, port, 0, name);
return dns_server_new(l->manager, NULL, DNS_SERVER_LINK, l, family, &a, port, 0, name, RESOLVE_CONFIG_SOURCE_NETWORKD);
}
static int link_update_dns_servers(Link *l) {

View file

@ -11,6 +11,7 @@
#include "af-list.h"
#include "alloc-util.h"
#include "bus-polkit.h"
#include "daemon-util.h"
#include "dirent-util.h"
#include "dns-domain.h"
#include "event-util.h"
@ -565,6 +566,73 @@ static int manager_memory_pressure_listen(Manager *m) {
return 0;
}
static void manager_set_defaults(Manager *m) {
assert(m);
m->llmnr_support = DEFAULT_LLMNR_MODE;
m->mdns_support = DEFAULT_MDNS_MODE;
m->dnssec_mode = DEFAULT_DNSSEC_MODE;
m->dns_over_tls_mode = DEFAULT_DNS_OVER_TLS_MODE;
m->enable_cache = DNS_CACHE_MODE_YES;
m->dns_stub_listener_mode = DNS_STUB_LISTENER_YES;
m->read_etc_hosts = true;
m->resolve_unicast_single_label = false;
m->cache_from_localhost = false;
m->stale_retention_usec = 0;
}
static int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
Manager *m = ASSERT_PTR(userdata);
int r;
(void) notify_reloading();
manager_set_defaults(m);
dns_server_unlink_on_reload(m->dns_servers);
dns_server_unlink_on_reload(m->fallback_dns_servers);
m->dns_extra_stub_listeners = ordered_set_free(m->dns_extra_stub_listeners);
dnssd_service_clear_on_reload(m->dnssd_services);
m->unicast_scope = dns_scope_free(m->unicast_scope);
dns_trust_anchor_flush(&m->trust_anchor);
r = dns_trust_anchor_load(&m->trust_anchor);
if (r < 0)
return r;
r = manager_parse_config_file(m);
if (r < 0)
log_warning_errno(r, "Failed to parse config file on reload: %m");
else
log_info("Config file reloaded.");
r = dnssd_load(m);
if (r < 0)
log_warning_errno(r, "Failed to load DNS-SD configuration files: %m");
/* The default scope configuration is influenced by the manager's configuration (modes, etc.), so
* recreate it on reload. */
r = dns_scope_new(m, &m->unicast_scope, NULL, DNS_PROTOCOL_DNS, AF_UNSPEC);
if (r < 0)
return r;
/* The configuration has changed, so reload the per-interface configuration too in order to take
* into account any changes (e.g.: enable/disable DNSSEC). */
r = on_network_event(/* sd_event_source= */ NULL, -EBADF, /* revents= */ 0, m);
if (r < 0)
log_warning_errno(r, "Failed to update network information: %m");
/* We have new configuration, which means potentially new servers, so close all connections and drop
* all caches, so that we can start fresh. */
(void) dns_stream_disconnect_all(m);
manager_flush_caches(m, LOG_INFO);
manager_verify_all(m);
(void) sd_notify(/* unset= */ false, NOTIFY_READY);
return 0;
}
int manager_new(Manager **ret) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
@ -584,21 +652,16 @@ int manager_new(Manager **ret) {
.mdns_ipv6_fd = -EBADF,
.hostname_fd = -EBADF,
.llmnr_support = DEFAULT_LLMNR_MODE,
.mdns_support = DEFAULT_MDNS_MODE,
.dnssec_mode = DEFAULT_DNSSEC_MODE,
.dns_over_tls_mode = DEFAULT_DNS_OVER_TLS_MODE,
.enable_cache = DNS_CACHE_MODE_YES,
.dns_stub_listener_mode = DNS_STUB_LISTENER_YES,
.read_resolv_conf = true,
.need_builtin_fallbacks = true,
.etc_hosts_last = USEC_INFINITY,
.read_etc_hosts = true,
.sigrtmin18_info.memory_pressure_handler = manager_memory_pressure,
.sigrtmin18_info.memory_pressure_userdata = m,
};
manager_set_defaults(m);
r = dns_trust_anchor_load(&m->trust_anchor);
if (r < 0)
return r;
@ -619,6 +682,7 @@ int manager_new(Manager **ret) {
(void) sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
(void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
(void) sd_event_add_signal(m->event, NULL, SIGHUP | SD_EVENT_SIGNAL_PROCMASK, manager_dispatch_reload_signal, m);
(void) sd_event_set_watchdog(m->event, true);

View file

@ -102,12 +102,21 @@ assert_in '_localdnsproxy' "$(dig @127.0.0.53 -x 127.0.0.54)"
mkdir -p /run/systemd/resolved.conf.d
{
echo "[Resolve]"
echo "MulticastDNS=yes"
echo "LLMNR=yes"
echo "MulticastDNS=no"
echo "LLMNR=no"
} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
restart_resolved
# make sure networkd is not running.
systemctl stop systemd-networkd.service
assert_in 'no' "$(resolvectl mdns hoge)"
assert_in 'no' "$(resolvectl llmnr hoge)"
# Tests that reloading works
{
echo "[Resolve]"
echo "MulticastDNS=yes"
echo "LLMNR=yes"
} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
systemctl reload systemd-resolved.service
# defaults to yes (both the global and per-link settings are yes)
assert_in 'yes' "$(resolvectl mdns hoge)"
assert_in 'yes' "$(resolvectl llmnr hoge)"
@ -130,7 +139,7 @@ assert_in 'no' "$(resolvectl llmnr hoge)"
echo "MulticastDNS=resolve"
echo "LLMNR=resolve"
} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
restart_resolved
systemctl reload systemd-resolved.service
# set per-link setting
resolvectl mdns hoge yes
resolvectl llmnr hoge yes
@ -150,7 +159,7 @@ assert_in 'no' "$(resolvectl llmnr hoge)"
echo "MulticastDNS=no"
echo "LLMNR=no"
} >/run/systemd/resolved.conf.d/mdns-llmnr.conf
restart_resolved
systemctl reload systemd-resolved.service
# set per-link setting
resolvectl mdns hoge yes
resolvectl llmnr hoge yes
@ -180,13 +189,13 @@ fd00:dead:beef:cafe::1 ns1.unsigned.test
127.128.0.5 localhost5 localhost5.localdomain localhost5.localdomain4 localhost.localdomain5 localhost5.localdomain5
EOF
mkdir -p /etc/systemd/network
cat >/etc/systemd/network/10-dns0.netdev <<EOF
mkdir -p /run/systemd/network
cat >/run/systemd/network/10-dns0.netdev <<EOF
[NetDev]
Name=dns0
Kind=dummy
EOF
cat >/etc/systemd/network/10-dns0.network <<EOF
cat >/run/systemd/network/10-dns0.network <<EOF
[Match]
Name=dns0
@ -197,12 +206,12 @@ DNSSEC=allow-downgrade
DNS=10.0.0.1
DNS=fd00:dead:beef:cafe::1
EOF
cat >/etc/systemd/network/10-dns1.netdev <<EOF
cat >/run/systemd/network/10-dns1.netdev <<EOF
[NetDev]
Name=dns1
Kind=dummy
EOF
cat >/etc/systemd/network/10-dns1.network <<EOF
cat >/run/systemd/network/10-dns1.network <<EOF
[Match]
Name=dns1
@ -254,7 +263,8 @@ ln -svf /etc/bind.keys /etc/bind/bind.keys
# Start the services
systemctl unmask systemd-networkd
systemctl start systemd-networkd
restart_resolved
/usr/lib/systemd/systemd-networkd-wait-online --interface=dns1:routable --timeout=60
systemctl reload systemd-resolved
systemctl start resolved-dummy-server
# Create knot's runtime dir, since from certain version it's provided only by
# the package and not created by tmpfiles/systemd
@ -720,7 +730,7 @@ if command -v nft >/dev/null; then
echo "StaleRetentionSec=1d"
} >/run/systemd/resolved.conf.d/test.conf
ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
restart_resolved
systemctl reload systemd-resolved.service
run dig stale1.unsigned.test -t A
grep -qE "NOERROR" "$RUN_OUT"
@ -850,6 +860,21 @@ test "$(resolvectl --json=short query -t A localhost)" == '{"key":{"class":1,"ty
test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":1}' --json=short)" == '{"rrs":[{"ifindex":1,"rr":{"key":{"class":1,"type":1,"name":"localhost"},"address":[127,0,0,1]},"raw":"CWxvY2FsaG9zdAAAAQABAAAAAAAEfwAAAQ=="}],"flags":786945}'
test "$(varlinkctl call /run/systemd/resolve/io.systemd.Resolve io.systemd.Resolve.ResolveRecord '{"name":"localhost","type":28}' --json=short)" == '{"rrs":[{"ifindex":1,"rr":{"key":{"class":1,"type":28,"name":"localhost"},"address":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]},"raw":"CWxvY2FsaG9zdAAAHAABAAAAAAAQAAAAAAAAAAAAAAAAAAAAAQ=="}],"flags":786945}'
# Ensure that reloading keeps the manually configured address
{
echo "[Resolve]"
echo "DNS=8.8.8.8"
} >/run/systemd/resolved.conf.d/reload.conf
resolvectl dns dns0 1.1.1.1
systemctl reload systemd-resolved.service
resolvectl status
resolvectl dns dns0 | grep -qF "1.1.1.1"
# For some reason piping this last command to grep fails with:
# 'resolvectl[1378]: Failed to print table: Broken pipe'
# so use an intermediate file in /tmp/
resolvectl >/tmp/output
grep -qF "DNS Servers: 8.8.8.8" /tmp/output
# Check if resolved exits cleanly.
restart_resolved

View file

@ -48,7 +48,7 @@ RuntimeDirectoryPreserve=yes
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service
Type=notify
Type=notify-reload
User=systemd-resolve
ImportCredential=network.dns
ImportCredential=network.search_domains