Merge pull request #16059 from keszybz/resolve-single-label-names

Optionally resolve single label names
This commit is contained in:
Lennart Poettering 2020-06-22 14:00:31 +02:00 committed by GitHub
commit 7830b5c103
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 97 additions and 63 deletions

8
NEWS
View File

@ -87,6 +87,12 @@ CHANGES WITH 246 in spe:
used, the DNS-over-TLS certificate is validated to match the
specified hostname.
* systemd-resolved may be configured to forward single-label DNS names.
This is not standard-conformant, but may make sense in setups where
public DNS servers are not used.
* systemd-resolved's DNS-over-TLS support gained SNI validation.
* The fs.suid_dumpable sysctl is set to 2 / "suidsafe". This allows
systemd-coredump to save core files for suid processes. When saving
the core file, systemd-coredump will use the effective uid and gid of
@ -538,8 +544,6 @@ CHANGES WITH 245:
* systemd-sysusers gained support for creating users with the primary
group named differently than the user.
* systemd-resolved's DNS-over-TLS support gained SNI validation.
* systemd-growfs (i.e. the x-systemd.growfs mount option in /etc/fstab)
gained support for growing XFS partitions. Previously it supported
only ext4 and btrfs partitions.

View File

@ -67,20 +67,28 @@
<varlistentry>
<term><varname>Domains=</varname></term>
<listitem><para>A space-separated list of domains. These domains are used as search suffixes when resolving
single-label hostnames (domain names which contain no dot), in order to qualify them into fully-qualified
domain names (FQDNs). Search domains are strictly processed in the order they are specified, until the name
with the suffix appended is found. For compatibility reasons, if this setting is not specified, the search
domains listed in <filename>/etc/resolv.conf</filename> are used instead, if that file exists and any domains
are configured in it. This setting defaults to the empty list.</para>
<listitem><para>A space-separated list of domains optionally prefixed with <literal>~</literal>,
used for two distinct purposes described below. Defaults to the empty list.</para>
<para>Specified domain names may optionally be prefixed with <literal>~</literal>. In this case they do not
define a search path, but preferably direct DNS queries for the indicated domains to the DNS servers configured
with the system <varname>DNS=</varname> setting (see above), in case additional, suitable per-link DNS servers
are known. If no per-link DNS servers are known using the <literal>~</literal> syntax has no effect. Use the
construct <literal>~.</literal> (which is composed of <literal>~</literal> to indicate a routing domain and
<literal>.</literal> to indicate the DNS root domain that is the implied suffix of all DNS domains) to use the
system DNS server defined with <varname>DNS=</varname> preferably for all domains.</para></listitem>
<para>Any domains <emphasis>not</emphasis> prefixed with <literal>~</literal> are used as search
suffixes when resolving single-label hostnames (domain names which contain no dot), in order to
qualify them into fully-qualified domain names (FQDNs). These "search domains" are strictly processed
in the order they are specified in, until the name with the suffix appended is found. For
compatibility reasons, if this setting is not specified, the search domains listed in
<filename>/etc/resolv.conf</filename> with the <varname>search</varname> keyword are used instead, if
that file exists and any domains are configured in it.</para>
<para>The domains prefixed with <literal>~</literal> are called "routing domains". All domains listed
here (both search domains and routing domains after removing the <literal>~</literal> prefix) define
a search path that preferably directs DNS queries to this inteface. This search path has an effect
only when suitable per-link DNS servers are known. Such servers may be defined through the
<varname>DNS=</varname> setting (see above) and dynamically at run time, for example from DHCP
leases. If no per-link DNS servers are known, routing domains have no effect.</para>
<para>Use the construct <literal>~.</literal> (which is composed from <literal>~</literal> to
indicate a routing domain and <literal>.</literal> to indicate the DNS root domain that is the
implied suffix of all DNS domains) to use the DNS servers defined for this link preferably for all
domains.</para></listitem>
</varlistentry>
<varlistentry>
@ -258,11 +266,28 @@
<varlistentry>
<term><varname>ReadEtcHosts=</varname></term>
<listitem><para>Takes a boolean argument. If <literal>yes</literal> (the default), the DNS stub resolver 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.</para></listitem>
<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.
</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ResolveUnicastSingleLabel=</varname></term>
<listitem><para>Takes a boolean argument. When false (the default),
<command>systemd-resolved</command> will not resolve A and AAAA queries for single-label names over
classic DNS. Note that such names may still be resolved if search domains are specified (see
<varname>Domains=</varname> above), or using other mechanisms, in particular via LLMNR or from
<filename>/etc/hosts</filename>. When true, queries for single-label names will be forwarded to
global DNS servers even if no search domains are defined.
</para>
<para>This option is provided for compatibility with configurations where <emphasis>public DNS
servers are not used</emphasis>. Forwarding single-label names to servers not under your control is
not standard-conformant, see <ulink
url="https://www.iab.org/documents/correspondence-reports-documents/2013-2/iab-statement-dotless-domains-considered-harmful/">IAB
Statement</ulink>, and may create a privacy and security risk.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -135,14 +135,16 @@
IPv6.</para></listitem>
<listitem><para>Resolution of address records (A and AAAA) via unicast DNS (i.e. not LLMNR or
MulticastDNS) for non-synthesized single-label names is only allowed for non-top-level domains. This
means that such records can only be resolved when search domains are defined. For any interface which
defines search domains, such look-ups are routed to that interface, suffixed with each of the search
domains defined on that interface in turn. When global search domains are defined, such look-ups are
routed to all interfaces, suffixed by each of the global search domains in turn. The details of which
servers are queried and how the final reply is chosen are described below. Note that this means that
address queries for single-label names are never sent out to remote DNS servers, and if no search
domains are defined, resolution will fail.</para></listitem>
MulticastDNS) for non-synthesized single-label names is allowed for non-top-level domains. This means
that such records can be resolved when search domains are defined. For any interface which defines
search domains, such look-ups are routed to that interface, suffixed with each of the search domains
defined on that interface in turn. When global search domains are defined, such look-ups are routed to
all interfaces, suffixed by each of the global search domains in turn. Additionally, lookup of
single-label names via unicast DNS may be enabled with the
<varname>ResolveUnicastSingleLabel=yes</varname> setting. The details of which servers are queried and
how the final reply is chosen are described below. Note that this means that address queries for
single-label names are never sent out to remote DNS servers by default, and if no search domains are
defined, resolution will fail.</para></listitem>
<listitem><para>Other multi-label names are routed to all local interfaces that have a DNS server
configured, plus the globally configured DNS servers if there are any. Note that by default, lookups for

