Symlink ns1 and ns2 to rollover/ns1 and rollover/ns2.
Symlink ns3/template.db.j2.manual to rollover/ns3/template.db.j2.manual.
The RSASHA256 keys are generated with dnssec-keygen, without a policy
provided. Thus we have to fake the lifetime for these keys.
Signing has to be done without the -z option, because the KSK should
not sign all records in case of a KSK/ZSK split. Update the signing
code to allow for extra options when signing with CSK only.
(cherry picked from commit 72d3551355)
Symlink ns1 and ns2 to rollover/ns1 and rollover/ns2.
Symlink ns3/template.db.j2.manual to rollover/ns3/template.db.j2.manual.
Since the bootstrapping is done before the templates are rendered
automatically, replace @DEFAULT_ALGORITHM@ in ns3/csk2.conf.j2 to
ecdsa256 and rename to ns3/csk2.conf.
(cherry picked from commit 3a6ed195fa)
Introduce rollover/setup.py for all setup related test code.
Introduce rollover/ns1 and rollover/ns2 to create a chain of trust to
all rollover related test zones. The tld zones in rollover/ns2 contain
a DSYNC record that at a later time will be used for testing Generalized
DNS Notifications.
Write a python version of private_type_record so we can put such
records in the zone via jinja2 templating.
(cherry picked from commit f31514e658)
There is no difference, so we are going to make it consistent. This will
make it easier to add a chain of trust for these zones (to be done in
a future commit).
(cherry picked from commit e620b29e35)
It was not explicitly clear that 'rndc sign' replaces signatures of
inactive keys and updates signatures that are not so fresh.
(cherry picked from commit 3f52303ef7)
A single spoofed DNAME answer can impact many names, and because of the
nature of DNAME, the attacker can use randomized query names to get
unlimited number of tries to spoof the answer. To limit impact, we
should not be accepting DNAME over insecure transport, like UDP without
cookies etc.
In short, the attacker tries to spoof at least one answer that has the
following form:
opcode QUERY
rcode NOERROR
flags QR AA
;QUESTION
trigger$RANDOM.test. IN A
;ANSWER
trigger$RANDOM.test. 3600 IN CNAME trigger$RANDOM.attacker.net.
test. 3600 IN DNAME attacker.net.
;AUTHORITY
;ADDITIONAL
This has been discovered internally.
Co-authored-by: Michał Kępień <michal@isc.org>
(cherry picked from commit e223ee7097)
In short, the attacker tries to spoof at least one answer that has the
following form:
rcode NOERROR
flags QR
;QUESTION
trigger$RANDOM.victim. IN TXT
;ANSWER
;AUTHORITY
trigger$RANDOM.victim. 3600 IN NS ns.victim.
;ADDITIONAL
ns.victim. 3600 IN A 10.53.0.3
This attack was originally reported as "test case 2".
Co-authored-by: Michał Kępień <michal@isc.org>
(cherry picked from commit b5dc46fe6e)
Before the fixes for CVE-2025-40778, an unsolicited in-bailiwick NS
record was accepted from a (spoofed) answer, enabling a single spoofed A
query/response to redirect traffic for a whole delegation.
In short, the attacker tries to spoof at least one answer that has the
following form:
rcode NOERROR
flags QR AA
;QUESTION
trigger$RANDOM.victim. IN TXT
;ANSWER
trigger$RANDOM.victim. 3600 IN TXT "spoofed answer with extra NS"
;AUTHORITY
victim. 3600 IN NS ns.attacker.
;ADDITIONAL
This attack was originally reported as "test case 1".
Co-authored-by: Michał Kępień <michal@isc.org>
(cherry picked from commit 658d2e9f8e)
Before the fixes for CVE-2025-40778, a positive answer was allowed to
overwrite sibling NS RRs. The answer had to be a positive AA=1 answer
with a fake NS along with it. This combination of conditions avoided
the code path with "unrelated <RRTYPE>" detection logic.
If it were some other answer, named from the main branch would detect
the attempt and log:
DNS format error from 10.53.0.1#16386 resolving trigger/A for <unknown>: unrelated NS victim in trigger authority section
In short, the attacker tries to spoof at least one answer that has the
following form:
opcode QUERY
rcode NOERROR
flags QR AA
;QUESTION
trigger$RANDOM. IN A
;ANSWER
trigger$RANDOM. 3600 IN A 10.53.0.3
;AUTHORITY
victim. 3600 IN NS ns.attacker.
;ADDITIONAL
ns.attacker. 3600 IN A 10.53.0.3
This attack was originally reported as "test case 1c".
Co-authored-by: Michał Kępień <michal@isc.org>
(cherry picked from commit 26eed16d61)
Add the zone files, configuration, and code that will be reused by all
tests related to CVE-2025-40778.
Co-authored-by: Michał Kępień <michal@isc.org>
(cherry picked from commit 607974b1bc)
Add bin/tests/system/ans.py, a bare-bones DNS server that can be used in
system tests instead of full-blown named instances when a server is only
required to return zone-based data. Where applicable, this reduces load
on the test host and the amount of generated logs.
(cherry picked from commit 440e510f75)
If we change from NSEC3 to NSEC we should not produce a zone with
missing NSEC records.
The code only considered having seen a record if there was previously
a signature present at the owner name. However with opt-out, insecure
delegations don't have a RRSIG record. Reconfiguring to NSEC causes
all insecure delegations to have a missing NSEC record.
Add a DNAME record to the test zone to also cover DNAME delegations.
(cherry picked from commit 3679bd4888)
Due to the way various asyncio-related objects (tasks, streams,
transports, selectors) are referencing each other, pausing reads for a
TCP transport (which in practice means removing the client socket from
the set of descriptors monitored by a selector) can cause the client
task (AsyncDnsServer._handle_tcp()) to be prematurely garbage-collected,
causing asyncio code to raise a "Task was destroyed but it is pending!"
exception. Who knew that solutions as elegant as the one introduced by
e407888507 could cause unexpected trouble?
Fix by making a horrible hack even more horrible, specifically by
keeping a reference to each incoming TCP connection to protect its
related asyncio objects from getting garbage-collected. This prevents
AsyncDnsServer from closing any of the ignored TCP connections
indefinitely, which is obviously a pretty brain-dead idea for a
production-grade DNS server, but AsyncDnsServer was never meant to be
one and this hack reliably solves the problem at hand.
Only apply this change for the IgnoreAllConnections handler as the
ConnectionReset handler triggers a connection reset immediately after
pausing reads for an incoming TCP connection.
As pointed out in e407888507, the proper
solution would require implementing a custom asyncio transport from
scratch and that is still deemed to be too much work for the purpose at
hand. Let's see how much longer we can limp along with the existing
approach.
(cherry picked from commit 1acde358ea)
Calling asyncio.Future.set_exception() or asyncio.Future.set_result()
more than once for a given Future object raises an
asyncio.InvalidStateError exception.
In the case of AsyncServer:
- it is enough to capture the first exception raised by higher-level
logic as no exceptions at all are expected to be raised in the first
place,
- no distinction is made between SIGINT and SIGTERM; the only purpose
of the signal handler is to make the server exit cleanly.
Given the above, make both AsyncServer._handle_exception() and
AsyncServer._signal_done() idempotent by ignoring
asyncio.InvalidStateError exceptions raised by the relevant
asyncio.Future.set_*() calls.
(cherry picked from commit 0ec94e501a)
Previously, this was only possible by making a new response by calling
make_response on qctx.query. This however ignored the `default_aa` and
`default_rcode` parameters of AsyncDnsServer.
Add prepare_new_response and save_initialized_response methods to
QueryContext.
(cherry picked from commit 5384998ccd)
Previously, ResponseHandlers had to reparse the queries themselves if
they wanted to use TSIG. This led to `default_aa` and `default_rcode`
information being lost from the newly created messages.
Add support for TSIG keyrings to the AsyncDnsServer class directly.
(cherry picked from commit de266fff4c)
Previously, the server relied on the modules being imported by the
isctest.asyncserver module. This is fragile and confuses tooling.
Clean up stray imports in the process.
(cherry picked from commit 5761de5531)
Previously, all responses had to be set as authoritative explicitly
using DnsResponseSend(..., authoritative=True). After using this,
it became obvious that this is obnoxious.
Add an optional keyword-only parameter to AsyncDnsServer that sets the
default value of the AA bit on outgoing responses.
Make all the other parameters keyword-only as well.
(cherry picked from commit 6e684d44e0)
When this class was introduced, the constructor of its base class had no
parameters. This was changed in the meantime and these parameters were
not accessible by users of the subclass.
Don't override the constructor.
Move command setup to methods.
Move subclass-specific storage to cached properties.
Take instances of Command instead of the classes themselves for
symmetry with install_response_handler.
(cherry picked from commit a0970f3d04)
A catalog zone is updated in an offloaded thread, which is not
stopped during a reconfiguration in an exclusive mode, and so
can cause a race condition with it.
Waiting for the offloaded threads to complete their work before
entering into the exclusive mode can potentially cause unwanted
delays, because offloaded threads are generally "allowed" to take
a longer amount of time before they complete.
Add a dns_catz_zone_prereconfig()/dns_catz_zone_postreconfig() pair
of functions which currently just lock the catalog zone when
reconfiguring it. The change should eliminate the race.
As a side note, there was already a similar pair of functions,
dns_catz_prereconfig() and dns_catz_postreconfig() which are called
before and after reconfiguring a 'dns_catz_zones_t' object.
Below are the stack traces of the reconfiguration thread which has
asserted, and a catalog zone update thread which was caught in the
middle of its work despite the fact that the exclusive mode is
turned on.
Stack trace of thread 23859:
#0 0x00007f80e7b8e52f raise (libc.so.6)
#1 0x00007f80e7b61e65 abort (libc.so.6)
#2 0x0000000000422558 assertion_failed (named)
#3 0x00007f80eaa6799e isc_assertion_failed (libisc-9.18.41.so)
#4 0x00007f80ea5bc788 dns_catz_entry_getname (libdns-9.18.41.so)
#5 0x000000000042ce0e catz_reconfigure (named)
#6 0x000000000042d3c5 configure_catz_zone (named)
#7 0x000000000042d7a4 configure_catz (named)
#8 0x0000000000430645 configure_view (named)
#9 0x000000000043d998 load_configuration (named)
#10 0x000000000044184f loadconfig (named)
#11 0x0000000000442525 named_server_reconfigcommand (named)
#12 0x000000000041b277 named_control_docommand (named)
#13 0x000000000041c74a control_command (named)
#14 0x00007f80eaa912ae task_run (libisc-9.18.41.so)
#15 0x00007f80eaa914cd isc_task_run (libisc-9.18.41.so)
#16 0x00007f80eaa46435 isc__nm_async_task (libisc-9.18.41.so)
#17 0x00007f80eaa467aa process_netievent (libisc-9.18.41.so)
#18 0x00007f80eaa475a6 process_queue (libisc-9.18.41.so)
#19 0x00007f80eaa46227 process_all_queues (libisc-9.18.41.so)
#20 0x00007f80eaa462a1 async_cb (libisc-9.18.41.so)
#21 0x00007f80e8d01893 uv__async_io.part.3 (libuv.so.1)
#22 0x00007f80e8d13ac4 uv__io_poll (libuv.so.1)
#23 0x00007f80e8d023fb uv_run (libuv.so.1)
#24 0x00007f80eaa45ced nm_thread (libisc-9.18.41.so)
#25 0x00007f80eaa9bda3 isc__trampoline_run (libisc-9.18.41.so)
#26 0x00007f80e7f1e1ca start_thread (libpthread.so.0)
#27 0x00007f80e7b798d3 __clone (libc.so.6)
...
...
Stack trace of thread 23912:
#0 0x00007f80ea5bc2da dns_catz_options_setdefault (libdns-9.18.41.so)
#1 0x00007f80ea5bd411 dns__catz_zones_merge (libdns-9.18.41.so)
#2 0x00007f80ea5c3c2f dns__catz_update_cb (libdns-9.18.41.so)
#3 0x00007f80eaa4fee9 isc__nm_work_run (libisc-9.18.41.so)
#4 0x00007f80eaa9bda3 isc__trampoline_run (libisc-9.18.41.so)
#5 0x00007f80eaa4ff48 isc__nm_work_cb (libisc-9.18.41.so)
#6 0x00007f80e8cfc75e worker (libuv.so.1)
#7 0x00007f80e7f1e1ca start_thread (libpthread.so.0)
#8 0x00007f80e7b798d3 __clone (libc.so.6)
(cherry picked from commit aed9cafd5c)
This test signs a large delegation with mostly insecure delegations
with NSEC3 optout. Once the NSEC3PARAM record is published, run
dnssec-verify to ensure the zone is correctly signed.
(cherry picked from commit 5e704bbb59)
The statistics channel's HTML presentation has charts, but it has
a couple of problems.
1. Because of an inconsistent condition, a chart's JS code might have
been executed, while the HTML markup wasn't present, and this is
causing a JS error.
2. The feature is turned off on Mozilla-based browsers.
Fix the inconsistency in conditions, remove the conditional rendering
which disabled the feature for Mozilla-based browsers by looking at
the XSLT processor's name, and fix indentation inconsistencies.
(cherry picked from commit 3a3013a8b1)
There are a total of 22 notifies required before the start of the test:
20 for each x1..x20 and two for x21, as it has a couple of servers.
(cherry picked from commit 8f326a7af7)
- ns3 had fips/rsasha1 config variants. These were refactored similarly
to the way they're handled in nsec3 test.
- ns3 special character zone contains @, which is interpreted by jinja2.
To avoid, {% raw %} directive was added
- ns6 contained unused policies and named2.conf, these were removed
(cherry picked from commit bf694d0628)
The ns1 named.conf files were sufficiently similar to allow for
de-duplication. No attempt to de-duplicate ns3 was made due to
significant differences among the config files.
(cherry picked from commit a8713fdcf8)
- Use a common ns2/named.conf.j2 template for all the "#TN"
replacements. Instead of commenting out with sed, render the template
differently into ns/namedX.conf using variables.
- Keep the final ns2/named7.conf.j2 (formerly ns2/named2.conf.in) as a
separate template for readability due to significant differences.
(cherry picked from commit 6114444007)
- The ns3/named.conf.j2 uses has a "#BAD" section that is only included
after restart. Turn it into ns3/named2.conf.
- Since the original config is then restored, keep a copy of it as
ns3/named1.conf using a symlink, causing it to be rendered twice.
- Use jinja2 templates for test* files to render the port number instead
of calling copy_setports in load_db().
(cherry picked from commit c93733263a)
- Instead of strings to be replaced by sed, use proper jinja templates.
- ns3/named1.conf.j2 is basically a copy of the default config, because
it needs to be restored later in the test.
(cherry picked from commit efc302b0ad)
- Move ns1/named.conf.j2 to ns1/named2.conf.j2 and adjust the python
test to render this template.
- Convert remaining .in files to .j2 and handle the multiple configs.
(cherry picked from commit c46208ffdb)
- Rename named.conf.j2 to named3.conf.j2 and adjust the python test to
render this template.
- Handle the n2 and ns3 multiple configs as in other similar cases
(ns2/named1.conf.in was moved to ns2/named.conf.j2).
(cherry picked from commit 2592f0d961)
The test uses a second named2.conf template. It only differs by using
dnssec-validation yes. Render both named configs and swap them when
needed.
(cherry picked from commit 1da733a015)
- Merge ns*/statistics-channels.conf.in config snippets into
conditionally rendered section in ns*/named.conf.j2 files.
- Turn ns2/named.conf.in into ns2/named1.conf.j2 because it is used
later in the test to restore the original config.
- Symlink the ns2/named.conf.j2 ns2/named1.conf.j2 to pick a starting
config.
(cherry picked from commit cb88d1eda6)
- Rename ns1/named-fips.conf.in to ns1/named.conf.j2.
- Move MD5-specific stuff into ns1/named-md5.conf.j2 and include it
conditionally.
(cherry picked from commit 8db37ac31c)
- Change ns2 header into jinja2 template.
- Keep the various ns2 config files as non-templates, same for the
named.default.conf to be consistent.
- Symlink the ns2/named.default.conf as a jinja2 template to pick a
starting config. It is rendered as a template to avoid an error when
the test would overwrite a git-tracked file.
- Use jinja2 templates for the ns3 files, keep named1.conf around
because it's needed later in the test to restore the config. Symlink
it to `ns3/named.conf.j2` to select a default config.
(cherry picked from commit 16e9ac4710)
- Merge ns1/tls.options.in into ns1/named.conf.j2 and render it
conditionally. Also conditionally include the additional
ns1/tls.conf.j2 which is always rendered.
- Use multiple templates for ns7 and replace the copy_setports.
- Use jinja2 template for verylarge.in as well.
(cherry picked from commit 54c95d6966)
- Merge options-tls.conf into named.conf in ns2 and ns4 and render it
conditionally. Also conditionally include the additional
named-tls.conf which is always rendered.
(cherry picked from commit 9fc446b6f5)
- Merge options-tls.conf into named.conf in ns2 and ns4 and render it
conditionally. Also conditionally include the additional
named-tls.conf which is always rendered.
- Use multiple templates for ns3 and ns9 and replace the copy_setports.
- In ns3/named2.conf, use "root2.db" directly rather than replacing it
with "sed" later.
(cherry picked from commit 8e525f2154)
- Replace all named*.in files with jinja2 templates.
- Rename the config files to named.*.conf convention.
- Rename named.plain.in to named.plain.conf.j2 and symlink it as te
default config.
- Rename named.plainconf.in to named.plainlog.conf.j2 (there is a slight
difference from named.plain, despite the similar name)
(cherry picked from commit 3f953266fb)
- Replace named-altX.conf.in with namedX.conf.j2 to stick with the same
naming convention across the entire code base. Note than due to
named1.conf being the first (default) config, the numbers for the altX
are incremented.
- Turn alt9 into named7 to stick with the same number sequence. Adjust
the related file names accordingly.
(cherry picked from commit 7f3b0afb09)