mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
resolve: add an option to explicitly disable query AAAA, SRV, MX, etc... (#34165)
Based on this patch i had submitted to RedHat (https://issues.redhat.com/browse/RHEL-56280), i am submitting this patch to this upstream systemd. There is no way to explicitly enable/disable IPv6 AAAA queries. Problem was that i am using RHEL9 and some applications does not use a newer glibc that supports `no-aaaa` option in `/etc/resolv.conf`. So some applications will still resolve IPv6 AAAA even with `no-aaaa` option and it is inconsistent across the system where some work and some don't. So this systemd-resolved patch catch-all queries and disable IPv6 AAAA queries for all applications in the OS by having an option `RefuseRecordTypes=AAAA` to disable IPv6 AAAA queries. Although https://github.com/systemd/systemd/pull/28136 tries to fix this automatically but it still does not work with `net.ipv6.conf.all.disable_ipv6 = 1`. Also tried with explicitly removing the conditional and force set `family = AF_INET` and still resolves AAAA records. The issue is that i want to explicitly disable IPv6 AAAA queries instead of systemd-resolved to figure out itself which address family it is using, which always have problems.
This commit is contained in:
committed by
GitHub
parent
1e4d0b00e5
commit
81ae2237c1
@@ -317,6 +317,21 @@
|
||||
<xi:include href="version-info.xml" xpointer="v232"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>RefuseRecordTypes=</varname></term>
|
||||
<listitem><para>Takes a list of DNS record types separated by space.
|
||||
Specified record types in each query will be refused. This option can be specified multiple times.
|
||||
If an empty string is specified, then all previous assignments are cleared.</para>
|
||||
<para>Examples:
|
||||
<programlisting>RefuseRecordTypes=AAAA SRV TXT</programlisting>
|
||||
</para>
|
||||
|
||||
<para>Note that to refuse AAAA record (unlike SRV, TXT, etc.) completely, please also disable IPv6 stack in kernel
|
||||
with "sysctl -w net.ipv6.conf.all.disable_ipv6=1".</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>DNSStubListenerExtra=</varname></term>
|
||||
<listitem><para>Takes an IPv4 or IPv6 address to listen on. The address may be optionally
|
||||
|
||||
@@ -29,6 +29,24 @@
|
||||
|
||||
BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_resolve_support, resolve_support, ResolveSupport);
|
||||
|
||||
static int dns_query_new_for_bus(
|
||||
Manager *m,
|
||||
DnsQuery **ret,
|
||||
DnsQuestion *question_utf8,
|
||||
DnsQuestion *question_idna,
|
||||
DnsPacket *question_bypass,
|
||||
int ifindex,
|
||||
uint64_t flags,
|
||||
sd_bus_error *error) {
|
||||
|
||||
int r;
|
||||
|
||||
r = dns_query_new(m, ret, question_utf8, question_idna, question_bypass, ifindex, flags);
|
||||
if (r == -ENOANO)
|
||||
return sd_bus_error_set(error, BUS_ERROR_DNS_REFUSED, "DNS query type refused.");
|
||||
return r;
|
||||
}
|
||||
|
||||
static int query_on_bus_track(sd_bus_track *t, void *userdata) {
|
||||
DnsQuery *q = ASSERT_PTR(userdata);
|
||||
|
||||
@@ -525,7 +543,7 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata,
|
||||
|
||||
bus_client_log(message, "hostname resolution");
|
||||
|
||||
r = dns_query_new(m, &q, question_utf8, question_idna ?: question_utf8, NULL, ifindex, flags);
|
||||
r = dns_query_new_for_bus(m, &q, question_utf8, question_idna ?: question_utf8, NULL, ifindex, flags, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -666,7 +684,7 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s
|
||||
|
||||
bus_client_log(message, "address resolution");
|
||||
|
||||
r = dns_query_new(m, &q, question, question, NULL, ifindex, flags|SD_RESOLVED_NO_SEARCH);
|
||||
r = dns_query_new_for_bus(m, &q, question, question, NULL, ifindex, flags|SD_RESOLVED_NO_SEARCH, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -845,7 +863,7 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd
|
||||
|
||||
/* Setting SD_RESOLVED_CLAMP_TTL: let's request that the TTL is fixed up for locally cached entries,
|
||||
* after all we return it in the wire format blob. */
|
||||
r = dns_query_new(m, &q, question, question, NULL, ifindex, flags|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_CLAMP_TTL);
|
||||
r = dns_query_new_for_bus(m, &q, question, question, NULL, ifindex, flags|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_CLAMP_TTL, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1209,6 +1227,8 @@ static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifin
|
||||
return r;
|
||||
|
||||
r = dns_query_new(q->manager, &aux, question, question, NULL, ifindex, q->flags|SD_RESOLVED_NO_SEARCH);
|
||||
if (r == -ENOANO)
|
||||
return reply_method_errorf(q, BUS_ERROR_DNS_REFUSED, "DNS query type refused.");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1372,7 +1392,7 @@ static int bus_method_resolve_service(sd_bus_message *message, void *userdata, s
|
||||
|
||||
bus_client_log(message, "service resolution");
|
||||
|
||||
r = dns_query_new(m, &q, question_utf8, question_idna, NULL, ifindex, flags|SD_RESOLVED_NO_SEARCH);
|
||||
r = dns_query_new_for_bus(m, &q, question_utf8, question_idna, NULL, ifindex, flags|SD_RESOLVED_NO_SEARCH, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -413,3 +413,43 @@ int manager_parse_config_file(Manager *m) {
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_record_types(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
unsigned line,
|
||||
const char *section,
|
||||
unsigned section_line,
|
||||
const char *lvalue,
|
||||
int ltype,
|
||||
const char *rvalue,
|
||||
void *data,
|
||||
void *userdata) {
|
||||
|
||||
Set **types = ASSERT_PTR(data);
|
||||
int r;
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
*types = set_free(*types);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (const char *p = rvalue;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
r = extract_first_word(&p, &word, NULL, 0);
|
||||
if (r < 0)
|
||||
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
|
||||
if (r == 0)
|
||||
return 1;
|
||||
|
||||
r = dns_type_from_string(word);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid DNS record type, ignoring: %s", word);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = set_ensure_put(types, NULL, INT_TO_PTR(r));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,3 +24,4 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dns_servers);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_search_domains);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dns_stub_listener_mode);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_dns_stub_listener_extra);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_record_types);
|
||||
|
||||
@@ -136,7 +136,7 @@ static int dns_query_candidate_next_search_domain(DnsQueryCandidate *c) {
|
||||
dns_search_domain_unref(c->search_domain);
|
||||
c->search_domain = dns_search_domain_ref(next);
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_query_candidate_add_transaction(
|
||||
@@ -508,6 +508,44 @@ DnsQuery *dns_query_free(DnsQuery *q) {
|
||||
return mfree(q);
|
||||
}
|
||||
|
||||
static int validate_and_mangle_question(DnsQuestion **question, Set *types) {
|
||||
int r;
|
||||
|
||||
if (set_isempty(types))
|
||||
return 0; /* No filtering configured. Let's shortcut. */
|
||||
|
||||
bool has_good = false, has_bad = false;
|
||||
DnsResourceKey *key;
|
||||
DNS_QUESTION_FOREACH(key, *question)
|
||||
if (set_contains(types, INT_TO_PTR(key->type)))
|
||||
has_bad = true;
|
||||
else
|
||||
has_good = true;
|
||||
|
||||
if (has_bad && !has_good)
|
||||
return -ENOANO; /* All bad, refuse.*/
|
||||
if (!has_bad) {
|
||||
assert(has_good); /* The question should have at least one key. */
|
||||
return 0; /* All good. Not necessary to filter. */
|
||||
}
|
||||
|
||||
/* Mangle the question suppressing bad entries, leaving good entries */
|
||||
_cleanup_(dns_question_unrefp) DnsQuestion *new_question = dns_question_new(dns_question_size(*question));
|
||||
if (!new_question)
|
||||
return -ENOMEM;
|
||||
|
||||
DnsQuestionItem *item;
|
||||
DNS_QUESTION_FOREACH_ITEM(item, *question) {
|
||||
if (set_contains(types, INT_TO_PTR(item->key->type)))
|
||||
continue;
|
||||
r = dns_question_add_raw(new_question, item->key, item->flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return free_and_replace_full(*question, new_question, dns_question_unref);
|
||||
}
|
||||
|
||||
int dns_query_new(
|
||||
Manager *m,
|
||||
DnsQuery **ret,
|
||||
@@ -524,6 +562,15 @@ int dns_query_new(
|
||||
|
||||
assert(m);
|
||||
|
||||
/* Check for records that is refused and refuse query for the records if matched in configuration */
|
||||
r = validate_and_mangle_question(&question_utf8, m->refuse_record_types);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = validate_and_mangle_question(&question_idna, m->refuse_record_types);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (question_bypass) {
|
||||
/* It's either a "bypass" query, or a regular one, but can't be both. */
|
||||
if (question_utf8 || question_idna)
|
||||
|
||||
@@ -996,6 +996,8 @@ static void dns_stub_process_query(Manager *m, DnsStubListenerExtra *l, DnsStrea
|
||||
(DNS_PACKET_CD(p) ? SD_RESOLVED_NO_VALIDATE | SD_RESOLVED_NO_CACHE : 0)|
|
||||
(DNS_PACKET_DO(p) ? SD_RESOLVED_REQUIRE_PRIMARY : 0)|
|
||||
SD_RESOLVED_CLAMP_TTL);
|
||||
if (r == -ENOANO) /* Refuse query if there is -ENOANO */
|
||||
return (void) dns_stub_send_failure(m, l, s, p, DNS_RCODE_REFUSED, false);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to generate query object: %m");
|
||||
dns_stub_send_failure(m, l, s, p, DNS_RCODE_SERVFAIL, false);
|
||||
|
||||
@@ -34,3 +34,4 @@ Resolve.ResolveUnicastSingleLabel, config_parse_bool, 0,
|
||||
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)
|
||||
Resolve.StaleRetentionSec, config_parse_sec, 0, offsetof(Manager, stale_retention_usec)
|
||||
Resolve.RefuseRecordTypes, config_parse_record_types, 0, offsetof(Manager, refuse_record_types)
|
||||
|
||||
@@ -843,6 +843,7 @@ Manager *manager_free(Manager *m) {
|
||||
dnstls_manager_free(m);
|
||||
#endif
|
||||
|
||||
set_free(m->refuse_record_types);
|
||||
hashmap_free(m->links);
|
||||
hashmap_free(m->dns_transactions);
|
||||
|
||||
|
||||
@@ -137,6 +137,9 @@ struct Manager {
|
||||
struct stat etc_hosts_stat;
|
||||
bool read_etc_hosts;
|
||||
|
||||
/* List of refused DNS Record Types*/
|
||||
Set *refuse_record_types;
|
||||
|
||||
OrderedSet *dns_extra_stub_listeners;
|
||||
|
||||
/* Local DNS stub on 127.0.0.53:53 */
|
||||
|
||||
@@ -37,6 +37,24 @@ static void lookup_parameters_destroy(LookupParameters *p) {
|
||||
free(p->name);
|
||||
}
|
||||
|
||||
static int dns_query_new_for_varlink(
|
||||
Manager *m,
|
||||
DnsQuery **ret,
|
||||
DnsQuestion *question_utf8,
|
||||
DnsQuestion *question_idna,
|
||||
DnsPacket *question_bypass,
|
||||
int ifindex,
|
||||
uint64_t flags,
|
||||
sd_varlink *link) {
|
||||
|
||||
int r;
|
||||
|
||||
r = dns_query_new(m, ret, question_utf8, question_idna, question_bypass, ifindex, flags);
|
||||
if (r == -ENOANO)
|
||||
return sd_varlink_error(link, "io.systemd.Resolve.QueryRefused", NULL);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int reply_query_state(DnsQuery *q) {
|
||||
|
||||
assert(q);
|
||||
@@ -384,7 +402,7 @@ static int vl_method_resolve_hostname(sd_varlink *link, sd_json_variant *paramet
|
||||
if (r < 0 && r != -EALREADY)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question_utf8, question_idna ?: question_utf8, NULL, p.ifindex, p.flags);
|
||||
r = dns_query_new_for_varlink(m, &q, question_utf8, question_idna ?: question_utf8, NULL, p.ifindex, p.flags, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -539,7 +557,7 @@ static int vl_method_resolve_address(sd_varlink *link, sd_json_variant *paramete
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question, question, NULL, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH);
|
||||
r = dns_query_new_for_varlink(m, &q, question, question, NULL, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -874,7 +892,7 @@ static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifin
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(q->manager, &aux, question, question, NULL, ifindex, q->flags|SD_RESOLVED_NO_SEARCH);
|
||||
r = dns_query_new_for_varlink(q->manager, &aux, question, question, NULL, ifindex, q->flags|SD_RESOLVED_NO_SEARCH, q->varlink_request);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1037,7 +1055,7 @@ static int vl_method_resolve_service(sd_varlink* link, sd_json_variant* paramete
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question_utf8, question_idna, NULL, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH);
|
||||
r = dns_query_new_for_varlink(m, &q, question_utf8, question_idna, NULL, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -1182,7 +1200,7 @@ static int vl_method_resolve_record(sd_varlink *link, sd_json_variant *parameter
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = dns_query_new(m, &q, question, question, NULL, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_CLAMP_TTL);
|
||||
r = dns_query_new_for_varlink(m, &q, question, question, NULL, p.ifindex, p.flags|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_CLAMP_TTL, link);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
||||
@@ -35,3 +35,4 @@
|
||||
#ReadEtcHosts=yes
|
||||
#ResolveUnicastSingleLabel=no
|
||||
#StaleRetentionSec=0
|
||||
#RefuseRecordTypes=
|
||||
|
||||
@@ -165,6 +165,7 @@ static SD_VARLINK_DEFINE_ERROR(QueryTimedOut);
|
||||
static SD_VARLINK_DEFINE_ERROR(MaxAttemptsReached);
|
||||
static SD_VARLINK_DEFINE_ERROR(InvalidReply);
|
||||
static SD_VARLINK_DEFINE_ERROR(QueryAborted);
|
||||
static SD_VARLINK_DEFINE_ERROR(QueryRefused);
|
||||
static SD_VARLINK_DEFINE_ERROR(
|
||||
DNSSECValidationFailed,
|
||||
SD_VARLINK_DEFINE_FIELD(result, SD_VARLINK_STRING, 0),
|
||||
@@ -218,6 +219,7 @@ SD_VARLINK_DEFINE_INTERFACE(
|
||||
&vl_error_MaxAttemptsReached,
|
||||
&vl_error_InvalidReply,
|
||||
&vl_error_QueryAborted,
|
||||
&vl_error_QueryRefused,
|
||||
&vl_error_DNSSECValidationFailed,
|
||||
&vl_error_NoTrustAnchor,
|
||||
&vl_error_ResourceRecordTypeUnsupported,
|
||||
|
||||
@@ -1084,6 +1084,59 @@ testcase_13_varlink_subscribe_dns_configuration() {
|
||||
<(jq -cr --seq '.configuration[] | select(.ifname == "dns0" and .servers != null and .searchDomains != null) | {"dns0":{servers: [.servers[] | .address], domains: [.searchDomains[] | .name]}}' "$tmpfile")
|
||||
}
|
||||
|
||||
# Test RefuseRecordTypes
|
||||
testcase_14_refuse_record_types() {
|
||||
# shellcheck disable=SC2317
|
||||
cleanup() {
|
||||
rm -f /run/systemd/resolved.conf.d/refuserecords.conf
|
||||
if [[ -e /etc/resolv.conf.bak ]]; then
|
||||
rm -f /etc/resolv.conf
|
||||
mv /etc/resolv.conf.bak /etc/resolv.conf
|
||||
fi
|
||||
restart_resolved
|
||||
}
|
||||
trap cleanup RETURN ERR
|
||||
|
||||
mkdir -p /run/systemd/resolved.conf.d
|
||||
{
|
||||
echo "[Resolve]"
|
||||
echo "RefuseRecordTypes=AAAA SRV TXT"
|
||||
} >/run/systemd/resolved.conf.d/refuserecords.conf
|
||||
if [[ -e /etc/resolv.conf ]]; then
|
||||
mv /etc/resolv.conf /etc/resolv.conf.bak
|
||||
fi
|
||||
ln -svf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
|
||||
systemctl reload systemd-resolved.service
|
||||
# disable_ipv6 is necessary do refuse AAAA
|
||||
disable_ipv6
|
||||
run dig localhost -t AAAA
|
||||
grep -qF "status: REFUSED" "$RUN_OUT"
|
||||
|
||||
run dig localhost -t SRV
|
||||
grep -qF "status: REFUSED" "$RUN_OUT"
|
||||
|
||||
run dig localhost -t TXT
|
||||
grep -qF "status: REFUSED" "$RUN_OUT"
|
||||
|
||||
run dig localhost -t A
|
||||
grep -qF "status: NOERROR" "$RUN_OUT"
|
||||
|
||||
run resolvectl query localhost5
|
||||
grep -qF "127.128.0.5" "$RUN_OUT"
|
||||
|
||||
(! run resolvectl query localhost5 --type=SRV)
|
||||
grep -qF "DNS query type refused." "$RUN_OUT"
|
||||
|
||||
(! run resolvectl query localhost5 --type=TXT)
|
||||
grep -qF "DNS query type refused." "$RUN_OUT"
|
||||
|
||||
(! run resolvectl query localhost5 --type=AAAA)
|
||||
grep -qF "DNS query type refused." "$RUN_OUT"
|
||||
|
||||
run resolvectl query localhost5 --type=A
|
||||
grep -qF "127.128.0.5" "$RUN_OUT"
|
||||
}
|
||||
|
||||
# PRE-SETUP
|
||||
systemctl unmask systemd-resolved.service
|
||||
systemctl enable --now systemd-resolved.service
|
||||
|
||||
Reference in New Issue
Block a user