Skip to content

Conversation

@sammko
Copy link
Contributor

@sammko sammko commented Apr 21, 2025

This is in reaction to caddy introducing a breaking change to all dns providers. The code vaguely works, but in all honesty I didn't spend too much time looking into it. Tests pass and some manual testing works as well. I will report back if I get this to work under caddy with https://github.com/caddy-dns/powerdns

I am expecting more breakage with ECH and HTTPS RRs.

Incorporates #5.
Fixes #10.

@sammko
Copy link
Contributor Author

sammko commented Apr 22, 2025

Seems to work under caddy, at least for acme dns challenges. Haven't tried ECH.

@jonaharagon
Copy link

This still does not work with ECH, although unlike in caddy-dns/powerdns#3 I'm not sure why this is the case because the record format appears to be correct.

2025/04/25 15:05:03.696 ERROR   tls     unable to publish ECH data to HTTPS DNS record  {"domain": "test.as401332.net", "zone": "as401332.net.", "dns_record_name": "test", "error": "unexpected status code 422: http://127.0.0.1:12355/api/v1/servers/localhost/zones/as401332.net. Record test.as401332.net./HTTPS '2 . ech=AEv+DQBHLAAgACD9vSG0FXozgCj7ExnF6lukn1VmusjlHAHyusuvvCYYNAAMAAEAAQABAAIAAQADIBBlY2guYXM0MDEzMzIubmV0AAA=': Not in expected format (parsed as '2 . ech=\"AEv+DQBHLAAgACD9vSG0FXozgCj7ExnF6lukn1VmusjlHAHyusuvvCYYNAAMAAEAAQABAAIAAQADIBBlY2guYXM0MDEzMzIubmV0AAA=\"')"}

@sammko
Copy link
Contributor Author

sammko commented Apr 25, 2025

Yeah I experienced the same thing. I haven't had time to look into it, and I'm not really interested in using ECH so not sure if I will.

Anyway here are some related tickets
StackExchange/dnscontrol#2992
PowerDNS/pdns#15135

@jonaharagon
Copy link

jonaharagon commented Apr 25, 2025

Yes, the second issue seems to be the problem.

I was looking into it though and I'm not sure where those double quotes around the ech= value are even being added

@jonaharagon
Copy link

jonaharagon commented Apr 25, 2025

Well this is a very dumb hack but it (enclosing the ech= data in double quotes, which is the opposite of those issues you linked?) does technically work.

--- libdns-powerdns-push-krruyzppowkz/client.go 2025-04-25 11:54:11
+++ libdns-powerdns-modified/client.go  2025-04-25 12:03:52
@@ -210,6 +210,9 @@
                if out[i].Type == "TXT" {
                        out[i].Data = txtsanitize.TXTSanitize(out[i].Data)
                }
+               if out[i].Type == "HTTPS" {
+                       out[i].Data = strings.ReplaceAll(out[i].Data, "ech=", "ech=\"") + "\""
+               }
        }
        return out
 }

I would assume there is a smarter way to do this.

@sammko
Copy link
Contributor Author

sammko commented Apr 26, 2025

Ah hmm I see. Should we ask caddy to include the quotes? It does sound like the generally safer option, although the spec doesn't seem to have an opinion.

@mholt
Copy link

mholt commented Apr 26, 2025

I believe -- correct me if I'm wrong -- the quotes are optional, and only needed to escape certain values.

We've got over 80 (close to 100, soon) DNS provider packages, it'd be really nice if we didn't have to cater to nuances of individual ones, especially when frankly our behavior is correct (AFAIK).

Could this package just adjust for the provider quirks instead? i.e. implicitly add quotes if it really requires them?

@sammko
Copy link
Contributor Author

sammko commented Apr 27, 2025

I think that's correct, and fair enough -- though I'm not proposing tailoring to this provider, but to add the (optional) quotes unconditionally. Though that may cause breakage elsewhere, of course.

