resolved: add ability to define additional local RRs via drop-ins

This is an extension of the /etc/hosts concept, but can provide any kind
of RRs (well, actually, we only parse A/AAAA/PTR for now, but the
concept is open for more).

Fixes: #17791
This commit is contained in:
Lennart Poettering
2026-03-24 21:24:48 +01:00
parent 7ad4411a61
commit 0718a21c13
13 changed files with 462 additions and 1 deletions
+23 -1
View File
@@ -363,12 +363,33 @@ DNSStubListenerExtra=udp:[2001:db8:0:f102::13]:9953</programlisting>
<term><varname>ReadEtcHosts=</varname></term>
<listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default),
<command>systemd-resolved</command> will read <filename>/etc/hosts</filename>, and try to resolve
hosts or address by using the entries in the file before sending query to DNS servers.
hosts or addresses by using the entries in the file before sending query to DNS servers.
</para>
<xi:include href="version-info.xml" xpointer="v240"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>ReadStaticRecords=</varname></term>
<listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default),
<command>systemd-resolved</command> will read
<filename>/etc/systemd/resolve/static.d/*.rr</filename>,
<filename>/run/systemd/resolve/static.d/*.rr</filename>,
<filename>/usr/local/lib/systemd/resolve/static.d/*.rr</filename>,
<filename>/usr/lib/systemd/resolve/static.d/*.rr</filename>, and try to resolve lookups by using the
entries in these files before sending query to DNS servers. This functionality is very similar to the
one controlled by <varname>ReadEtcHosts=</varname>, but allows more flexible control of DNS resource
records fields beyond just A/AAAA/PTR. See
<citerefentry><refentrytitle>systemd.rr</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details.</para>
<para>If both this option and <varname>ReadEtcHosts=</varname> are enabled then this mechanism takes
precedence: any records discovered via static resource records will take precedence over records
under the same name from <filename>/etc/hosts</filename>.</para>
<xi:include href="version-info.xml" xpointer="v261"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>ResolveUnicastSingleLabel=</varname></term>
<listitem><para>Takes a boolean argument. When false (the default),
@@ -418,6 +439,7 @@ DNSStubListenerExtra=udp:[2001:db8:0:f102::13]:9953</programlisting>
<member><citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>dnssec-trust-anchors.d</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.rr</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry project='man-pages'><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
+1
View File
@@ -1280,6 +1280,7 @@ manpages = [
['systemd.pcrlock', '5', ['systemd.pcrlock.d'], ''],
['systemd.preset', '5', [], ''],
['systemd.resource-control', '5', [], ''],
['systemd.rr', '5', [], 'ENABLE_RESOLVE'],
['systemd.scope', '5', [], ''],
['systemd.service', '5', [], ''],
['systemd.slice', '5', [], ''],
+11
View File
@@ -131,6 +131,16 @@
<listitem><para>The hostname <literal>_localdnsproxy</literal> is resolved to the IP address 127.0.0.54,
i.e. the address the local DNS proxy (see above) is listening on.</para></listitem>
<listitem><para>The files matching <filename>/etc/systemd/resolve/static.d/*.rr</filename>,
<filename>/run/systemd/resolve/static.d/*.rr</filename>,
<filename>/usr/local/lib/systemd/resolve/static.d/*.rr</filename>,
<filename>/usr/lib/systemd/resolve/static.d/*.rr</filename> may be used to define arbitrary
records. See
<citerefentry><refentrytitle>systemd.rr</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details. Support for this may be disabled with <varname>ReadStaticRecords=no</varname>, see
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
<listitem><para>The mappings defined in <filename>/etc/hosts</filename> are resolved to their
configured addresses and back, but they will not affect lookups for non-address types (like MX).
Support for <filename>/etc/hosts</filename> may be disabled with <varname>ReadEtcHosts=no</varname>,
@@ -510,6 +520,7 @@ search foobar.com barbar.com
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.dns-delegate</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.rr</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.dnssd</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>dnssec-trust-anchors.d</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
+95
View File
@@ -0,0 +1,95 @@
<?xml version='1.0'?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd.rr"
xmlns:xi="http://www.w3.org/2001/XInclude"
conditional='ENABLE_RESOLVE'>
<refentryinfo>
<title>systemd.rr</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd.rr</refentrytitle>
<manvolnum>5</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd.rr</refname>
<refpurpose>Local static DNS resource record definitions</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><simplelist>
<member><filename>/etc/systemd/resolve/static.d/*.rr</filename></member>
<member><filename>/run/systemd/resolve/static.d/*.rr</filename></member>
<member><filename>/usr/local/lib/systemd/resolve/static.d/*.rr</filename></member>
<member><filename>/usr/lib/systemd/resolve/static.d/*.rr</filename></member>
</simplelist></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>*.rr</filename> files may be used to define resource record sets ("RRsets") that shall be
resolvable locally, similar in style to address records defined by <filename>/etc/hosts</filename> (see
<citerefentry><refentrytitle>hosts</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details). These files are read by
<citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
and are used to synthesize local responses to local queries matching the defined resource record set.</para>
<para>These drop-in files are in JSON format. Each file may either contain a single top-level DNS RR
object, or an array of one or more DNS RR objects. Each RR object has at least a <literal>key</literal>
subobject consisting of a <literal>name</literal> string field and a <literal>type</literal> integer
field (which contains the RR type in numeric form). Depending on the chosen type the RR object also has
the following fields:</para>
<itemizedlist>
<listitem><para>For A/AAAA RRs, the RR object should have an <literal>address</literal> field set to
either an IP address formatted as string, or an array consisting of 4 or 16 8-bit unsigned integers for
the IP address.</para></listitem>
<listitem><para>For PTR/NS/CNAME/DNAME RRs, the RR object should have a <literal>name</literal> field
set to the name the record shall point to.</para></listitem>
</itemizedlist>
<para>This JSON serialization of DNS RRs matches the one returned by <command>resolvectl</command>.</para>
<para>Currently no other RR types are supported.</para>
</refsect1>
<refsect1>
<title>Examples</title>
<example>
<title>Simple A Record</title>
<para>To make local address lookups for <literal>foobar.example.com</literal> resolve to the
192.168.100.1 IPv4 address, create
<filename>/run/systemd/resolve/static.d/foobar_example_com.rr</filename>:
<programlisting>
{
"key" : {
"type" : 1,
"name" : "foobar.example.com"
},
"address" : [ 192, 168, 100, 1 ]
}</programlisting></para>
</example>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>hosts</refentrytitle><manvolnum>5</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>resolvectl</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>
+1
View File
@@ -36,6 +36,7 @@ systemd_resolved_extract_sources = files(
'resolved-mdns.c',
'resolved-resolv-conf.c',
'resolved-socket-graveyard.c',
'resolved-static-records.c',
'resolved-util.c',
'resolved-varlink.c',
)
+36
View File
@@ -21,6 +21,7 @@
#include "resolved-etc-hosts.h"
#include "resolved-hook.h"
#include "resolved-manager.h"
#include "resolved-static-records.h"
#include "resolved-timeouts.h"
#include "set.h"
#include "string-util.h"
@@ -910,6 +911,33 @@ static int dns_query_try_etc_hosts(DnsQuery *q) {
return 1;
}
static int dns_query_try_static_records(DnsQuery *q) {
int r;
assert(q);
if (FLAGS_SET(q->flags, SD_RESOLVED_NO_SYNTHESIZE))
return 0;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL;
r = manager_static_records_lookup(
q->manager,
q->question_bypass ? q->question_bypass->question : q->question_utf8,
&answer);
if (r <= 0)
return r;
dns_query_reset_answer(q);
q->answer = TAKE_PTR(answer);
q->answer_rcode = DNS_RCODE_SUCCESS;
q->answer_protocol = dns_synthesize_protocol(q->flags);
q->answer_family = dns_synthesize_family(q->flags);
q->answer_query_flags = SD_RESOLVED_AUTHENTICATED|SD_RESOLVED_CONFIDENTIAL|SD_RESOLVED_SYNTHETIC;
return 1;
}
static int dns_query_go_scopes(DnsQuery *q) {
int r;
@@ -1038,6 +1066,14 @@ int dns_query_go(DnsQuery *q) {
q->state != DNS_TRANSACTION_NULL)
return 0;
r = dns_query_try_static_records(q);
if (r < 0)
return r;
if (r > 0) {
dns_query_complete(q, DNS_TRANSACTION_SUCCESS);
return 1;
}
r = dns_query_try_etc_hosts(q);
if (r < 0)
return r;
+1
View File
@@ -31,6 +31,7 @@ Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0,
Resolve.Cache, config_parse_dns_cache_mode, DNS_CACHE_MODE_YES, offsetof(Manager, enable_cache)
Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode)
Resolve.ReadEtcHosts, config_parse_bool, 0, offsetof(Manager, read_etc_hosts)
Resolve.ReadStaticRecords, config_parse_bool, 0, offsetof(Manager, read_static_records)
Resolve.ResolveUnicastSingleLabel, config_parse_bool, 0, offsetof(Manager, resolve_unicast_single_label)
Resolve.DNSStubListenerExtra, config_parse_dns_stub_listener_extra, 0, offsetof(Manager, dns_extra_stub_listeners)
Resolve.CacheFromLocalhost, config_parse_bool, 0, offsetof(Manager, cache_from_localhost)
+5
View File
@@ -49,6 +49,7 @@
#include "resolved-mdns.h"
#include "resolved-resolv-conf.h"
#include "resolved-socket-graveyard.h"
#include "resolved-static-records.h"
#include "resolved-util.h"
#include "resolved-varlink.h"
#include "set.h"
@@ -637,6 +638,7 @@ static void manager_set_defaults(Manager *m) {
m->enable_cache = DNS_CACHE_MODE_YES;
m->dns_stub_listener_mode = DNS_STUB_LISTENER_YES;
m->read_etc_hosts = true;
m->read_static_records = true;
m->resolve_unicast_single_label = false;
m->cache_from_localhost = false;
m->stale_retention_usec = 0;
@@ -660,6 +662,7 @@ static int manager_dispatch_reload_signal(sd_event_source *s, const struct signa
m->delegates = hashmap_free(m->delegates);
dns_trust_anchor_flush(&m->trust_anchor);
manager_etc_hosts_flush(m);
manager_static_records_flush(m);
manager_set_defaults(m);
@@ -730,6 +733,7 @@ int manager_new(Manager **ret) {
.read_resolv_conf = true,
.need_builtin_fallbacks = true,
.etc_hosts_last = USEC_INFINITY,
.static_records_last = USEC_INFINITY,
.sigrtmin18_info.memory_pressure_handler = manager_memory_pressure,
.sigrtmin18_info.memory_pressure_userdata = m,
@@ -918,6 +922,7 @@ Manager* manager_free(Manager *m) {
dns_trust_anchor_flush(&m->trust_anchor);
manager_etc_hosts_flush(m);
manager_static_records_flush(m);
while ((sb = hashmap_first(m->dns_service_browsers)))
dns_service_browser_free(sb);
+6
View File
@@ -123,6 +123,12 @@ typedef struct Manager {
struct stat etc_hosts_stat;
bool read_etc_hosts;
/* Data from {/etc,/run,/usr/local/lib,/usr/lib}/systemd/resolve/static.d/ */
Hashmap *static_records;
usec_t static_records_last;
Set *static_records_stat;
bool read_static_records;
/* List of refused DNS Record Types */
Set *refuse_record_types;
+226
View File
@@ -0,0 +1,226 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-json.h"
#include "alloc-util.h"
#include "conf-files.h"
#include "constants.h"
#include "dns-answer.h"
#include "dns-domain.h"
#include "dns-question.h"
#include "dns-rr.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "hashmap.h"
#include "json-util.h"
#include "log.h"
#include "resolved-manager.h"
#include "resolved-static-records.h"
#include "set.h"
#include "stat-util.h"
/* This implements a mechanism to extend what systemd-resolved resolves locally, via .rr drop-ins in
* {/etc,/run,/usr/local/lib,/usr/lib}/systemd/resolve/static.d/. These files are in JSON format, and are RR
* serializations, that match the usual way we serialize RRs to JSON.
*
* Note that this deliberately doesn't use the (probably more user-friendly) classic DNS zone file format,
* to keep things a bit simpler, and symmetric to the places we currently already generate JSON
* serializations of DNS RRs. Also note the semantics are different from DNS zone file format, for example
* regarding delegation (i.e. the RRs defined here have no effect on subdomains), which is probably nicer for
* one-off mappings of domains to specific resources. Or in other words, this is supposed to be a drop-in
* based alternative to /etc/hosts, not a one to DNS zone files. (The JSON format is also a lot more
* extensible to us, for example we could teach it to map certain lookups to specific DNS errors, or extend
* it so that subdomains always get NXDOMAIN or similar).
*
* (That said, if there's a good reason, we can also support *.zone files too one day).
*/
/* Recheck static records at most once every 2s */
#define STATIC_RECORDS_RECHECK_USEC (2*USEC_PER_SEC)
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
answer_by_name_hash_ops,
char,
dns_name_hash_func,
dns_name_compare_func,
DnsAnswer,
dns_answer_unref);
static int load_static_record_file_item(sd_json_variant *rj, Hashmap **records) {
int r;
_cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL;
r = dns_resource_record_from_json(rj, &rr);
if (r < 0)
return log_error_errno(r, "Failed to parse DNS record from JSON: %m");
_cleanup_(dns_answer_unrefp) DnsAnswer *a =
hashmap_remove(*records, dns_resource_key_name(rr->key));
r = dns_answer_add_extend_full(&a, rr, /* ifindex= */ 0, DNS_ANSWER_AUTHENTICATED, /* rrsig= */ NULL, /* until= */ USEC_INFINITY);
if (r < 0)
return log_error_errno(r, "Failed to append RR to DNS answer: %m");
DnsAnswerItem *item = ASSERT_PTR(ordered_set_first(a->items));
r = hashmap_ensure_put(records, &answer_by_name_hash_ops, dns_resource_key_name(item->rr->key), a);
if (r < 0)
return log_error_errno(r, "Failed to add RR to static record set: %m");
TAKE_PTR(a);
log_debug("Added static resource record: %s", dns_resource_record_to_string(rr));
return 1;
}
static int load_static_record_file(const ConfFile *cf, Hashmap **records, Set **stats) {
int r;
assert(cf);
assert(records);
assert(stats);
/* Have we seen this file before? Then we might as well skip loading it again, it wouldn't have any
* additional effect anyway. (Note: masking/overriding has already been applied before we reach this
* point, here everything is purely additive.) */
if (set_contains(*stats, &cf->st))
return 0;
_cleanup_free_ struct stat *st_copy = memdup(&cf->st, sizeof(cf->st));
if (!st_copy)
return log_oom();
if (set_ensure_consume(stats, &inode_unmodified_hash_ops, TAKE_PTR(st_copy)) < 0)
return log_oom();
_cleanup_fclose_ FILE *f = NULL;
r = xfopenat(cf->fd, /* path= */ NULL, "re", /* open_flags= */ 0, &f);
if (r < 0) {
log_warning_errno(r, "Failed to open '%s', skipping: %m", cf->result);
return 0;
}
_cleanup_(sd_json_variant_unrefp) sd_json_variant *j = NULL;
unsigned line = 0, column = 0;
r = sd_json_parse_file(f, cf->result, /* flags= */ 0, &j, &line, &column);
if (r < 0) {
if (line > 0)
log_syntax(/* unit= */ NULL, LOG_WARNING, cf->result, line, r, "Failed to parse JSON, skipping: %m");
else
log_warning_errno(r, "Failed to parse JSON file '%s', skipping: %m", cf->result);
return 0;
}
if (sd_json_variant_is_array(j)) {
sd_json_variant *i;
int ret = 0;
JSON_VARIANT_ARRAY_FOREACH(i, j)
RET_GATHER(ret, load_static_record_file_item(i, records));
if (ret < 0)
return ret;
} else if (sd_json_variant_is_object(j)) {
r = load_static_record_file_item(j, records);
if (r < 0)
return r;
} else {
log_warning("JSON file '%s' contains neither array nor object, skipping.", cf->result);
return 0;
}
return 1;
}
static int manager_static_records_read(Manager *m) {
int r;
usec_t ts;
assert_se(sd_event_now(m->event, CLOCK_BOOTTIME, &ts) >= 0);
/* See if we checked the static records db recently already */
if (m->static_records_last != USEC_INFINITY && usec_add(m->static_records_last, STATIC_RECORDS_RECHECK_USEC) > ts)
return 0;
m->static_records_last = ts;
ConfFile **files = NULL;
size_t n_files = 0;
CLEANUP_ARRAY(files, n_files, conf_file_free_many);
r = conf_files_list_nulstr_full(
".rr",
/* root= */ NULL,
CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED|CONF_FILES_WARN,
CONF_PATHS_NULSTR("systemd/resolve/static.d/"),
&files,
&n_files);
if (r < 0)
return log_error_errno(r, "Failed to enumerate static record drop-ins: %m");
/* Let's suppress reloads if nothing changed. For that keep the set of inodes from the previous
* reload around, and see if there are any changes on them. */
bool reload;
if (set_size(m->static_records_stat) != n_files)
reload = true;
else {
reload = false;
FOREACH_ARRAY(f, files, n_files)
if (!set_contains(m->static_records_stat, &(*f)->st)) {
reload = true;
break;
}
}
if (!reload) {
log_debug("No static record files changed, not re-reading.");
return 0;
}
_cleanup_(hashmap_freep) Hashmap *records = NULL;
_cleanup_(set_freep) Set *stats = NULL;
FOREACH_ARRAY(f, files, n_files)
(void) load_static_record_file(*f, &records, &stats);
hashmap_free(m->static_records);
m->static_records = TAKE_PTR(records);
set_free(m->static_records_stat);
m->static_records_stat = TAKE_PTR(stats);
return 0;
}
int manager_static_records_lookup(Manager *m, DnsQuestion *q, DnsAnswer **answer) {
int r;
assert(m);
assert(q);
assert(answer);
if (!m->read_static_records)
return 0;
(void) manager_static_records_read(m);
const char *n = dns_question_first_name(q);
if (!n)
return 0;
DnsAnswer *f = hashmap_get(m->static_records, n);
if (!f)
return 0;
r = dns_answer_extend(answer, f);
if (r < 0)
return r;
return 1;
}
void manager_static_records_flush(Manager *m) {
assert(m);
m->static_records = hashmap_free(m->static_records);
m->static_records_stat = set_free(m->static_records_stat);
m->static_records_last = USEC_INFINITY;
}
+7
View File
@@ -0,0 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "resolved-forward.h"
void manager_static_records_flush(Manager *m);
int manager_static_records_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer);
+1
View File
@@ -39,6 +39,7 @@
#DNSStubListener=yes
#DNSStubListenerExtra=
#ReadEtcHosts=yes
#ReadStaticRecords=yes
#ResolveUnicastSingleLabel=no
#StaleRetentionSec=0
#RefuseRecordTypes=
+49
View File
@@ -1487,6 +1487,55 @@ EOF
grep -qF "1.2.3.4" "$RUN_OUT"
}
testcase_static_record() {
mkdir -p /run/systemd/resolve/static.d/
cat >/run/systemd/resolve/static.d/statictest.rr <<EOF
{
"key": { "name" : "statictest.waldo", "type" : 1 },
"address" : [ 5, 7, 9, 11 ]
}
EOF
cat >/run/systemd/resolve/static.d/statictest2.rr <<EOF
[
{
"key": { "name" : "statictest2.waldo", "type" : 1 },
"address" : [ 5, 7, 9, 12 ]
},
{
"key": { "name" : "statictest2.waldo", "type" : 28 },
"address" : [ 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 11, 10, 12 ]
}
]
EOF
cat >/run/systemd/resolve/static.d/garbage.rr <<EOF
[
{
"key": { "name" : "invalid...domain", "type" : 1 },
"address" : [ 5, 7, 9, 12 ]
},
]
EOF
cat >/run/systemd/resolve/static.d/garbage2.rr <<EOF
[
{
"key": { "name" : "piff", "type" : 9 },
},
]
EOF
systemctl reload systemd-resolved
run resolvectl query statictest.waldo
grep -qF 5.7.9.11 "$RUN_OUT"
run resolvectl query statictest2.waldo
grep -qF 5.7.9.12 "$RUN_OUT"
grep -qF a0b:a0b:a0b:a0b:a0b:a0b:a0b:a0c "$RUN_OUT"
rm /run/systemd/resolve/static.d/statictest*.rr /run/systemd/resolve/static.d/garbage*.rr
systemctl reload systemd-resolved
}
# PRE-SETUP
systemctl unmask systemd-resolved.service
systemctl enable --now systemd-resolved.service