Skip to content

pdns auth secondary serving a pre-signed DNSSEC zone can generate invalid NSEC records #17881

Description

@elburb
  • Program: Authoritative
  • Issue type: Bug report

Short description

A PowerDNS Authoritative secondary serving a pre-signed DNSSEC zone with NSEC semantics generates an incorrect NSEC successor when the zone contains a delegation with names below the delegation.

The primary is NSD and serves a correctly pre-signed zone. PowerDNS receives the correct NSEC and RRSIG through AXFR, but synthesizes a different NSEC on the secondary. The generated NSEC points into the delegated subtree, causing the existing pre-signed RRSIG to become invalid.

In the example below, the primary correctly serves:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NSEC a.d.c.0.3.2.0.0.2.4.d.f.ip6.arpa. NS RRSIG NSEC

The PowerDNS secondary instead serves:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NSEC 0.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. NS RRSIG NSEC

The latter is not the NSEC record covered by the transferred RRSIG, so DNSSEC validation fails.

Environment

Operating system: Debian 13 (amd64)
Software version: PowerDNS Authoritative Server 5.0.5
Software source: Debian package built from PowerDNS 5.0.5 source
Backend: SQLite3
Zone type: Secondary/slave
DNSSEC: Pre-signed zone (PRESIGNED=1)
NSEC semantics
Primary: NSD

Steps to reproduce

Configure a primary with a pre-signed DNSSEC zone containing a delegation such as:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NS ns1.grmml.dn42.
9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NS ns2.grmml.dn42.
9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NS ns3.grmml.dn42.

The delegated subtree contains names such as:

0.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NS ns1.grmml.dn42.
1.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NS ns1.grmml.dn42.
2.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NS ns1.grmml.dn42.
3.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NS ns1.grmml.dn42.

The next name in the parent zone is:

a.d.c.0.3.2.0.0.2.4.d.f.ip6.arpa.

The correctly signed NSEC therefore is:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NSEC a.d.c.0.3.2.0.0.2.4.d.f.ip6.arpa. NS RRSIG NSEC

Configure PowerDNS Authoritative as a secondary for the zone and set:

PRESIGNED=1

Verify that the zone reports:

This is a Slave zone
Zone is presigned
Zone has NSEC semantics

Allow the secondary to transfer the zone from the primary.

The transferred zone contains the correct NSEC and RRSIG:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NSEC a.d.c.0.3.2.0.0.2.4.d.f.ip6.arpa. NS RRSIG NSEC

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN RRSIG NSEC 10 14 900 ... 1647 d.f.ip6.arpa. ...

Query the NSEC record directly from the primary:

drill -t -D ANY a.d.c.0.3.2.0.0.2.4.d.f.ip6.arpa. @

The primary returns the expected NSEC:

a.d.c.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NSEC 9.9.0.0.8.2.0.0.2.4.d.f.ip6.arpa. NS RRSIG NSEC

Query the corresponding name from the PowerDNS secondary:

drill -D PTR 9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. @

The secondary returns:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NSEC 0.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. NS RRSIG NSEC

but returns the original RRSIG received from the primary, which covers:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. NSEC a.d.c.0.3.2.0.0.2.4.d.f.ip6.arpa.

Inspect the PowerDNS SQLite database:

SELECT name, type, auth, ordername
FROM records
WHERE domain_id = (
SELECT id FROM domains WHERE name = 'd.f.ip6.arpa'
)
AND name LIKE '%.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa'
ORDER BY ordername;

The delegated descendants are present in the parent zone database with ordernames immediately following the delegation:

0.0.0.0.9.4.1... 4 2 0 0 2 3 0 1 4 9 0 0 0 0
1.0.0.0.9.4.1... 4 2 0 0 2 3 0 1 4 9 0 0 0 1
2.0.0.0.9.4.1... 4 2 0 0 2 3 0 1 4 9 0 0 0 2
3.0.0.0.9.4.1... 4 2 0 0 2 3 0 1 4 9 0 0 0 3

The delegation itself has:

9.4.1.0.3.2.0.0.2.4... 4 2 0 0 2 3 0 1 4 9

This causes the next-order-name lookup to select 0.0.0.0.9.4.1... rather than the next name in the parent NSEC chain.

Expected behaviour

For a pre-signed secondary zone, PowerDNS should serve the NSEC records represented by the transferred pre-signed zone such that the NSEC owner/next-name pair matches the corresponding transferred RRSIG.

In particular, the NSEC at:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa.

should remain:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. NSEC a.d.c.0.3.2.0.0.2.4.d.f.ip6.arpa.

The names below the delegation at 9.4.1... should not be treated as candidates for the NSEC chain of the parent zone.

Actual behaviour

PowerDNS synthesizes the NSEC successor by selecting the lexicographically next ordername in the database.

Because the delegated descendants are present immediately after the delegation in ordername order, PowerDNS selects:

0.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa.

The secondary therefore returns:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NSEC 0.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. NS RRSIG NSEC

while retaining the RRSIG transferred from the primary, which signs the NSEC whose next name is:

a.d.c.0.3.2.0.0.2.4.d.f.ip6.arpa.

Consequently the NSEC RRSIG does not validate.

The relevant database records are:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. NS auth=0 ordername=4 2 0 0 2 3 0 1 4 9
0.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. NS auth=0 ordername=4 2 0 0 2 3 0 1 4 9 0 0 0 0
1.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. NS auth=0 ordername=4 2 0 0 2 3 0 1 4 9 0 0 0 1
2.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. NS auth=0 ordername=4 2 0 0 2 3 0 1 4 9 0 0 0 2
3.0.0.0.9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. NS auth=0 ordername=4 2 0 0 2 3 0 1 4 9 0 0 0 3
a.d.c.0.3.2.0.0.2.4.d.f.ip6.arpa. NS auth=0 ordername=4 2 0 0 2 3 0 c d a

The issue appears to be related to NSEC successor selection not accounting for DNS zone cuts/delegations when selecting the next ordername in a pre-signed secondary zone.

Other information

The primary is NSD and the zone is pre-signed externally.

The AXFR received by PowerDNS contains the correct NSEC and RRSIG. For example:

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN NSEC a.d.c.0.3.2.0.0.2.4.d.f.ip6.arpa. NS RRSIG NSEC

9.4.1.0.3.2.0.0.2.4.d.f.ip6.arpa. 900 IN RRSIG NSEC 10 14 900 20260811060615 20260807060615 1647 d.f.ip6.arpa. ...

The PowerDNS database confirms that the NSEC RRSIG was imported, but no NSEC record is stored for this owner name; PowerDNS synthesizes the NSEC response.

The relevant ordername lookup in the generic SQL backend appears to select the minimum ordername greater than the queried name without excluding names below a delegation. In this case that results in the delegated descendant 0.0.0.0.9.4.1... being selected as the NSEC successor.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions