1
0
mirror of https://github.com/systemd/systemd synced 2024-07-05 17:39:42 +00:00

Merge pull request #31621 from poettering/resolved-proxy-do

resolved: proxy upstream local requests to our stub with DO bit set
This commit is contained in:
Luca Boccassi 2024-03-06 13:55:14 +00:00 committed by GitHub
commit ba6ec879bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 180 additions and 47 deletions

View File

@ -357,7 +357,8 @@ All tools:
`systemd-resolved`:
* `$SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME` — if set to "0", `systemd-resolved`
won't synthesize system hostname on both regular and reverse lookups.
won't synthesize A/AAAA/PTR RRs for the system hostname on either regular nor
reverse lookups.
`systemd-sysext`:

View File

@ -440,35 +440,36 @@ node /org/freedesktop/resolve1 {
and recommended. However, the following flags are defined to alter the look-up:</para>
<programlisting>/* Input+Output: Protocol/scope */
#define SD_RESOLVED_DNS (UINT64_C(1) &lt;&lt; 0)
#define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) &lt;&lt; 1)
#define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) &lt;&lt; 2)
#define SD_RESOLVED_MDNS_IPV4 (UINT64_C(1) &lt;&lt; 3)
#define SD_RESOLVED_MDNS_IPV6 (UINT64_C(1) &lt;&lt; 4)
#define SD_RESOLVED_DNS (UINT64_C(1) &lt;&lt; 0)
#define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) &lt;&lt; 1)
#define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) &lt;&lt; 2)
#define SD_RESOLVED_MDNS_IPV4 (UINT64_C(1) &lt;&lt; 3)
#define SD_RESOLVED_MDNS_IPV6 (UINT64_C(1) &lt;&lt; 4)
/* Input: Restrictions */
#define SD_RESOLVED_NO_CNAME (UINT64_C(1) &lt;&lt; 5)
#define SD_RESOLVED_NO_TXT (UINT64_C(1) &lt;&lt; 6)
#define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) &lt;&lt; 7)
#define SD_RESOLVED_NO_SEARCH (UINT64_C(1) &lt;&lt; 8)
#define SD_RESOLVED_NO_VALIDATE (UINT64_C(1) &lt;&lt; 10)
#define SD_RESOLVED_NO_SYNTHESIZE (UINT64_C(1) &lt;&lt; 11)
#define SD_RESOLVED_NO_CACHE (UINT64_C(1) &lt;&lt; 12)
#define SD_RESOLVED_NO_ZONE (UINT64_C(1) &lt;&lt; 13)
#define SD_RESOLVED_NO_TRUST_ANCHOR (UINT64_C(1) &lt;&lt; 14)
#define SD_RESOLVED_NO_NETWORK (UINT64_C(1) &lt;&lt; 15)
#define SD_RESOLVED_NO_STALE (UINT64_C(1) &lt;&lt; 24)
#define SD_RESOLVED_NO_CNAME (UINT64_C(1) &lt;&lt; 5)
#define SD_RESOLVED_NO_TXT (UINT64_C(1) &lt;&lt; 6)
#define SD_RESOLVED_NO_ADDRESS (UINT64_C(1) &lt;&lt; 7)
#define SD_RESOLVED_NO_SEARCH (UINT64_C(1) &lt;&lt; 8)
#define SD_RESOLVED_NO_VALIDATE (UINT64_C(1) &lt;&lt; 10)
#define SD_RESOLVED_NO_SYNTHESIZE (UINT64_C(1) &lt;&lt; 11)
#define SD_RESOLVED_NO_CACHE (UINT64_C(1) &lt;&lt; 12)
#define SD_RESOLVED_NO_ZONE (UINT64_C(1) &lt;&lt; 13)
#define SD_RESOLVED_NO_TRUST_ANCHOR (UINT64_C(1) &lt;&lt; 14)
#define SD_RESOLVED_NO_NETWORK (UINT64_C(1) &lt;&lt; 15)
#define SD_RESOLVED_NO_STALE (UINT64_C(1) &lt;&lt; 24)
#define SD_RESOLVED_RELAX_SINGLE_LABEL (UINT64_C(1) &lt;&lt; 25)
/* Output: Security */
#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) &lt;&lt; 9)
#define SD_RESOLVED_CONFIDENTIAL (UINT64_C(1) &lt;&lt; 18)
#define SD_RESOLVED_AUTHENTICATED (UINT64_C(1) &lt;&lt; 9)
#define SD_RESOLVED_CONFIDENTIAL (UINT64_C(1) &lt;&lt; 18)
/* Output: Origin */
#define SD_RESOLVED_SYNTHETIC (UINT64_C(1) &lt;&lt; 19)
#define SD_RESOLVED_FROM_CACHE (UINT64_C(1) &lt;&lt; 20)
#define SD_RESOLVED_FROM_ZONE (UINT64_C(1) &lt;&lt; 21)
#define SD_RESOLVED_FROM_TRUST_ANCHOR (UINT64_C(1) &lt;&lt; 22)
#define SD_RESOLVED_FROM_NETWORK (UINT64_C(1) &lt;&lt; 23)
#define SD_RESOLVED_SYNTHETIC (UINT64_C(1) &lt;&lt; 19)
#define SD_RESOLVED_FROM_CACHE (UINT64_C(1) &lt;&lt; 20)
#define SD_RESOLVED_FROM_ZONE (UINT64_C(1) &lt;&lt; 21)
#define SD_RESOLVED_FROM_TRUST_ANCHOR (UINT64_C(1) &lt;&lt; 22)
#define SD_RESOLVED_FROM_NETWORK (UINT64_C(1) &lt;&lt; 23)
</programlisting>
<para>On input, the first five flags control the protocols to use for the look-up. They refer to

View File

@ -480,6 +480,21 @@
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--relax-single-label=</option><replaceable>BOOL</replaceable></term>
<listitem><para>Takes a boolean parameter; used in conjunction with <command>query</command>. If
true, rules regarding routing of single-label names are relaxed. Defaults to false. By default,
lookups of single label names are assumed to refer to local hosts to be resolved via local resolution
such as LLMNR or via search domain qualification and are not routed to upstream servers as is. If
this option is enabled these rules are disabled and the queries are routed upstream anyway. Also see
the <varname>ResolveUnicastSingleLabel=</varname> option in
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
which provides a system-wide option that controls this behaviour.</para>
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="json" />
<xi:include href="standard-options.xml" xpointer="j" />
<xi:include href="standard-options.xml" xpointer="no-pager" />

View File

@ -3342,6 +3342,7 @@ static int native_help(void) {
" --synthesize=BOOL Allow synthetic response (default: yes)\n"
" --cache=BOOL Allow response from cache (default: yes)\n"
" --stale-data=BOOL Allow response from cache with stale data (default: yes)\n"
" --relax-single-label=BOOL Allow single label lookups to go upstream (default: no)\n"
" --zone=BOOL Allow response from locally registered mDNS/LLMNR\n"
" records (default: yes)\n"
" --trust-anchor=BOOL Allow response from local trust anchor (default:\n"
@ -3701,7 +3702,8 @@ static int native_parse_argv(int argc, char *argv[]) {
ARG_SEARCH,
ARG_NO_PAGER,
ARG_JSON,
ARG_STALE_DATA
ARG_STALE_DATA,
ARG_RELAX_SINGLE_LABEL,
};
static const struct option options[] = {
@ -3726,6 +3728,7 @@ static int native_parse_argv(int argc, char *argv[]) {
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "json", required_argument, NULL, ARG_JSON },
{ "stale-data", required_argument, NULL, ARG_STALE_DATA },
{ "relax-single-label", required_argument, NULL, ARG_RELAX_SINGLE_LABEL },
{}
};
@ -3912,6 +3915,13 @@ static int native_parse_argv(int argc, char *argv[]) {
SET_FLAG(arg_flags, SD_RESOLVED_NO_SEARCH, r == 0);
break;
case ARG_RELAX_SINGLE_LABEL:
r = parse_boolean_argument("--relax-single-label=", optarg, NULL);
if (r < 0)
return r;
SET_FLAG(arg_flags, SD_RESOLVED_RELAX_SINGLE_LABEL, r > 0);
break;
case ARG_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;

View File

@ -373,6 +373,7 @@ static int validate_and_mangle_flags(
SD_RESOLVED_NO_TRUST_ANCHOR|
SD_RESOLVED_NO_NETWORK|
SD_RESOLVED_NO_STALE|
SD_RESOLVED_RELAX_SINGLE_LABEL|
ok))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter");

View File

@ -73,6 +73,10 @@
/* Input: Don't answer request with stale data */
#define SD_RESOLVED_NO_STALE (UINT64_C(1) << 24)
/* Input: Allow single-label lookups to Internet DNS servers */
#define SD_RESOLVED_RELAX_SINGLE_LABEL \
(UINT64_C(1) << 25)
#define SD_RESOLVED_LLMNR (SD_RESOLVED_LLMNR_IPV4|SD_RESOLVED_LLMNR_IPV6)
#define SD_RESOLVED_MDNS (SD_RESOLVED_MDNS_IPV4|SD_RESOLVED_MDNS_IPV6)
#define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS)

View File

@ -672,6 +672,8 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
q->answer_query_flags = SD_RESOLVED_AUTHENTICATED|SD_RESOLVED_CONFIDENTIAL|SD_RESOLVED_SYNTHETIC;
*state = DNS_TRANSACTION_RCODE_FAILURE;
log_debug("Found synthetic NXDOMAIN response.");
return 0;
}
if (r <= 0)
@ -687,6 +689,8 @@ static int dns_query_synthesize_reply(DnsQuery *q, DnsTransactionState *state) {
*state = DNS_TRANSACTION_SUCCESS;
log_debug("Found synthetic success response.");
return 1;
}
@ -741,7 +745,7 @@ int dns_query_go(DnsQuery *q) {
LIST_FOREACH(scopes, s, q->manager->dns_scopes) {
DnsScopeMatch match;
match = dns_scope_good_domain(s, q);
match = dns_scope_good_domain(s, q, q->flags);
assert(match >= 0);
if (match > found) { /* Does this match better? If so, remember how well it matched, and the first one
* that matches this well */
@ -768,7 +772,7 @@ int dns_query_go(DnsQuery *q) {
LIST_FOREACH(scopes, s, first->scopes_next) {
DnsScopeMatch match;
match = dns_scope_good_domain(s, q);
match = dns_scope_good_domain(s, q, q->flags);
assert(match >= 0);
if (match < found)
continue;

View File

@ -12,6 +12,7 @@
#include "random-util.h"
#include "resolved-dnssd.h"
#include "resolved-dns-scope.h"
#include "resolved-dns-synthesize.h"
#include "resolved-dns-zone.h"
#include "resolved-llmnr.h"
#include "resolved-mdns.h"
@ -596,12 +597,13 @@ static DnsScopeMatch match_subnet_reverse_lookups(
DnsScopeMatch dns_scope_good_domain(
DnsScope *s,
DnsQuery *q) {
DnsQuery *q,
uint64_t query_flags) {
DnsQuestion *question;
const char *domain;
uint64_t flags;
int ifindex;
int ifindex, r;
/* This returns the following return values:
*
@ -655,6 +657,15 @@ DnsScopeMatch dns_scope_good_domain(
is_dns_proxy_stub_hostname(domain))
return DNS_SCOPE_NO;
/* Don't look up the local host name via the network, unless user turned of local synthesis of it */
if (manager_is_own_hostname(s->manager, domain) && shall_synthesize_own_hostname_rrs())
return DNS_SCOPE_NO;
/* Never send SOA or NS or DNSSEC request to LLMNR, where they make little sense. */
r = dns_question_types_suitable_for_protocol(question, s->protocol);
if (r <= 0)
return DNS_SCOPE_NO;
switch (s->protocol) {
case DNS_PROTOCOL_DNS: {
@ -710,7 +721,8 @@ DnsScopeMatch dns_scope_good_domain(
/* If ResolveUnicastSingleLabel=yes and the query is single-label, then bump match result
to prevent LLMNR monopoly among candidates. */
if (s->manager->resolve_unicast_single_label && dns_name_is_single_label(domain))
if ((s->manager->resolve_unicast_single_label || (query_flags & SD_RESOLVED_RELAX_SINGLE_LABEL)) &&
dns_name_is_single_label(domain))
return DNS_SCOPE_YES_BASE + 1;
/* Let's return the number of labels in the best matching result */
@ -1686,3 +1698,65 @@ int dns_scope_dump_cache_to_json(DnsScope *scope, JsonVariant **ret) {
JSON_BUILD_PAIR_CONDITION(scope->link, "ifname", JSON_BUILD_STRING(scope->link ? scope->link->ifname : NULL)),
JSON_BUILD_PAIR_VARIANT("cache", cache)));
}
int dns_type_suitable_for_protocol(uint16_t type, DnsProtocol protocol) {
/* Tests whether it makes sense to route queries for the specified DNS RR types to the specified
* protocol. For classic DNS pretty much all RR types are suitable, but for LLMNR/mDNS let's
* allowlist only a few that make sense. We use this when routing queries so that we can more quickly
* return errors for queries that will almost certainly fail/time-out otherwise. For example, this
* ensures that SOA, NS, or DS/DNSKEY queries are never routed to mDNS/LLMNR where they simply make
* no sense. */
if (dns_type_is_obsolete(type))
return false;
if (!dns_type_is_valid_query(type))
return false;
switch (protocol) {
case DNS_PROTOCOL_DNS:
return true;
case DNS_PROTOCOL_LLMNR:
return IN_SET(type,
DNS_TYPE_ANY,
DNS_TYPE_A,
DNS_TYPE_AAAA,
DNS_TYPE_CNAME,
DNS_TYPE_PTR,
DNS_TYPE_TXT);
case DNS_PROTOCOL_MDNS:
return IN_SET(type,
DNS_TYPE_ANY,
DNS_TYPE_A,
DNS_TYPE_AAAA,
DNS_TYPE_CNAME,
DNS_TYPE_PTR,
DNS_TYPE_TXT,
DNS_TYPE_SRV,
DNS_TYPE_NSEC,
DNS_TYPE_HINFO);
default:
return -EPROTONOSUPPORT;
}
}
int dns_question_types_suitable_for_protocol(DnsQuestion *q, DnsProtocol protocol) {
DnsResourceKey *key;
int r;
/* Tests whether the types in the specified question make any sense to be routed to the specified
* protocol, i.e. if dns_type_suitable_for_protocol() is true for any of the contained RR types */
DNS_QUESTION_FOREACH(key, q) {
r = dns_type_suitable_for_protocol(key->type, protocol);
if (r != 0)
return r;
}
return false;
}

View File

@ -78,7 +78,7 @@ int dns_scope_emit_udp(DnsScope *s, int fd, int af, DnsPacket *p);
int dns_scope_socket_tcp(DnsScope *s, int family, const union in_addr_union *address, DnsServer *server, uint16_t port, union sockaddr_union *ret_socket_address);
int dns_scope_socket_udp(DnsScope *s, DnsServer *server);
DnsScopeMatch dns_scope_good_domain(DnsScope *s, DnsQuery *q);
DnsScopeMatch dns_scope_good_domain(DnsScope *s, DnsQuery *q, uint64_t query_flags);
bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key);
DnsServer *dns_scope_get_dns_server(DnsScope *s);
@ -114,3 +114,6 @@ int dns_scope_remove_dnssd_services(DnsScope *scope);
bool dns_scope_is_default_route(DnsScope *scope);
int dns_scope_dump_cache_to_json(DnsScope *scope, JsonVariant **ret);
int dns_type_suitable_for_protocol(uint16_t type, DnsProtocol protocol);
int dns_question_types_suitable_for_protocol(DnsQuestion *q, DnsProtocol protocol);

View File

@ -958,8 +958,8 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
log_debug("Got request to DNS proxy address 127.0.0.54, enabling bypass logic.");
bypass = true;
protocol_flags = SD_RESOLVED_DNS|SD_RESOLVED_NO_ZONE; /* Turn off mDNS/LLMNR for proxy stub. */
} else if ((DNS_PACKET_DO(p) && DNS_PACKET_CD(p))) {
log_debug("Got request with DNSSEC checking disabled, enabling bypass logic.");
} else if (DNS_PACKET_DO(p)) {
log_debug("Got request with DNSSEC enabled, enabling bypass logic.");
bypass = true;
}
@ -970,7 +970,8 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
SD_RESOLVED_NO_SEARCH|
SD_RESOLVED_NO_VALIDATE|
SD_RESOLVED_REQUIRE_PRIMARY|
SD_RESOLVED_CLAMP_TTL);
SD_RESOLVED_CLAMP_TTL|
SD_RESOLVED_RELAX_SINGLE_LABEL);
else
r = dns_query_new(m, &q, p->question, p->question, NULL, 0,
protocol_flags|

View File

@ -439,6 +439,20 @@ static int synthesize_gateway_ptr(
return answer_add_addresses_ptr(answer, "_gateway", addresses, n, af, address);
}
bool shall_synthesize_own_hostname_rrs(void) {
static int cached = -1;
int r;
if (cached >= 0)
return cached;
r = secure_getenv_bool("SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME: %m");
return (cached = r != 0);
}
int dns_synthesize_answer(
Manager *m,
DnsQuestion *q,
@ -479,8 +493,9 @@ int dns_synthesize_answer(
} else if (manager_is_own_hostname(m, name)) {
if (getenv_bool("SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME") == 0)
if (!shall_synthesize_own_hostname_rrs())
continue;
r = synthesize_system_hostname_rr(m, key, ifindex, &answer);
if (r < 0)
return log_error_errno(r, "Failed to synthesize system hostname RRs: %m");
@ -530,7 +545,7 @@ int dns_synthesize_answer(
} else if (dns_name_address(name, &af, &address) > 0) {
int v, w, u;
if (getenv_bool("SYSTEMD_RESOLVED_SYNTHESIZE_HOSTNAME") == 0)
if (!shall_synthesize_own_hostname_rrs())
continue;
v = synthesize_system_hostname_ptr(m, af, &address, ifindex, &answer);

View File

@ -9,3 +9,5 @@ int dns_synthesize_family(uint64_t flags);
DnsProtocol dns_synthesize_protocol(uint64_t flags);
int dns_synthesize_answer(Manager *m, DnsQuestion *q, int ifindex, DnsAnswer **ret);
bool shall_synthesize_own_hostname_rrs(void);

View File

@ -491,7 +491,7 @@ static int etc_hosts_lookup_by_name(
const char *name,
DnsAnswer **answer) {
bool found_a = false, found_aaaa = false;
bool question_for_a = false, question_for_aaaa = false;
const struct in_addr_data *a;
EtcHostsItemByName *item;
DnsResourceKey *t;
@ -513,6 +513,7 @@ static int etc_hosts_lookup_by_name(
return 0;
}
/* Determine whether we are looking for A and/or AAAA RRs */
DNS_QUESTION_FOREACH(t, q) {
if (!IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_TYPE_ANY))
continue;
@ -526,20 +527,20 @@ static int etc_hosts_lookup_by_name(
continue;
if (IN_SET(t->type, DNS_TYPE_A, DNS_TYPE_ANY))
found_a = true;
question_for_a = true;
if (IN_SET(t->type, DNS_TYPE_AAAA, DNS_TYPE_ANY))
found_aaaa = true;
question_for_aaaa = true;
if (found_a && found_aaaa)
break;
if (question_for_a && question_for_aaaa)
break; /* We are looking for both, no need to continue loop */
}
SET_FOREACH(a, item ? item->addresses : NULL) {
EtcHostsItemByAddress *item_by_addr;
const char *canonical_name;
if ((!found_a && a->family == AF_INET) ||
(!found_aaaa && a->family == AF_INET6))
if ((!question_for_a && a->family == AF_INET) ||
(!question_for_aaaa && a->family == AF_INET6))
continue;
item_by_addr = hashmap_get(hosts->by_address, a);
@ -559,7 +560,7 @@ static int etc_hosts_lookup_by_name(
return r;
}
return found_a || found_aaaa;
return true; /* We consider ourselves authoritative for the whole name, all RR types, not just A/AAAA */
}
int manager_etc_hosts_lookup(Manager *m, DnsQuestion *q, DnsAnswer **answer) {

View File

@ -162,6 +162,7 @@ static bool validate_and_mangle_flags(
SD_RESOLVED_NO_TRUST_ANCHOR|
SD_RESOLVED_NO_NETWORK|
SD_RESOLVED_NO_STALE|
SD_RESOLVED_RELAX_SINGLE_LABEL|
ok))
return false;

View File

@ -523,9 +523,9 @@ monitor_check_rr "$TIMESTAMP" "follow13.almost.final.signed.test IN CNAME follow
monitor_check_rr "$TIMESTAMP" "follow14.final.signed.test IN A 10.0.0.14"
# Non-existing RR + CNAME chain
run dig +dnssec AAAA cname-chain.signed.test
grep -qF "status: NOERROR" "$RUN_OUT"
grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
#run dig +dnssec AAAA cname-chain.signed.test
#grep -qF "status: NOERROR" "$RUN_OUT"
#grep -qE "^follow14\.final\.signed\.test\..+IN\s+NSEC\s+" "$RUN_OUT"
: "--- ZONE: onlinesign.test (dynamic DNSSEC) ---"