Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PowerDNS: support for HTTPS/SVCB records #2992

Open
alanpearce opened this issue Jun 4, 2024 · 9 comments
Open

PowerDNS: support for HTTPS/SVCB records #2992

alanpearce opened this issue Jun 4, 2024 · 9 comments

Comments

@alanpearce
Copy link

I tried to add this myself, but got stuck. Setting providers.{CanUseHTTPS,CanUseSVCB} allows get-zones to correctly pull the records from pdns (generating HTTPS('@', 1, '.', 'alpn=h2'), but making any changes results in an error as the field values (i.e. h2 in this example) get encoded with double quotes, resulting in the following error

#1: ± MODIFY example.com HTTPS (1 . alpn="h2" ttl=86400) -> (1 . alpn="h3" ttl=86400)
FAILURE! unexpected status code 422: https://pdns.example.com/api/v1/servers/localhost/zones/example.com Record example.com./HTTPS '1 . alpn="h3"': Not in expected format (parsed as '1 . alpn=h3')

I've verified that the API works correctly when called manually:

curl -X PATCH https://pdns.example.com/api/v1/servers/localhost/zones/example.com. \
-H 'x-api-key: <snip>' -H 'content-type: application/json' \
-d '{"rrsets":[{"name":"example.com.","type":"HTTPS","ttl":86400,"changetype":"REPLACE","records":[{"content":"1 . alpn=h3"}]}]}'

Unfortunately, I couldn't figure out where to make the change for this. It looks as though the responsible code is all the way down in github.com/miekg/dns/svcb.go and I couldn't figure out how to implement a special case in the dnscontrol provider.

@cafferata
Copy link
Collaborator

Ping @jpbede, the maintainer of the PowerDNS provider.

@mbunkus
Copy link

mbunkus commented Jan 22, 2025

I'd also like to see those record types supported. I'm new to dnscontrol, started tests with my first zone & ran into this. For me it's a blocker, unfortunately; meaning I won't continue testing & implementing it just now.

Why this is important to me: other DNS providers I usually use (Hetzner, hosting.de) don't support those record types via their native web interfaces/APIs yet. However, their slave servers do support the record types. Meaning I can run my own primary DNS server with PowerDNS & use their slave servers as the name servers for my zones. Would have loved to be able to manage all this with dnscontrol.

@jpbede
Copy link
Contributor

jpbede commented Jan 22, 2025

I'll see if I can figure out anything in the next few days.

@mbunkus
Copy link

mbunkus commented Jan 22, 2025

That's very kind of you. Thanks!

@Veratil
Copy link
Contributor

Veratil commented Feb 9, 2025

I've looked into this and found prior to PowerDNS Authoritative 4.9.3 (released 2025/12/17) there was a bug handling the port parameter. This was fixed in PowerDNS/pdns#14968.

As of now, activating SVCB and HTTPS in PowerDNS can be done, but there's still an issue with adding parameters (after showing they work without parameters).

$ git diff
diff --git a/providers/powerdns/powerdnsProvider.go b/providers/powerdns/powerdnsProvider.go
index fd611bf4..e0f5b397 100644
--- a/providers/powerdns/powerdnsProvider.go
+++ b/providers/powerdns/powerdnsProvider.go
@@ -20,12 +20,14 @@ var features = providers.DocumentationNotes{
        providers.CanUseCAA:              providers.Can(),
        providers.CanUseDS:               providers.Can(),
        providers.CanUseDHCID:            providers.Can(),
+       providers.CanUseHTTPS:            providers.Can(),
        providers.CanUseLOC:              providers.Unimplemented("Normalization within the PowerDNS API seems to be buggy, so disabled", "https://github.com/PowerDNS/pdns/issues/10558"),
        providers.CanUseNAPTR:            providers.Can(),
        providers.CanUsePTR:              providers.Can(),
        providers.CanUseSOA:              providers.Can(),
        providers.CanUseSRV:              providers.Can(),
        providers.CanUseSSHFP:            providers.Can(),
+       providers.CanUseSVCB:             providers.Can(),
        providers.CanUseTLSA:             providers.Can(),
        providers.DocCreateDomains:       providers.Can(),
        providers.DocDualHost:            providers.Can(),

Creating a zone that has some content and adding the SVCB and HTTPS records without any parameters:

var REG_NONE = NewRegistrar("none");
var PROV_PDNS = NewDnsProvider("pdns");

D("test.internal", REG_NONE, DnsProvider(PROV_PDNS),
        SOA('@', 'ns.test.internal.', 'hostmaster.test.internal.', 60, 60, 604800, 60, TTL(60)),
        NAMESERVER("ns.test.internal."),
        A('ns', '172.18.0.2'),
        A("@", "172.18.0.2"),
        A("bump", "172.18.0.3"),
        SVCB("_8443._https", 1, "bump.test.internal.", ""),
        HTTPS("www", 1, '.', ''),
END);

I added the SVCB and HTTPS in separate pushes:

$ dnscontrol push
CONCURRENTLY gathering 0 zone(s)
SERIALLY gathering 1 zone(s)
Serially Gathering: "test.internal"
******************** Domain: test.internal
1 correction (pdns)
#1: ± BATCHED CHANGE/CREATEs for test.internal
+ CREATE _8443._https.test.internal SVCB 1 bump.test.internal. ttl=300
SUCCESS!
Done. 1 corrections.

$ dnscontrol push
CONCURRENTLY gathering 0 zone(s)
SERIALLY gathering 1 zone(s)
Serially Gathering: "test.internal"
******************** Domain: test.internal
1 correction (pdns)
#1: ± BATCHED CHANGE/CREATEs for test.internal
+ CREATE www.test.internal HTTPS 1 . ttl=300
SUCCESS!
Done. 1 corrections.

Once I add a parameter PowerDNS fails parsing it. I'm leaning towards this is a PowerDNS parsing issue, but I'm not sure.

$ dnscontrol push
CONCURRENTLY gathering 0 zone(s)
SERIALLY gathering 1 zone(s)
Serially Gathering: "test.internal"
******************** Domain: test.internal
1 correction (pdns)
#1: ± BATCHED CHANGE/CREATEs for test.internal
± MODIFY _8443._https.test.internal SVCB (1 bump.test.internal. ttl=300) -> (1 bump.test.internal. ipv4hint="172.18.0.3" ttl=300)
FAILURE! unexpected status code 422: http://127.0.0.1:8081/api/v1/servers/localhost/zones/test.internal. Record _8443._https.test.internal./SVCB '1 bump.test.internal. ipv4hint="172.18.0.3"': Not in expected format (parsed as '1 bump.test.internal. ipv4hint=172.18.0.3')
Done. 1 corrections.
completed with errors

I don't suspect I have the records wrong, but I'm not familiar with using them and not sure if PowerDNS expects them in a certain format. I've tried all sorts of variations of parameters and they all result with the Not in expected format error.

@Veratil
Copy link
Contributor

Veratil commented Feb 9, 2025

Did some more testing, I managed to get HTTPS to work with a parameter using curl.

Attempting to replicate this with dnscontrol though failed. Trying some more iterations led me to finding out that the quotes are causing the parsing error.

This caused an error:

      "records": [
        {
          "content": "1 www.test.internal. ipv4hint=\"172.18.0.3\"",
          "disabled": false
        }
      ],

whereas this did not and successfully created the record:

      "records": [
        {
          "content": "1 www.test.internal. ipv4hint=172.18.0.3",
          "disabled": false
        }
      ],

The quotes are added through the miekg/dns package used here: https://github.com/StackExchange/dnscontrol/blob/main/models/target.go#L88

Here's full after converting:

(dlv) p full
"www.test.internal.\t300\tIN\tHTTPS\t1 www.test.internal. ipv4hint=\"172.18.0.3\""

and in the mittwald/go-powerdns Record format:

(dlv) p records[0]
github.com/mittwald/go-powerdns/apis/zones.Record {
        Content: "1 www.test.internal. ipv4hint=\"172.18.0.3\"",
        Disabled: false,
        SetPTR: false,}

I tried removing all quotes from SVCB and HTTPS records by adding this to the powerdns provider function which converts to the mittwald/go-powerdns API format:

diff --git a/providers/powerdns/diff.go b/providers/powerdns/diff.go
index fdd2ee20..9df4893d 100644
--- a/providers/powerdns/diff.go
+++ b/providers/powerdns/diff.go
@@ -81,9 +81,16 @@ func (dsp *powerdnsProvider) getDiff2DomainCorrections(dc *models.DomainConfig,
 // buildRecordList returns a list of records for the PowerDNS resource record set from a change
 func buildRecordList(change diff2.Change) (records []zones.Record) {
        for _, recordContent := range change.New {
-               records = append(records, zones.Record{
+               newRecord := zones.Record{
                        Content: recordContent.GetTargetCombined(),
-               })
+               }
+               switch recordContent.Type {
+               case "HTTPS":
+                       newRecord.Content = strings.ReplaceAll(newRecord.Content, "\"", "")
+               case "SVCB":
+                       newRecord.Content = strings.ReplaceAll(newRecord.Content, "\"", "")
+               }
+               records = append(records, newRecord)
        }
        return
 }

which let me get to this result:

$ dnscontrol push
CONCURRENTLY gathering 0 zone(s)
SERIALLY gathering 1 zone(s)
Serially Gathering: "test.internal"
******************** Domain: test.internal
1 correction (pdns)
#1: ± BATCHED CHANGE/CREATEs for test.internal
+ CREATE www.test.internal HTTPS 1 www.test.internal. ipv4hint="172.18.0.3" ttl=300
SUCCESS!
Done. 1 corrections.

This does lead me to believe that this needs to be fixed on the PowerDNS side, similar to how the port issue mentioned above was fixed.

@Veratil
Copy link
Contributor

Veratil commented Feb 9, 2025

I've opened PowerDNS/pdns#15135 to check if this is a bug on their side.

@mbunkus
Copy link

mbunkus commented Feb 9, 2025

That turned out to be quite the odyssey. Thank you so much for all your work.

@jpbede
Copy link
Contributor

jpbede commented Feb 11, 2025

Thanks @Veratil for your extensive research on this, much appreciated! 👍

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

No branches or pull requests

6 participants