From 9ec5c8a18fd8417e28c62455b97d9b541a94abd7 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Mon, 28 Nov 2022 18:53:13 +0100 Subject: [PATCH] fix: return an error when extracting record name (#1766) --- challenge/dns01/domain.go | 24 ++++ challenge/dns01/domain_test.go | 104 ++++++++++++++++++ providers/dns/alidns/alidns.go | 10 +- providers/dns/arvancloud/arvancloud.go | 16 +-- providers/dns/civo/civo.go | 23 ++-- providers/dns/constellix/constellix.go | 14 ++- providers/dns/desec/desec.go | 14 ++- providers/dns/dnsimple/dnsimple.go | 33 +++--- providers/dns/dnspod/dnspod.go | 35 +++--- providers/dns/exoscale/exoscale.go | 8 +- providers/dns/gandi/gandi.go | 10 +- providers/dns/gandiv5/gandiv5.go | 12 +- providers/dns/glesys/glesys.go | 11 +- providers/dns/godaddy/godaddy.go | 27 ++--- providers/dns/hetzner/hetzner.go | 23 ++-- providers/dns/infomaniak/infomaniak.go | 13 +-- providers/dns/mythicbeasts/mythicbeasts.go | 14 ++- providers/dns/namedotcom/namedotcom.go | 18 ++- providers/dns/namesilo/namesilo.go | 20 ++-- .../dns/nearlyfreespeech/nearlyfreespeech.go | 18 ++- providers/dns/ovh/ovh.go | 15 +-- providers/dns/sakuracloud/client.go | 26 ++--- providers/dns/servercow/servercow.go | 14 ++- providers/dns/stackpath/stackpath.go | 23 ++-- providers/dns/tencentcloud/client.go | 10 +- providers/dns/vkcloud/vkcloud.go | 14 ++- providers/dns/vultr/vultr.go | 23 ++-- providers/dns/yandexcloud/yandexcloud.go | 14 ++- providers/dns/zoneee/zoneee.go | 2 +- 29 files changed, 363 insertions(+), 225 deletions(-) create mode 100644 challenge/dns01/domain.go create mode 100644 challenge/dns01/domain_test.go diff --git a/challenge/dns01/domain.go b/challenge/dns01/domain.go new file mode 100644 index 00000000..e9b0cec5 --- /dev/null +++ b/challenge/dns01/domain.go @@ -0,0 +1,24 @@ +package dns01 + +import ( + "fmt" + "strings" + + "github.com/miekg/dns" +) + +// ExtractSubDomain extracts the subdomain part from a domain and a zone. +func ExtractSubDomain(domain, zone string) (string, error) { + canonDomain := dns.Fqdn(domain) + canonZone := dns.Fqdn(zone) + + if canonDomain == canonZone { + return "", fmt.Errorf("no subdomain because the domain and the zone are identical: %s", canonDomain) + } + + if !dns.IsSubDomain(canonZone, canonDomain) { + return "", fmt.Errorf("%s is not a subdomain of %s", canonDomain, canonZone) + } + + return strings.TrimSuffix(canonDomain, "."+canonZone), nil +} diff --git a/challenge/dns01/domain_test.go b/challenge/dns01/domain_test.go new file mode 100644 index 00000000..fcb16a93 --- /dev/null +++ b/challenge/dns01/domain_test.go @@ -0,0 +1,104 @@ +package dns01 + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestExtractSubDomain(t *testing.T) { + testCases := []struct { + desc string + domain string + zone string + expected string + }{ + { + desc: "no FQDN", + domain: "_acme-challenge.example.com", + zone: "example.com", + expected: "_acme-challenge", + }, + { + desc: "no FQDN zone", + domain: "_acme-challenge.example.com.", + zone: "example.com", + expected: "_acme-challenge", + }, + { + desc: "no FQDN domain", + domain: "_acme-challenge.example.com", + zone: "example.com.", + expected: "_acme-challenge", + }, + { + desc: "FQDN", + domain: "_acme-challenge.example.com.", + zone: "example.com.", + expected: "_acme-challenge", + }, + { + desc: "multi-level subdomain", + domain: "_acme-challenge.one.example.com.", + zone: "example.com.", + expected: "_acme-challenge.one", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + subDomain, err := ExtractSubDomain(test.domain, test.zone) + require.NoError(t, err) + + assert.Equal(t, test.expected, subDomain) + }) + } +} + +func TestExtractSubDomain_errors(t *testing.T) { + testCases := []struct { + desc string + domain string + zone string + }{ + { + desc: "same domain", + domain: "example.com", + zone: "example.com", + }, + { + desc: "same domain, no FQDN zone", + domain: "example.com.", + zone: "example.com", + }, + { + desc: "same domain, no FQDN domain", + domain: "example.com", + zone: "example.com.", + }, + { + desc: "same domain, FQDN", + domain: "example.com.", + zone: "example.com.", + }, + { + desc: "zone and domain are unrelated", + domain: "_acme-challenge.example.com", + zone: "example.org", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + _, err := ExtractSubDomain(test.domain, test.zone) + require.Error(t, err) + }) + } +} diff --git a/providers/dns/alidns/alidns.go b/providers/dns/alidns/alidns.go index 339909bb..9340dc28 100644 --- a/providers/dns/alidns/alidns.go +++ b/providers/dns/alidns/alidns.go @@ -4,7 +4,6 @@ package alidns import ( "errors" "fmt" - "strings" "time" "github.com/aliyun/alibaba-cloud-sdk-go/sdk" @@ -269,9 +268,10 @@ func extractRecordName(fqdn, zone string) (string, error) { return "", fmt.Errorf("fail to convert punycode: %w", err) } - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+asciiDomain); idx != -1 { - return name[:idx], nil + subDomain, err := dns01.ExtractSubDomain(fqdn, asciiDomain) + if err != nil { + return "", err } - return name, nil + + return subDomain, nil } diff --git a/providers/dns/arvancloud/arvancloud.go b/providers/dns/arvancloud/arvancloud.go index 1b630a45..e1034c9e 100644 --- a/providers/dns/arvancloud/arvancloud.go +++ b/providers/dns/arvancloud/arvancloud.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "strings" "sync" "time" @@ -114,9 +113,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return err } + subDomain, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("arvancloud: %w", err) + } + record := internal.DNSRecord{ Type: "txt", - Name: extractRecordName(fqdn, authZone), + Name: subDomain, Value: internal.TXTRecordValue{Text: value}, TTL: d.config.TTL, UpstreamHTTPS: "default", @@ -176,11 +180,3 @@ func getZone(fqdn string) (string, error) { return dns01.UnFqdn(authZone), nil } - -func extractRecordName(fqdn, zone string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+zone); idx != -1 { - return name[:idx] - } - return name -} diff --git a/providers/dns/civo/civo.go b/providers/dns/civo/civo.go index fa3cbf68..f228d1ef 100644 --- a/providers/dns/civo/civo.go +++ b/providers/dns/civo/civo.go @@ -4,7 +4,6 @@ package civo import ( "errors" "fmt" - "strings" "time" "github.com/civo/civogo" @@ -104,8 +103,13 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("civo: %w", err) } + subDomain, err := dns01.ExtractSubDomain(fqdn, zone) + if err != nil { + return fmt.Errorf("civo: %w", err) + } + _, err = d.client.CreateDNSRecord(dnsDomain.ID, &civogo.DNSRecordConfig{ - Name: extractRecordName(fqdn, zone), + Name: subDomain, Value: value, Type: civogo.DNSRecordTypeTXT, TTL: d.config.TTL, @@ -136,9 +140,14 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("civo: %w", err) } + subDomain, err := dns01.ExtractSubDomain(fqdn, zone) + if err != nil { + return fmt.Errorf("civo: %w", err) + } + var dnsRecord civogo.DNSRecord for _, entry := range dnsRecords { - if entry.Name == extractRecordName(fqdn, zone) && entry.Value == value { + if entry.Name == subDomain && entry.Value == value { dnsRecord = entry break } @@ -166,11 +175,3 @@ func getZone(fqdn string) (string, error) { return dns01.UnFqdn(authZone), nil } - -func extractRecordName(fqdn, zone string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+zone); idx != -1 { - return name[:idx] - } - return name -} diff --git a/providers/dns/constellix/constellix.go b/providers/dns/constellix/constellix.go index 8e972ba9..6c0b069f 100644 --- a/providers/dns/constellix/constellix.go +++ b/providers/dns/constellix/constellix.go @@ -109,7 +109,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("constellix: failed to get domain (%s): %w", authZone, err) } - recordName := getRecordName(fqdn, authZone) + recordName, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("constellix: %w", err) + } records, err := d.client.TxtRecords.Search(dom.ID, internal.Exact, recordName) if err != nil { @@ -147,7 +150,10 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("constellix: failed to get domain (%s): %w", authZone, err) } - recordName := getRecordName(fqdn, authZone) + recordName, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("constellix: %w", err) + } records, err := d.client.TxtRecords.Search(dom.ID, internal.Exact, recordName) if err != nil { @@ -262,7 +268,3 @@ func containsValue(record *internal.Record, value string) bool { return false } - -func getRecordName(fqdn, authZone string) string { - return fqdn[0 : len(fqdn)-len(authZone)-1] -} diff --git a/providers/dns/desec/desec.go b/providers/dns/desec/desec.go index 2d133ea0..87ebcbcb 100644 --- a/providers/dns/desec/desec.go +++ b/providers/dns/desec/desec.go @@ -109,7 +109,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("desec: could not find zone for domain %q and fqdn %q : %w", domain, fqdn, err) } - recordName := getRecordName(fqdn, authZone) + recordName, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("desec: %w", err) + } domainName := dns01.UnFqdn(authZone) @@ -156,7 +159,10 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("desec: could not find zone for domain %q and fqdn %q : %w", domain, fqdn, err) } - recordName := getRecordName(fqdn, authZone) + recordName, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("desec: %w", err) + } domainName := dns01.UnFqdn(authZone) @@ -179,7 +185,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } - -func getRecordName(fqdn, authZone string) string { - return fqdn[0 : len(fqdn)-len(authZone)-1] -} diff --git a/providers/dns/dnsimple/dnsimple.go b/providers/dns/dnsimple/dnsimple.go index 58ff0aeb..23120fc8 100644 --- a/providers/dns/dnsimple/dnsimple.go +++ b/providers/dns/dnsimple/dnsimple.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "strconv" - "strings" "time" "github.com/dnsimple/dnsimple-go/dnsimple" @@ -103,7 +102,11 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("dnsimple: %w", err) } - recordAttributes := newTxtRecord(zoneName, fqdn, value, d.config.TTL) + recordAttributes, err := newTxtRecord(zoneName, fqdn, value, d.config.TTL) + if err != nil { + return fmt.Errorf("dnsimple: %w", err) + } + _, err = d.client.Zones.CreateRecord(context.Background(), accountID, zoneName, recordAttributes) if err != nil { return fmt.Errorf("dnsimple: API call failed: %w", err) @@ -186,9 +189,12 @@ func (d *DNSProvider) findTxtRecords(fqdn string) ([]dnsimple.ZoneRecord, error) return nil, err } - recordName := extractRecordName(fqdn, zoneName) + subDomain, err := dns01.ExtractSubDomain(fqdn, zoneName) + if err != nil { + return nil, err + } - result, err := d.client.Zones.ListRecords(context.Background(), accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: &recordName, Type: dnsimple.String("TXT"), ListOptions: dnsimple.ListOptions{}}) + result, err := d.client.Zones.ListRecords(context.Background(), accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: &subDomain, Type: dnsimple.String("TXT"), ListOptions: dnsimple.ListOptions{}}) if err != nil { return nil, fmt.Errorf("API call has failed: %w", err) } @@ -196,23 +202,18 @@ func (d *DNSProvider) findTxtRecords(fqdn string) ([]dnsimple.ZoneRecord, error) return result.Data, nil } -func newTxtRecord(zoneName, fqdn, value string, ttl int) dnsimple.ZoneRecordAttributes { - name := extractRecordName(fqdn, zoneName) +func newTxtRecord(zoneName, fqdn, value string, ttl int) (dnsimple.ZoneRecordAttributes, error) { + subDomain, err := dns01.ExtractSubDomain(fqdn, zoneName) + if err != nil { + return dnsimple.ZoneRecordAttributes{}, err + } return dnsimple.ZoneRecordAttributes{ Type: "TXT", - Name: &name, + Name: &subDomain, Content: value, TTL: ttl, - } -} - -func extractRecordName(fqdn, zone string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+zone); idx != -1 { - return name[:idx] - } - return name + }, nil } func (d *DNSProvider) getAccountID() (string, error) { diff --git a/providers/dns/dnspod/dnspod.go b/providers/dns/dnspod/dnspod.go index 02a0c92e..622b2f34 100644 --- a/providers/dns/dnspod/dnspod.go +++ b/providers/dns/dnspod/dnspod.go @@ -6,7 +6,6 @@ import ( "fmt" "net/http" "strconv" - "strings" "time" "github.com/go-acme/lego/v4/challenge/dns01" @@ -94,7 +93,11 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return err } - recordAttributes := d.newTxtRecord(zoneName, fqdn, value, d.config.TTL) + recordAttributes, err := d.newTxtRecord(zoneName, fqdn, value, d.config.TTL) + if err != nil { + return err + } + _, _, err = d.client.Records.Create(zoneID, *recordAttributes) if err != nil { return fmt.Errorf("API call failed: %w", err) @@ -157,40 +160,38 @@ func (d *DNSProvider) getHostedZone(domain string) (string, string, error) { return hostedZone.ID.String(), hostedZone.Name, nil } -func (d *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Record { - name := extractRecordName(fqdn, zone) +func (d *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) (*dnspod.Record, error) { + subDomain, err := dns01.ExtractSubDomain(fqdn, zone) + if err != nil { + return nil, err + } return &dnspod.Record{ Type: "TXT", - Name: name, + Name: subDomain, Value: value, Line: "默认", TTL: strconv.Itoa(ttl), - } + }, nil } func (d *DNSProvider) findTxtRecords(fqdn, zoneID, zoneName string) ([]dnspod.Record, error) { - recordName := extractRecordName(fqdn, zoneName) + subDomain, err := dns01.ExtractSubDomain(fqdn, zoneName) + if err != nil { + return nil, err + } var records []dnspod.Record - result, _, err := d.client.Records.List(zoneID, recordName) + result, _, err := d.client.Records.List(zoneID, subDomain) if err != nil { return records, fmt.Errorf("API call has failed: %w", err) } for _, record := range result { - if record.Name == recordName { + if record.Name == subDomain { records = append(records, record) } } return records, nil } - -func extractRecordName(fqdn, zone string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+zone); idx != -1 { - return name[:idx] - } - return name -} diff --git a/providers/dns/exoscale/exoscale.go b/providers/dns/exoscale/exoscale.go index 59a9495a..db9da874 100644 --- a/providers/dns/exoscale/exoscale.go +++ b/providers/dns/exoscale/exoscale.go @@ -254,8 +254,10 @@ func (d *DNSProvider) findZoneAndRecordName(fqdn string) (string, string, error) zone = dns01.UnFqdn(zone) - name := dns01.UnFqdn(fqdn) - name = name[:len(name)-len("."+zone)] + subDomain, err := dns01.ExtractSubDomain(fqdn, zone) + if err != nil { + return "", "", err + } - return zone, name, nil + return zone, subDomain, nil } diff --git a/providers/dns/gandi/gandi.go b/providers/dns/gandi/gandi.go index 3e2086e7..c04c3d51 100644 --- a/providers/dns/gandi/gandi.go +++ b/providers/dns/gandi/gandi.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "strings" "sync" "time" @@ -131,11 +130,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } // determine name of TXT record - if !strings.HasSuffix( - strings.ToLower(fqdn), strings.ToLower("."+authZone)) { - return fmt.Errorf("gandi: unexpected authZone %s for fqdn %s", authZone, fqdn) + subDomain, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("gandi: %w", err) } - name := fqdn[:len(fqdn)-len("."+authZone)] // acquire lock and check there is not a challenge already in // progress for this value of authZone @@ -160,7 +158,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("gandi: %w", err) } - err = d.addTXTRecord(newZoneID, newZoneVersion, name, value, d.config.TTL) + err = d.addTXTRecord(newZoneID, newZoneVersion, subDomain, value, d.config.TTL) if err != nil { return fmt.Errorf("gandi: %w", err) } diff --git a/providers/dns/gandiv5/gandiv5.go b/providers/dns/gandiv5/gandiv5.go index ea1650a9..4e72952b 100644 --- a/providers/dns/gandiv5/gandiv5.go +++ b/providers/dns/gandiv5/gandiv5.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "strings" "sync" "time" @@ -120,11 +119,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } // determine name of TXT record - if !strings.HasSuffix( - strings.ToLower(fqdn), strings.ToLower("."+authZone)) { - return fmt.Errorf("gandiv5: unexpected authZone %s for fqdn %s", authZone, fqdn) + subDomain, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("gandiv5: %w", err) } - name := fqdn[:len(fqdn)-len("."+authZone)] // acquire lock and check there is not a challenge already in // progress for this value of authZone @@ -132,7 +130,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { defer d.inProgressMu.Unlock() // add TXT record into authZone - err = d.addTXTRecord(dns01.UnFqdn(authZone), name, value, d.config.TTL) + err = d.addTXTRecord(dns01.UnFqdn(authZone), subDomain, value, d.config.TTL) if err != nil { return err } @@ -140,7 +138,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // save data necessary for CleanUp d.inProgressFQDNs[fqdn] = inProgressInfo{ authZone: authZone, - fieldName: name, + fieldName: subDomain, } return nil } diff --git a/providers/dns/glesys/glesys.go b/providers/dns/glesys/glesys.go index 81f8e7db..702157d4 100644 --- a/providers/dns/glesys/glesys.go +++ b/providers/dns/glesys/glesys.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "strings" "sync" "time" @@ -107,12 +106,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("glesys: findZoneByFqdn failure: %w", err) } - // determine name of TXT record - if !strings.HasSuffix( - strings.ToLower(fqdn), strings.ToLower("."+authZone)) { - return fmt.Errorf("glesys: unexpected authZone %s for fqdn %s", authZone, fqdn) + subDomain, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("glesys: %w", err) } - name := fqdn[:len(fqdn)-len("."+authZone)] // acquire lock and check there is not a challenge already in // progress for this value of authZone @@ -121,7 +118,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // add TXT record into authZone // TODO(ldez) replace domain by FQDN to follow CNAME. - recordID, err := d.addTXTRecord(domain, dns01.UnFqdn(authZone), name, value, d.config.TTL) + recordID, err := d.addTXTRecord(domain, dns01.UnFqdn(authZone), subDomain, value, d.config.TTL) if err != nil { return err } diff --git a/providers/dns/godaddy/godaddy.go b/providers/dns/godaddy/godaddy.go index a3befd13..1976d891 100644 --- a/providers/dns/godaddy/godaddy.go +++ b/providers/dns/godaddy/godaddy.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "strings" "time" "github.com/go-acme/lego/v4/challenge/dns01" @@ -110,9 +109,12 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("godaddy: failed to get zone: %w", err) } - recordName := extractRecordName(fqdn, domainZone) + subDomain, err := dns01.ExtractSubDomain(fqdn, domainZone) + if err != nil { + return fmt.Errorf("godaddy: %w", err) + } - records, err := d.client.GetRecords(domainZone, "TXT", recordName) + records, err := d.client.GetRecords(domainZone, "TXT", subDomain) if err != nil { return fmt.Errorf("godaddy: failed to get TXT records: %w", err) } @@ -126,13 +128,13 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { record := internal.DNSRecord{ Type: "TXT", - Name: recordName, + Name: subDomain, Data: value, TTL: d.config.TTL, } newRecords = append(newRecords, record) - err = d.client.UpdateTxtRecords(newRecords, domainZone, recordName) + err = d.client.UpdateTxtRecords(newRecords, domainZone, subDomain) if err != nil { return fmt.Errorf("godaddy: failed to add TXT record: %w", err) } @@ -149,9 +151,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("godaddy: failed to get zone: %w", err) } - recordName := extractRecordName(fqdn, domainZone) + subDomain, err := dns01.ExtractSubDomain(fqdn, domainZone) + if err != nil { + return fmt.Errorf("godaddy: %w", err) + } - records, err := d.client.GetRecords(domainZone, "TXT", recordName) + records, err := d.client.GetRecords(domainZone, "TXT", subDomain) if err != nil { return fmt.Errorf("godaddy: failed to get TXT records: %w", err) } @@ -186,14 +191,6 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func extractRecordName(fqdn, zone string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+zone); idx != -1 { - return name[:idx] - } - return name -} - func getZone(fqdn string) (string, error) { authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { diff --git a/providers/dns/hetzner/hetzner.go b/providers/dns/hetzner/hetzner.go index 33ffa8ba..a15d9004 100644 --- a/providers/dns/hetzner/hetzner.go +++ b/providers/dns/hetzner/hetzner.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "strings" "time" "github.com/go-acme/lego/v4/challenge/dns01" @@ -111,9 +110,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("hetzner: %w", err) } + subDomain, err := dns01.ExtractSubDomain(fqdn, zone) + if err != nil { + return fmt.Errorf("hetzner: %w", err) + } + record := internal.DNSRecord{ Type: "TXT", - Name: extractRecordName(fqdn, zone), + Name: subDomain, Value: value, TTL: d.config.TTL, ZoneID: zoneID, @@ -140,9 +144,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("hetzner: %w", err) } - recordName := extractRecordName(fqdn, zone) + subDomain, err := dns01.ExtractSubDomain(fqdn, zone) + if err != nil { + return fmt.Errorf("hetzner: %w", err) + } - record, err := d.client.GetTxtRecord(recordName, value, zoneID) + record, err := d.client.GetTxtRecord(subDomain, value, zoneID) if err != nil { return fmt.Errorf("hetzner: %w", err) } @@ -154,14 +161,6 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } -func extractRecordName(fqdn, zone string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+zone); idx != -1 { - return name[:idx] - } - return name -} - func getZone(fqdn string) (string, error) { authZone, err := dns01.FindZoneByFqdn(fqdn) if err != nil { diff --git a/providers/dns/infomaniak/infomaniak.go b/providers/dns/infomaniak/infomaniak.go index b68614b9..ca996abd 100644 --- a/providers/dns/infomaniak/infomaniak.go +++ b/providers/dns/infomaniak/infomaniak.go @@ -122,8 +122,13 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { d.domainIDs[token] = ikDomain.ID d.domainIDsMu.Unlock() + subDomain, err := dns01.ExtractSubDomain(fqdn, ikDomain.CustomerName) + if err != nil { + return fmt.Errorf("infomaniak: %w", err) + } + record := internal.Record{ - Source: extractRecordName(fqdn, ikDomain.CustomerName), + Source: subDomain, Target: value, Type: "TXT", TTL: d.config.TTL, @@ -184,9 +189,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -func extractRecordName(fqdn, domain string) string { - name := dns01.UnFqdn(fqdn) - - return name[:len(name)-len(domain)-1] -} diff --git a/providers/dns/mythicbeasts/mythicbeasts.go b/providers/dns/mythicbeasts/mythicbeasts.go index 40760f57..dcd3e898 100644 --- a/providers/dns/mythicbeasts/mythicbeasts.go +++ b/providers/dns/mythicbeasts/mythicbeasts.go @@ -114,7 +114,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("mythicbeasts: %w", err) } - leaf := fqdn[:len(fqdn)-(len(authZone)+1)] + subDomain, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("mythicbeasts: %w", err) + } authZone = dns01.UnFqdn(authZone) @@ -123,7 +126,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("mythicbeasts: %w", err) } - err = d.createTXTRecord(authZone, leaf, value) + err = d.createTXTRecord(authZone, subDomain, value) if err != nil { return fmt.Errorf("mythicbeasts: %w", err) } @@ -140,7 +143,10 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("mythicbeasts: %w", err) } - leaf := fqdn[:len(fqdn)-(len(authZone)+1)] + subDomain, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("mythicbeasts: %w", err) + } authZone = dns01.UnFqdn(authZone) @@ -149,7 +155,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("mythicbeasts: %w", err) } - err = d.removeTXTRecord(authZone, leaf, value) + err = d.removeTXTRecord(authZone, subDomain, value) if err != nil { return fmt.Errorf("mythicbeasts: %w", err) } diff --git a/providers/dns/namedotcom/namedotcom.go b/providers/dns/namedotcom/namedotcom.go index cee75df8..bb55061b 100644 --- a/providers/dns/namedotcom/namedotcom.go +++ b/providers/dns/namedotcom/namedotcom.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "strings" "time" "github.com/go-acme/lego/v4/challenge/dns01" @@ -111,13 +110,18 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { // TODO(ldez) replace domain by FQDN to follow CNAME. domainDetails, err := d.client.GetDomain(&namecom.GetDomainRequest{DomainName: domain}) if err != nil { - return fmt.Errorf("namedotcom API call failed: %w", err) + return fmt.Errorf("namedotcom: API call failed: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(fqdn, domainDetails.DomainName) + if err != nil { + return fmt.Errorf("namedotcom: %w", err) } // TODO(ldez) replace domain by FQDN to follow CNAME. request := &namecom.Record{ DomainName: domain, - Host: extractRecordName(fqdn, domainDetails.DomainName), + Host: subDomain, Type: "TXT", TTL: uint32(d.config.TTL), Answer: value, @@ -183,11 +187,3 @@ func (d *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) { return records, nil } - -func extractRecordName(fqdn, zone string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+zone); idx != -1 { - return name[:idx] - } - return name -} diff --git a/providers/dns/namesilo/namesilo.go b/providers/dns/namesilo/namesilo.go index 8cb9cd4b..5c490c6d 100644 --- a/providers/dns/namesilo/namesilo.go +++ b/providers/dns/namesilo/namesilo.go @@ -4,7 +4,6 @@ package namesilo import ( "errors" "fmt" - "strings" "time" "github.com/go-acme/lego/v4/challenge/dns01" @@ -94,10 +93,15 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("namesilo: %w", err) } + subdomain, err := dns01.ExtractSubDomain(fqdn, zoneName) + if err != nil { + return fmt.Errorf("namesilo: %w", err) + } + _, err = d.client.DnsAddRecord(&namesilo.DnsAddRecordParams{ Domain: zoneName, Type: "TXT", - Host: getRecordName(fqdn, zoneName), + Host: subdomain, Value: value, TTL: d.config.TTL, }) @@ -121,10 +125,14 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("namesilo: %w", err) } + subdomain, err := dns01.ExtractSubDomain(fqdn, zoneName) + if err != nil { + return fmt.Errorf("namesilo: %w", err) + } + var lastErr error - name := getRecordName(fqdn, zoneName) for _, r := range resp.Reply.ResourceRecord { - if r.Type == "TXT" && (r.Host == name || r.Host == dns01.UnFqdn(fqdn)) { + if r.Type == "TXT" && (r.Host == subdomain || r.Host == dns01.UnFqdn(fqdn)) { _, err := d.client.DnsDeleteRecord(&namesilo.DnsDeleteRecordParams{Domain: zoneName, ID: r.RecordID}) if err != nil { lastErr = fmt.Errorf("namesilo: %w", err) @@ -147,7 +155,3 @@ func getZoneNameByDomain(domain string) (string, error) { } return dns01.UnFqdn(zone), nil } - -func getRecordName(domain, zone string) string { - return strings.TrimSuffix(dns01.ToFqdn(domain), "."+dns01.ToFqdn(zone)) -} diff --git a/providers/dns/nearlyfreespeech/nearlyfreespeech.go b/providers/dns/nearlyfreespeech/nearlyfreespeech.go index 62fa1842..e6aba7ff 100644 --- a/providers/dns/nearlyfreespeech/nearlyfreespeech.go +++ b/providers/dns/nearlyfreespeech/nearlyfreespeech.go @@ -115,8 +115,13 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("nearlyfreespeech: could not determine zone for domain %q: %w", fqdn, err) } + recordName, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("nearlyfreespeech: %w", err) + } + record := internal.Record{ - Name: getRecordName(fqdn, authZone), + Name: recordName, Type: "TXT", Data: value, TTL: d.config.TTL, @@ -139,8 +144,13 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("nearlyfreespeech: could not determine zone for domain %q: %w", fqdn, err) } + recordName, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("nearlyfreespeech: %w", err) + } + record := internal.Record{ - Name: getRecordName(fqdn, authZone), + Name: recordName, Type: "TXT", Data: value, } @@ -152,7 +162,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } - -func getRecordName(fqdn, authZone string) string { - return fqdn[0 : len(fqdn)-len(authZone)-1] -} diff --git a/providers/dns/ovh/ovh.go b/providers/dns/ovh/ovh.go index 5ce5f172..bb35462d 100644 --- a/providers/dns/ovh/ovh.go +++ b/providers/dns/ovh/ovh.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "strings" "sync" "time" @@ -132,7 +131,11 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } authZone = dns01.UnFqdn(authZone) - subDomain := extractRecordName(fqdn, authZone) + + subDomain, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("ovh: %w", err) + } reqURL := fmt.Sprintf("/domain/zone/%s/record", authZone) reqData := Record{FieldType: "TXT", SubDomain: subDomain, Target: value, TTL: d.config.TTL} @@ -204,11 +207,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -func extractRecordName(fqdn, zone string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+zone); idx != -1 { - return name[:idx] - } - return "" -} diff --git a/providers/dns/sakuracloud/client.go b/providers/dns/sakuracloud/client.go index b45221f1..ed124dd6 100644 --- a/providers/dns/sakuracloud/client.go +++ b/providers/dns/sakuracloud/client.go @@ -3,7 +3,6 @@ package sakuracloud import ( "context" "fmt" - "strings" "sync" "github.com/go-acme/lego/v4/challenge/dns01" @@ -21,17 +20,21 @@ func (d *DNSProvider) addTXTRecord(fqdn, value string, ttl int) error { zone, err := d.getHostedZone(fqdn) if err != nil { - return fmt.Errorf("%w", err) + return err } - name := extractRecordName(fqdn, zone.Name) + subDomain, err := dns01.ExtractSubDomain(fqdn, zone.Name) + if err != nil { + return err + } records := append(zone.Records, &iaas.DNSRecord{ - Name: name, + Name: subDomain, Type: "TXT", RData: value, TTL: ttl, }) + _, err = d.client.UpdateSettings(context.Background(), zone.ID, &iaas.DNSUpdateSettingsRequest{ Records: records, SettingsHash: zone.SettingsHash, @@ -52,11 +55,14 @@ func (d *DNSProvider) cleanupTXTRecord(fqdn, value string) error { return err } - recordName := extractRecordName(fqdn, zone.Name) + subDomain, err := dns01.ExtractSubDomain(fqdn, zone.Name) + if err != nil { + return err + } var updRecords iaas.DNSRecords for _, r := range zone.Records { - if !(r.Name == recordName && r.Type == "TXT" && r.RData == value) { + if !(r.Name == subDomain && r.Type == "TXT" && r.RData == value) { updRecords = append(updRecords, r) } } @@ -104,11 +110,3 @@ func (d *DNSProvider) getHostedZone(domain string) (*iaas.DNS, error) { return nil, fmt.Errorf("zone %s not found", zoneName) } - -func extractRecordName(fqdn, zone string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+zone); idx != -1 { - return name[:idx] - } - return name -} diff --git a/providers/dns/servercow/servercow.go b/providers/dns/servercow/servercow.go index 664f07ae..7298cc24 100644 --- a/providers/dns/servercow/servercow.go +++ b/providers/dns/servercow/servercow.go @@ -109,7 +109,10 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("servercow: %w", err) } - recordName := getRecordName(fqdn, authZone) + recordName, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("servercow: %w", err) + } record := findRecords(records, recordName) @@ -162,7 +165,10 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("servercow: failed to get TXT records: %w", err) } - recordName := getRecordName(fqdn, authZone) + recordName, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("servercow: %w", err) + } record := findRecords(records, recordName) if record == nil { @@ -231,7 +237,3 @@ func containsValue(record *internal.Record, value string) bool { return false } - -func getRecordName(fqdn, authZone string) string { - return fqdn[0 : len(fqdn)-len(authZone)-2] -} diff --git a/providers/dns/stackpath/stackpath.go b/providers/dns/stackpath/stackpath.go index 14abd3c2..0398d252 100644 --- a/providers/dns/stackpath/stackpath.go +++ b/providers/dns/stackpath/stackpath.go @@ -8,7 +8,6 @@ import ( "fmt" "net/http" "net/url" - "strings" "time" "github.com/go-acme/lego/v4/challenge/dns01" @@ -120,8 +119,13 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("stackpath: %w", err) } + subDomain, err := dns01.ExtractSubDomain(fqdn, zone.Domain) + if err != nil { + return fmt.Errorf("stackpath: %w", err) + } + record := Record{ - Name: extractRecordName(fqdn, zone.Domain), + Name: subDomain, Type: "TXT", TTL: d.config.TTL, Data: value, @@ -139,9 +143,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return fmt.Errorf("stackpath: %w", err) } - recordName := extractRecordName(fqdn, zone.Domain) + subDomain, err := dns01.ExtractSubDomain(fqdn, zone.Domain) + if err != nil { + return fmt.Errorf("stackpath: %w", err) + } - records, err := d.getZoneRecords(recordName, zone) + records, err := d.getZoneRecords(subDomain, zone) if err != nil { return err } @@ -161,11 +168,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { return d.config.PropagationTimeout, d.config.PollingInterval } - -func extractRecordName(fqdn, zone string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+zone); idx != -1 { - return name[:idx] - } - return name -} diff --git a/providers/dns/tencentcloud/client.go b/providers/dns/tencentcloud/client.go index 22315c36..84643133 100644 --- a/providers/dns/tencentcloud/client.go +++ b/providers/dns/tencentcloud/client.go @@ -3,7 +3,6 @@ package tencentcloud import ( "errors" "fmt" - "strings" "github.com/go-acme/lego/v4/challenge/dns01" "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" @@ -84,9 +83,10 @@ func extractRecordName(fqdn, zone string) (string, error) { return "", fmt.Errorf("fail to convert punycode: %w", err) } - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+asciiDomain); idx != -1 { - return name[:idx], nil + subDomain, err := dns01.ExtractSubDomain(fqdn, asciiDomain) + if err != nil { + return "", err } - return name, nil + + return subDomain, nil } diff --git a/providers/dns/vkcloud/vkcloud.go b/providers/dns/vkcloud/vkcloud.go index 1eb00e05..840c3dc5 100644 --- a/providers/dns/vkcloud/vkcloud.go +++ b/providers/dns/vkcloud/vkcloud.go @@ -144,9 +144,12 @@ func (r *DNSProvider) Present(domain, _, keyAuth string) error { return fmt.Errorf("vkcloud: cant find dns zone %s in VK Cloud", authZone) } - name := fqdn[:len(fqdn)-len(authZone)-1] + subDomain, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("vkcloud: %w", err) + } - err = r.upsertTXTRecord(zoneUUID, name, value) + err = r.upsertTXTRecord(zoneUUID, subDomain, value) if err != nil { return fmt.Errorf("vkcloud: %w", err) } @@ -182,9 +185,12 @@ func (r *DNSProvider) CleanUp(domain, _, keyAuth string) error { return nil } - name := fqdn[:len(fqdn)-len(authZone)-1] + subDomain, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("vkcloud: %w", err) + } - err = r.removeTXTRecord(zoneUUID, name, value) + err = r.removeTXTRecord(zoneUUID, subDomain, value) if err != nil { return fmt.Errorf("vkcloud: %w", err) } diff --git a/providers/dns/vultr/vultr.go b/providers/dns/vultr/vultr.go index c29f8428..23370db1 100644 --- a/providers/dns/vultr/vultr.go +++ b/providers/dns/vultr/vultr.go @@ -105,10 +105,13 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("vultr: %w", err) } - name := extractRecordName(fqdn, zoneDomain) + subDomain, err := dns01.ExtractSubDomain(fqdn, zoneDomain) + if err != nil { + return fmt.Errorf("vultr: %w", err) + } req := govultr.DomainRecordReq{ - Name: name, + Name: subDomain, Type: "TXT", Data: `"` + value + `"`, TTL: d.config.TTL, @@ -196,6 +199,11 @@ func (d *DNSProvider) findTxtRecords(ctx context.Context, domain, fqdn string) ( return "", nil, err } + subDomain, err := dns01.ExtractSubDomain(fqdn, zoneDomain) + if err != nil { + return "", nil, err + } + listOptions := &govultr.ListOptions{PerPage: 25} var records []govultr.DomainRecord @@ -205,9 +213,8 @@ func (d *DNSProvider) findTxtRecords(ctx context.Context, domain, fqdn string) ( return "", records, fmt.Errorf("API call has failed: %w", err) } - recordName := extractRecordName(fqdn, zoneDomain) for _, record := range result { - if record.Type == "TXT" && record.Name == recordName { + if record.Type == "TXT" && record.Name == subDomain { records = append(records, record) } } @@ -221,11 +228,3 @@ func (d *DNSProvider) findTxtRecords(ctx context.Context, domain, fqdn string) ( return zoneDomain, records, nil } - -func extractRecordName(fqdn, zone string) string { - name := dns01.UnFqdn(fqdn) - if idx := strings.Index(name, "."+zone); idx != -1 { - return name[:idx] - } - return name -} diff --git a/providers/dns/yandexcloud/yandexcloud.go b/providers/dns/yandexcloud/yandexcloud.go index 4eb19a0c..bcad16d2 100644 --- a/providers/dns/yandexcloud/yandexcloud.go +++ b/providers/dns/yandexcloud/yandexcloud.go @@ -128,9 +128,12 @@ func (r *DNSProvider) Present(domain, _, keyAuth string) error { return fmt.Errorf("yandexcloud: cant find dns zone %s in yandex cloud", authZone) } - name := fqdn[:len(fqdn)-len(authZone)-1] + subDomain, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("yandexcloud: %w", err) + } - err = r.upsertRecordSetData(ctx, zoneID, name, value) + err = r.upsertRecordSetData(ctx, zoneID, subDomain, value) if err != nil { return fmt.Errorf("yandexcloud: %w", err) } @@ -166,9 +169,12 @@ func (r *DNSProvider) CleanUp(domain, _, keyAuth string) error { return nil } - name := fqdn[:len(fqdn)-len(authZone)-1] + subDomain, err := dns01.ExtractSubDomain(fqdn, authZone) + if err != nil { + return fmt.Errorf("yandexcloud: %w", err) + } - err = r.removeRecordSetData(ctx, zoneID, name, value) + err = r.removeRecordSetData(ctx, zoneID, subDomain, value) if err != nil { return fmt.Errorf("yandexcloud: %w", err) } diff --git a/providers/dns/zoneee/zoneee.go b/providers/dns/zoneee/zoneee.go index fbb07593..32f02fe7 100644 --- a/providers/dns/zoneee/zoneee.go +++ b/providers/dns/zoneee/zoneee.go @@ -108,7 +108,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { fqdn, value := dns01.GetRecord(domain, keyAuth) record := txtRecord{ - Name: fqdn[:len(fqdn)-1], + Name: dns01.UnFqdn(fqdn), Destination: value, }