[GH-ISSUE #2234] add DNSSEC validation to Recursor #934

Closed
opened 2026-03-16 01:00:43 +03:00 by kerem · 1 comment
Owner

Originally created by @japaric on GitHub (Jun 11, 2024).
Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/2234

creating this ticket to signal that I'm working on this feature

also see #2194 (though that's the next step: after Recursor can do DNSSEC validation, we can expose that via hickory-server's configuration)

Originally created by @japaric on GitHub (Jun 11, 2024). Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/2234 creating this ticket to signal that I'm working on this feature also see #2194 (though that's the next step: after `Recursor` can do DNSSEC validation, we can expose that via `hickory-server`'s configuration)
kerem closed this issue 2026-03-16 01:00:48 +03:00
Author
Owner

@japaric commented on GitHub (Jun 11, 2024):

my first attempt has been to implement DnsHandle for Recursor such that you can wrap Recursor in DnssecHandle to do DNSSEC validating resolution. similarly, to how Resolver does DNSSEC validation when it's configured to do so

AFAICT, it seems to Just Work. it does the recursive resolution and the chain of trust validation using example.com.

fn main() -> Result<(), Box<dyn Error>> {
    let stdout_log = tracing_subscriber::fmt::layer().pretty();
    tracing_subscriber::registry()
        .with(stdout_log.with_filter(filter::LevelFilter::TRACE))
        .init();

    let mut config = NameServerConfigGroup::default();
    config.push(NameServerConfig::new(
        SocketAddrV4::new(Ipv4Addr::new(198, 41, 0, 4), 53).into(), // a.root-servers.net
        Protocol::Udp,
    ));

    let recursor = Recursor::builder().security_aware(true).build(config)?;
    let inner = recursor.inner; // HACK this field should not be public

    let dnssec = DnssecDnsHandle::new(inner);

    let options = DnsRequestOptions::default();
    let resp = tokio_test::block_on(
        dnssec
            .lookup(
                Query::query(Name::from_ascii("example.com")?, RecordType::A),
                options,
            )
            .first_answer(),
    )
    .unwrap();

    let ans = resp.answers().first().unwrap();

    println!("{ans:#?}");

    Ok(())
}
(..)
2024-06-11T11:21:33.177502Z TRACE hickory_proto::xfer::dnssec_dns_handle: validated dnskey: example.com.
    at /home/jorge/git/github/hickory-dns/hickory-dns/crates/proto/src/xfer/dnssec_dns_handle.rs:487

  2024-06-11T11:21:33.177511Z DEBUG hickory_proto::xfer::dnssec_dns_handle: verified: example.com. record_type: DNSKEY
    at /home/jorge/git/github/hickory-dns/hickory-dns/crates/proto/src/xfer/dnssec_dns_handle.rs:348

  2024-06-11T11:21:33.177607Z DEBUG hickory_proto::xfer::dnssec_dns_handle: validated (example.com., A) with (example.com., 256 3 13 nGzka+/3LSNN+P6JHx7Co8pdJ8Vjr2muj9neZK31FqTTEQkB/kQauyjLBxFwLgZcotUBgEu2+K/SWG4jAsW5+Q==)
    at /home/jorge/git/github/hickory-dns/hickory-dns/crates/proto/src/xfer/dnssec_dns_handle.rs:765

  2024-06-11T11:21:33.177630Z DEBUG hickory_proto::xfer::dnssec_dns_handle: verified: example.com. record_type: A
    at /home/jorge/git/github/hickory-dns/hickory-dns/crates/proto/src/xfer/dnssec_dns_handle.rs:348

Record {
    name_labels: Name("example.com."),
    rr_type: A,
    dns_class: IN,
    ttl: 3600,
    rdata: Some(
        A(
            A(
                93.184.215.14,
            ),
        ),
    ),
    proof: Secure, // <=
}

the current implementation is super hacky but was meant as a proof of the concept.

the goal would be make Recursor (internally) like the LookupEither enum such that it can either be in a NonValidating variant, which would be today's behavior, or a Validating variant, which would be a newtype over DnssecHandle, depending on how you configure the recursor via its builder.

the Recursor type exposed to end users would not implement the DnsHandle. instead some hidden type inside Recursor would implement the trait. this is to avoid misuse as, for example, one should not be sending UPDATE messages through a Recursor, which the DnsHandle allows

<!-- gh-comment-id:2160547897 --> @japaric commented on GitHub (Jun 11, 2024): my first attempt has been to implement `DnsHandle` for `Recursor` such that you can wrap `Recursor` in `DnssecHandle` to do DNSSEC validating resolution. similarly, to how `Resolver` does DNSSEC validation when it's configured to do so AFAICT, it seems to Just Work. it does the recursive resolution and the chain of trust validation using `example.com.` ``` rust fn main() -> Result<(), Box<dyn Error>> { let stdout_log = tracing_subscriber::fmt::layer().pretty(); tracing_subscriber::registry() .with(stdout_log.with_filter(filter::LevelFilter::TRACE)) .init(); let mut config = NameServerConfigGroup::default(); config.push(NameServerConfig::new( SocketAddrV4::new(Ipv4Addr::new(198, 41, 0, 4), 53).into(), // a.root-servers.net Protocol::Udp, )); let recursor = Recursor::builder().security_aware(true).build(config)?; let inner = recursor.inner; // HACK this field should not be public let dnssec = DnssecDnsHandle::new(inner); let options = DnsRequestOptions::default(); let resp = tokio_test::block_on( dnssec .lookup( Query::query(Name::from_ascii("example.com")?, RecordType::A), options, ) .first_answer(), ) .unwrap(); let ans = resp.answers().first().unwrap(); println!("{ans:#?}"); Ok(()) } ``` ``` console (..) 2024-06-11T11:21:33.177502Z TRACE hickory_proto::xfer::dnssec_dns_handle: validated dnskey: example.com. at /home/jorge/git/github/hickory-dns/hickory-dns/crates/proto/src/xfer/dnssec_dns_handle.rs:487 2024-06-11T11:21:33.177511Z DEBUG hickory_proto::xfer::dnssec_dns_handle: verified: example.com. record_type: DNSKEY at /home/jorge/git/github/hickory-dns/hickory-dns/crates/proto/src/xfer/dnssec_dns_handle.rs:348 2024-06-11T11:21:33.177607Z DEBUG hickory_proto::xfer::dnssec_dns_handle: validated (example.com., A) with (example.com., 256 3 13 nGzka+/3LSNN+P6JHx7Co8pdJ8Vjr2muj9neZK31FqTTEQkB/kQauyjLBxFwLgZcotUBgEu2+K/SWG4jAsW5+Q==) at /home/jorge/git/github/hickory-dns/hickory-dns/crates/proto/src/xfer/dnssec_dns_handle.rs:765 2024-06-11T11:21:33.177630Z DEBUG hickory_proto::xfer::dnssec_dns_handle: verified: example.com. record_type: A at /home/jorge/git/github/hickory-dns/hickory-dns/crates/proto/src/xfer/dnssec_dns_handle.rs:348 Record { name_labels: Name("example.com."), rr_type: A, dns_class: IN, ttl: 3600, rdata: Some( A( A( 93.184.215.14, ), ), ), proof: Secure, // <= } ``` --- the current implementation is super hacky but was meant as a proof of the concept. the goal would be make `Recursor` (internally) like the [`LookupEither` enum](https://github.com/hickory-dns/hickory-dns/blob/ed192864f3e1143bafd0e577baf9bee7e3b34a3c/crates/resolver/src/lookup.rs#L230) such that it can either be in a `NonValidating` variant, which would be today's behavior, or a `Validating` variant, which would be a newtype over `DnssecHandle`, depending on how you configure the recursor via its builder. the `Recursor` type exposed to end users would *not* implement the `DnsHandle`. instead some hidden type inside `Recursor` would implement the trait. this is to avoid misuse as, for example, one should not be sending UPDATE messages through a `Recursor`, which the `DnsHandle` allows
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/hickory-dns#934
No description provided.