If we want to implement this here, we

  • mustn't assume ech is the only option present, hence we essentially need a full and correct parser for the value. This is what I was trying to avoid. edit: the libdns interface for this is structured enough, so shouldn't be hard.
  • should understand the pdns issue in more detail, the linked ticket observes pdns removing quotes instead of adding them. Maybe if we're unlucky and the base64 string contains only alphanumerics (by chance), pdns' normalisation step will behave differently.

@sammko
Copy link
Contributor Author

sammko commented Apr 27, 2025

I believe the pdns serializer includes the quotes unconditionally in ech options:
https://github.com/PowerDNS/pdns/blob/cb460fbe30c4a562cba6a92078d41415e701684c/pdns/rcpgenerator.cc#L939-L945

The gist of the issue is that it parses the input, serializes it back to a string and compares. If they don't match, it rejects the value.
The linked issue is about ipv4hint, which behaves differently.

@mholt
Copy link

mholt commented Apr 28, 2025

I think that's correct, and fair enough -- though I'm not proposing tailoring to this provider, but to add the (optional) quotes unconditionally.

I get what you mean (like an "if" statement depending on the provider, I think?) but, in my view, adding otherwise-unnecessary code just to make it work with one provider's quirks is tailoring to a provider. So, still not inclined to do this, but let me know if we can make this easier for you in a more generally useful way!

pdns currently requires ech parameters to be quoted even if the
contained value does not need quoting. Copy the SVCB serialisation logic
from parent libdns and modify it to quote ech parameter values
unconditionally.
@sammko
Copy link
Contributor Author

sammko commented Apr 29, 2025

Just to clarify what I meant was to add something like the following to func (params SvcParams) String() string in libdns. (I thought initially this was done already in Caddy, so my mistake on mixing that up above):

if key == "ech" {
    needsQuotes = true
}

So nothing provider-specific in the sense that there's no if provider == "powerdns", but also not beautiful as you would have to justify it because of a bug in pdns.

I've pushed a commit here that duplicates some of the libdns logic and modifies it so that the quotes are added. It works fine, but it's a bit sad how much I had to copy.

@mholt
Copy link

mholt commented Apr 30, 2025

Just to clarify what I meant was to add something like the following to func (params SvcParams) String() string in libdns. (I thought initially this was done already in Caddy, so my mistake on mixing that up above):

if key == "ech" {
    needsQuotes = true
}

So nothing provider-specific in the sense that there's no if provider == "powerdns"

Well, but that IS provider-specific code to appease a specific provider, that is totally unnecessary otherwise. (And wasteful, as adding quotes adds bytes to the wire.)

Thanks for fixing the bug in this repo!

(You could probably avoid duplication with a hacky call to SvcParams.String() where you then replace ech=... in the output with ech="...", like with a regex or something -- but that's probably less proper than what you've done, and either way is fine IMO. Thanks again!)

@Crosswind
Copy link

Is there anything else that can be done to help get this merged?

Checking the issue against powerdns itself hasn’t shown much updates. Once powerdns accepts quotes the code here could be cleaned up again. I suppose it’s not worth waiting for that but going with the “hacky” version. Thanks a lot @sammko

@Crosswind
Copy link

Crosswind commented Aug 12, 2025

@nathanejohnson @oliemansm can either of you have a look at this PR and see if this is mergeable? If there is more work that needs to be done, let us know.

@boecks
Copy link

boecks commented Oct 17, 2025

The provider still doesn’t build against the current libdns interface, and it looks like a few of us are maintaining temporary forks or workarounds to keep Caddy + PowerDNS builds working.

Has anyone found a clean interim solution, or could one of the maintainers take a look at this PR when possible? It would unblock DNS-01 support for PowerDNS users again.

Thanks for all the work so far — happy to help test or verify the fix if needed.

Edit: I’ve also opened an issue in the libdns repo to surface this more broadly for maintainers.

Copy link

