]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: read DNS conf also from creds and kernel cmdline
authorLennart Poettering <lennart@poettering.net>
Thu, 5 Jan 2023 14:35:20 +0000 (15:35 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 5 Jan 2023 17:52:15 +0000 (18:52 +0100)
Note that this drops ProtectProc=invisible from
systemd-resolved.service.

This is done because othewise access to the booted "kernel" command line is not
necessarily available. That's because in containers we want to read
/proc/1/cmdline for that.

Fixes: #24103
man/kernel-command-line.xml
man/systemd-resolved.service.xml
man/systemd.system-credentials.xml
src/resolve/resolved-conf.c
units/systemd-resolved.service.in

index 545dc40798ae803bab86783f64bf155069b1f64d..0528c4b67255abd6dae80d465ab72119fd6ff8e7 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>nameserver=</varname></term>
+        <term><varname>domain=</varname></term>
+
+        <listitem><para>Configures DNS server information and search domains, see
+        <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+        for details.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>resume=</varname></term>
         <term><varname>resumeflags=</varname></term>
index c006c03b534f3991cbd29942e073e44632a9d012..7003c36db7e1e6ac71c8030fb02ff74ad7b8d930 100644 (file)
@@ -399,6 +399,49 @@ search foobar.com barbar.com
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>Credentials</title>
+
+    <para><command>systemd-resolved</command> supports the service credentials logic as implemented by
+    <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
+    <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+    details). The following credentials are used when passed in:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><varname>network.dns</varname></term>
+        <term><varname>network.search_domains</varname></term>
+
+        <listitem><para>May contain a space separated list of DNS server IP addresses and DNS search
+        domains. This information is only used when no explicit configuration via
+        <filename>/etc/systemd/resolved.conf</filename>, <filename>/etc/resolv.conf</filename> or the kernel
+        command line has been provided.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Kernel Command Line</title>
+
+    <para><command>systemd-resolved</command> also honours two kernel command line options:</para>
+
+    <variablelist class='kernel-commandline-options'>
+      <varlistentry>
+        <term><varname>nameserver=</varname></term>
+        <term><varname>domain=</varname></term>
+
+        <listitem><para>Takes the IP address of a DNS server (in case of <varname>nameserver=</varname>), and
+        a DNS search domain (in case of <varname>domain=</varname>). May be used multiple times, to define
+        multiple DNS servers/search domains. If either of these options are specified
+        <filename>/etc/resolv.conf</filename> will not be read and the <varname>DNS=</varname> and
+        <varname>Domains=</varname> settings of
+        <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        will be ignored. These two kernel command line options hence override system
+        configuration.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para>
index 3eadf9b9852cf2b850d36a926d82453427804721..9e49e3feaea18e8e45c807a8bf7c436d90a9e023 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>network.dns</varname></term>
+        <term><varname>network.search_domains</varname></term>
+        <listitem>
+          <para>DNS server information and search domains. Read by
+          <citerefentry><refentrytitle>systemd-resolved.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>passwd.hashed-password.root</varname></term>
         <term><varname>passwd.plaintext-password.root</varname></term>
index 2be5986f9b729ac75e6cc82ce6ce358d5b92d3d6..d6929984e9153d687e06c67f8ed8a69bac8e354a 100644 (file)
@@ -3,15 +3,17 @@
 #include "alloc-util.h"
 #include "conf-parser.h"
 #include "constants.h"
+#include "creds-util.h"
+#include "dns-domain.h"
 #include "extract-word.h"
 #include "hexdecoct.h"
 #include "parse-util.h"
+#include "proc-cmdline.h"
 #include "resolved-conf.h"
-#include "resolved-dnssd.h"
-#include "resolved-manager.h"
 #include "resolved-dns-search-domain.h"
 #include "resolved-dns-stub.h"
-#include "dns-domain.h"
+#include "resolved-dnssd.h"
+#include "resolved-manager.h"
 #include "socket-netlink.h"
 #include "specifier.h"
 #include "string-table.h"
@@ -463,6 +465,99 @@ int config_parse_dns_stub_listener_extra(
         return 0;
 }
 