View File

@ -513,7 +513,7 @@ static int on_query_timeout(sd_event_source *s, usec_t usec, void *userdata) {
}
static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
DnsQueryCandidate *c;
_cleanup_(dns_query_candidate_freep) DnsQueryCandidate *c = NULL;
int r;
assert(q);
@ -524,24 +524,21 @@ static int dns_query_add_candidate(DnsQuery *q, DnsScope *s) {
return r;
/* If this a single-label domain on DNS, we might append a suitable search domain first. */
if ((q->flags & SD_RESOLVED_NO_SEARCH) == 0 &&
dns_scope_name_needs_search_domain(s, dns_question_first_name(q->question_idna))) {
/* OK, we need a search domain now. Let's find one for this scope */
if (!FLAGS_SET(q->flags, SD_RESOLVED_NO_SEARCH) &&
dns_scope_name_wants_search_domain(s, dns_question_first_name(q->question_idna))) {
/* OK, we want a search domain now. Let's find one for this scope */
r = dns_query_candidate_next_search_domain(c);
if (r <= 0) /* if there's no search domain, then we won't add any transaction. */
goto fail;
if (r < 0)
return r;
}
r = dns_query_candidate_setup_transactions(c);
if (r < 0)
goto fail;
return r;
TAKE_PTR(c);
return 0;
fail:
dns_query_candidate_free(c);
return r;
}
static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {

View File

@ -102,6 +102,8 @@ enum {
};
DnsQueryCandidate* dns_query_candidate_free(DnsQueryCandidate *c);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQueryCandidate*, dns_query_candidate_free);
void dns_query_candidate_notify(DnsQueryCandidate *c);
int dns_query_new(Manager *m, DnsQuery **q, DnsQuestion *question_utf8, DnsQuestion *question_idna, int family, uint64_t flags);

View File

@ -619,7 +619,7 @@ DnsScopeMatch dns_scope_good_domain(
manager_is_own_hostname(s->manager, domain) <= 0)) /* never resolve the local hostname via LLMNR */
return DNS_SCOPE_YES_BASE + 1; /* Return +1, as we consider ourselves authoritative
* for single-label names, i.e. one label. This is
* particular relevant as it means a "." route on some
* particularly relevant as it means a "." route on some
* other scope won't pull all traffic away from
* us. (If people actually want to pull traffic away
* from us they should turn off LLMNR on the
@ -651,20 +651,21 @@ bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) {
if (s->protocol == DNS_PROTOCOL_DNS) {
/* On classic DNS, looking up non-address RRs is always
* fine. (Specifically, we want to permit looking up
* DNSKEY and DS records on the root and top-level
* domains.) */
/* On classic DNS, looking up non-address RRs is always fine. (Specifically, we want to
* permit looking up DNSKEY and DS records on the root and top-level domains.) */
if (!dns_resource_key_is_address(key))
return true;
/* However, we refuse to look up A and AAAA RRs on the
* root and single-label domains, under the assumption
* that those should be resolved via LLMNR or search
* path only, and should not be leaked onto the
* internet. */
return !(dns_name_is_single_label(dns_resource_key_name(key)) ||
dns_name_is_root(dns_resource_key_name(key)));
/* Unless explicitly overridden, we refuse to look up A and AAAA RRs on the root and
* single-label domains, under the assumption that those should be resolved via LLMNR or
* search path only, and should not be leaked onto the internet. */
const char* name = dns_resource_key_name(key);
if (!s->manager->resolve_unicast_single_label &&
dns_name_is_single_label(name))
return false;
return !dns_name_is_root(name);
}
/* On mDNS and LLMNR, send A and AAAA queries only on the
@ -1169,7 +1170,7 @@ DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s) {
return s->manager->search_domains;
}
bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name) {
bool dns_scope_name_wants_search_domain(DnsScope *s, const char *name) {
assert(s);
if (s->protocol != DNS_PROTOCOL_DNS)

View File

@ -99,7 +99,7 @@ void dns_scope_dump(DnsScope *s, FILE *f);
DnsSearchDomain *dns_scope_get_search_domains(DnsScope *s);
bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name);
bool dns_scope_name_wants_search_domain(DnsScope *s, const char *name);
bool dns_scope_network_good(DnsScope *s);

View File

@ -18,13 +18,14 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
Resolve.Domains, config_parse_search_domains, 0, 0
Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support)
Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support)
Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Manager, dns_over_tls_mode)
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.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, 0
Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0
Resolve.Domains, config_parse_search_domains, 0, 0
Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support)
Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support)
Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode)
Resolve.DNSOverTLS, config_parse_dns_over_tls_mode, 0, offsetof(Manager, dns_over_tls_mode)
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.ResolveUnicastSingleLabel, config_parse_bool, 0, offsetof(Manager, resolve_unicast_single_label)

View File

@ -70,9 +70,10 @@ struct Manager {
LIST_HEAD(DnsSearchDomain, search_domains);
unsigned n_search_domains;
bool need_builtin_fallbacks:1;
bool need_builtin_fallbacks;
bool read_resolv_conf;
bool resolve_unicast_single_label;
bool read_resolv_conf:1;
struct stat resolv_conf_stat;
DnsTrustAnchor trust_anchor;

View File

@ -22,3 +22,4 @@
#Cache=yes
#DNSStubListener=yes
#ReadEtcHosts=yes
#ResolveUnicastSingleLabel=no