@gucci-on-fleek gucci-on-fleek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't use PowerDNS and I haven't tested this at all, but this all looks reasonable to me. Thanks for the PR!


I agree that the quoting stuff is pretty awkward. Per RFC 9460 §A, the quotes should be optional in this case, and the ech= RFC gives examples both with and without the quotes.

Both dig (Bind) and kdig (Knot) quote alpn= but not ech=, while sdig (PowerDNS) quotes ech= but not alpn=, and drill (NSD) quotes nothing. None of these values need to be quoted (but all of them are allowed to be quoted), so I find this behaviour fairly surprising:

$ dig +short maxchernoff.ca. HTTPS
1 . alpn="h3,h2" ipv4hint=152.53.36.213 ech=AE3+DQBJywAgACB3z7T5b444Ovb++6VpxjU6ZXWdtzp8maLOs5GKpTbhWQAMAAEAAQABAAIAAQADIhJlY2gubWF4Y2hlcm5vZmYuY2EAAA== ipv6hint=2a0a:4cc0:2000:172::1

$ kdig +short maxchernoff.ca. HTTPS
1 . alpn="h3,h2" ipv4hint=152.53.36.213 ech=AE3+DQBJywAgACB3z7T5b444Ovb++6VpxjU6ZXWdtzp8maLOs5GKpTbhWQAMAAEAAQABAAIAAQADIhJlY2gubWF4Y2hlcm5vZmYuY2EAAA== ipv6hint=2a0a:4cc0:2000:172::1

$ sdig 127.0.0.53 53 maxchernoff.ca. HTTPS recurse
Reply to question for qname='maxchernoff.ca.', qtype=HTTPS
Rcode: 0 (No Error), RD: 1, QR: 1, TC: 0, AA: 0, opcode: 0
0	maxchernoff.ca.	6324	IN	HTTPS	1 . alpn=h3,h2 ipv4hint=152.53.36.213 ech="AE3+DQBJywAgACB3z7T5b444Ovb++6VpxjU6ZXWdtzp8maLOs5GKpTbhWQAMAAEAAQABAAIAAQADIhJlY2gubWF4Y2hlcm5vZmYuY2EAAA==" ipv6hint=2a0a:4cc0:2000:172::1

$ drill -Q maxchernoff.ca. HTTPS
1 . alpn=h3,h2 ipv4hint=152.53.36.213 ech=AE3+DQBJywAgACB3z7T5b444Ovb++6VpxjU6ZXWdtzp8maLOs5GKpTbhWQAMAAEAAQABAAIAAQADIhJlY2gubWF4Y2hlcm5vZmYuY2EAAA== ipv6hint=2a0a:4cc0:2000:172::1

If there are other providers that require the ech= key to be quoted, then I guess we should probably quote that in the base libdns package, but if only PowerDNS requires this, then I think that we'll probably leave the current behaviour as-is.

Copy link

@boecks boecks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking a look and for the detailed notes on quoting behavior!

That aligns with my understanding as well — quoting is optional per RFC 9460, but since PowerDNS currently expects ech= values to be quoted, it seems most practical to handle this quirk within the PowerDNS provider itself.

That way we stay RFC-compliant and avoid changing behavior for other providers unnecessarily.

If other providers later turn out to have similar parsing expectations, the quoting logic could always be centralized in libdns at that point.

For now, keeping the fix at the provider level seems like a balanced and low-risk approach.

Hence, approval from my side as well for the proposed solution.

@nathanejohnson nathanejohnson merged commit 8899755 into libdns:master Oct 17, 2025
@nathanejohnson
Copy link
Collaborator

sorry this took so long to get merged. I've merged this and tagged v0.1.4

@francislavoie
Copy link

@nathanejohnson don't forget to also update the go.mod in https://github.com/caddy-dns/powerdns to match!

@nathanejohnson
Copy link
Collaborator

caddy-dns/powerdns#6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upgrade to libdns 1.0 (currently in beta)

9 participants