+static void read_credentials(Manager *m) {
+        _cleanup_free_ char *dns = NULL, *domains = NULL;
+        int r;
+
+        assert(m);
+
+        /* Hmm, if we aren't supposed to read /etc/resolv.conf because the DNS settings were already
+         * configured explicitly in our config file, we don't want to honour credentials either */
+        if (!m->read_resolv_conf)
+                return;
+
+        r = read_credential_strings_many(
+                        "network.dns", &dns,
+                        "network.search_domains", &domains);
+        if (r < 0 && !IN_SET(r, -ENXIO, -ENOENT))
+                log_warning_errno(r, "Failed to read credentials, ignoring: %m");
+
+        if (dns) {
+                r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_SYSTEM, dns);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse credential provided DNS server string '%s', ignoring.", dns);
+
+                m->read_resolv_conf = false;
+        }
+
+        if (domains) {
+                r = manager_parse_search_domains_and_warn(m, domains);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse credential provided search domain string '%s', ignoring.", domains);
+
+                m->read_resolv_conf = false;
+        }
+}
+
+struct ProcCmdlineInfo {
+        Manager *manager;
+
+        /* If there's a setting configured via /proc/cmdline we want to reset the configured lists, but only
+         * once, so that multiple nameserver= or domain= settings can be specified on the kernel command line
+         * and will be combined. These booleans will be set once we erase the list once. */
+        bool dns_server_unlinked;
+        bool search_domain_unlinked;
+};
+
+static int proc_cmdline_callback(const char *key, const char *value, void *data) {
+        struct ProcCmdlineInfo *info = ASSERT_PTR(data);
+        int r;
+
+        assert(info->manager);
+
+        /* The kernel command line option names are chosen to be compatible with what various tools already
+         * interpret, for example dracut and SUSE Linux. */
+
+        if (proc_cmdline_key_streq(key, "nameserver")) {
+                if (!info->dns_server_unlinked) {
+                        /* The kernel command line overrides any prior configuration */
+                        dns_server_unlink_all(manager_get_first_dns_server(info->manager, DNS_SERVER_SYSTEM));
+                        info->dns_server_unlinked = true;
+                }
+
+                r = manager_parse_dns_server_string_and_warn(info->manager, DNS_SERVER_SYSTEM, value);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse DNS server string '%s', ignoring.", value);
+
+                info->manager->read_resolv_conf = false;
+
+        } else if (proc_cmdline_key_streq(key, "domain")) {
+
+                if (!info->search_domain_unlinked) {
+                        dns_search_domain_unlink_all(info->manager->search_domains);
+                        info->search_domain_unlinked = true;
+                }
+
+                r = manager_parse_search_domains_and_warn(info->manager, value);
+                if (r < 0)
+                        log_warning_errno(r, "Failed to parse credential provided search domain string '%s', ignoring.", value);
+
+                info->manager->read_resolv_conf = false;
+        }
+
+        return 0;
+}
+
+static void read_proc_cmdline(Manager *m) {
+        int r;
+
+        assert(m);
+
+        r = proc_cmdline_parse(proc_cmdline_callback, &(struct ProcCmdlineInfo) { .manager = m }, 0);
+        if (r < 0)
+                log_warning_errno(r, "Failed to read kernel command line, ignoring: %m");
+}
+
 int manager_parse_config_file(Manager *m) {
         int r;
 
@@ -479,6 +574,9 @@ int manager_parse_config_file(Manager *m) {
         if (r < 0)
                 return r;
 
+        read_credentials(m);   /* credentials are only used when nothing is explicitly configured … */
+        read_proc_cmdline(m);  /* … but kernel command line overrides local configuration. */
+
         if (m->need_builtin_fallbacks) {
                 r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS);
                 if (r < 0)
index 621fe34224daef97050a7f1b91fbfd587415db62..b4227ffd429298d70085bd87597ca6651e2ad54b 100644 (file)
@@ -30,7 +30,6 @@ MemoryDenyWriteExecute=yes
 NoNewPrivileges=yes
 PrivateDevices=yes
 PrivateTmp=yes
-ProtectProc=invisible
 ProtectClock=yes
 ProtectControlGroups=yes
 ProtectHome=yes
@@ -51,6 +50,8 @@ SystemCallErrorNumber=EPERM
 SystemCallFilter=@system-service
 Type=notify
 User=systemd-resolve
+LoadCredential=network.dns
+LoadCredential=network.search_domains
 {{SERVICE_WATCHDOG}}
 
 [Install]