[PR #2977] [MERGED] server: implement TSIG authentication for SqliteAuthority updates #3463

Closed
opened 2026-03-16 11:44:59 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/hickory-dns/hickory-dns/pull/2977
Author: @cpu
Created: 5/7/2025
Status: Merged
Merged: 5/8/2025
Merged by: @djc

Base: mainHead: cpu-tsig-sqlite-store_dev


📝 Commits (6)

  • 043de9c server: lift up sqlite store SIG(0) verification
  • eb0881e server: remove unused sqlite store clippy allow
  • c2cc375 server: inline sqlite store log fmt args
  • 3b220b7 server: remove superfluous sqlite store type annotations
  • 42a9a58 server: impl TSIG auth for Sqlite authority
  • 9499309 integration-tests: add SqliteAuthority TSIG update tests

📊 Changes

3 files changed (+424 additions, -82 deletions)

View changed files

📝 bin/tests/integration/store_sqlite_tests.rs (+4 -0)
📝 crates/server/src/store/sqlite/mod.rs (+195 -79)
📝 tests/integration-tests/tests/integration/sqlite_authority_tests.rs (+225 -3)

📄 Description

Description

This PR extends the hickory-server's SqliteAuthority to support RFC 8945 TSIG authentication in addition to the pre-existing SIG(0) authentication support.

This support is helpful because some other notable DNS projects like kea and k8s external-dns don't support SIG(0) and using asymmetric cryptography to authenticate requests, and instead only offer the simpler TSIG mechanism based on pre-shared symmetric MAC keys. Most of the TSIG machinery was already present (via https://github.com/hickory-dns/hickory-dns/pull/1459), but only used by a DNS client to authenticate outbound messages (and verify their responses) so there isn't any new cryptography added here - just plumbing.

Since TSIG is separate from DNSSEC, and only the SqliteAuthority implements support at this time, I've chosen to place the TSIG signer configuration elements in the SqliteConfig. This might need to be revisited if other authorities are extended to support TSIG authentication in the future.

Resolves https://github.com/hickory-dns/hickory-dns/issues/2822

Testing

I've added a handful of simple integration tests, but I've also been able to demonstrate this working end-to-end using a simple Go program that uses miekg/dns as an independent TSIG/DNS implementation.

HickoryDNS config.toml
log_level = "trace"

[[zones]]
zone = "example.com"
zone_type = "Primary"
file = "example.com.zone"
allow_axfr = true

[zones.stores]
type = "sqlite"
zone_file_path = "example.com.zone"
journal_file_path = "/tmp/journals/example.com_dnssec_update.jrnl"
allow_update = true

[[zones.stores.tsig_keys]]
name = "tsig-key"
algorithm = "hmac-sha256"
key_file = "/tmp/tsig.key"
Sample zone file:
@   IN          SOA     hickory-dns.org. root.hickory-dns.org. (
                                199609203 ; Serial
                                8h        ; Refresh
                                120m      ; Retry
                                7d        ; Expire
                                24h)      ; Minimum TTL

                NS      bbb

                MX      1 alias

                ANAME   www

www             A       127.0.0.1
                AAAA    ::1

bbb             A       127.0.0.2
this.has.dots   A       127.0.0.4

alias           CNAME   www
alias-chain     CNAME   alias

aname-chain     ANAME   alias

; _Service._Proto.Name TTL Class SRV Priority Weight Port Target
server          SRV     1 1 443 alias

*.wildcard      CNAME   www

no-service 86400 IN MX 0 .
A simple Go client program:
package main

import (
	"fmt"
	"github.com/miekg/dns"
	"log"
	"net"
	"os"
	"time"
)

const (
	keyFile  = "tsig-key.base64"
	keyName  = "tsig-key."
	zoneName = "example.com."
	server   = "localhost:5454"
)

func main() {
	var encodedKey []byte
	var err error
	if _, err := os.Stat(keyFile); err == nil {
		fmt.Printf("Loading existing TSIG key from %s\n", keyFile)
		encodedKey, err = os.ReadFile(keyFile)
		if err != nil {
			log.Fatalf("failed to read key file: %v\n", err)
		}
	}

	tsigSecrets := map[string]string{keyName: string(encodedKey)}

	oldIP := net.ParseIP("127.0.0.1")
	newIP := net.ParseIP("192.168.1.10")

	update := new(dns.Msg)
	update.SetUpdate(zoneName)

	// Define a record to remove
	oldRR := &dns.A{
		Hdr: dns.RR_Header{
			Name:   zoneName,
			Rrtype: dns.TypeA,
			Class:  dns.ClassINET,
			Ttl:    3600,
		},
		A: oldIP,
	}

	// Define a record to add
	newRR := &dns.A{
		Hdr: dns.RR_Header{
			Name:   zoneName,
			Rrtype: dns.TypeA,
			Class:  dns.ClassINET,
			Ttl:    3600,
		},
		A: newIP,
	}

	update.Remove([]dns.RR{oldRR})
	update.Insert([]dns.RR{newRR})
	// NOTE(@cpu): Presently the HickoryDNS TSIG verifier requires compression to be enabled or the computed
	// MAC doesn't match. This should be fixed.
	update.Compress = true

	update.SetTsig(keyName, dns.HmacSHA256, 300, time.Now().Unix())

	fmt.Printf("Sending DNS update to change %s A record from %s to %s\n",
		zoneName, oldIP.String(), newIP.String())

	client := new(dns.Client)
	client.TsigSecret = tsigSecrets

	response, _, err := client.Exchange(update, server)
	if err != nil {
		log.Fatalf("update request failed: %v", err)
	}

	if response.Rcode != dns.RcodeSuccess {
		log.Fatalf("update failed with rcode: %s", dns.RcodeToString[response.Rcode])
	}

	fmt.Println("Zone updated successfully")
}
Client-side logs:
❯ dig +short @localhost -p 5454 example.com A
127.0.0.1

❯ go run main.go
Loading existing TSIG key from tsig-key.base64
Sending DNS update to change example.com. A record from 127.0.0.1 to 192.168.1.10
Zone updated successfully

❯ dig +short @localhost -p 5454 example.com A
192.168.1.10
Server-side logs:
<snipped>
1746647933:INFO:hickory_dns:453:server starting up, awaiting connections...
1746647937:DEBUG:hickory_server::server:128:received udp request from: [::1]:53578
1746647937:TRACE:hickory_proto::rr::record_data:850:reading OPT
1746647937:DEBUG:hickory_server::server:962:request:42453 src:udp://::1#53578 type:QUERY dnssec:false QUERY qflags:RD,AD
1746647937:DEBUG:hickory_server::server:974:query:example.com.:A:IN
1746647937:TRACE:hickory_server::authority::catalog:70:request: Request { message: MessageRequest { header: Header { id: 42453, message_type: Query, op_code: Query, authoritative: false, truncation: false, recursion_desired: true, recursion_available: false, authentic_data: true, checking_disabled: false, response_code: NoError, query_count: 1, answer_count: 0, name_server_count: 0, additional_count: 1 }, queries: Queries { queries: [LowerQuery { name: LowerName(Name("example.com.")), original: Query { name: Name("example.com."), query_type: A, query_class: IN } }], original: [7, 101, 120, 97, 109, 112, 108, 101, 3, 99, 111, 109, 0, 0, 1, 0, 1] }, answers: [], name_servers: [], additionals: [], signature: Unsigned, edns: Some(Edns { rcode_high: 0, version: 0, flags: EdnsFlags { dnssec_ok: false, z: 0 }, max_payload: 1232, options: OPT { options: [(Cookie, Unknown(10, [142, 223, 107, 40, 191, 106, 61, 60]))] } }) }, src: [::1]:53578, protocol: Udp }
1746647937:DEBUG:hickory_server::authority::catalog:123:query received: 42453
1746647937:DEBUG:hickory_server::authority::catalog:390:searching authorities for: example.com.
1746647937:DEBUG:hickory_server::authority::catalog:421:performing name: example.com. type: A class: IN on authority example.com. with request id 42453
1746647937:DEBUG:hickory_server::authority::authority_object:272:performing name: example.com. type: A class: IN on example.com.
1746647937:DEBUG:hickory_server::store::in_memory:578:searching InMemoryAuthority for: name: example.com. type: A class: IN
1746647937:TRACE:hickory_server::authority::catalog:434:catalog::lookup::authority did handle request with continue
1746647937:TRACE:hickory_server::authority::catalog:440:skipping current authority consult (index 0)
1746647937:TRACE:hickory_server::authority::catalog:443:calling authority consult (index 1)
1746647937:DEBUG:hickory_server::server::response_handler:113:sending response id=42453 response_code=No Error
1746647937:TRACE:hickory_server::server::response_handler:124:setting response max size: 1232 for protocol: Udp
1746647937:INFO:hickory_server::server:889:request:42453 src:udp://::1#53578 QUERY qflags:RD,AD response:NoError rr:1/0/3 rflags:RD,AA
1746647937:INFO:hickory_server::server:904:query:example.com.:A:IN
1746647959:DEBUG:hickory_server::server:128:received udp request from: [::1]:55901
1746647959:TRACE:hickory_proto::rr::record_data:784:reading A
1746647959:TRACE:hickory_proto::rr::record_data:784:reading A
1746647959:TRACE:hickory_proto::dnssec::rdata:624:reading TSIG
1746647959:DEBUG:hickory_server::server:962:request:57531 src:udp://::1#55901 type:QUERY dnssec:false UPDATE qflags:
1746647959:DEBUG:hickory_server::server:974:query:example.com.:SOA:IN
1746647959:TRACE:hickory_server::authority::catalog:70:request: Request { message: MessageRequest { header: Header { id: 57531, message_type: Query, op_code: Update, authoritative: false, truncation: false, recursion_desired: false, recursion_available: false, authentic_data: false, checking_disabled: false, response_code: NoError, query_count: 1, answer_count: 0, name_server_count: 2, additional_count: 1 }, queries: Queries { queries: [LowerQuery { name: LowerName(Name("example.com.")), original: Query { name: Name("example.com."), query_type: SOA, query_class: IN } }], original: [7, 101, 120, 97, 109, 112, 108, 101, 3, 99, 111, 109, 0, 0, 6, 0, 1] }, answers: [], name_servers: [Record { name_labels: Name("example.com."), dns_class: NONE, ttl: 0, rdata: A(A(127.0.0.1)), proof: Indeterminate }, Record { name_labels: Name("example.com."), dns_class: IN, ttl: 3600, rdata: A(A(192.168.1.10)), proof: Indeterminate }], additionals: [], signature: Tsig(Record { name_labels: Name("tsig-key."), dns_class: ANY, ttl: 0, rdata: DNSSEC(TSIG(TSIG { algorithm: HmacSha256, time: 1746647959, fudge: 300, mac: [106, 244, 140, 16, 112, 71, 13, 218, 42, 199, 138, 233, 39, 2, 50, 1, 118, 86, 234, 197, 241, 236, 228, 53, 83, 55, 10, 130, 220, 50, 226, 214], oid: 57531, error: 0, other: [] })), proof: Indeterminate }), edns: None }, src: [::1]:55901, protocol: Udp }
1746647959:DEBUG:hickory_server::authority::catalog:129:update received: 57531
1746647959:DEBUG:hickory_server::authority::catalog:390:searching authorities for: example.com.
1746647959:DEBUG:hickory_server::store::sqlite:934:authorizing with: Record { name_labels: Name("tsig-key."), dns_class: ANY, ttl: 0, rdata: DNSSEC(TSIG(TSIG { algorithm: HmacSha256, time: 1746647959, fudge: 300, mac: [106, 244, 140, 16, 112, 71, 13, 218, 42, 199, 138, 233, 39, 2, 50, 1, 118, 86, 234, 197, 241, 236, 228, 53, 83, 55, 10, 130, 220, 50, 226, 214], oid: 57531, error: 0, other: [] })), proof: Indeterminate }
1746647959:TRACE:hickory_proto::rr::record_data:784:reading A
1746647959:TRACE:hickory_proto::rr::record_data:784:reading A
1746647959:TRACE:hickory_proto::dnssec::rdata:624:reading TSIG
1746647959:INFO:hickory_server::store::sqlite:828:deleting specific record: Record { name_labels: Name("example.com."), dns_class: NONE, ttl: 0, rdata: A(A(127.0.0.1)), proof: Indeterminate }
1746647959:INFO:hickory_server::store::sqlite:747:upserting record: Record { name_labels: Name("example.com."), dns_class: IN, ttl: 3600, rdata: A(A(192.168.1.10)), proof: Indeterminate }
1746647959:DEBUG:hickory_server::server::response_handler:113:sending response id=57531 response_code=No Error
1746647959:TRACE:hickory_server::server::response_handler:124:setting response max size: 4096 for protocol: Udp
1746647959:INFO:hickory_server::server:889:request:57531 src:udp://::1#55901 UPDATE qflags: response:NoError rr:0/0/0 rflags:
1746647959:INFO:hickory_server::server:904:query:example.com.:SOA:IN
1746648027:DEBUG:hickory_server::server:128:received udp request from: [::1]:35019
1746648027:TRACE:hickory_proto::rr::record_data:850:reading OPT
1746648027:DEBUG:hickory_server::server:962:request:61945 src:udp://::1#35019 type:QUERY dnssec:false QUERY qflags:RD,AD
1746648027:DEBUG:hickory_server::server:974:query:example.com.:A:IN
1746648027:TRACE:hickory_server::authority::catalog:70:request: Request { message: MessageRequest { header: Header { id: 61945, message_type: Query, op_code: Query, authoritative: false, truncation: false, recursion_desired: true, recursion_available: false, authentic_data: true, checking_disabled: false, response_code: NoError, query_count: 1, answer_count: 0, name_server_count: 0, additional_count: 1 }, queries: Queries { queries: [LowerQuery { name: LowerName(Name("example.com.")), original: Query { name: Name("example.com."), query_type: A, query_class: IN } }], original: [7, 101, 120, 97, 109, 112, 108, 101, 3, 99, 111, 109, 0, 0, 1, 0, 1] }, answers: [], name_servers: [], additionals: [], signature: Unsigned, edns: Some(Edns { rcode_high: 0, version: 0, flags: EdnsFlags { dnssec_ok: false, z: 0 }, max_payload: 1232, options: OPT { options: [(Cookie, Unknown(10, [13, 106, 83, 49, 65, 2, 42, 20]))] } }) }, src: [::1]:35019, protocol: Udp }
1746648027:DEBUG:hickory_server::authority::catalog:123:query received: 61945
1746648027:DEBUG:hickory_server::authority::catalog:390:searching authorities for: example.com.
1746648027:DEBUG:hickory_server::authority::catalog:421:performing name: example.com. type: A class: IN on authority example.com. with request id 61945
1746648027:DEBUG:hickory_server::authority::authority_object:272:performing name: example.com. type: A class: IN on example.com.
1746648027:DEBUG:hickory_server::store::in_memory:578:searching InMemoryAuthority for: name: example.com. type: A class: IN
1746648027:TRACE:hickory_server::authority::catalog:434:catalog::lookup::authority did handle request with continue
1746648027:TRACE:hickory_server::authority::catalog:440:skipping current authority consult (index 0)
1746648027:TRACE:hickory_server::authority::catalog:443:calling authority consult (index 1)
1746648027:DEBUG:hickory_server::server::response_handler:113:sending response id=61945 response_code=No Error
1746648027:TRACE:hickory_server::server::response_handler:124:setting response max size: 1232 for protocol: Udp
1746648027:INFO:hickory_server::server:889:request:61945 src:udp://::1#35019 QUERY qflags:RD,AD response:NoError rr:1/0/1 rflags:RD,AA
1746648027:INFO:hickory_server::server:904:query:example.com.:A:IN

A note on name compression

One interesting thing that fell out of the above testing is that if I don't enable name compression on the client-side, the HickoryDNS side computes the wrong message MAC and so authentication fails. You can reproduce this by deleting the line in main.go where it sets update.Compress = true.

It seems like the HickoryDNS-side is reconstituting the TBS message bytes differently than they are received on the wire w.r.t name compression. I think this should be investigated & fixed as a follow-up.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/hickory-dns/hickory-dns/pull/2977 **Author:** [@cpu](https://github.com/cpu) **Created:** 5/7/2025 **Status:** ✅ Merged **Merged:** 5/8/2025 **Merged by:** [@djc](https://github.com/djc) **Base:** `main` ← **Head:** `cpu-tsig-sqlite-store_dev` --- ### 📝 Commits (6) - [`043de9c`](https://github.com/hickory-dns/hickory-dns/commit/043de9c4494081f321095df319954f76e0c69787) server: lift up sqlite store SIG(0) verification - [`eb0881e`](https://github.com/hickory-dns/hickory-dns/commit/eb0881e4e1e06fc77279097228c7d3013a263681) server: remove unused sqlite store clippy allow - [`c2cc375`](https://github.com/hickory-dns/hickory-dns/commit/c2cc375aecc93db698837ef89252392db570984b) server: inline sqlite store log fmt args - [`3b220b7`](https://github.com/hickory-dns/hickory-dns/commit/3b220b7118694ea3f02f397690d00d248b127924) server: remove superfluous sqlite store type annotations - [`42a9a58`](https://github.com/hickory-dns/hickory-dns/commit/42a9a586d7e450e43c92c6f9c540e79922396416) server: impl TSIG auth for Sqlite authority - [`9499309`](https://github.com/hickory-dns/hickory-dns/commit/949930971312348d839e1eed045695298d9a0dea) integration-tests: add SqliteAuthority TSIG update tests ### 📊 Changes **3 files changed** (+424 additions, -82 deletions) <details> <summary>View changed files</summary> 📝 `bin/tests/integration/store_sqlite_tests.rs` (+4 -0) 📝 `crates/server/src/store/sqlite/mod.rs` (+195 -79) 📝 `tests/integration-tests/tests/integration/sqlite_authority_tests.rs` (+225 -3) </details> ### 📄 Description ### Description This PR extends the `hickory-server`'s `SqliteAuthority` to support [RFC 8945](https://www.rfc-editor.org/rfc/rfc8945.html) TSIG authentication in addition to the pre-existing SIG(0) authentication support. This support is helpful because some other notable DNS projects like [kea](https://www.isc.org/kea/) and k8s [external-dns](https://github.com/kubernetes-sigs/external-dns) don't support SIG(0) and using asymmetric cryptography to authenticate requests, and instead only offer the simpler TSIG mechanism based on pre-shared symmetric MAC keys. Most of the TSIG machinery was already present (via https://github.com/hickory-dns/hickory-dns/pull/1459), but only used by a DNS _client_ to authenticate outbound messages (and verify their responses) so there isn't any new cryptography added here - just plumbing. Since TSIG is separate from DNSSEC, and only the `SqliteAuthority` implements support at this time, I've chosen to place the TSIG signer configuration elements in the `SqliteConfig`. This might need to be revisited if other authorities are extended to support TSIG authentication in the future. Resolves https://github.com/hickory-dns/hickory-dns/issues/2822 ### Testing I've added a handful of simple integration tests, but I've also been able to demonstrate this working end-to-end using a simple Go program that uses [`miekg/dns`](https://github.com/miekg/dns) as an independent TSIG/DNS implementation. <details> <summary>HickoryDNS config.toml</summary> ```toml log_level = "trace" [[zones]] zone = "example.com" zone_type = "Primary" file = "example.com.zone" allow_axfr = true [zones.stores] type = "sqlite" zone_file_path = "example.com.zone" journal_file_path = "/tmp/journals/example.com_dnssec_update.jrnl" allow_update = true [[zones.stores.tsig_keys]] name = "tsig-key" algorithm = "hmac-sha256" key_file = "/tmp/tsig.key" ``` </details> <details> <summary>Sample zone file:</summary> ```bind @ IN SOA hickory-dns.org. root.hickory-dns.org. ( 199609203 ; Serial 8h ; Refresh 120m ; Retry 7d ; Expire 24h) ; Minimum TTL NS bbb MX 1 alias ANAME www www A 127.0.0.1 AAAA ::1 bbb A 127.0.0.2 this.has.dots A 127.0.0.4 alias CNAME www alias-chain CNAME alias aname-chain ANAME alias ; _Service._Proto.Name TTL Class SRV Priority Weight Port Target server SRV 1 1 443 alias *.wildcard CNAME www no-service 86400 IN MX 0 . ``` </details> <details> <summary>A simple Go client program:</summary> ```go package main import ( "fmt" "github.com/miekg/dns" "log" "net" "os" "time" ) const ( keyFile = "tsig-key.base64" keyName = "tsig-key." zoneName = "example.com." server = "localhost:5454" ) func main() { var encodedKey []byte var err error if _, err := os.Stat(keyFile); err == nil { fmt.Printf("Loading existing TSIG key from %s\n", keyFile) encodedKey, err = os.ReadFile(keyFile) if err != nil { log.Fatalf("failed to read key file: %v\n", err) } } tsigSecrets := map[string]string{keyName: string(encodedKey)} oldIP := net.ParseIP("127.0.0.1") newIP := net.ParseIP("192.168.1.10") update := new(dns.Msg) update.SetUpdate(zoneName) // Define a record to remove oldRR := &dns.A{ Hdr: dns.RR_Header{ Name: zoneName, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600, }, A: oldIP, } // Define a record to add newRR := &dns.A{ Hdr: dns.RR_Header{ Name: zoneName, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600, }, A: newIP, } update.Remove([]dns.RR{oldRR}) update.Insert([]dns.RR{newRR}) // NOTE(@cpu): Presently the HickoryDNS TSIG verifier requires compression to be enabled or the computed // MAC doesn't match. This should be fixed. update.Compress = true update.SetTsig(keyName, dns.HmacSHA256, 300, time.Now().Unix()) fmt.Printf("Sending DNS update to change %s A record from %s to %s\n", zoneName, oldIP.String(), newIP.String()) client := new(dns.Client) client.TsigSecret = tsigSecrets response, _, err := client.Exchange(update, server) if err != nil { log.Fatalf("update request failed: %v", err) } if response.Rcode != dns.RcodeSuccess { log.Fatalf("update failed with rcode: %s", dns.RcodeToString[response.Rcode]) } fmt.Println("Zone updated successfully") } ``` </details> <details> <summary>Client-side logs:</summary> ```bash ❯ dig +short @localhost -p 5454 example.com A 127.0.0.1 ❯ go run main.go Loading existing TSIG key from tsig-key.base64 Sending DNS update to change example.com. A record from 127.0.0.1 to 192.168.1.10 Zone updated successfully ❯ dig +short @localhost -p 5454 example.com A 192.168.1.10 ``` </details> <details> <summary>Server-side logs:</summary> ``` <snipped> 1746647933:INFO:hickory_dns:453:server starting up, awaiting connections... 1746647937:DEBUG:hickory_server::server:128:received udp request from: [::1]:53578 1746647937:TRACE:hickory_proto::rr::record_data:850:reading OPT 1746647937:DEBUG:hickory_server::server:962:request:42453 src:udp://::1#53578 type:QUERY dnssec:false QUERY qflags:RD,AD 1746647937:DEBUG:hickory_server::server:974:query:example.com.:A:IN 1746647937:TRACE:hickory_server::authority::catalog:70:request: Request { message: MessageRequest { header: Header { id: 42453, message_type: Query, op_code: Query, authoritative: false, truncation: false, recursion_desired: true, recursion_available: false, authentic_data: true, checking_disabled: false, response_code: NoError, query_count: 1, answer_count: 0, name_server_count: 0, additional_count: 1 }, queries: Queries { queries: [LowerQuery { name: LowerName(Name("example.com.")), original: Query { name: Name("example.com."), query_type: A, query_class: IN } }], original: [7, 101, 120, 97, 109, 112, 108, 101, 3, 99, 111, 109, 0, 0, 1, 0, 1] }, answers: [], name_servers: [], additionals: [], signature: Unsigned, edns: Some(Edns { rcode_high: 0, version: 0, flags: EdnsFlags { dnssec_ok: false, z: 0 }, max_payload: 1232, options: OPT { options: [(Cookie, Unknown(10, [142, 223, 107, 40, 191, 106, 61, 60]))] } }) }, src: [::1]:53578, protocol: Udp } 1746647937:DEBUG:hickory_server::authority::catalog:123:query received: 42453 1746647937:DEBUG:hickory_server::authority::catalog:390:searching authorities for: example.com. 1746647937:DEBUG:hickory_server::authority::catalog:421:performing name: example.com. type: A class: IN on authority example.com. with request id 42453 1746647937:DEBUG:hickory_server::authority::authority_object:272:performing name: example.com. type: A class: IN on example.com. 1746647937:DEBUG:hickory_server::store::in_memory:578:searching InMemoryAuthority for: name: example.com. type: A class: IN 1746647937:TRACE:hickory_server::authority::catalog:434:catalog::lookup::authority did handle request with continue 1746647937:TRACE:hickory_server::authority::catalog:440:skipping current authority consult (index 0) 1746647937:TRACE:hickory_server::authority::catalog:443:calling authority consult (index 1) 1746647937:DEBUG:hickory_server::server::response_handler:113:sending response id=42453 response_code=No Error 1746647937:TRACE:hickory_server::server::response_handler:124:setting response max size: 1232 for protocol: Udp 1746647937:INFO:hickory_server::server:889:request:42453 src:udp://::1#53578 QUERY qflags:RD,AD response:NoError rr:1/0/3 rflags:RD,AA 1746647937:INFO:hickory_server::server:904:query:example.com.:A:IN 1746647959:DEBUG:hickory_server::server:128:received udp request from: [::1]:55901 1746647959:TRACE:hickory_proto::rr::record_data:784:reading A 1746647959:TRACE:hickory_proto::rr::record_data:784:reading A 1746647959:TRACE:hickory_proto::dnssec::rdata:624:reading TSIG 1746647959:DEBUG:hickory_server::server:962:request:57531 src:udp://::1#55901 type:QUERY dnssec:false UPDATE qflags: 1746647959:DEBUG:hickory_server::server:974:query:example.com.:SOA:IN 1746647959:TRACE:hickory_server::authority::catalog:70:request: Request { message: MessageRequest { header: Header { id: 57531, message_type: Query, op_code: Update, authoritative: false, truncation: false, recursion_desired: false, recursion_available: false, authentic_data: false, checking_disabled: false, response_code: NoError, query_count: 1, answer_count: 0, name_server_count: 2, additional_count: 1 }, queries: Queries { queries: [LowerQuery { name: LowerName(Name("example.com.")), original: Query { name: Name("example.com."), query_type: SOA, query_class: IN } }], original: [7, 101, 120, 97, 109, 112, 108, 101, 3, 99, 111, 109, 0, 0, 6, 0, 1] }, answers: [], name_servers: [Record { name_labels: Name("example.com."), dns_class: NONE, ttl: 0, rdata: A(A(127.0.0.1)), proof: Indeterminate }, Record { name_labels: Name("example.com."), dns_class: IN, ttl: 3600, rdata: A(A(192.168.1.10)), proof: Indeterminate }], additionals: [], signature: Tsig(Record { name_labels: Name("tsig-key."), dns_class: ANY, ttl: 0, rdata: DNSSEC(TSIG(TSIG { algorithm: HmacSha256, time: 1746647959, fudge: 300, mac: [106, 244, 140, 16, 112, 71, 13, 218, 42, 199, 138, 233, 39, 2, 50, 1, 118, 86, 234, 197, 241, 236, 228, 53, 83, 55, 10, 130, 220, 50, 226, 214], oid: 57531, error: 0, other: [] })), proof: Indeterminate }), edns: None }, src: [::1]:55901, protocol: Udp } 1746647959:DEBUG:hickory_server::authority::catalog:129:update received: 57531 1746647959:DEBUG:hickory_server::authority::catalog:390:searching authorities for: example.com. 1746647959:DEBUG:hickory_server::store::sqlite:934:authorizing with: Record { name_labels: Name("tsig-key."), dns_class: ANY, ttl: 0, rdata: DNSSEC(TSIG(TSIG { algorithm: HmacSha256, time: 1746647959, fudge: 300, mac: [106, 244, 140, 16, 112, 71, 13, 218, 42, 199, 138, 233, 39, 2, 50, 1, 118, 86, 234, 197, 241, 236, 228, 53, 83, 55, 10, 130, 220, 50, 226, 214], oid: 57531, error: 0, other: [] })), proof: Indeterminate } 1746647959:TRACE:hickory_proto::rr::record_data:784:reading A 1746647959:TRACE:hickory_proto::rr::record_data:784:reading A 1746647959:TRACE:hickory_proto::dnssec::rdata:624:reading TSIG 1746647959:INFO:hickory_server::store::sqlite:828:deleting specific record: Record { name_labels: Name("example.com."), dns_class: NONE, ttl: 0, rdata: A(A(127.0.0.1)), proof: Indeterminate } 1746647959:INFO:hickory_server::store::sqlite:747:upserting record: Record { name_labels: Name("example.com."), dns_class: IN, ttl: 3600, rdata: A(A(192.168.1.10)), proof: Indeterminate } 1746647959:DEBUG:hickory_server::server::response_handler:113:sending response id=57531 response_code=No Error 1746647959:TRACE:hickory_server::server::response_handler:124:setting response max size: 4096 for protocol: Udp 1746647959:INFO:hickory_server::server:889:request:57531 src:udp://::1#55901 UPDATE qflags: response:NoError rr:0/0/0 rflags: 1746647959:INFO:hickory_server::server:904:query:example.com.:SOA:IN 1746648027:DEBUG:hickory_server::server:128:received udp request from: [::1]:35019 1746648027:TRACE:hickory_proto::rr::record_data:850:reading OPT 1746648027:DEBUG:hickory_server::server:962:request:61945 src:udp://::1#35019 type:QUERY dnssec:false QUERY qflags:RD,AD 1746648027:DEBUG:hickory_server::server:974:query:example.com.:A:IN 1746648027:TRACE:hickory_server::authority::catalog:70:request: Request { message: MessageRequest { header: Header { id: 61945, message_type: Query, op_code: Query, authoritative: false, truncation: false, recursion_desired: true, recursion_available: false, authentic_data: true, checking_disabled: false, response_code: NoError, query_count: 1, answer_count: 0, name_server_count: 0, additional_count: 1 }, queries: Queries { queries: [LowerQuery { name: LowerName(Name("example.com.")), original: Query { name: Name("example.com."), query_type: A, query_class: IN } }], original: [7, 101, 120, 97, 109, 112, 108, 101, 3, 99, 111, 109, 0, 0, 1, 0, 1] }, answers: [], name_servers: [], additionals: [], signature: Unsigned, edns: Some(Edns { rcode_high: 0, version: 0, flags: EdnsFlags { dnssec_ok: false, z: 0 }, max_payload: 1232, options: OPT { options: [(Cookie, Unknown(10, [13, 106, 83, 49, 65, 2, 42, 20]))] } }) }, src: [::1]:35019, protocol: Udp } 1746648027:DEBUG:hickory_server::authority::catalog:123:query received: 61945 1746648027:DEBUG:hickory_server::authority::catalog:390:searching authorities for: example.com. 1746648027:DEBUG:hickory_server::authority::catalog:421:performing name: example.com. type: A class: IN on authority example.com. with request id 61945 1746648027:DEBUG:hickory_server::authority::authority_object:272:performing name: example.com. type: A class: IN on example.com. 1746648027:DEBUG:hickory_server::store::in_memory:578:searching InMemoryAuthority for: name: example.com. type: A class: IN 1746648027:TRACE:hickory_server::authority::catalog:434:catalog::lookup::authority did handle request with continue 1746648027:TRACE:hickory_server::authority::catalog:440:skipping current authority consult (index 0) 1746648027:TRACE:hickory_server::authority::catalog:443:calling authority consult (index 1) 1746648027:DEBUG:hickory_server::server::response_handler:113:sending response id=61945 response_code=No Error 1746648027:TRACE:hickory_server::server::response_handler:124:setting response max size: 1232 for protocol: Udp 1746648027:INFO:hickory_server::server:889:request:61945 src:udp://::1#35019 QUERY qflags:RD,AD response:NoError rr:1/0/1 rflags:RD,AA 1746648027:INFO:hickory_server::server:904:query:example.com.:A:IN ``` </details> ### A note on name compression One interesting thing that fell out of the above testing is that if I don't enable name compression on the client-side, the HickoryDNS side computes the wrong message MAC and so authentication fails. You can reproduce this by deleting the line in `main.go` where it sets `update.Compress = true`. It seems like the HickoryDNS-side is reconstituting the TBS message bytes differently than they are received on the wire w.r.t name compression. I think this should be investigated & fixed as a follow-up. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
kerem 2026-03-16 11:44:59 +03:00
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#3463
No description provided.