diff --git a/.github/ISSUE_TEMPLATE/new_dns_provider.yml b/.github/ISSUE_TEMPLATE/new_dns_provider.yml
index b319bc287..cfd6e5c8c 100644
--- a/.github/ISSUE_TEMPLATE/new_dns_provider.yml
+++ b/.github/ISSUE_TEMPLATE/new_dns_provider.yml
@@ -14,15 +14,9 @@ body:
required: true
- label: Yes, I know that the lego maintainers don't have an account in all DNS providers in the world.
required: true
-
- - type: checkboxes
- id: pr
- attributes:
- label: Implementation
- options:
- label: Yes, I'm able to create a pull request and be able to maintain the implementation.
required: false
- - label: Yes, I can test an implementation with the help of the maintainers if someone creates a pull request.
+ - label: Yes, I'm able to test an implementation if someone creates a pull request to add the support of this DNS provider.
required: false
- type: dropdown
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index 33ca106cc..5df64db7f 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
env:
GO_VERSION: stable
- GOLANGCI_LINT_VERSION: v2.10
+ GOLANGCI_LINT_VERSION: v2.8.0
HUGO_VERSION: 0.148.2
CGO_ENABLED: 0
LEGO_E2E_TESTS: CI
@@ -50,7 +50,7 @@ jobs:
run: go install github.com/letsencrypt/pebble/v2/cmd/pebble-challtestsrv@v2.9.0
- name: Set up a Memcached server
- run: docker run -d --rm -p 11211:11211 memcached:1.6-alpine
+ uses: niden/actions-memcached@v7
- name: Make
run: |
diff --git a/.golangci.yml b/.golangci.yml
index b6ab51ccc..b851169ff 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -183,9 +183,6 @@ linters:
- text: "var-naming: avoid meaningless package names"
linters:
- revive
- - text: "var-naming: avoid package names that conflict with Go standard library package names"
- linters:
- - revive
- path: certcrypto/crypto.go
text: (tlsFeatureExtensionOID|ocspMustStapleFeature) is a global variable
linters:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae73f70f3..ee191cdb4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,36 +6,6 @@ Everybody thinks that the others will donate, but in the end, nobody does.
So if you think that lego is worth it, please consider [donating](https://donate.ldez.dev).
-## v4.32.0
-
-- Release date: 2026-02-19
-- Tag: [v4.32.0](https://github.com/go-acme/lego/releases/tag/v4.32.0)
-
-### Added
-
-- **[dnsprovider]** Add DNS provider for ArtFiles
-- **[dnsprovider]** Add DNS provider for Leaseweb
-- **[dnsprovider]** Add DNS provider for FusionLayer NameSurfer
-- **[dnsprovider]** Add DNS provider for DDNSS
-- **[dnsprovider]** Add DNS provider for Bluecat v2
-- **[dnsprovider]** Add DNS provider for TodayNIC/时代互联
-- **[dnsprovider]** Add DNS provider for DNSExit
-- **[dnsprovider]** alidns: add line record option
-
-### Changed
-
-- **[dnsprovider]** azure: reinforces deprecation
-- **[dnsprovider]** allinkl: detect zone through API
-
-### Fixed
-
-- **[ari]** fix: implement parsing for Retry-After header according to RFC 7231
-- **[dnsprovider]** namesurfer: fix updateDNSHost
-- **[dnsprovider]** timewebcloud: fix subdomain support
-- **[dnsprovider]** fix: deduplicate authz for DNS01 challenge
-- **[lib,cli]** fix: use IPs to define the main domain
-- **[lib]** fix: preserve domain order
-
## v4.31.0
- Release date: 2026-01-08
diff --git a/README.md b/README.md
index e90e94962..c02347e23 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
# Lego
-[ACME](https://www.rfc-editor.org/rfc/rfc8555.html) client and library for Let's Encrypt and other ACME CAs written in Go.
+Let's Encrypt client and ACME library written in Go.
[](https://pkg.go.dev/github.com/go-acme/lego/v4)
[](https://github.com//go-acme/lego/actions)
@@ -73,233 +73,223 @@ If your DNS provider is not supported, please open an [issue](https://github.com
| Amazon Route 53 |
Anexia CloudDNS |
- ANS SafeDNS |
- ArtFiles |
-
| ArvanCloud |
Aurora DNS |
+
| Autodns |
Axelname |
-
| Azion |
Azure (deprecated) |
+
| Azure DNS |
Baidu Cloud |
-
| Beget.com |
Binary Lane |
+
| Bindman |
Bluecat |
-
- | Bluecat v2 |
BookMyName |
Brandit (deprecated) |
- Bunny |
+ | Bunny |
Checkdomain |
Civo |
Cloud.ru |
- CloudDNS |
+ | CloudDNS |
Cloudflare |
ClouDNS |
CloudXNS (Deprecated) |
- ConoHa v2 |
+ | ConoHa v2 |
ConoHa v3 |
Constellix |
Core-Networks |
- CPanel/WHM |
- | Czechia |
- DDnss (DynDNS Service) |
+ CPanel/WHM |
Derak Cloud |
deSEC.io |
-
| Designate DNSaaS for Openstack |
+
| Digital Ocean |
DirectAdmin |
DNS Made Easy |
-
- | DNSExit |
dnsHome.de |
+
| DNSimple |
DNSPod (deprecated) |
-
| Domain Offensive (do.de) |
Domeneshop |
+
| DreamHost |
Duck DNS |
-
| Dyn |
DynDnsFree.de |
+
| Dynu |
EasyDNS |
-
| EdgeCenter |
Efficient IP |
- Epik |
- EuroDNS |
- | Excedo |
+ Epik |
Exoscale |
External program |
F5 XC |
| freemyip.com |
- FusionLayer NameSurfer |
G-Core |
Gandi |
-
| Gandi Live DNS (v5) |
+
| Gigahost.no |
Glesys |
Go Daddy |
-
| Google Cloud |
+
| Google Domains |
Gravity |
Hetzner |
-
| Hosting.de |
+
| Hosting.nl |
Hostinger |
Hosttech |
-
| HTTP request |
+
| http.net |
Huawei Cloud |
Hurricane Electric DNS |
-
| HyperOne |
+
| IBM Cloud (SoftLayer) |
IIJ DNS Platform Service |
Infoblox |
-
| Infomaniak |
+
| Internet Initiative Japan |
Internet.bs |
INWX |
-
| Ionos |
+
| Ionos Cloud |
IPv64 |
ISPConfig 3 |
-
| ISPConfig 3 - Dynamic DNS (DDNS) Module |
+
| iwantmyname (Deprecated) |
JD Cloud |
Joker |
-
| Joohoi's ACME-DNS |
- KeyHelp |
- Leaseweb |
- Liara |
+ | KeyHelp |
+ Liara |
Lima-City |
Linode (v4) |
+
| Liquid Web |
Loopia |
-
| LuaDNS |
Mail-in-a-Box |
+
| ManageEngine CloudDNS |
Manual |
-
| Metaname |
Metaregistrar |
+
| mijn.host |
Mittwald |
-
| myaddr.{tools,dev,io} |
MyDNS.jp |
+
| MythicBeasts |
Name.com |
-
| Namecheap |
Namesilo |
+
| NearlyFreeSpeech.NET |
Neodigit |
-
| Netcup |
Netlify |
+
| Nicmanager |
NIFCloud |
-
| Njalla |
Nodion |
+
| NS1 |
Octenium |
-
| Open Telekom Cloud |
Oracle Cloud |
+
| OVH |
plesk.com |
-
| Porkbun |
PowerDNS |
+
| Rackspace |
Rain Yun/雨云 |
-
| RcodeZero |
reg.ru |
+
| Regfish |
RFC2136 |
-
| RimuHosting |
RU CENTER |
+
| Sakura Cloud |
Scaleway |
-
| Selectel |
Selectel v2 |
+
| SelfHost.(de|eu) |
Servercow |
-
| Shellrent |
Simply.com |
+
| Sonic |
Spaceship |
-
| Stackpath |
Syse |
+
| Technitium |
Tencent Cloud DNS |
-
| Tencent EdgeOne |
Timeweb Cloud |
- TodayNIC/时代互联 |
- TransIP |
+ | TransIP |
+ UKFast SafeDNS |
Ultradns |
United-Domains |
+
| Variomedia |
VegaDNS |
-
| Vercel |
Versio.[nl|eu|uk] |
+
| VinylDNS |
Virtualname |
-
| VK Cloud |
Volcano Engine/火山引擎 |
+
| Vscale |
Vultr |
-
| webnames.ca |
webnames.ru |
+
| Websupport |
WEDOS |
-
| West.cn/西部数码 |
Yandex 360 |
+
| Yandex Cloud |
Yandex PDD |
-
| Zone.ee |
ZoneEdit |
+
| Zonomi |
|
+ |
+ |
diff --git a/acme/api/identifier.go b/acme/api/identifier.go
index 245ed8515..42a8fd391 100644
--- a/acme/api/identifier.go
+++ b/acme/api/identifier.go
@@ -2,6 +2,7 @@ package api
import (
"cmp"
+ "maps"
"net"
"slices"
@@ -9,9 +10,7 @@ import (
)
func createIdentifiers(domains []string) []acme.Identifier {
- uniqIdentifiers := make(map[string]struct{})
-
- var identifiers []acme.Identifier
+ uniqIdentifiers := make(map[string]acme.Identifier)
for _, domain := range domains {
if _, ok := uniqIdentifiers[domain]; ok {
@@ -24,12 +23,10 @@ func createIdentifiers(domains []string) []acme.Identifier {
ident.Type = "ip"
}
- identifiers = append(identifiers, ident)
-
- uniqIdentifiers[domain] = struct{}{}
+ uniqIdentifiers[domain] = ident
}
- return identifiers
+ return slices.AppendSeq(make([]acme.Identifier, 0, len(uniqIdentifiers)), maps.Values(uniqIdentifiers))
}
// compareIdentifiers compares 2 slices of [acme.Identifier].
diff --git a/acme/api/internal/sender/useragent.go b/acme/api/internal/sender/useragent.go
index 51a1b4770..6b1ebb1c7 100644
--- a/acme/api/internal/sender/useragent.go
+++ b/acme/api/internal/sender/useragent.go
@@ -4,10 +4,10 @@ package sender
const (
// ourUserAgent is the User-Agent of this underlying library package.
- ourUserAgent = "xenolf-acme/4.32.0"
+ ourUserAgent = "xenolf-acme/4.31.0"
// ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package.
// values: detach|release
// NOTE: Update this with each tagged release.
- ourUserAgentComment = "detach"
+ ourUserAgentComment = "release"
)
diff --git a/acme/api/service.go b/acme/api/service.go
index 22ce05124..65518e1d9 100644
--- a/acme/api/service.go
+++ b/acme/api/service.go
@@ -1,11 +1,8 @@
package api
import (
- "fmt"
"net/http"
"regexp"
- "strconv"
- "time"
)
type service struct {
@@ -59,29 +56,3 @@ func getRetryAfter(resp *http.Response) string {
return resp.Header.Get("Retry-After")
}
-
-// ParseRetryAfter parses the Retry-After header value according to RFC 7231.
-// The header can be either delay-seconds (numeric) or HTTP-date (RFC 1123 format).
-// https://datatracker.ietf.org/doc/html/rfc7231#section-7.1.3
-// Returns the duration until the retry time.
-// TODO(ldez): unexposed this function in v5.
-func ParseRetryAfter(value string) (time.Duration, error) {
- if value == "" {
- return 0, nil
- }
-
- if seconds, err := strconv.ParseInt(value, 10, 64); err == nil {
- return time.Duration(seconds) * time.Second, nil
- }
-
- if retryTime, err := time.Parse(time.RFC1123, value); err == nil {
- duration := time.Until(retryTime)
- if duration < 0 {
- return 0, nil
- }
-
- return duration, nil
- }
-
- return 0, fmt.Errorf("invalid Retry-After value: %q", value)
-}
diff --git a/acme/api/service_test.go b/acme/api/service_test.go
index 57ea45708..2dbd795c9 100644
--- a/acme/api/service_test.go
+++ b/acme/api/service_test.go
@@ -3,10 +3,8 @@ package api
import (
"net/http"
"testing"
- "time"
"github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
func Test_getLink(t *testing.T) {
@@ -55,38 +53,3 @@ func Test_getLink(t *testing.T) {
})
}
}
-
-func TestParseRetryAfter(t *testing.T) {
- testCases := []struct {
- desc string
- value string
- expected time.Duration
- }{
- {
- desc: "empty header value",
- value: "",
- expected: time.Duration(0),
- },
- {
- desc: "delay-seconds",
- value: "123",
- expected: 123 * time.Second,
- },
- {
- desc: "HTTP-date",
- value: time.Now().Add(3 * time.Second).Format(time.RFC1123),
- expected: 3 * time.Second,
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- rt, err := ParseRetryAfter(test.value)
- require.NoError(t, err)
-
- assert.InDelta(t, test.expected.Seconds(), rt.Seconds(), 1)
- })
- }
-}
diff --git a/acme/errors.go b/acme/errors.go
index cd447d7b4..be4721c9d 100644
--- a/acme/errors.go
+++ b/acme/errors.go
@@ -29,18 +29,18 @@ type ProblemDetails struct {
}
func (p *ProblemDetails) Error() string {
- msg := new(strings.Builder)
+ var msg strings.Builder
- _, _ = fmt.Fprintf(msg, "acme: error: %d", p.HTTPStatus)
+ msg.WriteString(fmt.Sprintf("acme: error: %d", p.HTTPStatus))
if p.Method != "" || p.URL != "" {
- _, _ = fmt.Fprintf(msg, " :: %s :: %s", p.Method, p.URL)
+ msg.WriteString(fmt.Sprintf(" :: %s :: %s", p.Method, p.URL))
}
- _, _ = fmt.Fprintf(msg, " :: %s :: %s", p.Type, p.Detail)
+ msg.WriteString(fmt.Sprintf(" :: %s :: %s", p.Type, p.Detail))
for _, sub := range p.SubProblems {
- _, _ = fmt.Fprintf(msg, ", problem: %q :: %s", sub.Type, sub.Detail)
+ msg.WriteString(fmt.Sprintf(", problem: %q :: %s", sub.Type, sub.Detail))
}
if p.Instance != "" {
diff --git a/certcrypto/crypto.go b/certcrypto/crypto.go
index 800bb3f5b..00f0654b9 100644
--- a/certcrypto/crypto.go
+++ b/certcrypto/crypto.go
@@ -242,15 +242,15 @@ func ParsePEMCertificate(cert []byte) (*x509.Certificate, error) {
}
func GetCertificateMainDomain(cert *x509.Certificate) (string, error) {
- return getMainDomain(cert.Subject, cert.DNSNames, cert.IPAddresses)
+ return getMainDomain(cert.Subject, cert.DNSNames)
}
func GetCSRMainDomain(cert *x509.CertificateRequest) (string, error) {
- return getMainDomain(cert.Subject, cert.DNSNames, cert.IPAddresses)
+ return getMainDomain(cert.Subject, cert.DNSNames)
}
-func getMainDomain(subject pkix.Name, dnsNames []string, ips []net.IP) (string, error) {
- if subject.CommonName == "" && len(dnsNames) == 0 && len(ips) == 0 {
+func getMainDomain(subject pkix.Name, dnsNames []string) (string, error) {
+ if subject.CommonName == "" && len(dnsNames) == 0 {
return "", errors.New("missing domain")
}
@@ -258,11 +258,7 @@ func getMainDomain(subject pkix.Name, dnsNames []string, ips []net.IP) (string,
return subject.CommonName, nil
}
- if len(dnsNames) > 0 {
- return dnsNames[0], nil
- }
-
- return ips[0].String(), nil
+ return dnsNames[0], nil
}
func ExtractDomains(cert *x509.Certificate) []string {
diff --git a/certificate/renewal.go b/certificate/renewal.go
index 59d31cfb5..15e804745 100644
--- a/certificate/renewal.go
+++ b/certificate/renewal.go
@@ -11,7 +11,6 @@ import (
"time"
"github.com/go-acme/lego/v4/acme"
- "github.com/go-acme/lego/v4/acme/api"
)
// RenewalInfoRequest contains the necessary renewal information.
@@ -93,9 +92,9 @@ func (c *Certifier) GetRenewalInfo(req RenewalInfoRequest) (*RenewalInfoResponse
}
if retry := resp.Header.Get("Retry-After"); retry != "" {
- info.RetryAfter, err = api.ParseRetryAfter(retry)
+ info.RetryAfter, err = time.ParseDuration(retry + "s")
if err != nil {
- return nil, fmt.Errorf("failed to parse Retry-After header: %w", err)
+ return nil, err
}
}
diff --git a/certificate/renewal_test.go b/certificate/renewal_test.go
index 23209638a..6ce43e0aa 100644
--- a/certificate/renewal_test.go
+++ b/certificate/renewal_test.go
@@ -74,42 +74,6 @@ func TestCertifier_GetRenewalInfo(t *testing.T) {
assert.Equal(t, time.Duration(21600000000000), ri.RetryAfter)
}
-func TestCertifier_GetRenewalInfo_retryAfter(t *testing.T) {
- leaf, err := certcrypto.ParsePEMCertificate([]byte(ariLeafPEM))
- require.NoError(t, err)
-
- server := tester.MockACMEServer().
- Route("GET /renewalInfo/"+ariLeafCertID,
- servermock.RawStringResponse(`{
- "suggestedWindow": {
- "start": "2020-03-17T17:51:09Z",
- "end": "2020-03-17T18:21:09Z"
- },
- "explanationUrl": "https://aricapable.ca.example/docs/renewal-advice/"
- }
- }`).
- WithHeader("Content-Type", "application/json").
- WithHeader("Retry-After", time.Now().UTC().Add(6*time.Hour).Format(time.RFC1123))).
- BuildHTTPS(t)
-
- key, err := rsa.GenerateKey(rand.Reader, 2048)
- require.NoError(t, err, "Could not generate test key")
-
- core, err := api.New(server.Client(), "lego-test", server.URL+"/dir", "", key)
- require.NoError(t, err)
-
- certifier := NewCertifier(core, &resolverMock{}, CertifierOptions{KeyType: certcrypto.RSA2048})
-
- ri, err := certifier.GetRenewalInfo(RenewalInfoRequest{leaf})
- require.NoError(t, err)
- require.NotNil(t, ri)
- assert.Equal(t, "2020-03-17T17:51:09Z", ri.SuggestedWindow.Start.Format(time.RFC3339))
- assert.Equal(t, "2020-03-17T18:21:09Z", ri.SuggestedWindow.End.Format(time.RFC3339))
- assert.Equal(t, "https://aricapable.ca.example/docs/renewal-advice/", ri.ExplanationURL)
-
- assert.InDelta(t, 6, ri.RetryAfter.Hours(), 0.001)
-}
-
func TestCertifier_GetRenewalInfo_errors(t *testing.T) {
leaf, err := certcrypto.ParsePEMCertificate([]byte(ariLeafPEM))
require.NoError(t, err)
diff --git a/challenge/resolver/errors.go b/challenge/resolver/errors.go
index 65a6ccdb7..6a859922c 100644
--- a/challenge/resolver/errors.go
+++ b/challenge/resolver/errors.go
@@ -3,8 +3,6 @@ package resolver
import (
"bytes"
"fmt"
- "maps"
- "slices"
"sort"
)
@@ -27,7 +25,3 @@ func (e obtainError) Error() string {
return buffer.String()
}
-
-func (e obtainError) Unwrap() []error {
- return slices.AppendSeq(make([]error, 0, len(e)), maps.Values(e))
-}
diff --git a/challenge/resolver/errors_test.go b/challenge/resolver/errors_test.go
deleted file mode 100644
index d4ab3c481..000000000
--- a/challenge/resolver/errors_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package resolver
-
-import (
- "errors"
- "testing"
-
- "github.com/go-acme/lego/v4/acme"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func Test_obtainError_Error(t *testing.T) {
- err := obtainError{
- "a": &acme.ProblemDetails{Type: "001"},
- "b": errors.New("oops"),
- "c": errors.New("I did it again"),
- }
-
- require.EqualError(t, err, `error: one or more domains had a problem:
-[a] acme: error: 0 :: 001 ::
-[b] oops
-[c] I did it again
-`)
-}
-
-func Test_obtainError_Unwrap(t *testing.T) {
- testCases := []struct {
- desc string
- err obtainError
- assert assert.BoolAssertionFunc
- }{
- {
- desc: "one ok",
- err: obtainError{
- "a": &acme.ProblemDetails{},
- "b": errors.New("oops"),
- "c": errors.New("I did it again"),
- },
- assert: assert.True,
- },
- {
- desc: "all ok",
- err: obtainError{
- "a": &acme.ProblemDetails{Type: "001"},
- "b": &acme.ProblemDetails{Type: "002"},
- "c": &acme.ProblemDetails{Type: "002"},
- },
- assert: assert.True,
- },
- {
- desc: "nope",
- err: obtainError{
- "a": errors.New("hello"),
- "b": errors.New("oops"),
- "c": errors.New("I did it again"),
- },
- assert: assert.False,
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- var pd *acme.ProblemDetails
-
- test.assert(t, errors.As(test.err, &pd))
- })
- }
-}
diff --git a/challenge/resolver/prober.go b/challenge/resolver/prober.go
index 66b12c7a7..aac1016d8 100644
--- a/challenge/resolver/prober.go
+++ b/challenge/resolver/prober.go
@@ -98,24 +98,11 @@ func (p *Prober) Solve(authorizations []acme.Authorization) error {
}
func sequentialSolve(authSolvers []*selectedAuthSolver, failures obtainError) {
- // Some CA are using the same token,
- // this can be a problem with the DNS01 challenge when the DNS provider doesn't support duplicate TXT records.
- // In the sequential mode, this is not a problem because we can solve the challenges in order.
- // But it can reduce the number of call the DNS provider APIs.
- uniq := make(map[string]struct{})
-
for i, authSolver := range authSolvers {
// Submit the challenge
domain := challenge.GetTargetedDomain(authSolver.authz)
- chlg, _ := challenge.FindChallenge(challenge.DNS01, authSolver.authz)
-
if solvr, ok := authSolver.solver.(preSolver); ok {
- if _, ok := uniq[authSolver.authz.Identifier.Value+chlg.Token]; ok && chlg.Token != "" {
- log.Infof("acme: duplicate token for %q (DNS-01); skipping pre-solve.", authSolver.authz.Identifier.Value)
- continue
- }
-
err := solvr.PreSolve(authSolver.authz)
if err != nil {
failures[domain] = err
@@ -124,8 +111,6 @@ func sequentialSolve(authSolvers []*selectedAuthSolver, failures obtainError) {
continue
}
-
- uniq[authSolver.authz.Identifier.Value+chlg.Token] = struct{}{}
}
// Solve challenge
@@ -138,43 +123,22 @@ func sequentialSolve(authSolvers []*selectedAuthSolver, failures obtainError) {
continue
}
- if _, ok := uniq[authSolver.authz.Identifier.Value+chlg.Token]; ok || chlg.Token == "" {
- // Clean challenge
- cleanUp(authSolver.solver, authSolver.authz)
+ // Clean challenge
+ cleanUp(authSolver.solver, authSolver.authz)
- if len(authSolvers)-1 > i {
- solvr := authSolver.solver.(sequential)
- _, interval := solvr.Sequential()
- log.Infof("sequence: wait for %s", interval)
- time.Sleep(interval)
- }
-
- delete(uniq, authSolver.authz.Identifier.Value+chlg.Token)
- } else {
- log.Infof("acme: duplicate token for %q (DNS-01); skipping cleanup.", authSolver.authz.Identifier.Value)
+ if len(authSolvers)-1 > i {
+ solvr := authSolver.solver.(sequential)
+ _, interval := solvr.Sequential()
+ log.Infof("sequence: wait for %s", interval)
+ time.Sleep(interval)
}
}
}
func parallelSolve(authSolvers []*selectedAuthSolver, failures obtainError) {
- // Some CA are using the same token,
- // this can be a problem with the DNS01 challenge when the DNS provider doesn't support duplicate TXT records.
- uniq := make(map[string]struct{})
-
// For all valid preSolvers, first submit the challenges, so they have max time to propagate
for _, authSolver := range authSolvers {
authz := authSolver.authz
-
- chlg, err := challenge.FindChallenge(challenge.DNS01, authz)
- if err == nil {
- if _, ok := uniq[authz.Identifier.Value+chlg.Token]; ok {
- log.Infof("acme: duplicate token for %q (DNS-01); skipping pre-solve.", authSolver.authz.Identifier.Value)
- continue
- }
-
- uniq[authz.Identifier.Value+chlg.Token] = struct{}{}
- }
-
if solvr, ok := authSolver.solver.(preSolver); ok {
err := solvr.PreSolve(authz)
if err != nil {
@@ -186,16 +150,6 @@ func parallelSolve(authSolvers []*selectedAuthSolver, failures obtainError) {
defer func() {
// Clean all created TXT records
for _, authSolver := range authSolvers {
- chlg, err := challenge.FindChallenge(challenge.DNS01, authSolver.authz)
- if err == nil {
- if _, ok := uniq[authSolver.authz.Identifier.Value+chlg.Token]; ok {
- delete(uniq, authSolver.authz.Identifier.Value+chlg.Token)
- } else {
- log.Infof("acme: duplicate token for %q (DNS-01); skipping cleanup.", authSolver.authz.Identifier.Value)
- continue
- }
- }
-
cleanUp(authSolver.solver, authSolver.authz)
}
}()
diff --git a/challenge/resolver/prober_mock_test.go b/challenge/resolver/prober_mock_test.go
index dc7ad8dec..5a91fe075 100644
--- a/challenge/resolver/prober_mock_test.go
+++ b/challenge/resolver/prober_mock_test.go
@@ -1,7 +1,6 @@
package resolver
import (
- "fmt"
"time"
"github.com/go-acme/lego/v4/acme"
@@ -12,68 +11,34 @@ type preSolverMock struct {
preSolve map[string]error
solve map[string]error
cleanUp map[string]error
-
- preSolveCounter int
- solveCounter int
- cleanUpCounter int
}
func (s *preSolverMock) PreSolve(authorization acme.Authorization) error {
- s.preSolveCounter++
-
return s.preSolve[authorization.Identifier.Value]
}
func (s *preSolverMock) Solve(authorization acme.Authorization) error {
- s.solveCounter++
-
return s.solve[authorization.Identifier.Value]
}
func (s *preSolverMock) CleanUp(authorization acme.Authorization) error {
- s.cleanUpCounter++
-
return s.cleanUp[authorization.Identifier.Value]
}
-func (s *preSolverMock) String() string {
- return fmt.Sprintf("PreSolve: %d, Solve: %d, CleanUp: %d", s.preSolveCounter, s.solveCounter, s.cleanUpCounter)
-}
-
func createStubAuthorizationHTTP01(domain, status string) acme.Authorization {
- return createStubAuthorization(domain, status, false, acme.Challenge{
- Type: challenge.HTTP01.String(),
- Validated: time.Now(),
- })
-}
-
-func createStubAuthorizationDNS01(domain string, wildcard bool) acme.Authorization {
- var chlgs []acme.Challenge
-
- if wildcard {
- chlgs = append(chlgs, acme.Challenge{
- Type: challenge.HTTP01.String(),
- Validated: time.Now(),
- })
- }
-
- chlgs = append(chlgs, acme.Challenge{
- Type: challenge.DNS01.String(),
- Validated: time.Now(),
- })
-
- return createStubAuthorization(domain, acme.StatusProcessing, wildcard, chlgs...)
-}
-
-func createStubAuthorization(domain, status string, wildcard bool, chlgs ...acme.Challenge) acme.Authorization {
return acme.Authorization{
- Wildcard: wildcard,
- Status: status,
- Expires: time.Now(),
+ Status: status,
+ Expires: time.Now(),
Identifier: acme.Identifier{
- Type: "dns",
+ Type: challenge.HTTP01.String(),
Value: domain,
},
- Challenges: chlgs,
+ Challenges: []acme.Challenge{
+ {
+ Type: challenge.HTTP01.String(),
+ Validated: time.Now(),
+ Error: nil,
+ },
+ },
}
}
diff --git a/challenge/resolver/prober_test.go b/challenge/resolver/prober_test.go
index 829b16883..06ff07d2c 100644
--- a/challenge/resolver/prober_test.go
+++ b/challenge/resolver/prober_test.go
@@ -2,22 +2,19 @@ package resolver
import (
"errors"
- "fmt"
"testing"
"github.com/go-acme/lego/v4/acme"
"github.com/go-acme/lego/v4/challenge"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestProber_Solve(t *testing.T) {
testCases := []struct {
- desc string
- solvers map[challenge.Type]solver
- authz []acme.Authorization
- expectedError string
- expectedCounters map[challenge.Type]string
+ desc string
+ solvers map[challenge.Type]solver
+ authz []acme.Authorization
+ expectedError string
}{
{
desc: "success",
@@ -33,30 +30,6 @@ func TestProber_Solve(t *testing.T) {
createStubAuthorizationHTTP01("example.org", acme.StatusProcessing),
createStubAuthorizationHTTP01("example.net", acme.StatusProcessing),
},
- expectedCounters: map[challenge.Type]string{
- challenge.HTTP01: "PreSolve: 3, Solve: 3, CleanUp: 3",
- },
- },
- {
- desc: "DNS-01 deduplicate",
- solvers: map[challenge.Type]solver{
- challenge.DNS01: &preSolverMock{
- preSolve: map[string]error{},
- solve: map[string]error{},
- cleanUp: map[string]error{},
- },
- },
- authz: []acme.Authorization{
- createStubAuthorizationDNS01("a.example", false),
- createStubAuthorizationDNS01("a.example", true),
- createStubAuthorizationDNS01("b.example", false),
- createStubAuthorizationDNS01("b.example", true),
- createStubAuthorizationDNS01("c.example", true),
- createStubAuthorizationDNS01("d.example", false),
- },
- expectedCounters: map[challenge.Type]string{
- challenge.DNS01: "PreSolve: 4, Solve: 6, CleanUp: 4",
- },
},
{
desc: "already valid",
@@ -72,9 +45,6 @@ func TestProber_Solve(t *testing.T) {
createStubAuthorizationHTTP01("example.org", acme.StatusValid),
createStubAuthorizationHTTP01("example.net", acme.StatusValid),
},
- expectedCounters: map[challenge.Type]string{
- challenge.HTTP01: "PreSolve: 0, Solve: 0, CleanUp: 0",
- },
},
{
desc: "when preSolve fail, auth is flagged as error and skipped",
@@ -99,9 +69,6 @@ func TestProber_Solve(t *testing.T) {
expectedError: `error: one or more domains had a problem:
[example.com] preSolve error example.com
`,
- expectedCounters: map[challenge.Type]string{
- challenge.HTTP01: "PreSolve: 3, Solve: 2, CleanUp: 3",
- },
},
{
desc: "errors at different stages",
@@ -128,9 +95,6 @@ func TestProber_Solve(t *testing.T) {
[example.com] preSolve error example.com
[example.org] solve error example.org
`,
- expectedCounters: map[challenge.Type]string{
- challenge.HTTP01: "PreSolve: 3, Solve: 2, CleanUp: 3",
- },
},
}
@@ -148,10 +112,6 @@ func TestProber_Solve(t *testing.T) {
} else {
require.NoError(t, err)
}
-
- for n, s := range test.solvers {
- assert.Equal(t, test.expectedCounters[n], fmt.Sprintf("%s", s))
- }
})
}
}
diff --git a/challenge/resolver/solver_manager.go b/challenge/resolver/solver_manager.go
index 87cf6e2d8..48d9194b9 100644
--- a/challenge/resolver/solver_manager.go
+++ b/challenge/resolver/solver_manager.go
@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"sort"
+ "strconv"
"time"
"github.com/cenkalti/backoff/v5"
@@ -93,20 +94,22 @@ func validate(core *api.Core, domain string, chlg acme.Challenge) error {
return nil
}
- retryAfter, err := api.ParseRetryAfter(chlng.RetryAfter)
- if err != nil || retryAfter == 0 {
+ ra, err := strconv.Atoi(chlng.RetryAfter)
+ if err != nil {
// The ACME server MUST return a Retry-After.
- // If it doesn't, or if it's invalid, we'll just poll hard.
+ // If it doesn't, we'll just poll hard.
// Boulder does not implement the ability to retry challenges or the Retry-After header.
// https://github.com/letsencrypt/boulder/blob/master/docs/acme-divergences.md#section-82
- retryAfter = 5 * time.Second
+ ra = 5
}
+ initialInterval := time.Duration(ra) * time.Second
+
ctx := context.Background()
bo := backoff.NewExponentialBackOff()
- bo.InitialInterval = retryAfter
- bo.MaxInterval = 10 * retryAfter
+ bo.InitialInterval = initialInterval
+ bo.MaxInterval = 10 * initialInterval
// After the path is sent, the ACME server will access our server.
// Repeatedly check the server for an updated status on our request.
@@ -131,7 +134,7 @@ func validate(core *api.Core, domain string, chlg acme.Challenge) error {
return wait.Retry(ctx, operation,
backoff.WithBackOff(bo),
- backoff.WithMaxElapsedTime(100*retryAfter))
+ backoff.WithMaxElapsedTime(100*initialInterval))
}
func checkChallengeStatus(chlng acme.ExtendedChallenge) (bool, error) {
diff --git a/cmd/cmd_list.go b/cmd/cmd_list.go
index 53cd12c3c..483592d47 100644
--- a/cmd/cmd_list.go
+++ b/cmd/cmd_list.go
@@ -3,7 +3,6 @@ package cmd
import (
"encoding/json"
"fmt"
- "net"
"net/url"
"os"
"path/filepath"
@@ -101,11 +100,6 @@ func listCertificates(ctx *cli.Context) error {
} else {
fmt.Println(" Certificate Name:", name)
fmt.Println(" Domains:", strings.Join(pCert.DNSNames, ", "))
-
- if len(pCert.IPAddresses) > 0 {
- fmt.Println(" IPs:", formatIPAddresses(pCert.IPAddresses))
- }
-
fmt.Println(" Expiry Date:", pCert.NotAfter)
fmt.Println(" Certificate Path:", filename)
fmt.Println()
@@ -156,12 +150,3 @@ func listAccount(ctx *cli.Context) error {
return nil
}
-
-func formatIPAddresses(ipAddresses []net.IP) string {
- var ips []string
- for _, ip := range ipAddresses {
- ips = append(ips, ip.String())
- }
-
- return strings.Join(ips, ", ")
-}
diff --git a/cmd/cmd_run.go b/cmd/cmd_run.go
index 5924c4b66..16814b4de 100644
--- a/cmd/cmd_run.go
+++ b/cmd/cmd_run.go
@@ -104,9 +104,9 @@ Your account credentials have been saved in your
configuration directory at "%s".
You should make a secure backup of this folder now. This
-configuration directory will also contain private keys
-generated by lego and certificates obtained from the ACME
-server. Making regular backups of this folder is ideal.
+configuration directory will also contain certificates and
+private keys obtained from the ACME server so making regular
+backups of this folder is ideal.
`
func run(ctx *cli.Context) error {
diff --git a/cmd/lego/zz_gen_version.go b/cmd/lego/zz_gen_version.go
index cf9ad00ef..f3fb3d253 100644
--- a/cmd/lego/zz_gen_version.go
+++ b/cmd/lego/zz_gen_version.go
@@ -2,7 +2,7 @@
package main
-const defaultVersion = "v4.32.0+dev-detach"
+const defaultVersion = "v4.31.0+dev-release"
var version = ""
diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go
index f73f3920b..a918a1484 100644
--- a/cmd/zz_gen_cmd_dnshelp.go
+++ b/cmd/zz_gen_cmd_dnshelp.go
@@ -19,7 +19,6 @@ func allDNSCodes() string {
"allinkl",
"alwaysdata",
"anexia",
- "artfiles",
"arvancloud",
"auroradns",
"autodns",
@@ -32,7 +31,6 @@ func allDNSCodes() string {
"binarylane",
"bindman",
"bluecat",
- "bluecatv2",
"bookmyname",
"brandit",
"bunny",
@@ -49,14 +47,11 @@ func allDNSCodes() string {
"constellix",
"corenetworks",
"cpanel",
- "czechia",
- "ddnss",
"derak",
"desec",
"designate",
"digitalocean",
"directadmin",
- "dnsexit",
"dnshomede",
"dnsimple",
"dnsmadeeasy",
@@ -74,8 +69,6 @@ func allDNSCodes() string {
"edgeone",
"efficientip",
"epik",
- "eurodns",
- "excedo",
"exec",
"exoscale",
"f5xc",
@@ -115,7 +108,6 @@ func allDNSCodes() string {
"jdcloud",
"joker",
"keyhelp",
- "leaseweb",
"liara",
"lightsail",
"limacity",
@@ -136,7 +128,6 @@ func allDNSCodes() string {
"namecheap",
"namedotcom",
"namesilo",
- "namesurfer",
"nearlyfreespeech",
"neodigit",
"netcup",
@@ -178,7 +169,6 @@ func allDNSCodes() string {
"technitium",
"tencentcloud",
"timewebcloud",
- "todaynic",
"transip",
"ultradns",
"uniteddomains",
@@ -269,10 +259,8 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln(`Additional Configuration:`)
ew.writeln(` - "ALICLOUD_HTTP_TIMEOUT": API request timeout in seconds (Default: 10)`)
- ew.writeln(` - "ALICLOUD_LINE": Line (Default: default)`)
ew.writeln(` - "ALICLOUD_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`)
ew.writeln(` - "ALICLOUD_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 60)`)
- ew.writeln(` - "ALICLOUD_REGION_ID": Region ID (Default: cn-hangzhou)`)
ew.writeln(` - "ALICLOUD_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 600)`)
ew.writeln()
@@ -363,27 +351,6 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/anexia`)
- case "artfiles":
- // generated from: providers/dns/artfiles/artfiles.toml
- ew.writeln(`Configuration for ArtFiles.`)
- ew.writeln(`Code: 'artfiles'`)
- ew.writeln(`Since: 'v4.32.0'`)
- ew.writeln()
-
- ew.writeln(`Credentials:`)
- ew.writeln(` - "ARTFILES_PASSWORD": API password`)
- ew.writeln(` - "ARTFILES_USERNAME": API username`)
- ew.writeln()
-
- ew.writeln(`Additional Configuration:`)
- ew.writeln(` - "ARTFILES_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
- ew.writeln(` - "ARTFILES_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`)
- ew.writeln(` - "ARTFILES_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 360)`)
- ew.writeln(` - "ARTFILES_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)`)
-
- ew.writeln()
- ew.writeln(`More information: https://go-acme.github.io/lego/dns/artfiles`)
-
case "arvancloud":
// generated from: providers/dns/arvancloud/arvancloud.toml
ew.writeln(`Configuration for ArvanCloud.`)
@@ -653,31 +620,6 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/bluecat`)
- case "bluecatv2":
- // generated from: providers/dns/bluecatv2/bluecatv2.toml
- ew.writeln(`Configuration for Bluecat v2.`)
- ew.writeln(`Code: 'bluecatv2'`)
- ew.writeln(`Since: 'v4.32.0'`)
- ew.writeln()
-
- ew.writeln(`Credentials:`)
- ew.writeln(` - "BLUECATV2_CONFIG_NAME": Configuration name`)
- ew.writeln(` - "BLUECATV2_PASSWORD": API password`)
- ew.writeln(` - "BLUECATV2_USERNAME": API username`)
- ew.writeln(` - "BLUECATV2_VIEW_NAME": DNS View Name`)
- ew.writeln(` - "BLUECAT_SERVER_URL": The server URL: it should have a scheme, hostname, and port (if required) of the authoritative Bluecat BAM serve`)
- ew.writeln()
-
- ew.writeln(`Additional Configuration:`)
- ew.writeln(` - "BLUECATV2_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
- ew.writeln(` - "BLUECATV2_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`)
- ew.writeln(` - "BLUECATV2_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 60)`)
- ew.writeln(` - "BLUECATV2_SKIP_DEPLOY": Skip quick deployements`)
- ew.writeln(` - "BLUECATV2_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)`)
-
- ew.writeln()
- ew.writeln(`More information: https://go-acme.github.io/lego/dns/bluecatv2`)
-
case "bookmyname":
// generated from: providers/dns/bookmyname/bookmyname.toml
ew.writeln(`Configuration for BookMyName.`)
@@ -1029,47 +971,6 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/cpanel`)
- case "czechia":
- // generated from: providers/dns/czechia/czechia.toml
- ew.writeln(`Configuration for Czechia.`)
- ew.writeln(`Code: 'czechia'`)
- ew.writeln(`Since: 'v4.33.0'`)
- ew.writeln()
-
- ew.writeln(`Credentials:`)
- ew.writeln(` - "CZECHIA_TOKEN": Authorization token`)
- ew.writeln()
-
- ew.writeln(`Additional Configuration:`)
- ew.writeln(` - "CZECHIA_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
- ew.writeln(` - "CZECHIA_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`)
- ew.writeln(` - "CZECHIA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 60)`)
- ew.writeln(` - "CZECHIA_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)`)
-
- ew.writeln()
- ew.writeln(`More information: https://go-acme.github.io/lego/dns/czechia`)
-
- case "ddnss":
- // generated from: providers/dns/ddnss/ddnss.toml
- ew.writeln(`Configuration for DDnss (DynDNS Service).`)
- ew.writeln(`Code: 'ddnss'`)
- ew.writeln(`Since: 'v4.32.0'`)
- ew.writeln()
-
- ew.writeln(`Credentials:`)
- ew.writeln(` - "DDNSS_KEY": Update key`)
- ew.writeln()
-
- ew.writeln(`Additional Configuration:`)
- ew.writeln(` - "DDNSS_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
- ew.writeln(` - "DDNSS_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`)
- ew.writeln(` - "DDNSS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 60)`)
- ew.writeln(` - "DDNSS_SEQUENCE_INTERVAL": Time between sequential requests in seconds (Default: 60)`)
- ew.writeln(` - "DDNSS_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)`)
-
- ew.writeln()
- ew.writeln(`More information: https://go-acme.github.io/lego/dns/ddnss`)
-
case "derak":
// generated from: providers/dns/derak/derak.toml
ew.writeln(`Configuration for Derak Cloud.`)
@@ -1185,26 +1086,6 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/directadmin`)
- case "dnsexit":
- // generated from: providers/dns/dnsexit/dnsexit.toml
- ew.writeln(`Configuration for DNSExit.`)
- ew.writeln(`Code: 'dnsexit'`)
- ew.writeln(`Since: 'v4.32.0'`)
- ew.writeln()
-
- ew.writeln(`Credentials:`)
- ew.writeln(` - "DNSEXIT_API_KEY": API key`)
- ew.writeln()
-
- ew.writeln(`Additional Configuration:`)
- ew.writeln(` - "DNSEXIT_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
- ew.writeln(` - "DNSEXIT_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 10)`)
- ew.writeln(` - "DNSEXIT_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 300)`)
- ew.writeln(` - "DNSEXIT_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)`)
-
- ew.writeln()
- ew.writeln(`More information: https://go-acme.github.io/lego/dns/dnsexit`)
-
case "dnshomede":
// generated from: providers/dns/dnshomede/dnshomede.toml
ew.writeln(`Configuration for dnsHome.de.`)
@@ -1564,48 +1445,6 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/epik`)
- case "eurodns":
- // generated from: providers/dns/eurodns/eurodns.toml
- ew.writeln(`Configuration for EuroDNS.`)
- ew.writeln(`Code: 'eurodns'`)
- ew.writeln(`Since: 'v4.33.0'`)
- ew.writeln()
-
- ew.writeln(`Credentials:`)
- ew.writeln(` - "EURODNS_API_KEY": API key`)
- ew.writeln(` - "EURODNS_APP_ID": Application ID`)
- ew.writeln()
-
- ew.writeln(`Additional Configuration:`)
- ew.writeln(` - "EURODNS_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
- ew.writeln(` - "EURODNS_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`)
- ew.writeln(` - "EURODNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 60)`)
- ew.writeln(` - "EURODNS_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 600)`)
-
- ew.writeln()
- ew.writeln(`More information: https://go-acme.github.io/lego/dns/eurodns`)
-
- case "excedo":
- // generated from: providers/dns/excedo/excedo.toml
- ew.writeln(`Configuration for Excedo.`)
- ew.writeln(`Code: 'excedo'`)
- ew.writeln(`Since: 'v4.33.0'`)
- ew.writeln()
-
- ew.writeln(`Credentials:`)
- ew.writeln(` - "EXCEDO_API_KEY": API key`)
- ew.writeln(` - "EXCEDO_API_URL": API base URL`)
- ew.writeln()
-
- ew.writeln(`Additional Configuration:`)
- ew.writeln(` - "EXCEDO_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
- ew.writeln(` - "EXCEDO_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 10)`)
- ew.writeln(` - "EXCEDO_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 300)`)
- ew.writeln(` - "EXCEDO_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 60)`)
-
- ew.writeln()
- ew.writeln(`More information: https://go-acme.github.io/lego/dns/excedo`)
-
case "exec":
// generated from: providers/dns/exec/exec.toml
ew.writeln(`Configuration for External program.`)
@@ -2424,26 +2263,6 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/keyhelp`)
- case "leaseweb":
- // generated from: providers/dns/leaseweb/leaseweb.toml
- ew.writeln(`Configuration for Leaseweb.`)
- ew.writeln(`Code: 'leaseweb'`)
- ew.writeln(`Since: 'v4.32.0'`)
- ew.writeln()
-
- ew.writeln(`Credentials:`)
- ew.writeln(` - "LEASEWEB_API_KEY": API key`)
- ew.writeln()
-
- ew.writeln(`Additional Configuration:`)
- ew.writeln(` - "LEASEWEB_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
- ew.writeln(` - "LEASEWEB_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`)
- ew.writeln(` - "LEASEWEB_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 60)`)
- ew.writeln(` - "LEASEWEB_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)`)
-
- ew.writeln()
- ew.writeln(`More information: https://go-acme.github.io/lego/dns/leaseweb`)
-
case "liara":
// generated from: providers/dns/liara/liara.toml
ew.writeln(`Configuration for Liara.`)
@@ -2459,7 +2278,6 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln(` - "LIARA_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
ew.writeln(` - "LIARA_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`)
ew.writeln(` - "LIARA_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 60)`)
- ew.writeln(` - "LIARA_TEAM_ID": The team ID to access services in a team`)
ew.writeln(` - "LIARA_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 3600)`)
ew.writeln()
@@ -2852,30 +2670,6 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/namesilo`)
- case "namesurfer":
- // generated from: providers/dns/namesurfer/namesurfer.toml
- ew.writeln(`Configuration for FusionLayer NameSurfer.`)
- ew.writeln(`Code: 'namesurfer'`)
- ew.writeln(`Since: 'v4.32.0'`)
- ew.writeln()
-
- ew.writeln(`Credentials:`)
- ew.writeln(` - "NAMESURFER_API_KEY": API key name`)
- ew.writeln(` - "NAMESURFER_API_SECRET": API secret`)
- ew.writeln(` - "NAMESURFER_BASE_URL": The base URL of NameSurfer API (jsonrpc10) endpoint URL (e.g., https://foo.example.com:8443/API/NSService_10)`)
- ew.writeln()
-
- ew.writeln(`Additional Configuration:`)
- ew.writeln(` - "NAMESURFER_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
- ew.writeln(` - "NAMESURFER_INSECURE_SKIP_VERIFY": Whether to verify the API certificate`)
- ew.writeln(` - "NAMESURFER_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`)
- ew.writeln(` - "NAMESURFER_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 120)`)
- ew.writeln(` - "NAMESURFER_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 300)`)
- ew.writeln(` - "NAMESURFER_VIEW": DNS view name (optional, default: empty string)`)
-
- ew.writeln()
- ew.writeln(`More information: https://go-acme.github.io/lego/dns/namesurfer`)
-
case "nearlyfreespeech":
// generated from: providers/dns/nearlyfreespeech/nearlyfreespeech.toml
ew.writeln(`Configuration for NearlyFreeSpeech.NET.`)
@@ -3439,7 +3233,7 @@ func displayDNSHelp(w io.Writer, name string) error {
case "safedns":
// generated from: providers/dns/safedns/safedns.toml
- ew.writeln(`Configuration for ANS SafeDNS.`)
+ ew.writeln(`Configuration for UKFast SafeDNS.`)
ew.writeln(`Code: 'safedns'`)
ew.writeln(`Since: 'v4.6.0'`)
ew.writeln()
@@ -3780,27 +3574,6 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/timewebcloud`)
- case "todaynic":
- // generated from: providers/dns/todaynic/todaynic.toml
- ew.writeln(`Configuration for TodayNIC/时代互联.`)
- ew.writeln(`Code: 'todaynic'`)
- ew.writeln(`Since: 'v4.32.0'`)
- ew.writeln()
-
- ew.writeln(`Credentials:`)
- ew.writeln(` - "TODAYNIC_API_KEY": API key`)
- ew.writeln(` - "TODAYNIC_AUTH_USER_ID": account ID`)
- ew.writeln()
-
- ew.writeln(`Additional Configuration:`)
- ew.writeln(` - "TODAYNIC_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`)
- ew.writeln(` - "TODAYNIC_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 2)`)
- ew.writeln(` - "TODAYNIC_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 60)`)
- ew.writeln(` - "TODAYNIC_TTL": The TTL of the TXT record used for the DNS challenge in seconds (Default: 600)`)
-
- ew.writeln()
- ew.writeln(`More information: https://go-acme.github.io/lego/dns/todaynic`)
-
case "transip":
// generated from: providers/dns/transip/transip.toml
ew.writeln(`Configuration for TransIP.`)
diff --git a/docs/content/dns/zz_gen_alidns.md b/docs/content/dns/zz_gen_alidns.md
index 4ded782ab..e498a31dd 100644
--- a/docs/content/dns/zz_gen_alidns.md
+++ b/docs/content/dns/zz_gen_alidns.md
@@ -58,10 +58,8 @@ More information [here]({{% ref "dns#configuration-and-credentials" %}}).
| Environment Variable Name | Description |
|--------------------------------|-------------|
| `ALICLOUD_HTTP_TIMEOUT` | API request timeout in seconds (Default: 10) |
-| `ALICLOUD_LINE` | Line (Default: default) |
| `ALICLOUD_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
| `ALICLOUD_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 60) |
-| `ALICLOUD_REGION_ID` | Region ID (Default: cn-hangzhou) |
| `ALICLOUD_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 600) |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
diff --git a/docs/content/dns/zz_gen_artfiles.md b/docs/content/dns/zz_gen_artfiles.md
deleted file mode 100644
index 15ac2d964..000000000
--- a/docs/content/dns/zz_gen_artfiles.md
+++ /dev/null
@@ -1,69 +0,0 @@
----
-title: "ArtFiles"
-date: 2019-03-03T16:39:46+01:00
-draft: false
-slug: artfiles
-dnsprovider:
- since: "v4.32.0"
- code: "artfiles"
- url: "https://www.artfiles.de/extras/domains/"
----
-
-
-
-
-
-
-Configuration for [ArtFiles](https://www.artfiles.de/extras/domains/).
-
-
-
-
-- Code: `artfiles`
-- Since: v4.32.0
-
-
-Here is an example bash command using the ArtFiles provider:
-
-```bash
-ARTFILES_USERNAME="xxx" \
-ARTFILES_PASSWORD="yyy" \
-lego --dns artfiles -d '*.example.com' -d example.com run
-```
-
-
-
-
-## Credentials
-
-| Environment Variable Name | Description |
-|-----------------------|-------------|
-| `ARTFILES_PASSWORD` | API password |
-| `ARTFILES_USERNAME` | API username |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-## Additional Configuration
-
-| Environment Variable Name | Description |
-|--------------------------------|-------------|
-| `ARTFILES_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
-| `ARTFILES_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
-| `ARTFILES_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 360) |
-| `ARTFILES_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 120) |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-
-
-## More information
-
-- [API documentation](https://support.artfiles.de/DCP-API#dns)
-
-
-
-
diff --git a/docs/content/dns/zz_gen_bluecatv2.md b/docs/content/dns/zz_gen_bluecatv2.md
deleted file mode 100644
index 7d748df99..000000000
--- a/docs/content/dns/zz_gen_bluecatv2.md
+++ /dev/null
@@ -1,76 +0,0 @@
----
-title: "Bluecat v2"
-date: 2019-03-03T16:39:46+01:00
-draft: false
-slug: bluecatv2
-dnsprovider:
- since: "v4.32.0"
- code: "bluecatv2"
- url: "https://www.bluecatnetworks.com"
----
-
-
-
-
-
-
-Configuration for [Bluecat v2](https://www.bluecatnetworks.com).
-
-
-
-
-- Code: `bluecatv2`
-- Since: v4.32.0
-
-
-Here is an example bash command using the Bluecat v2 provider:
-
-```bash
-BLUECATV2_SERVER_URL="https://example.com" \
-BLUECATV2_USERNAME="xxx" \
-BLUECATV2_PASSWORD="yyy" \
-BLUECATV2_CONFIG_NAME="myConfiguration" \
-BLUECATV2_VIEW_NAME="myView" \
-lego --dns bluecatv2 -d '*.example.com' -d example.com run
-```
-
-
-
-
-## Credentials
-
-| Environment Variable Name | Description |
-|-----------------------|-------------|
-| `BLUECATV2_CONFIG_NAME` | Configuration name |
-| `BLUECATV2_PASSWORD` | API password |
-| `BLUECATV2_USERNAME` | API username |
-| `BLUECATV2_VIEW_NAME` | DNS View Name |
-| `BLUECAT_SERVER_URL` | The server URL: it should have a scheme, hostname, and port (if required) of the authoritative Bluecat BAM serve |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-## Additional Configuration
-
-| Environment Variable Name | Description |
-|--------------------------------|-------------|
-| `BLUECATV2_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
-| `BLUECATV2_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
-| `BLUECATV2_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 60) |
-| `BLUECATV2_SKIP_DEPLOY` | Skip quick deployements |
-| `BLUECATV2_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 120) |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-
-
-## More information
-
-- [API documentation](https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Introduction/9.6.0)
-
-
-
-
diff --git a/docs/content/dns/zz_gen_czechia.md b/docs/content/dns/zz_gen_czechia.md
deleted file mode 100644
index 7b1cdd1ae..000000000
--- a/docs/content/dns/zz_gen_czechia.md
+++ /dev/null
@@ -1,67 +0,0 @@
----
-title: "Czechia"
-date: 2019-03-03T16:39:46+01:00
-draft: false
-slug: czechia
-dnsprovider:
- since: "v4.33.0"
- code: "czechia"
- url: "https://www.czechia.com/"
----
-
-
-
-
-
-
-Configuration for [Czechia](https://www.czechia.com/).
-
-
-
-
-- Code: `czechia`
-- Since: v4.33.0
-
-
-Here is an example bash command using the Czechia provider:
-
-```bash
-CZECHIA_TOKEN="xxxxxxxxxxxxxxxxxxxxx" \
-lego --dns czechia -d '*.example.com' -d example.com run
-```
-
-
-
-
-## Credentials
-
-| Environment Variable Name | Description |
-|-----------------------|-------------|
-| `CZECHIA_TOKEN` | Authorization token |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-## Additional Configuration
-
-| Environment Variable Name | Description |
-|--------------------------------|-------------|
-| `CZECHIA_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
-| `CZECHIA_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
-| `CZECHIA_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 60) |
-| `CZECHIA_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 120) |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-
-
-## More information
-
-- [API documentation](https://api.czechia.com/swagger/index.html)
-
-
-
-
diff --git a/docs/content/dns/zz_gen_ddnss.md b/docs/content/dns/zz_gen_ddnss.md
deleted file mode 100644
index e159d58b4..000000000
--- a/docs/content/dns/zz_gen_ddnss.md
+++ /dev/null
@@ -1,68 +0,0 @@
----
-title: "DDnss (DynDNS Service)"
-date: 2019-03-03T16:39:46+01:00
-draft: false
-slug: ddnss
-dnsprovider:
- since: "v4.32.0"
- code: "ddnss"
- url: "https://ddnss.de/"
----
-
-
-
-
-
-
-Configuration for [DDnss (DynDNS Service)](https://ddnss.de/).
-
-
-
-
-- Code: `ddnss`
-- Since: v4.32.0
-
-
-Here is an example bash command using the DDnss (DynDNS Service) provider:
-
-```bash
-DDNSS_KEY="xxxxxxxxxxxxxxxxxxxxx" \
-lego --dns ddnss -d '*.example.com' -d example.com run
-```
-
-
-
-
-## Credentials
-
-| Environment Variable Name | Description |
-|-----------------------|-------------|
-| `DDNSS_KEY` | Update key |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-## Additional Configuration
-
-| Environment Variable Name | Description |
-|--------------------------------|-------------|
-| `DDNSS_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
-| `DDNSS_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
-| `DDNSS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 60) |
-| `DDNSS_SEQUENCE_INTERVAL` | Time between sequential requests in seconds (Default: 60) |
-| `DDNSS_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 120) |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-
-
-## More information
-
-- [API documentation](https://ddnss.de/info.php)
-
-
-
-
diff --git a/docs/content/dns/zz_gen_dnsexit.md b/docs/content/dns/zz_gen_dnsexit.md
deleted file mode 100644
index aca5357e8..000000000
--- a/docs/content/dns/zz_gen_dnsexit.md
+++ /dev/null
@@ -1,67 +0,0 @@
----
-title: "DNSExit"
-date: 2019-03-03T16:39:46+01:00
-draft: false
-slug: dnsexit
-dnsprovider:
- since: "v4.32.0"
- code: "dnsexit"
- url: "https://dnsexit.com"
----
-
-
-
-
-
-
-Configuration for [DNSExit](https://dnsexit.com).
-
-
-
-
-- Code: `dnsexit`
-- Since: v4.32.0
-
-
-Here is an example bash command using the DNSExit provider:
-
-```bash
-DNSEXIT_API_KEY="xxxxxxxxxxxxxxxxxxxxx" \
-lego --dns dnsexit -d '*.example.com' -d example.com run
-```
-
-
-
-
-## Credentials
-
-| Environment Variable Name | Description |
-|-----------------------|-------------|
-| `DNSEXIT_API_KEY` | API key |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-## Additional Configuration
-
-| Environment Variable Name | Description |
-|--------------------------------|-------------|
-| `DNSEXIT_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
-| `DNSEXIT_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 10) |
-| `DNSEXIT_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 300) |
-| `DNSEXIT_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 120) |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-
-
-## More information
-
-- [API documentation](https://dnsexit.com/dns/dns-api/)
-
-
-
-
diff --git a/docs/content/dns/zz_gen_eurodns.md b/docs/content/dns/zz_gen_eurodns.md
deleted file mode 100644
index cb5a0418d..000000000
--- a/docs/content/dns/zz_gen_eurodns.md
+++ /dev/null
@@ -1,69 +0,0 @@
----
-title: "EuroDNS"
-date: 2019-03-03T16:39:46+01:00
-draft: false
-slug: eurodns
-dnsprovider:
- since: "v4.33.0"
- code: "eurodns"
- url: "https://www.eurodns.com/"
----
-
-
-
-
-
-
-Configuration for [EuroDNS](https://www.eurodns.com/).
-
-
-
-
-- Code: `eurodns`
-- Since: v4.33.0
-
-
-Here is an example bash command using the EuroDNS provider:
-
-```bash
-EURODNS_APP_ID="xxx" \
-EURODNS_API_KEY="yyy" \
-lego --dns eurodns -d '*.example.com' -d example.com run
-```
-
-
-
-
-## Credentials
-
-| Environment Variable Name | Description |
-|-----------------------|-------------|
-| `EURODNS_API_KEY` | API key |
-| `EURODNS_APP_ID` | Application ID |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-## Additional Configuration
-
-| Environment Variable Name | Description |
-|--------------------------------|-------------|
-| `EURODNS_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
-| `EURODNS_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
-| `EURODNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 60) |
-| `EURODNS_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 600) |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-
-
-## More information
-
-- [API documentation](https://docapi.eurodns.com/)
-
-
-
-
diff --git a/docs/content/dns/zz_gen_excedo.md b/docs/content/dns/zz_gen_excedo.md
deleted file mode 100644
index 456e6f60a..000000000
--- a/docs/content/dns/zz_gen_excedo.md
+++ /dev/null
@@ -1,69 +0,0 @@
----
-title: "Excedo"
-date: 2019-03-03T16:39:46+01:00
-draft: false
-slug: excedo
-dnsprovider:
- since: "v4.33.0"
- code: "excedo"
- url: "https://excedo.se/"
----
-
-
-
-
-
-
-Configuration for [Excedo](https://excedo.se/).
-
-
-
-
-- Code: `excedo`
-- Since: v4.33.0
-
-
-Here is an example bash command using the Excedo provider:
-
-```bash
-EXCEDO_API_KEY=your-api-key \
-EXCEDO_API_URL=your-base-url \
-lego --dns excedo -d '*.example.com' -d example.com run
-```
-
-
-
-
-## Credentials
-
-| Environment Variable Name | Description |
-|-----------------------|-------------|
-| `EXCEDO_API_KEY` | API key |
-| `EXCEDO_API_URL` | API base URL |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-## Additional Configuration
-
-| Environment Variable Name | Description |
-|--------------------------------|-------------|
-| `EXCEDO_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
-| `EXCEDO_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 10) |
-| `EXCEDO_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 300) |
-| `EXCEDO_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 60) |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-
-
-## More information
-
-- [API documentation](none)
-
-
-
-
diff --git a/docs/content/dns/zz_gen_leaseweb.md b/docs/content/dns/zz_gen_leaseweb.md
deleted file mode 100644
index 13ded490a..000000000
--- a/docs/content/dns/zz_gen_leaseweb.md
+++ /dev/null
@@ -1,67 +0,0 @@
----
-title: "Leaseweb"
-date: 2019-03-03T16:39:46+01:00
-draft: false
-slug: leaseweb
-dnsprovider:
- since: "v4.32.0"
- code: "leaseweb"
- url: "https://www.leaseweb.com/en/"
----
-
-
-
-
-
-
-Configuration for [Leaseweb](https://www.leaseweb.com/en/).
-
-
-
-
-- Code: `leaseweb`
-- Since: v4.32.0
-
-
-Here is an example bash command using the Leaseweb provider:
-
-```bash
-LEASEWEB_API_KEY="xxxxxxxxxxxxxxxxxxxxx" \
-lego --dns leaseweb -d '*.example.com' -d example.com run
-```
-
-
-
-
-## Credentials
-
-| Environment Variable Name | Description |
-|-----------------------|-------------|
-| `LEASEWEB_API_KEY` | API key |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-## Additional Configuration
-
-| Environment Variable Name | Description |
-|--------------------------------|-------------|
-| `LEASEWEB_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
-| `LEASEWEB_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
-| `LEASEWEB_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 60) |
-| `LEASEWEB_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 120) |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-
-
-## More information
-
-- [API documentation](https://developer.leaseweb.com/docs/#tag/DNS)
-
-
-
-
diff --git a/docs/content/dns/zz_gen_liara.md b/docs/content/dns/zz_gen_liara.md
index 658ce8077..8a6ddbd99 100644
--- a/docs/content/dns/zz_gen_liara.md
+++ b/docs/content/dns/zz_gen_liara.md
@@ -50,7 +50,6 @@ More information [here]({{% ref "dns#configuration-and-credentials" %}}).
| `LIARA_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
| `LIARA_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
| `LIARA_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 60) |
-| `LIARA_TEAM_ID` | The team ID to access services in a team |
| `LIARA_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 3600) |
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
diff --git a/docs/content/dns/zz_gen_manual.md b/docs/content/dns/zz_gen_manual.md
index 832ccaf58..056726c74 100644
--- a/docs/content/dns/zz_gen_manual.md
+++ b/docs/content/dns/zz_gen_manual.md
@@ -54,13 +54,13 @@ If you accept the linked Terms of Service, hit `Enter`.
[INFO] acme: Registering account for you@example.com
!!!! HEADS UP !!!!
-Your account credentials have been saved in your
-configuration directory at "./.lego/accounts".
+ Your account credentials have been saved in your Let's Encrypt
+ configuration directory at "./.lego/accounts".
-You should make a secure backup of this folder now. This
-configuration directory will also contain private keys
-generated by lego and certificates obtained from the ACME
-server. Making regular backups of this folder is ideal.
+ You should make a secure backup of this folder now. This
+ configuration directory will also contain certificates and
+ private keys obtained from Let's Encrypt so making regular
+ backups of this folder is ideal.
[INFO] [example.com] acme: Obtaining bundled SAN certificate
[INFO] [example.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/2345678901
[INFO] [example.com] acme: Could not find solver for: tls-alpn-01
diff --git a/docs/content/dns/zz_gen_namesurfer.md b/docs/content/dns/zz_gen_namesurfer.md
deleted file mode 100644
index 9a2802d0e..000000000
--- a/docs/content/dns/zz_gen_namesurfer.md
+++ /dev/null
@@ -1,73 +0,0 @@
----
-title: "FusionLayer NameSurfer"
-date: 2019-03-03T16:39:46+01:00
-draft: false
-slug: namesurfer
-dnsprovider:
- since: "v4.32.0"
- code: "namesurfer"
- url: "https://www.fusionlayer.com/"
----
-
-
-
-
-
-
-Configuration for [FusionLayer NameSurfer](https://www.fusionlayer.com/).
-
-
-
-
-- Code: `namesurfer`
-- Since: v4.32.0
-
-
-Here is an example bash command using the FusionLayer NameSurfer provider:
-
-```bash
-NAMESURFER_BASE_URL=https://foo.example.com:8443/API/NSService_10 \
-NAMESURFER_API_KEY=xxx \
-NAMESURFER_API_SECRET=yyy \
-lego --dns namesurfer -d '*.example.com' -d example.com run
-```
-
-
-
-
-## Credentials
-
-| Environment Variable Name | Description |
-|-----------------------|-------------|
-| `NAMESURFER_API_KEY` | API key name |
-| `NAMESURFER_API_SECRET` | API secret |
-| `NAMESURFER_BASE_URL` | The base URL of NameSurfer API (jsonrpc10) endpoint URL (e.g., https://foo.example.com:8443/API/NSService_10) |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-## Additional Configuration
-
-| Environment Variable Name | Description |
-|--------------------------------|-------------|
-| `NAMESURFER_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
-| `NAMESURFER_INSECURE_SKIP_VERIFY` | Whether to verify the API certificate |
-| `NAMESURFER_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
-| `NAMESURFER_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 120) |
-| `NAMESURFER_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 300) |
-| `NAMESURFER_VIEW` | DNS view name (optional, default: empty string) |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-
-
-## More information
-
-- [API documentation](https://web.archive.org/web/20260213170737/http://95.128.3.201:8053/API/NSService_10)
-
-
-
-
diff --git a/docs/content/dns/zz_gen_safedns.md b/docs/content/dns/zz_gen_safedns.md
index 4c20fca6a..e040a8a9f 100644
--- a/docs/content/dns/zz_gen_safedns.md
+++ b/docs/content/dns/zz_gen_safedns.md
@@ -1,12 +1,12 @@
---
-title: "ANS SafeDNS"
+title: "UKFast SafeDNS"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: safedns
dnsprovider:
since: "v4.6.0"
code: "safedns"
- url: "https://www.ans.co.uk/"
+ url: "https://www.ukfast.co.uk/dns-hosting.html"
---
@@ -14,7 +14,7 @@ dnsprovider:
-Configuration for [ANS SafeDNS](https://www.ans.co.uk/).
+Configuration for [UKFast SafeDNS](https://www.ukfast.co.uk/dns-hosting.html).
@@ -23,7 +23,7 @@ Configuration for [ANS SafeDNS](https://www.ans.co.uk/).
- Since: v4.6.0
-Here is an example bash command using the ANS SafeDNS provider:
+Here is an example bash command using the UKFast SafeDNS provider:
```bash
SAFEDNS_AUTH_TOKEN=xxxxxx \
diff --git a/docs/content/dns/zz_gen_todaynic.md b/docs/content/dns/zz_gen_todaynic.md
deleted file mode 100644
index 7b06c012d..000000000
--- a/docs/content/dns/zz_gen_todaynic.md
+++ /dev/null
@@ -1,69 +0,0 @@
----
-title: "TodayNIC/时代互联"
-date: 2019-03-03T16:39:46+01:00
-draft: false
-slug: todaynic
-dnsprovider:
- since: "v4.32.0"
- code: "todaynic"
- url: "https://www.todaynic.com/"
----
-
-
-
-
-
-
-Configuration for [TodayNIC/时代互联](https://www.todaynic.com/).
-
-
-
-
-- Code: `todaynic`
-- Since: v4.32.0
-
-
-Here is an example bash command using the TodayNIC/时代互联 provider:
-
-```bash
-TODAYNIC_AUTH_USER_ID="xxx" \
-TODAYNIC_API_KEY="yyy" \
-lego --dns todaynic -d '*.example.com' -d example.com run
-```
-
-
-
-
-## Credentials
-
-| Environment Variable Name | Description |
-|-----------------------|-------------|
-| `TODAYNIC_API_KEY` | API key |
-| `TODAYNIC_AUTH_USER_ID` | account ID |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-## Additional Configuration
-
-| Environment Variable Name | Description |
-|--------------------------------|-------------|
-| `TODAYNIC_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) |
-| `TODAYNIC_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 2) |
-| `TODAYNIC_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 60) |
-| `TODAYNIC_TTL` | The TTL of the TXT record used for the DNS challenge in seconds (Default: 600) |
-
-The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
-More information [here]({{% ref "dns#configuration-and-credentials" %}}).
-
-
-
-
-## More information
-
-- [API documentation](https://www.todaynic.com/partner/mode_Http_Api_detail.php)
-
-
-
-
diff --git a/docs/data/zz_cli_help.toml b/docs/data/zz_cli_help.toml
index 139143b17..7d1a4b2c3 100644
--- a/docs/data/zz_cli_help.toml
+++ b/docs/data/zz_cli_help.toml
@@ -152,7 +152,7 @@ To display the documentation for a specific DNS provider, run:
$ lego dnshelp -c code
Supported DNS providers:
- acme-dns, active24, alidns, aliesa, allinkl, alwaysdata, anexia, artfiles, arvancloud, auroradns, autodns, axelname, azion, azure, azuredns, baiducloud, beget, binarylane, bindman, bluecat, bluecatv2, bookmyname, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, com35, conoha, conohav3, constellix, corenetworks, cpanel, czechia, ddnss, derak, desec, designate, digitalocean, directadmin, dnsexit, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dyndnsfree, dynu, easydns, edgecenter, edgedns, edgeone, efficientip, epik, eurodns, excedo, exec, exoscale, f5xc, freemyip, gandi, gandiv5, gcloud, gcore, gigahostno, glesys, godaddy, googledomains, gravity, hetzner, hostingde, hostinger, hostingnl, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ionoscloud, ipv64, ispconfig, ispconfigddns, iwantmyname, jdcloud, joker, keyhelp, leaseweb, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, metaregistrar, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, namesurfer, nearlyfreespeech, neodigit, netcup, netlify, nicmanager, nicru, nifcloud, njalla, nodion, ns1, octenium, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rainyun, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, spaceship, stackpath, syse, technitium, tencentcloud, timewebcloud, todaynic, transip, ultradns, uniteddomains, variomedia, vegadns, vercel, versio, vinyldns, virtualname, vkcloud, volcengine, vscale, vultr, webnames, webnamesca, websupport, wedos, westcn, yandex, yandex360, yandexcloud, zoneedit, zoneee, zonomi
+ acme-dns, active24, alidns, aliesa, allinkl, alwaysdata, anexia, arvancloud, auroradns, autodns, axelname, azion, azure, azuredns, baiducloud, beget, binarylane, bindman, bluecat, bookmyname, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, com35, conoha, conohav3, constellix, corenetworks, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dyndnsfree, dynu, easydns, edgecenter, edgedns, edgeone, efficientip, epik, exec, exoscale, f5xc, freemyip, gandi, gandiv5, gcloud, gcore, gigahostno, glesys, godaddy, googledomains, gravity, hetzner, hostingde, hostinger, hostingnl, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ionoscloud, ipv64, ispconfig, ispconfigddns, iwantmyname, jdcloud, joker, keyhelp, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, metaregistrar, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, neodigit, netcup, netlify, nicmanager, nicru, nifcloud, njalla, nodion, ns1, octenium, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rainyun, rcodezero, regfish, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, selfhostde, servercow, shellrent, simply, sonic, spaceship, stackpath, syse, technitium, tencentcloud, timewebcloud, transip, ultradns, uniteddomains, variomedia, vegadns, vercel, versio, vinyldns, virtualname, vkcloud, volcengine, vscale, vultr, webnames, webnamesca, websupport, wedos, westcn, yandex, yandex360, yandexcloud, zoneedit, zoneee, zonomi
More information: https://go-acme.github.io/lego/dns
"""
diff --git a/go.mod b/go.mod
index b8e88428e..42e11820b 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.24.0
require (
cloud.google.com/go/compute/metadata v0.9.0
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0
@@ -15,28 +15,28 @@ require (
github.com/Azure/go-autorest/autorest/to v0.4.1
github.com/BurntSushi/toml v1.6.0
github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0
- github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.15
+ github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13
github.com/alibabacloud-go/tea v1.4.0
github.com/aliyun/credentials-go v1.4.7
- github.com/aws/aws-sdk-go-v2 v1.41.1
- github.com/aws/aws-sdk-go-v2/config v1.32.8
- github.com/aws/aws-sdk-go-v2/credentials v1.19.8
- github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.11
- github.com/aws/aws-sdk-go-v2/service/route53 v1.62.1
- github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0
- github.com/aws/aws-sdk-go-v2/service/sts v1.41.6
+ github.com/aws/aws-sdk-go-v2 v1.41.0
+ github.com/aws/aws-sdk-go-v2/config v1.32.6
+ github.com/aws/aws-sdk-go-v2/credentials v1.19.6
+ github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10
+ github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0
+ github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0
+ github.com/aws/aws-sdk-go-v2/service/sts v1.41.5
github.com/aziontech/azionapi-go-sdk v0.144.0
- github.com/baidubce/bce-sdk-go v0.9.260
+ github.com/baidubce/bce-sdk-go v0.9.256
github.com/cenkalti/backoff/v5 v5.0.3
github.com/dnsimple/dnsimple-go/v4 v4.0.0
github.com/exoscale/egoscale/v3 v3.1.33
github.com/go-acme/alidns-20150109/v4 v4.7.0
- github.com/go-acme/esa-20240910/v2 v2.48.0
+ github.com/go-acme/esa-20240910/v2 v2.44.0
github.com/go-acme/jdcloud-sdk-go v1.64.0
- github.com/go-acme/tencentclouddnspod v1.3.24
- github.com/go-acme/tencentedgdeone v1.3.38
+ github.com/go-acme/tencentclouddnspod v1.1.25
+ github.com/go-acme/tencentedgdeone v1.1.48
github.com/go-jose/go-jose/v4 v4.1.3
- github.com/go-viper/mapstructure/v2 v2.5.0
+ github.com/go-viper/mapstructure/v2 v2.4.0
github.com/google/go-cmp v0.7.0
github.com/google/go-querystring v1.2.0
github.com/google/uuid v1.6.0
@@ -44,18 +44,18 @@ require (
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56
github.com/hashicorp/go-retryablehttp v0.7.8
github.com/hashicorp/go-version v1.8.0
- github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.187
+ github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.182
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0
github.com/labbsr0x/bindman-dns-webhook v1.0.2
github.com/ldez/grignotin v0.10.1
- github.com/linode/linodego v1.65.0
+ github.com/linode/linodego v1.64.0
github.com/liquidweb/liquidweb-go v1.6.4
github.com/mattn/go-isatty v0.0.20
- github.com/miekg/dns v1.1.72
+ github.com/miekg/dns v1.1.69
github.com/mimuret/golang-iij-dpf v0.9.1
github.com/namedotcom/go/v4 v4.0.2
- github.com/nrdcg/auroradns v1.2.0
+ github.com/nrdcg/auroradns v1.1.0
github.com/nrdcg/bunny-go v0.1.0
github.com/nrdcg/desec v0.11.1
github.com/nrdcg/dnspod-go v0.4.0
@@ -65,8 +65,8 @@ require (
github.com/nrdcg/mailinabox v0.3.0
github.com/nrdcg/namesilo v0.5.0
github.com/nrdcg/nodion v0.1.0
- github.com/nrdcg/oci-go-sdk/common/v1065 v1065.108.2
- github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.108.2
+ github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.2
+ github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.2
github.com/nrdcg/porkbun v0.4.0
github.com/nrdcg/vegadns v0.3.0
github.com/nzdjb/go-metaname v1.0.0
@@ -81,29 +81,29 @@ require (
github.com/selectel/go-selvpcclient/v4 v4.1.0
github.com/softlayer/softlayer-go v1.2.1
github.com/stretchr/testify v1.11.1
- github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.48
+ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.28
github.com/transip/gotransip/v6 v6.26.1
github.com/ultradns/ultradns-go-sdk v1.8.1-20250722213956-faef419
github.com/urfave/cli/v2 v2.27.7
github.com/vinyldns/go-vinyldns v0.9.17
- github.com/volcengine/volc-sdk-golang v1.0.237
- github.com/vultr/govultr/v3 v3.27.0
- github.com/yandex-cloud/go-genproto v0.54.0
- github.com/yandex-cloud/go-sdk/services/dns v0.0.36
- github.com/yandex-cloud/go-sdk/v2 v2.56.0
- golang.org/x/crypto v0.48.0
- golang.org/x/net v0.50.0
- golang.org/x/oauth2 v0.35.0
- golang.org/x/text v0.34.0
+ github.com/volcengine/volc-sdk-golang v1.0.233
+ github.com/vultr/govultr/v3 v3.26.1
+ github.com/yandex-cloud/go-genproto v0.43.0
+ github.com/yandex-cloud/go-sdk/services/dns v0.0.25
+ github.com/yandex-cloud/go-sdk/v2 v2.37.0
+ golang.org/x/crypto v0.46.0
+ golang.org/x/net v0.48.0
+ golang.org/x/oauth2 v0.34.0
+ golang.org/x/text v0.32.0
golang.org/x/time v0.14.0
- google.golang.org/api v0.267.0
- gopkg.in/ns1/ns1-go.v2 v2.17.2
+ google.golang.org/api v0.259.0
+ gopkg.in/ns1/ns1-go.v2 v2.16.0
gopkg.in/yaml.v2 v2.4.0
software.sslmate.com/src/go-pkcs12 v0.7.0
)
require (
- cloud.google.com/go/auth v0.18.1 // indirect
+ cloud.google.com/go/auth v0.18.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
@@ -119,23 +119,22 @@ require (
github.com/alibabacloud-go/openapi-util v0.1.1 // indirect
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect
- github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 // indirect
- github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect
- github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect
+ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
- github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect
- github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 // indirect
- github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect
- github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 // indirect
- github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16 // indirect
+ github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 // indirect
+ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect
github.com/aws/smithy-go v1.24.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
- github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
@@ -161,8 +160,8 @@ require (
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/s2a-go v0.1.9 // indirect
- github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
- github.com/googleapis/gax-go/v2 v2.17.0 // indirect
+ github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect
+ github.com/googleapis/gax-go/v2 v2.16.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
@@ -206,23 +205,23 @@ require (
go.mongodb.org/mongo-driver v1.13.1 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
- go.opentelemetry.io/otel v1.39.0 // indirect
- go.opentelemetry.io/otel/metric v1.39.0 // indirect
- go.opentelemetry.io/otel/trace v1.39.0 // indirect
+ go.opentelemetry.io/otel v1.38.0 // indirect
+ go.opentelemetry.io/otel/metric v1.38.0 // indirect
+ go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/ratelimit v0.3.1 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
- golang.org/x/mod v0.32.0 // indirect
+ golang.org/x/mod v0.30.0 // indirect
golang.org/x/sync v0.19.0 // indirect
- golang.org/x/sys v0.41.0 // indirect
- golang.org/x/tools v0.41.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/tools v0.39.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
google.golang.org/grpc v1.78.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
- gopkg.in/ini.v1 v1.67.1 // indirect
+ gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index f5b87c9fe..889e6b5b5 100644
--- a/go.sum
+++ b/go.sum
@@ -13,8 +13,8 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
-cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs=
-cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA=
+cloud.google.com/go/auth v0.18.0 h1:wnqy5hrv7p3k7cShwAU/Br3nzod7fxoqG+k0VZ+/Pk0=
+cloud.google.com/go/auth v0.18.0/go.mod h1:wwkPM1AgE1f2u6dG443MiWoD8C3BtOywNsUMcUTVDRo=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
@@ -42,8 +42,8 @@ github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYs
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
@@ -121,10 +121,8 @@ github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC
github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8=
github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc=
github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=
+github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 h1:Q00FU3H94Ts0ZIHDmY+fYGgB7dV9D/YX6FGsgorQPgw=
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE=
-github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.14/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE=
-github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.15 h1:Mubp9hXZMTPWZK+WxrR+kKOVFp4Q/PDZrIIM7ByXI9Y=
-github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.15/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
@@ -171,54 +169,54 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4=
-github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
-github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
+github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4=
+github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4=
-github.com/aws/aws-sdk-go-v2/config v1.32.8 h1:iu+64gwDKEoKnyTQskSku72dAwggKI5sV6rNvgSMpMs=
-github.com/aws/aws-sdk-go-v2/config v1.32.8/go.mod h1:MI2XvA+qDi3i9AJxX1E2fu730syEBzp/jnXrjxuHwgI=
-github.com/aws/aws-sdk-go-v2/credentials v1.19.8 h1:Jp2JYH1lRT3KhX4mshHPvVYsR5qqRec3hGvEarNYoR0=
-github.com/aws/aws-sdk-go-v2/credentials v1.19.8/go.mod h1:fZG9tuvyVfxknv1rKibIz3DobRaFw1Poe8IKtXB3XYY=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY=
-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
-github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
+github.com/aws/aws-sdk-go-v2/config v1.32.6 h1:hFLBGUKjmLAekvi1evLi5hVvFQtSo3GYwi+Bx4lpJf8=
+github.com/aws/aws-sdk-go-v2/config v1.32.6/go.mod h1:lcUL/gcd8WyjCrMnxez5OXkO3/rwcNmvfno62tnXNcI=
+github.com/aws/aws-sdk-go-v2/credentials v1.19.6 h1:F9vWao2TwjV2MyiyVS+duza0NIRtAslgLUM0vTA1ZaE=
+github.com/aws/aws-sdk-go-v2/credentials v1.19.6/go.mod h1:SgHzKjEVsdQr6Opor0ihgWtkWdfRAIwxYzSJ8O85VHY=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17 h1:JqcdRG//czea7Ppjb+g/n4o8i/R50aTBHkA7vu0lK+k=
-github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.17/go.mod h1:CO+WeGmIdj/MlPel2KwID9Gt7CNq4M65HUfBW97liM0=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16 h1:CjMzUs78RDDv4ROu3JnJn/Ig1r6ZD7/T2DXLLRpejic=
+github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.16/go.mod h1:uVW4OLBqbJXSHJYA9svT9BluSvvwbzLQ2Crf6UPzR3c=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8 h1:Z5EiPIzXKewUQK0QTMkutjiaPVeVYXX7KIqhXu/0fXs=
-github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.8/go.mod h1:FsTpJtvC4U1fyDXk7c71XoDv3HlRm8V3NiYLeYLh5YE=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY=
-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17 h1:bGeHBsGZx0Dvu/eJC0Lh9adJa3M1xREcndxLNZlve2U=
-github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.17/go.mod h1:dcW24lbU0CzHusTE8LLHhRLI42ejmINN8Lcr22bwh/g=
-github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.11 h1:VM5e5M39zRSs+aT0O9SoxHjUXqXxhbw3Yi0FdMQWPIc=
-github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.11/go.mod h1:0jvzYPIQGCpnY/dmdaotTk2JH4QuBlnW0oeyrcGLWJ4=
-github.com/aws/aws-sdk-go-v2/service/route53 v1.62.1 h1:1jIdwWOulae7bBLIgB36OZ0DINACb1wxM6wdGlx4eHE=
-github.com/aws/aws-sdk-go-v2/service/route53 v1.62.1/go.mod h1:tE2zGlMIlxWv+7Otap7ctRp3qeKqtnja7DZguj3Vu/Y=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0 h1:oeu8VPlOre74lBA/PMhxa5vewaMIMmILM+RraSyB8KA=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.96.0/go.mod h1:5jggDlZ2CLQhwJBiZJb4vfk4f0GxWdEDruWKEJ1xOdo=
-github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y=
-github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M=
-github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk=
-github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 h1:0jbJeuEHlwKJ9PfXtpSFc4MF+WIWORdhN1n30ITZGFM=
-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo=
-github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ=
-github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7 h1:DIBqIrJ7hv+e4CmIk2z3pyKT+3B6qVMgRsawHiR3qso=
+github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.7/go.mod h1:vLm00xmBke75UmpNvOcZQ/Q30ZFjbczeLFqGx5urmGo=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16 h1:NSbvS17MlI2lurYgXnCOLvCFX38sBW4eiVER7+kkgsU=
+github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.16/go.mod h1:SwT8Tmqd4sA6G1qaGdzWCJN99bUmPGHfRwwq3G5Qb+A=
+github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10 h1:MQuZZ6Tq1qQabPlkVxrCMdyVl70Ogl4AERZKo+y9Wzo=
+github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10/go.mod h1:U5C3JME1ibKESmpzBAqlRpTYZfVbTqrb5ICJm+sVVd8=
+github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0 h1:80pDB3Tpmb2RCSZORrK9/3iQxsd+w6vSzVqpT1FGiwE=
+github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0/go.mod h1:6EZUGGNLPLh5Unt30uEoA+KQcByERfXIkax9qrc80nA=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0 h1:MIWra+MSq53CFaXXAywB2qg9YvVZifkk6vEGl/1Qor0=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.95.0/go.mod h1:79S2BdqCJpScXZA2y+cpZuocWsjGjJINyXnOsf5DTz8=
+github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ=
+github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU=
+github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 h1:aM/Q24rIlS3bRAhTyFurowU8A0SMyGDtEOY/l/s/1Uw=
+github.com/aws/aws-sdk-go-v2/service/sso v1.30.8/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0=
+github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70=
+github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
github.com/aziontech/azionapi-go-sdk v0.144.0 h1:T+/w18o+FCiZsk3Z0ACBVVe7c/5EGLG15S3P8JfuPfo=
github.com/aziontech/azionapi-go-sdk v0.144.0/go.mod h1:OKxP/R0iVXnJJakYwMhh2BGAXnud8Ruy55Ak9ANuWoU=
-github.com/baidubce/bce-sdk-go v0.9.260 h1:1v1+2GTP+NGK3L24rJ+bnoiTaDaIy2YoaUM+ot2GTcw=
-github.com/baidubce/bce-sdk-go v0.9.260/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
+github.com/baidubce/bce-sdk-go v0.9.256 h1:/6UwBzDp+dRFpKRIb5WsvxfSiG4SLOIOghvagOK/q4Y=
+github.com/baidubce/bce-sdk-go v0.9.256/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@@ -241,8 +239,6 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
-github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -319,14 +315,14 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-acme/alidns-20150109/v4 v4.7.0 h1:PqJ/wR0JTpL4v0Owu1uM7bPQ1Yww0eQLAuuSdLjjQaQ=
github.com/go-acme/alidns-20150109/v4 v4.7.0/go.mod h1:btQvB6xZoN6ykKB74cPhiR+uvhrEE2AFVXm6RDmCHm0=
-github.com/go-acme/esa-20240910/v2 v2.48.0 h1:muSDyhjDTejxUGe3FTthCPCqRaEdYY9cG3N/AmU52Lc=
-github.com/go-acme/esa-20240910/v2 v2.48.0/go.mod h1:shPb6hzc1rJL15IJBY8HQ4GZk4E8RC52+52twutEwIg=
+github.com/go-acme/esa-20240910/v2 v2.44.0 h1:ACi2uFb7ig4ousFs/YiFBR+aw3A4SHtOxvkMWB2Hbcs=
+github.com/go-acme/esa-20240910/v2 v2.44.0/go.mod h1:ZYdN9EN9ikn26SNapxCVjZ65pHT/1qm4fzuJ7QGVX6g=
github.com/go-acme/jdcloud-sdk-go v1.64.0 h1:AW9j5khk8tRYbpBJPxKmqdwIqgLs2Fz3HUK3hn2YXjs=
github.com/go-acme/jdcloud-sdk-go v1.64.0/go.mod h1:qc/m8HNX1Zgd7GAv2DSEinup8fwy3Ted3/VVx7LB5bU=
-github.com/go-acme/tencentclouddnspod v1.3.24 h1:uCSiOW1EJttcnOON+MVVyVDJguFL/Q4NIGkq1CrT9p8=
-github.com/go-acme/tencentclouddnspod v1.3.24/go.mod h1:RKcB2wSoZncjBA0OEFj59s1ko1XDy+ZsAtk+9uMxUF0=
-github.com/go-acme/tencentedgdeone v1.3.38 h1:5YsVl0H4A+cwtiUqR1eZbKFdr4OWfYp2KYJopifzKyQ=
-github.com/go-acme/tencentedgdeone v1.3.38/go.mod h1:yyjTKVmGpMtFv5HqGODqehHnZJ4KWAbG6dAiwWDgCDY=
+github.com/go-acme/tencentclouddnspod v1.1.25 h1:7H3ZKshkaHzCXfRpAHVB5nvxeDDl2XLeNZfrNHiZj/s=
+github.com/go-acme/tencentclouddnspod v1.1.25/go.mod h1:XXfzp0AYV7UAUsHKT6R0KAUJFhqAUXmWGF07Elpa5cE=
+github.com/go-acme/tencentedgdeone v1.1.48 h1:WLyLBsRVhSLFmtbEFXk0naLODSQn7X6J0Fc/qR8xVUk=
+github.com/go-acme/tencentedgdeone v1.1.48/go.mod h1:mu6tA+bPhlSd+CKUfzRikE0mfxmTlBI6dVTn9LY9dRI=
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@@ -370,8 +366,8 @@ github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8Wd
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
-github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
-github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
+github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
+github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA=
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
@@ -471,12 +467,12 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao=
-github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8=
+github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ=
+github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=
-github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=
+github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y=
+github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14=
github.com/gophercloud/gophercloud v1.3.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
github.com/gophercloud/gophercloud v1.14.1 h1:DTCNaTVGl8/cFu58O1JwWgis9gtISAFONqpMKNg/Vpw=
github.com/gophercloud/gophercloud v1.14.1/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
@@ -541,8 +537,8 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.187 h1:J+U6+eUjIsBhefolFdZW5hQNJbkMj+7msxZrv56Cg2g=
-github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.187/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI=
+github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.182 h1:B3W9acgpqu5XsN8v+W8SOTfqn/6n4JsjgoKBsm30HFY=
+github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.182/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI=
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -616,8 +612,8 @@ github.com/ldez/grignotin v0.10.1/go.mod h1:UlDbXFCARrXbWGNGP3S5vsysNXAPhnSuBufp
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
-github.com/linode/linodego v1.65.0 h1:SdsuGD8VSsPWeShXpE7ihl5vec+fD3MgwhnfYC/rj7k=
-github.com/linode/linodego v1.65.0/go.mod h1:tOFiTErdjkbVnV+4S0+NmIE9dqqZUEM2HsJaGu8wMh8=
+github.com/linode/linodego v1.64.0 h1:If6pULIwHuQytgogtpQaBdVLX7z2TTHUF5u1tj2TPiY=
+github.com/linode/linodego v1.64.0/go.mod h1:GoiwLVuLdBQcAebxAVKVL3mMYUgJZR/puOUSla04xBE=
github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
github.com/liquidweb/liquidweb-cli v0.6.9 h1:acbIvdRauiwbxIsOCEMXGwF75aSJDbDiyAWPjVnwoYM=
github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
@@ -653,8 +649,8 @@ github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3N
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
-github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
-github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
+github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc=
+github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g=
github.com/mimuret/golang-iij-dpf v0.9.1 h1:Gj6EhHJkOhr+q2RnvRPJsPMcjuVnWPSccEHyoEehU34=
github.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M=
github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
@@ -695,8 +691,8 @@ github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1t
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nrdcg/auroradns v1.2.0 h1:Jg407vTdXZvZKsART9CNWMp8rQOyhBk04q0MsOf0YR4=
-github.com/nrdcg/auroradns v1.2.0/go.mod h1:hnByA4Z7MOmV4EPRw5eOmEaNRFavcCIz6kONpNxp9LI=
+github.com/nrdcg/auroradns v1.1.0 h1:KekGh8kmf2MNwqZVVYo/fw/ZONt8QMEmbMFOeljteWo=
+github.com/nrdcg/auroradns v1.1.0/go.mod h1:O7tViUZbAcnykVnrGkXzIJTHoQCHcgalgAe6X1mzHfk=
github.com/nrdcg/bunny-go v0.1.0 h1:GAHTRpHaG/TxfLZlqoJ8OJFzw8rI74+jOTkzxWh0uHA=
github.com/nrdcg/bunny-go v0.1.0/go.mod h1:u+C9dgsspgtWVaAz6QkyV17s9fxD8viwwKoxb9XMz1A=
github.com/nrdcg/desec v0.11.1 h1:ilpKmCr4gGsLcyq3RHfHNmlRzm9fzT2XbWxoVaUCS0s=
@@ -715,10 +711,10 @@ github.com/nrdcg/namesilo v0.5.0 h1:6QNxT/XxE+f5B+7QlfWorthNzOzcGlBLRQxqi6YeBrE=
github.com/nrdcg/namesilo v0.5.0/go.mod h1:4UkwlwQfDt74kSGmhLaDylnBrD94IfflnpoEaj6T2qw=
github.com/nrdcg/nodion v0.1.0 h1:zLKaqTn2X0aDuBHHfyA1zFgeZfiCpmu/O9DM73okavw=
github.com/nrdcg/nodion v0.1.0/go.mod h1:inbuh3neCtIWlMPZHtEpe43TmRXxHV6+hk97iCZicms=
-github.com/nrdcg/oci-go-sdk/common/v1065 v1065.108.2 h1:OWijzl3nHUApvTivl+3+78dbBwmyEHOnb+W9m6ixGbk=
-github.com/nrdcg/oci-go-sdk/common/v1065 v1065.108.2/go.mod h1:Gcs8GCaZXL3FdiDWgdnMxlOLEdRprJJnPYB22TX1jw8=
-github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.108.2 h1:9LsjN/zaIN7H8JE61NHpbWhxF0UGY96+kMlk3g8OvGU=
-github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.108.2/go.mod h1:32vZH06TuwZSn+IDMO1qcDvC2vHVlzUALCwXGWPA+dc=
+github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.2 h1:l0tH15ACQADZAzC+LZ+mo2tIX4H6uZu0ulrVmG5Tqz0=
+github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.2/go.mod h1:Gcs8GCaZXL3FdiDWgdnMxlOLEdRprJJnPYB22TX1jw8=
+github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.2 h1:gzB4c6ztb38C/jYiqEaFC+mCGcWFHDji9e6jwymY9d4=
+github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.2/go.mod h1:l1qIPIq2uRV5WTSvkbhbl/ndbeOu7OCb3UZ+0+2ZSb8=
github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw=
github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54=
github.com/nrdcg/vegadns v0.3.0 h1:11FQMw7xVIRUWO9o5+Z/5YZhmPWlm4oxUUH3F6EVqQU=
@@ -908,10 +904,10 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.24/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.38/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.48 h1:bCs+z6dxRaHWm/C1D/XkSOcCZ0+W2+/6HmIXjpAj+fY=
-github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.48/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.25/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.48/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.28 h1:Rj1WXXNPm9AsPf0PJhWCvlsqfcKPUYdyVnkmEc3O8sI=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.28/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
@@ -926,10 +922,10 @@ github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
github.com/vinyldns/go-vinyldns v0.9.17 h1:hfPZfCaxcRBX6Gsgl42rLCeoal58/BH8kkvJShzjjdI=
github.com/vinyldns/go-vinyldns v0.9.17/go.mod h1:pwWhE9K/leGDOIduVhRGvQ3ecVMHWRfEnKYUTEU3gB4=
-github.com/volcengine/volc-sdk-golang v1.0.237 h1:hpLKiS2BwDcSBtZWSz034foCbd0h3FrHTKlUMqHIdc4=
-github.com/volcengine/volc-sdk-golang v1.0.237/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM=
-github.com/vultr/govultr/v3 v3.27.0 h1:J8etMyu/Jh5+idMsu2YZpOWmDXXHeW4VZnkYXmJYHx8=
-github.com/vultr/govultr/v3 v3.27.0/go.mod h1:9WwnWGCKnwDlNjHjtt+j+nP+0QWq6hQXzaHgddqrLWY=
+github.com/volcengine/volc-sdk-golang v1.0.233 h1:Hh2pzwu/Wq19rsZgNo3HdpjQB28D/F0+m6EjLVggmhM=
+github.com/volcengine/volc-sdk-golang v1.0.233/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM=
+github.com/vultr/govultr/v3 v3.26.1 h1:G/M0rMQKwVSmL+gb0UgETbW5mcQi0Vf/o/ZSGdBCxJw=
+github.com/vultr/govultr/v3 v3.26.1/go.mod h1:9WwnWGCKnwDlNjHjtt+j+nP+0QWq6hQXzaHgddqrLWY=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
@@ -938,12 +934,12 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
-github.com/yandex-cloud/go-genproto v0.54.0 h1:LjEwDPBAtF39HvcPQe8I+ImCnFasCPCOVh2b2Sr2eAg=
-github.com/yandex-cloud/go-genproto v0.54.0/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo=
-github.com/yandex-cloud/go-sdk/services/dns v0.0.36 h1:sD622+baDvJ2ujhCfoFsCH0XeNsaZNW6loRqvRavjtE=
-github.com/yandex-cloud/go-sdk/services/dns v0.0.36/go.mod h1:Hh7IKJxULaRzmyM19lQZw+yUDyMM8M3Qrk1LbWqhCkc=
-github.com/yandex-cloud/go-sdk/v2 v2.56.0 h1:rihPAZbPbHU/BKTLuT64nU1uhbBrO20HhdlLR3Hyoz0=
-github.com/yandex-cloud/go-sdk/v2 v2.56.0/go.mod h1:jzVBQgamNHoiDsmjog2dPZHMXuGZqmxf/epH+Qb7Emc=
+github.com/yandex-cloud/go-genproto v0.43.0 h1:HjBesEmCN8ZOhjjh8gs605vvi9/MBJAW3P20OJ4iQnw=
+github.com/yandex-cloud/go-genproto v0.43.0/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo=
+github.com/yandex-cloud/go-sdk/services/dns v0.0.25 h1:BcGEuOnwq2X3LS2kvFC6BOdZkOq4Lc7XAYvzap/SJJY=
+github.com/yandex-cloud/go-sdk/services/dns v0.0.25/go.mod h1:B4QHijALUHIjRxL3aqmOwDrHYUI2XdeeG4WKItth3jI=
+github.com/yandex-cloud/go-sdk/v2 v2.37.0 h1:WvttW6p9xcWag9j+GQv+GJXPggggXGwOlIJNfkWmFWw=
+github.com/yandex-cloud/go-sdk/v2 v2.37.0/go.mod h1:Dt4a81enjRsm4xMJyW5E1Y/vaUYwXJvUGRdDLuM2k6I=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
@@ -973,16 +969,16 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
-go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
-go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
-go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
-go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
-go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
-go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
+go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
+go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
+go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
+go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
+go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
+go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
-go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
-go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
+go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
+go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -1035,8 +1031,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
-golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
-golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1080,8 +1076,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
-golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
+golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
+golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1139,16 +1135,16 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
-golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
-golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
-golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
+golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
+golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1252,8 +1248,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
-golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1268,8 +1264,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
-golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
-golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
+golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
+golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1288,8 +1284,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
-golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
-golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1355,8 +1351,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
-golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
-golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
+golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
+golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1385,8 +1381,8 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
-google.golang.org/api v0.267.0 h1:w+vfWPMPYeRs8qH1aYYsFX68jMls5acWl/jocfLomwE=
-google.golang.org/api v0.267.0/go.mod h1:Jzc0+ZfLnyvXma3UtaTl023TdhZu6OMBP9tJ+0EmFD0=
+google.golang.org/api v0.259.0 h1:90TaGVIxScrh1Vn/XI2426kRpBqHwWIzVBzJsVZ5XrQ=
+google.golang.org/api v0.259.0/go.mod h1:LC2ISWGWbRoyQVpxGntWwLWN/vLNxxKBK9KuJRI8Te4=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1425,12 +1421,12 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
-google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
-google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
-google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
+google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934=
+google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:yJ2HH4EHEDTd3JiLmhds6NkJ17ITVYOdV3m3VKOnws0=
+google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
+google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1479,12 +1475,11 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k=
-gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
-gopkg.in/ns1/ns1-go.v2 v2.17.2 h1:x8YKHqCJWkC/hddfUhw7FRqTG0x3fr/0ZnWYN+i4THs=
-gopkg.in/ns1/ns1-go.v2 v2.17.2/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
+gopkg.in/ns1/ns1-go.v2 v2.16.0 h1:mUczKFnrCystSV7yIODzVSbENoud3T7DwstmyVZfqg4=
+gopkg.in/ns1/ns1-go.v2 v2.16.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
diff --git a/providers/dns/alidns/alidns.go b/providers/dns/alidns/alidns.go
index cdd8e75e0..a5c883fcb 100644
--- a/providers/dns/alidns/alidns.go
+++ b/providers/dns/alidns/alidns.go
@@ -27,7 +27,6 @@ const (
EnvSecretKey = envNamespace + "SECRET_KEY"
EnvSecurityToken = envNamespace + "SECURITY_TOKEN"
EnvRegionID = envNamespace + "REGION_ID"
- EnvLine = envNamespace + "LINE"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
@@ -46,7 +45,6 @@ type Config struct {
SecretKey string
SecurityToken string
RegionID string
- Line string
PropagationTimeout time.Duration
PollingInterval time.Duration
TTL int
@@ -76,7 +74,6 @@ type DNSProvider struct {
func NewDNSProvider() (*DNSProvider, error) {
config := NewDefaultConfig()
config.RegionID = env.GetOrFile(EnvRegionID)
- config.Line = env.GetOrFile(EnvLine)
values, err := env.Get(EnvRAMRole)
if err == nil {
@@ -257,18 +254,12 @@ func (d *DNSProvider) newTxtRecord(zone, fqdn, value string) (*alidns.AddDomainR
return nil, err
}
- adrr := new(alidns.AddDomainRecordRequest).
+ return new(alidns.AddDomainRecordRequest).
SetType("TXT").
SetDomainName(zone).
SetRR(rr).
SetValue(value).
- SetTTL(int64(d.config.TTL))
-
- if d.config.Line != "" {
- adrr.SetLine(d.config.Line)
- }
-
- return adrr, nil
+ SetTTL(int64(d.config.TTL)), nil
}
func (d *DNSProvider) findTxtRecords(ctx context.Context, fqdn string) ([]*alidns.DescribeDomainRecordsResponseBodyDomainRecordsRecord, error) {
diff --git a/providers/dns/alidns/alidns.toml b/providers/dns/alidns/alidns.toml
index b78e1859d..9a93bd24f 100644
--- a/providers/dns/alidns/alidns.toml
+++ b/providers/dns/alidns/alidns.toml
@@ -23,8 +23,6 @@ lego --dns alidns - -d '*.example.com' -d example.com run
ALICLOUD_SECRET_KEY = "Access Key secret"
ALICLOUD_SECURITY_TOKEN = "STS Security Token (optional)"
[Configuration.Additional]
- ALICLOUD_REGION_ID = "Region ID (Default: cn-hangzhou)"
- ALICLOUD_LINE = "Line (Default: default)"
ALICLOUD_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
ALICLOUD_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 60)"
ALICLOUD_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 600)"
diff --git a/providers/dns/allinkl/allinkl.go b/providers/dns/allinkl/allinkl.go
index 376b0903c..4a0aadd2b 100644
--- a/providers/dns/allinkl/allinkl.go
+++ b/providers/dns/allinkl/allinkl.go
@@ -11,7 +11,6 @@ import (
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/log"
"github.com/go-acme/lego/v4/platform/config/env"
"github.com/go-acme/lego/v4/providers/dns/allinkl/internal"
"github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
@@ -122,6 +121,11 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
info := dns01.GetChallengeInfo(domain, keyAuth)
+ authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
+ if err != nil {
+ return fmt.Errorf("allinkl: could not find zone for domain %q: %w", domain, err)
+ }
+
ctx := context.Background()
credential, err := d.identifier.Authentication(ctx, 60, true)
@@ -131,11 +135,6 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
ctx = internal.WithContext(ctx, credential)
- authZone, err := d.findZone(ctx, info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("allinkl: %w", err)
- }
-
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
if err != nil {
return fmt.Errorf("allinkl: %w", err)
@@ -193,17 +192,3 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
return nil
}
-
-func (d *DNSProvider) findZone(ctx context.Context, fqdn string) (string, error) {
- for z := range dns01.DomainsSeq(fqdn) {
- _, errG := d.client.GetDNSSettings(ctx, z, "")
- if errG != nil {
- log.Infof("get DNS settings zone[%q] %v", z, errG)
- continue
- }
-
- return z, nil
- }
-
- return "", fmt.Errorf("unable to find auth zone for '%s'", fqdn)
-}
diff --git a/providers/dns/allinkl/allinkl_test.go b/providers/dns/allinkl/allinkl_test.go
index 7da47aee4..b42adce5d 100644
--- a/providers/dns/allinkl/allinkl_test.go
+++ b/providers/dns/allinkl/allinkl_test.go
@@ -1,18 +1,9 @@
package allinkl
import (
- "encoding/json"
- "encoding/xml"
- "fmt"
- "io"
- "net/http"
- "net/http/httptest"
- "net/url"
"testing"
"github.com/go-acme/lego/v4/platform/tester"
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/go-acme/lego/v4/providers/dns/allinkl/internal"
"github.com/stretchr/testify/require"
)
@@ -152,108 +143,3 @@ func TestLiveCleanUp(t *testing.T) {
err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
require.NoError(t, err)
}
-
-func mockBuilder() *servermock.Builder[*DNSProvider] {
- return servermock.NewBuilder(
- func(server *httptest.Server) (*DNSProvider, error) {
- config := NewDefaultConfig()
- config.Login = "user"
- config.Password = "secret"
- config.HTTPClient = server.Client()
-
- p, err := NewDNSProviderConfig(config)
- if err != nil {
- return nil, err
- }
-
- p.client.BaseURL, _ = url.Parse(server.URL)
- p.identifier.BaseURL, _ = url.Parse(server.URL)
-
- return p, err
- },
- ).Route("POST /KasAuth.php",
- servermock.ResponseFromInternal("auth.xml"),
- servermock.CheckRequestBodyFromInternal("auth-request.xml").
- IgnoreWhitespace(),
- )
-}
-
-func extractKasRequest(reader io.Reader) (*internal.KasRequest, error) {
- type ReqEnvelope struct {
- XMLName xml.Name `xml:"Envelope"`
- Body struct {
- KasAPI struct {
- Params string `xml:"Params"`
- } `xml:"KasApi"`
- } `xml:"Body"`
- }
-
- raw, err := io.ReadAll(reader)
- if err != nil {
- return nil, err
- }
-
- reqEnvelope := ReqEnvelope{}
-
- err = xml.Unmarshal(raw, &reqEnvelope)
- if err != nil {
- return nil, err
- }
-
- var kReq internal.KasRequest
-
- err = json.Unmarshal([]byte(reqEnvelope.Body.KasAPI.Params), &kReq)
- if err != nil {
- return nil, err
- }
-
- return &kReq, nil
-}
-
-func TestDNSProvider_Present(t *testing.T) {
- provider := mockBuilder().
- Route("POST /KasApi.php",
- http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
- kReq, err := extractKasRequest(req.Body)
- if err != nil {
- http.Error(rw, err.Error(), http.StatusBadRequest)
- return
- }
-
- switch kReq.Action {
- case "get_dns_settings":
- params := kReq.RequestParams.(map[string]any)
-
- if params["zone_host"] == "_acme-challenge.example.com." {
- servermock.ResponseFromInternal("get_dns_settings_not_found.xml").ServeHTTP(rw, req)
- } else {
- servermock.ResponseFromInternal("get_dns_settings.xml").ServeHTTP(rw, req)
- }
-
- case "add_dns_settings":
- servermock.ResponseFromInternal("add_dns_settings.xml").ServeHTTP(rw, req)
-
- default:
- http.Error(rw, fmt.Sprintf("unknown action: %v", kReq.Action), http.StatusBadRequest)
- }
- }),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp(t *testing.T) {
- provider := mockBuilder().
- Route("POST /KasApi.php",
- servermock.ResponseFromInternal("delete_dns_settings.xml"),
- servermock.CheckRequestBodyFromInternal("delete_dns_settings-request.xml").
- IgnoreWhitespace()).
- Build(t)
-
- provider.recordIDs["abc"] = "57347450"
-
- err := provider.CleanUp("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
diff --git a/providers/dns/allinkl/internal/client.go b/providers/dns/allinkl/internal/client.go
index d4403cac5..d747e9b36 100644
--- a/providers/dns/allinkl/internal/client.go
+++ b/providers/dns/allinkl/internal/client.go
@@ -6,21 +6,16 @@ import (
"encoding/json"
"fmt"
"net/http"
- "net/url"
"strconv"
"strings"
"sync"
"time"
- "github.com/cenkalti/backoff/v5"
- "github.com/go-acme/lego/v4/platform/wait"
"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
"github.com/go-viper/mapstructure/v2"
)
-const defaultBaseURL = "https://kasapi.kasserver.com/soap/"
-
-const apiPath = "KasApi.php"
+const apiEndpoint = "https://kasapi.kasserver.com/soap/KasApi.php"
type Authentication interface {
Authentication(ctx context.Context, sessionLifetime int, sessionUpdateLifetime bool) (string, error)
@@ -33,21 +28,16 @@ type Client struct {
floodTime time.Time
muFloodTime sync.Mutex
- maxElapsedTime time.Duration
-
- BaseURL *url.URL
+ baseURL string
HTTPClient *http.Client
}
// NewClient creates a new Client.
func NewClient(login string) *Client {
- baseURL, _ := url.Parse(defaultBaseURL)
-
return &Client{
- login: login,
- BaseURL: baseURL,
- maxElapsedTime: 3 * time.Minute,
- HTTPClient: &http.Client{Timeout: 10 * time.Second},
+ login: login,
+ baseURL: apiEndpoint,
+ HTTPClient: &http.Client{Timeout: 10 * time.Second},
}
}
@@ -61,9 +51,14 @@ func (c *Client) GetDNSSettings(ctx context.Context, zone, recordID string) ([]R
requestParams["record_id"] = recordID
}
- var g APIResponse[GetDNSSettingsResponse]
+ req, err := c.newRequest(ctx, "get_dns_settings", requestParams)
+ if err != nil {
+ return nil, err
+ }
- err := c.doRequest(ctx, "get_dns_settings", requestParams, &g)
+ var g GetDNSSettingsAPIResponse
+
+ err = c.do(req, &g)
if err != nil {
return nil, err
}
@@ -75,9 +70,14 @@ func (c *Client) GetDNSSettings(ctx context.Context, zone, recordID string) ([]R
// AddDNSSettings Creation of a DNS resource record.
func (c *Client) AddDNSSettings(ctx context.Context, record DNSRequest) (string, error) {
- var g APIResponse[AddDNSSettingsResponse]
+ req, err := c.newRequest(ctx, "add_dns_settings", record)
+ if err != nil {
+ return "", err
+ }
- err := c.doRequest(ctx, "add_dns_settings", record, &g)
+ var g AddDNSSettingsAPIResponse
+
+ err = c.do(req, &g)
if err != nil {
return "", err
}
@@ -91,9 +91,14 @@ func (c *Client) AddDNSSettings(ctx context.Context, record DNSRequest) (string,
func (c *Client) DeleteDNSSettings(ctx context.Context, recordID string) (string, error) {
requestParams := map[string]string{"record_id": recordID}
- var g APIResponse[DeleteDNSSettingsResponse]
+ req, err := c.newRequest(ctx, "delete_dns_settings", requestParams)
+ if err != nil {
+ return "", err
+ }
- err := c.doRequest(ctx, "delete_dns_settings", requestParams, &g)
+ var g DeleteDNSSettingsAPIResponse
+
+ err = c.do(req, &g)
if err != nil {
return "", err
}
@@ -119,9 +124,7 @@ func (c *Client) newRequest(ctx context.Context, action string, requestParams an
payload := []byte(strings.TrimSpace(fmt.Sprintf(kasAPIEnvelope, body)))
- endpoint := c.BaseURL.JoinPath(apiPath)
-
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), bytes.NewReader(payload))
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL, bytes.NewReader(payload))
if err != nil {
return nil, fmt.Errorf("unable to create request: %w", err)
}
@@ -129,21 +132,6 @@ func (c *Client) newRequest(ctx context.Context, action string, requestParams an
return req, nil
}
-func (c *Client) doRequest(ctx context.Context, action string, requestParams, result any) error {
- return wait.Retry(ctx,
- func() error {
- req, err := c.newRequest(ctx, action, requestParams)
- if err != nil {
- return backoff.Permanent(err)
- }
-
- return c.do(req, result)
- },
- backoff.WithBackOff(&backoff.ZeroBackOff{}),
- backoff.WithMaxElapsedTime(c.maxElapsedTime),
- )
-}
-
func (c *Client) do(req *http.Request, result any) error {
c.muFloodTime.Lock()
time.Sleep(time.Until(c.floodTime))
@@ -151,40 +139,29 @@ func (c *Client) do(req *http.Request, result any) error {
resp, err := c.HTTPClient.Do(req)
if err != nil {
- return backoff.Permanent(errutils.NewHTTPDoError(req, err))
+ return errutils.NewHTTPDoError(req, err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode != http.StatusOK {
- return backoff.Permanent(errutils.NewUnexpectedResponseStatusCodeError(req, resp))
+ return errutils.NewUnexpectedResponseStatusCodeError(req, resp)
}
envlp, err := decodeXML[KasAPIResponseEnvelope](resp.Body)
if err != nil {
- return backoff.Permanent(err)
+ return err
}
if envlp.Body.Fault != nil {
- if envlp.Body.Fault.Message == "flood_protection" {
- ft, errP := strconv.ParseFloat(envlp.Body.Fault.Detail, 64)
- if errP != nil {
- return fmt.Errorf("parse flood protection delay: %w", envlp.Body.Fault)
- }
-
- c.updateFloodTime(ft)
-
- return envlp.Body.Fault
- }
-
- return backoff.Permanent(envlp.Body.Fault)
+ return envlp.Body.Fault
}
raw := getValue(envlp.Body.KasAPIResponse.Return)
err = mapstructure.Decode(raw, result)
if err != nil {
- return backoff.Permanent(fmt.Errorf("response struct decode: %w", err))
+ return fmt.Errorf("response struct decode: %w", err)
}
return nil
diff --git a/providers/dns/allinkl/internal/client_test.go b/providers/dns/allinkl/internal/client_test.go
index 949f45bf9..4b111e31c 100644
--- a/providers/dns/allinkl/internal/client_test.go
+++ b/providers/dns/allinkl/internal/client_test.go
@@ -2,9 +2,7 @@ package internal
import (
"net/http/httptest"
- "net/url"
"testing"
- "time"
"github.com/go-acme/lego/v4/platform/tester/servermock"
"github.com/stretchr/testify/assert"
@@ -13,17 +11,15 @@ import (
func setupClient(server *httptest.Server) (*Client, error) {
client := NewClient("user")
- client.BaseURL, _ = url.Parse(server.URL)
+ client.baseURL = server.URL
client.HTTPClient = server.Client()
- client.maxElapsedTime = 1 * time.Second
-
return client, nil
}
func TestClient_GetDNSSettings(t *testing.T) {
client := servermock.NewBuilder[*Client](setupClient).
- Route("POST /KasApi.php", servermock.ResponseFromFixture("get_dns_settings.xml"),
+ Route("POST /", servermock.ResponseFromFixture("get_dns_settings.xml"),
servermock.CheckRequestBodyFromFixture("get_dns_settings-request.xml").
IgnoreWhitespace()).
Build(t)
@@ -100,24 +96,9 @@ func TestClient_GetDNSSettings(t *testing.T) {
assert.Equal(t, expected, records)
}
-func TestClient_GetDNSSettings_error_flood_protection(t *testing.T) {
- client := servermock.NewBuilder[*Client](setupClient).
- Route("POST /KasApi.php",
- servermock.ResponseFromFixture("flood_protection.xml"),
- ).
- Build(t)
-
- assert.Zero(t, client.floodTime)
-
- _, err := client.GetDNSSettings(mockContext(t), "example.com", "")
- require.EqualError(t, err, "KasApi: SOAP-ENV:Server: flood_protection: 0.0688529014587")
-
- assert.NotZero(t, client.floodTime)
-}
-
func TestClient_AddDNSSettings(t *testing.T) {
client := servermock.NewBuilder[*Client](setupClient).
- Route("POST /KasApi.php", servermock.ResponseFromFixture("add_dns_settings.xml"),
+ Route("POST /", servermock.ResponseFromFixture("add_dns_settings.xml"),
servermock.CheckRequestBodyFromFixture("add_dns_settings-request.xml").
IgnoreWhitespace()).
Build(t)
@@ -137,7 +118,7 @@ func TestClient_AddDNSSettings(t *testing.T) {
func TestClient_DeleteDNSSettings(t *testing.T) {
client := servermock.NewBuilder[*Client](setupClient).
- Route("POST /KasApi.php", servermock.ResponseFromFixture("delete_dns_settings.xml"),
+ Route("POST /", servermock.ResponseFromFixture("delete_dns_settings.xml"),
servermock.CheckRequestBodyFromFixture("delete_dns_settings-request.xml").
IgnoreWhitespace()).
Build(t)
diff --git a/providers/dns/allinkl/internal/fixtures/auth-request.xml b/providers/dns/allinkl/internal/fixtures/auth-request.xml
deleted file mode 100644
index 1cba86f10..000000000
--- a/providers/dns/allinkl/internal/fixtures/auth-request.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- {"kas_login":"user","kas_auth_data":"secret","kas_auth_type":"plain","session_lifetime":60,"session_update_lifetime":"Y"}
-
-
-
diff --git a/providers/dns/allinkl/internal/fixtures/flood_protection.xml b/providers/dns/allinkl/internal/fixtures/flood_protection.xml
deleted file mode 100644
index b8da10fab..000000000
--- a/providers/dns/allinkl/internal/fixtures/flood_protection.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
- SOAP-ENV:Server
- flood_protection
- KasApi
- 0.0688529014587
-
-
-
diff --git a/providers/dns/allinkl/internal/fixtures/get_dns_settings-zone_not_found.xml b/providers/dns/allinkl/internal/fixtures/get_dns_settings-zone_not_found.xml
deleted file mode 100644
index 478d07a3a..000000000
--- a/providers/dns/allinkl/internal/fixtures/get_dns_settings-zone_not_found.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
- SOAP-ENV:Server
- zone_not_found
- KasApi
- example.com
-
-
-
diff --git a/providers/dns/allinkl/internal/fixtures/get_dns_settings-zone_syntax_incorrect.xml b/providers/dns/allinkl/internal/fixtures/get_dns_settings-zone_syntax_incorrect.xml
deleted file mode 100644
index c77d733db..000000000
--- a/providers/dns/allinkl/internal/fixtures/get_dns_settings-zone_syntax_incorrect.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
- SOAP-ENV:Server
- zone_syntax_incorrect
- KasApi
- _acme-challenge.example.com
-
-
-
diff --git a/providers/dns/allinkl/internal/identity.go b/providers/dns/allinkl/internal/identity.go
index e95e78899..ba8d4d90e 100644
--- a/providers/dns/allinkl/internal/identity.go
+++ b/providers/dns/allinkl/internal/identity.go
@@ -6,14 +6,14 @@ import (
"encoding/json"
"fmt"
"net/http"
- "net/url"
"strings"
"time"
"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
)
-const authPath = "KasAuth.php"
+// authEndpoint represents the Identity API endpoint to call.
+const authEndpoint = "https://kasapi.kasserver.com/soap/KasAuth.php"
type token string
@@ -24,19 +24,17 @@ type Identifier struct {
login string
password string
- BaseURL *url.URL
- HTTPClient *http.Client
+ authEndpoint string
+ HTTPClient *http.Client
}
// NewIdentifier creates a new Identifier.
func NewIdentifier(login, password string) *Identifier {
- baseURL, _ := url.Parse(defaultBaseURL)
-
return &Identifier{
- login: login,
- password: password,
- BaseURL: baseURL,
- HTTPClient: &http.Client{Timeout: 10 * time.Second},
+ login: login,
+ password: password,
+ authEndpoint: authEndpoint,
+ HTTPClient: &http.Client{Timeout: 10 * time.Second},
}
}
@@ -64,9 +62,7 @@ func (c *Identifier) Authentication(ctx context.Context, sessionLifetime int, se
payload := []byte(strings.TrimSpace(fmt.Sprintf(kasAuthEnvelope, body)))
- endpoint := c.BaseURL.JoinPath(authPath)
-
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), bytes.NewReader(payload))
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.authEndpoint, bytes.NewReader(payload))
if err != nil {
return "", fmt.Errorf("unable to create request: %w", err)
}
diff --git a/providers/dns/allinkl/internal/identity_test.go b/providers/dns/allinkl/internal/identity_test.go
index 41d092b13..7b93b7688 100644
--- a/providers/dns/allinkl/internal/identity_test.go
+++ b/providers/dns/allinkl/internal/identity_test.go
@@ -3,7 +3,6 @@ package internal
import (
"context"
"net/http/httptest"
- "net/url"
"testing"
"github.com/go-acme/lego/v4/platform/tester/servermock"
@@ -13,7 +12,7 @@ import (
func setupIdentifierClient(server *httptest.Server) (*Identifier, error) {
client := NewIdentifier("user", "secret")
- client.BaseURL, _ = url.Parse(server.URL)
+ client.authEndpoint = server.URL
client.HTTPClient = server.Client()
return client, nil
@@ -27,13 +26,10 @@ func mockContext(t *testing.T) context.Context {
func TestIdentifier_Authentication(t *testing.T) {
client := servermock.NewBuilder[*Identifier](setupIdentifierClient).
- Route("POST /KasAuth.php",
- servermock.ResponseFromFixture("auth.xml"),
- servermock.CheckRequestBodyFromFixture("auth-request.xml").
- IgnoreWhitespace()).
+ Route("POST /", servermock.ResponseFromFixture("auth.xml")).
Build(t)
- credentialToken, err := client.Authentication(t.Context(), 60, true)
+ credentialToken, err := client.Authentication(t.Context(), 60, false)
require.NoError(t, err)
assert.Equal(t, "593959ca04f0de9689b586c6a647d15d", credentialToken)
@@ -41,7 +37,7 @@ func TestIdentifier_Authentication(t *testing.T) {
func TestIdentifier_Authentication_error(t *testing.T) {
client := servermock.NewBuilder[*Identifier](setupIdentifierClient).
- Route("POST /KasAuth.php", servermock.ResponseFromFixture("auth_fault.xml")).
+ Route("POST /", servermock.ResponseFromFixture("auth_fault.xml")).
Build(t)
_, err := client.Authentication(t.Context(), 60, false)
diff --git a/providers/dns/allinkl/internal/types.go b/providers/dns/allinkl/internal/types.go
index 51f7065b5..b0aa9b4ff 100644
--- a/providers/dns/allinkl/internal/types.go
+++ b/providers/dns/allinkl/internal/types.go
@@ -26,11 +26,10 @@ type Fault struct {
Code string `xml:"faultcode"`
Message string `xml:"faultstring"`
Actor string `xml:"faultactor"`
- Detail string `xml:"detail"`
}
-func (f *Fault) Error() string {
- return fmt.Sprintf("%s: %s: %s: %s", f.Actor, f.Code, f.Message, f.Detail)
+func (f Fault) Error() string {
+ return fmt.Sprintf("%s: %s: %s", f.Actor, f.Code, f.Message)
}
// KasResponse a KAS SOAP response.
diff --git a/providers/dns/allinkl/internal/types_api.go b/providers/dns/allinkl/internal/types_api.go
index a11f3aac0..22f2c32ed 100644
--- a/providers/dns/allinkl/internal/types_api.go
+++ b/providers/dns/allinkl/internal/types_api.go
@@ -53,8 +53,8 @@ type DNSRequest struct {
// ---
-type APIResponse[T any] struct {
- Response T `json:"Response" mapstructure:"Response"`
+type GetDNSSettingsAPIResponse struct {
+ Response GetDNSSettingsResponse `json:"Response" mapstructure:"Response"`
}
type GetDNSSettingsResponse struct {
@@ -73,12 +73,20 @@ type ReturnInfo struct {
Aux int `json:"record_aux,omitempty" mapstructure:"record_aux"`
}
+type AddDNSSettingsAPIResponse struct {
+ Response AddDNSSettingsResponse `json:"Response" mapstructure:"Response"`
+}
+
type AddDNSSettingsResponse struct {
KasFloodDelay float64 `json:"KasFloodDelay" mapstructure:"KasFloodDelay"`
ReturnInfo string `json:"ReturnInfo" mapstructure:"ReturnInfo"`
ReturnString string `json:"ReturnString" mapstructure:"ReturnString"`
}
+type DeleteDNSSettingsAPIResponse struct {
+ Response DeleteDNSSettingsResponse `json:"Response"`
+}
+
type DeleteDNSSettingsResponse struct {
KasFloodDelay float64 `json:"KasFloodDelay"`
ReturnString string `json:"ReturnString"`
diff --git a/providers/dns/artfiles/artfiles.go b/providers/dns/artfiles/artfiles.go
deleted file mode 100644
index c918d77f6..000000000
--- a/providers/dns/artfiles/artfiles.go
+++ /dev/null
@@ -1,204 +0,0 @@
-// Package artfiles implements a DNS provider for solving the DNS-01 challenge using ArtFiles.
-package artfiles
-
-import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "net/http"
- "slices"
- "time"
-
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/platform/config/env"
- "github.com/go-acme/lego/v4/providers/dns/artfiles/internal"
- "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
-)
-
-// Environment variables names.
-const (
- envNamespace = "ARTFILES_"
-
- EnvUsername = envNamespace + "USERNAME"
- EnvPassword = envNamespace + "PASSWORD"
-
- EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
- EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
- EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
-)
-
-// Config is used to configure the creation of the DNSProvider.
-type Config struct {
- Username string
- Password string
-
- PropagationTimeout time.Duration
- PollingInterval time.Duration
- HTTPClient *http.Client
-}
-
-// NewDefaultConfig returns a default configuration for the DNSProvider.
-func NewDefaultConfig() *Config {
- return &Config{
- PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 6*time.Minute),
- PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
- HTTPClient: &http.Client{
- Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
- },
- }
-}
-
-// DNSProvider implements the challenge.Provider interface.
-type DNSProvider struct {
- config *Config
- client *internal.Client
-}
-
-// NewDNSProvider returns a DNSProvider instance configured for ArtFiles.
-func NewDNSProvider() (*DNSProvider, error) {
- values, err := env.Get(EnvUsername, EnvPassword)
- if err != nil {
- return nil, fmt.Errorf("artfiles: %w", err)
- }
-
- config := NewDefaultConfig()
- config.Username = values[EnvUsername]
- config.Password = values[EnvPassword]
-
- return NewDNSProviderConfig(config)
-}
-
-// NewDNSProviderConfig return a DNSProvider instance configured for ArtFiles.
-func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
- if config == nil {
- return nil, errors.New("artfiles: the configuration of the DNS provider is nil")
- }
-
- client, err := internal.NewClient(config.Username, config.Password)
- if err != nil {
- return nil, fmt.Errorf("artfiles: %w", err)
- }
-
- if config.HTTPClient != nil {
- client.HTTPClient = config.HTTPClient
- }
-
- client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
-
- return &DNSProvider{
- config: config,
- client: client,
- }, nil
-}
-
-// Present creates a TXT record using the specified parameters.
-func (d *DNSProvider) Present(domain, token, keyAuth string) error {
- ctx := context.Background()
-
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- zone, err := d.findZone(ctx, info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("artfiles: %w", err)
- }
-
- records, err := d.client.GetRecords(ctx, zone)
- if err != nil {
- return fmt.Errorf("artfiles: get records: %w", err)
- }
-
- rv := internal.RecordValue{}
-
- if len(records["TXT"]) > 0 {
- var raw string
-
- err = json.Unmarshal(records["TXT"], &raw)
- if err != nil {
- return fmt.Errorf("artfiles: unmarshal TXT records: %w", err)
- }
-
- rv = internal.ParseRecordValue(raw)
- }
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone)
- if err != nil {
- return fmt.Errorf("artfiles: %w", err)
- }
-
- rv.Add(subDomain, info.Value)
-
- err = d.client.SetRecords(ctx, zone, "TXT", rv)
- if err != nil {
- return fmt.Errorf("artfiles: set TXT records: %w", err)
- }
-
- return nil
-}
-
-// CleanUp removes the TXT record matching the specified parameters.
-func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
- ctx := context.Background()
-
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- zone, err := d.findZone(ctx, info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("artfiles: %w", err)
- }
-
- records, err := d.client.GetRecords(ctx, zone)
- if err != nil {
- return fmt.Errorf("artfiles: get records: %w", err)
- }
-
- var raw string
-
- err = json.Unmarshal(records["TXT"], &raw)
- if err != nil {
- return fmt.Errorf("artfiles: unmarshal TXT records: %w", err)
- }
-
- rv := internal.ParseRecordValue(raw)
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone)
- if err != nil {
- return fmt.Errorf("artfiles: %w", err)
- }
-
- rv.RemoveValue(subDomain, info.Value)
-
- err = d.client.SetRecords(ctx, zone, "TXT", rv)
- if err != nil {
- return fmt.Errorf("artfiles: set TXT records: %w", err)
- }
-
- return nil
-}
-
-// Timeout returns the timeout and interval to use when checking for DNS propagation.
-// Adjusting here to cope with spikes in propagation times.
-func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
- return d.config.PropagationTimeout, d.config.PollingInterval
-}
-
-func (d *DNSProvider) findZone(ctx context.Context, fqdn string) (string, error) {
- domains, err := d.client.GetDomains(ctx)
- if err != nil {
- return "", fmt.Errorf("artfiles: get domains: %w", err)
- }
-
- var zone string
-
- for s := range dns01.UnFqdnDomainsSeq(fqdn) {
- if slices.Contains(domains, s) {
- zone = s
- }
- }
-
- if zone == "" {
- return "", fmt.Errorf("artfiles: could not find the zone for domain %q", fqdn)
- }
-
- return zone, nil
-}
diff --git a/providers/dns/artfiles/artfiles.toml b/providers/dns/artfiles/artfiles.toml
deleted file mode 100644
index 00ff12342..000000000
--- a/providers/dns/artfiles/artfiles.toml
+++ /dev/null
@@ -1,24 +0,0 @@
-Name = "ArtFiles"
-Description = ''''''
-URL = "https://www.artfiles.de/extras/domains/"
-Code = "artfiles"
-Since = "v4.32.0"
-
-Example = '''
-ARTFILES_USERNAME="xxx" \
-ARTFILES_PASSWORD="yyy" \
-lego --dns artfiles -d '*.example.com' -d example.com run
-'''
-
-[Configuration]
- [Configuration.Credentials]
- ARTFILES_USERNAME = "API username"
- ARTFILES_PASSWORD = "API password"
- [Configuration.Additional]
- ARTFILES_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
- ARTFILES_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 360)"
- ARTFILES_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)"
- ARTFILES_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
-
-[Links]
- API = "https://support.artfiles.de/DCP-API#dns"
diff --git a/providers/dns/artfiles/artfiles_test.go b/providers/dns/artfiles/artfiles_test.go
deleted file mode 100644
index 42490f10d..000000000
--- a/providers/dns/artfiles/artfiles_test.go
+++ /dev/null
@@ -1,228 +0,0 @@
-package artfiles
-
-import (
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester"
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/require"
-)
-
-const envDomain = envNamespace + "DOMAIN"
-
-var envTest = tester.NewEnvTest(EnvUsername, EnvPassword).WithDomain(envDomain)
-
-func TestNewDNSProvider(t *testing.T) {
- testCases := []struct {
- desc string
- envVars map[string]string
- expected string
- }{
- {
- desc: "success",
- envVars: map[string]string{
- EnvUsername: "user",
- EnvPassword: "secret",
- },
- },
- {
- desc: "missing username",
- envVars: map[string]string{
- EnvUsername: "",
- EnvPassword: "secret",
- },
- expected: "artfiles: some credentials information are missing: ARTFILES_USERNAME",
- },
- {
- desc: "missing password",
- envVars: map[string]string{
- EnvUsername: "user",
- EnvPassword: "",
- },
- expected: "artfiles: some credentials information are missing: ARTFILES_PASSWORD",
- },
- {
- desc: "missing credentials",
- envVars: map[string]string{},
- expected: "artfiles: some credentials information are missing: ARTFILES_USERNAME,ARTFILES_PASSWORD",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(test.envVars)
-
- p, err := NewDNSProvider()
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestNewDNSProviderConfig(t *testing.T) {
- testCases := []struct {
- desc string
- username string
- password string
- expected string
- }{
- {
- desc: "success",
- username: "user",
- password: "secret",
- },
- {
- desc: "missing username",
- password: "secret",
- expected: "artfiles: credentials missing",
- },
- {
- desc: "missing Example",
- username: "user",
- expected: "artfiles: credentials missing",
- },
- {
- desc: "missing credentials",
- expected: "artfiles: credentials missing",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- config := NewDefaultConfig()
- config.Username = test.username
- config.Password = test.password
-
- p, err := NewDNSProviderConfig(config)
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestLivePresent(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.Present(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func TestLiveCleanUp(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func mockBuilder() *servermock.Builder[*DNSProvider] {
- return servermock.NewBuilder(
- func(server *httptest.Server) (*DNSProvider, error) {
- config := NewDefaultConfig()
- config.Username = "user"
- config.Password = "secret"
- config.HTTPClient = server.Client()
-
- p, err := NewDNSProviderConfig(config)
- if err != nil {
- return nil, err
- }
-
- p.client.BaseURL, _ = url.Parse(server.URL)
-
- return p, nil
- },
- servermock.CheckHeader().
- WithBasicAuth("user", "secret"),
- )
-}
-
-func TestDNSProvider_Present(t *testing.T) {
- provider := mockBuilder().
- Route("GET /domain/get_domains.html",
- servermock.ResponseFromInternal("domains.txt"),
- ).
- Route("GET /dns/get_dns.html",
- servermock.ResponseFromInternal("get_dns.json"),
- servermock.CheckQueryParameter().Strict().
- With("domain", "example.com"),
- ).
- Route("POST /dns/set_dns.html",
- servermock.ResponseFromInternal("set_dns.json"),
- servermock.CheckQueryParameter().Strict().
- With("TXT", `@ "v=spf1 a mx ~all"
-_acme-challenge "TheAcmeChallenge"
-_acme-challenge "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY"
-_dmarc "v=DMARC1;p=reject;sp=reject;adkim=r;aspf=r;pct=100;rua=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;ri=86400;ruf=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;fo=1;rf=afrf"
-_mta-sts "v=STSv1;id=yyyymmddTHHMMSS;"
-_smtp._tls "v=TLSRPTv1;rua=mailto:someone@in.mailhardener.com"
-selector._domainkey "v=DKIM1;k=rsa;p=Base64Stuff" "MoreBase64Stuff" "Even++MoreBase64Stuff" "YesMoreBase64Stuff" "And+Yes+Even+MoreBase64Stuff" "Sure++MoreBase64Stuff" "LastBase64Stuff"
-selectorecc._domainkey "v=DKIM1;k=ed25519;p=Base64Stuff"`).
- With("domain", "example.com"),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp(t *testing.T) {
- provider := mockBuilder().
- Route("GET /domain/get_domains.html",
- servermock.ResponseFromInternal("domains.txt"),
- ).
- Route("GET /dns/get_dns.html",
- servermock.ResponseFromInternal("get_dns.json"),
- servermock.CheckQueryParameter().Strict().
- With("domain", "example.com"),
- ).
- Route("POST /dns/set_dns.html",
- servermock.ResponseFromInternal("set_dns.json"),
- servermock.CheckQueryParameter().Strict().
- With("TXT", `@ "v=spf1 a mx ~all"
-_acme-challenge "TheAcmeChallenge"
-_dmarc "v=DMARC1;p=reject;sp=reject;adkim=r;aspf=r;pct=100;rua=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;ri=86400;ruf=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;fo=1;rf=afrf"
-_mta-sts "v=STSv1;id=yyyymmddTHHMMSS;"
-_smtp._tls "v=TLSRPTv1;rua=mailto:someone@in.mailhardener.com"
-selector._domainkey "v=DKIM1;k=rsa;p=Base64Stuff" "MoreBase64Stuff" "Even++MoreBase64Stuff" "YesMoreBase64Stuff" "And+Yes+Even+MoreBase64Stuff" "Sure++MoreBase64Stuff" "LastBase64Stuff"
-selectorecc._domainkey "v=DKIM1;k=ed25519;p=Base64Stuff"`).
- With("domain", "example.com"),
- ).
- Build(t)
-
- err := provider.CleanUp("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
diff --git a/providers/dns/artfiles/internal/client.go b/providers/dns/artfiles/internal/client.go
deleted file mode 100644
index 61b350511..000000000
--- a/providers/dns/artfiles/internal/client.go
+++ /dev/null
@@ -1,133 +0,0 @@
-package internal
-
-import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "time"
-
- "github.com/go-acme/lego/v4/providers/dns/internal/errutils"
- "github.com/go-acme/lego/v4/providers/dns/internal/useragent"
-)
-
-const defaultBaseURL = "https://dcp.c.artfiles.de/api/"
-
-// Client the ArtFiles API client.
-type Client struct {
- username string
- password string
-
- BaseURL *url.URL
- HTTPClient *http.Client
-}
-
-// NewClient creates a new Client.
-func NewClient(username, password string) (*Client, error) {
- if username == "" || password == "" {
- return nil, errors.New("credentials missing")
- }
-
- baseURL, _ := url.Parse(defaultBaseURL)
-
- return &Client{
- username: username,
- password: password,
- BaseURL: baseURL,
- HTTPClient: &http.Client{Timeout: 10 * time.Second},
- }, nil
-}
-
-func (c *Client) GetDomains(ctx context.Context) ([]string, error) {
- endpoint := c.BaseURL.JoinPath("domain", "get_domains.html")
-
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
- if err != nil {
- return nil, fmt.Errorf("unable to create request: %w", err)
- }
-
- raw, err := c.do(req)
- if err != nil {
- return nil, err
- }
-
- return parseDomains(string(raw))
-}
-
-func (c *Client) GetRecords(ctx context.Context, domain string) (map[string]json.RawMessage, error) {
- endpoint := c.BaseURL.JoinPath("dns", "get_dns.html")
-
- query := endpoint.Query()
- query.Set("domain", domain)
-
- endpoint.RawQuery = query.Encode()
-
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
- if err != nil {
- return nil, fmt.Errorf("unable to create request: %w", err)
- }
-
- raw, err := c.do(req)
- if err != nil {
- return nil, err
- }
-
- var result Records
-
- err = json.Unmarshal(raw, &result)
- if err != nil {
- return nil, errutils.NewUnmarshalError(req, http.StatusOK, raw, err)
- }
-
- return result.Data, nil
-}
-
-func (c *Client) SetRecords(ctx context.Context, domain, rType string, value RecordValue) error {
- endpoint := c.BaseURL.JoinPath("dns", "set_dns.html")
-
- query := endpoint.Query()
- query.Set("domain", domain)
- query.Set(rType, value.String())
-
- endpoint.RawQuery = query.Encode()
-
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), nil)
- if err != nil {
- return fmt.Errorf("unable to create request: %w", err)
- }
-
- _, err = c.do(req)
-
- return err
-}
-
-func (c *Client) do(req *http.Request) ([]byte, error) {
- useragent.SetHeader(req.Header)
-
- req.SetBasicAuth(c.username, c.password)
-
- if req.Method == http.MethodPost {
- req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- }
-
- resp, err := c.HTTPClient.Do(req)
- if err != nil {
- return nil, errutils.NewHTTPDoError(req, err)
- }
-
- defer func() { _ = resp.Body.Close() }()
-
- raw, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil, errutils.NewReadResponseError(req, resp.StatusCode, err)
- }
-
- if resp.StatusCode/100 != 2 {
- return nil, errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
- }
-
- return raw, nil
-}
diff --git a/providers/dns/artfiles/internal/client_test.go b/providers/dns/artfiles/internal/client_test.go
deleted file mode 100644
index cc76f06f5..000000000
--- a/providers/dns/artfiles/internal/client_test.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package internal
-
-import (
- "encoding/json"
- "net/http/httptest"
- "net/url"
- "strconv"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func mockBuilder() *servermock.Builder[*Client] {
- return servermock.NewBuilder[*Client](
- func(server *httptest.Server) (*Client, error) {
- client, err := NewClient("user", "secret")
- if err != nil {
- return nil, err
- }
-
- client.BaseURL, _ = url.Parse(server.URL)
- client.HTTPClient = server.Client()
-
- return client, nil
- },
- servermock.CheckHeader().
- WithBasicAuth("user", "secret"),
- )
-}
-
-func TestClient_GetDomains(t *testing.T) {
- client := mockBuilder().
- Route("GET /domain/get_domains.html",
- servermock.ResponseFromFixture("domains.txt"),
- ).
- Build(t)
-
- zones, err := client.GetDomains(t.Context())
- require.NoError(t, err)
-
- expected := []string{"example.com", "example.org", "example.net"}
-
- assert.Equal(t, expected, zones)
-}
-
-func TestClient_GetRecords(t *testing.T) {
- client := mockBuilder().
- Route("GET /dns/get_dns.html",
- servermock.ResponseFromFixture("get_dns.json"),
- servermock.CheckQueryParameter().Strict().
- With("domain", "example.com"),
- ).
- Build(t)
-
- records, err := client.GetRecords(t.Context(), "example.com")
- require.NoError(t, err)
-
- expected := map[string]json.RawMessage{
- "A": json.RawMessage(strconv.Quote("sub1 1.2.3.4\nsub2 1.2.3.4\nsub3 1.2.3.4\nsub4 1.2.3.4\nsub5 1.2.3.4\nsub6 1.2.3.4\nsub7 1.2.3.4\nsub8 1.2.3.4\nsub9 1.2.3.4\nsub10 1.2.3.4\nsub11 1.2.3.4\nsub12 1.2.3.4\nsub13 1.2.3.4\nsub14 1.2.3.4\nsub15 1.2.3.4\nsub16 1.2.3.4\nsub17 1.2.3.4\nsub18 1.2.3.4\n@ 1.2.3.4")),
- "AAAA": json.RawMessage(strconv.Quote("")),
- "CAA": json.RawMessage(strconv.Quote("@ 128 iodef \"mailto:someone@example.tld\"\n@ 128 issue \"letsencrypt.org\"\n@ 128 issuewild \"letsencrypt.org\"")),
- "CName": json.RawMessage(strconv.Quote("some cname.to.example.tld.")),
- "MX": json.RawMessage(strconv.Quote("10 mail.example.tld.")),
- "SRV": json.RawMessage(strconv.Quote("_imap._tcp 0 0 0 .\n_imaps._tcp 0 1 993 mail.example.tld.\n_pop3._tcp 0 0 0 .\n_pop3s._tcp 0 0 0 .")),
- "TLSA": json.RawMessage(strconv.Quote("_25._tcp.mail.example.tld. 2 1 1 CBBC559B44D524D6A132BDAC672744DA3407F12AAE5D5F722C5F6C7913871C75\n_25._tcp.mail.example.tld. 2 1 1 885BF0572252C6741DC9A52F5044487FEF2A93B811CDEDFAD7624CC283B7CDD5\n_25._tcp.mail.example.tld. 2 1 1 F1440A9B76E1E41E53A4CB461329BF6337B419726BE513E42E19F1C691C5D4B2\n_465._tcp.mail.example.tld. 2 1 1 CBBC559B44D524D6A132BDAC672744DA3407F12AAE5D5F722C5F6C7913871C75\n_465._tcp.mail.example.tld. 2 1 1 885BF0572252C6741DC9A52F5044487FEF2A93B811CDEDFAD7624CC283B7CDD5\n_465._tcp.mail.example.tld. 2 1 1 F1440A9B76E1E41E53A4CB461329BF6337B419726BE513E42E19F1C691C5D4B2\n_587._tcp.mail.example.tld. 2 1 1 CBBC559B44D524D6A132BDAC672744DA3407F12AAE5D5F722C5F6C7913871C75\n_587._tcp.mail.example.tld. 2 1 1 885BF0572252C6741DC9A52F5044487FEF2A93B811CDEDFAD7624CC283B7CDD5\n_587._tcp.mail.example.tld. 2 1 1 F1440A9B76E1E41E53A4CB461329BF6337B419726BE513E42E19F1C691C5D4B2")),
- "TXT": json.RawMessage(strconv.Quote("_dmarc \"v=DMARC1;p=reject;sp=reject;adkim=r;aspf=r;pct=100;rua=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;ri=86400;ruf=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;fo=1;rf=afrf\"\n_mta-sts \"v=STSv1;id=yyyymmddTHHMMSS;\"\n_smtp._tls \"v=TLSRPTv1;rua=mailto:someone@in.mailhardener.com\"\n@ \"v=spf1 a mx ~all\"\nselector._domainkey \"v=DKIM1;k=rsa;p=Base64Stuff\" \"MoreBase64Stuff\" \"Even++MoreBase64Stuff\" \"YesMoreBase64Stuff\" \"And+Yes+Even+MoreBase64Stuff\" \"Sure++MoreBase64Stuff\" \"LastBase64Stuff\"\nselectorecc._domainkey \"v=DKIM1;k=ed25519;p=Base64Stuff\"\n_acme-challenge \"TheAcmeChallenge\"")),
- "TTL": json.RawMessage("3600"),
- "comment": json.RawMessage(strconv.Quote("TLSA RR:\nInfo -> https://dnssec-stats.ant.isi.edu/~viktor/x3hosts.html\nTest 1 -> https://stats.dnssec-tools.org/explore/?example.tld\nTest 2 -> https://dane.sys4.de/smtp/example.tld\n\nSMIMEA RR:\nGenerator -> https://www.smimea.info/smimea-generator.php\nTest -> https://www.smimea.info/smimea-test.php")),
- "nameserver": json.RawMessage(strconv.Quote("auth1.artfiles.de.\nauth2.artfiles.de.")),
- }
-
- assert.Equal(t, expected, records)
-}
-
-func TestClient_SetRecords(t *testing.T) {
- client := mockBuilder().
- Route("POST /dns/set_dns.html",
- servermock.ResponseFromFixture("set_dns.json"),
- servermock.CheckQueryParameter().Strict().
- With("TXT", "a b\nc \"d\"").
- With("domain", "example.com"),
- ).
- Build(t)
-
- err := client.SetRecords(t.Context(), "example.com", "TXT", RecordValue{"c": []string{`"d"`}, "a": []string{"b"}})
- require.NoError(t, err)
-}
diff --git a/providers/dns/artfiles/internal/fixtures/domains.txt b/providers/dns/artfiles/internal/fixtures/domains.txt
deleted file mode 100644
index b8a1247d2..000000000
--- a/providers/dns/artfiles/internal/fixtures/domains.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-example.com normal 2026-10-01 2017-09-18 163477
-example.org normal 2026-08-01 2016-07-07 156216
-example.net normal 2026-07-01 2017-06-06 162462
diff --git a/providers/dns/artfiles/internal/fixtures/get_dns.json b/providers/dns/artfiles/internal/fixtures/get_dns.json
deleted file mode 100644
index fa672e0e1..000000000
--- a/providers/dns/artfiles/internal/fixtures/get_dns.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "data": {
- "SRV": "_imap._tcp 0 0 0 .\n_imaps._tcp 0 1 993 mail.example.tld.\n_pop3._tcp 0 0 0 .\n_pop3s._tcp 0 0 0 .",
- "AAAA": "",
- "MX": "10 mail.example.tld.",
- "CAA": "@ 128 iodef \"mailto:someone@example.tld\"\n@ 128 issue \"letsencrypt.org\"\n@ 128 issuewild \"letsencrypt.org\"",
- "TTL": 3600,
- "comment": "TLSA RR:\nInfo -> https://dnssec-stats.ant.isi.edu/~viktor/x3hosts.html\nTest 1 -> https://stats.dnssec-tools.org/explore/?example.tld\nTest 2 -> https://dane.sys4.de/smtp/example.tld\n\nSMIMEA RR:\nGenerator -> https://www.smimea.info/smimea-generator.php\nTest -> https://www.smimea.info/smimea-test.php",
- "TXT": "_dmarc \"v=DMARC1;p=reject;sp=reject;adkim=r;aspf=r;pct=100;rua=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;ri=86400;ruf=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;fo=1;rf=afrf\"\n_mta-sts \"v=STSv1;id=yyyymmddTHHMMSS;\"\n_smtp._tls \"v=TLSRPTv1;rua=mailto:someone@in.mailhardener.com\"\n@ \"v=spf1 a mx ~all\"\nselector._domainkey \"v=DKIM1;k=rsa;p=Base64Stuff\" \"MoreBase64Stuff\" \"Even++MoreBase64Stuff\" \"YesMoreBase64Stuff\" \"And+Yes+Even+MoreBase64Stuff\" \"Sure++MoreBase64Stuff\" \"LastBase64Stuff\"\nselectorecc._domainkey \"v=DKIM1;k=ed25519;p=Base64Stuff\"\n_acme-challenge \"TheAcmeChallenge\"",
- "A": "sub1 1.2.3.4\nsub2 1.2.3.4\nsub3 1.2.3.4\nsub4 1.2.3.4\nsub5 1.2.3.4\nsub6 1.2.3.4\nsub7 1.2.3.4\nsub8 1.2.3.4\nsub9 1.2.3.4\nsub10 1.2.3.4\nsub11 1.2.3.4\nsub12 1.2.3.4\nsub13 1.2.3.4\nsub14 1.2.3.4\nsub15 1.2.3.4\nsub16 1.2.3.4\nsub17 1.2.3.4\nsub18 1.2.3.4\n@ 1.2.3.4",
- "nameserver": "auth1.artfiles.de.\nauth2.artfiles.de.",
- "CName": "some cname.to.example.tld.",
- "TLSA": "_25._tcp.mail.example.tld. 2 1 1 CBBC559B44D524D6A132BDAC672744DA3407F12AAE5D5F722C5F6C7913871C75\n_25._tcp.mail.example.tld. 2 1 1 885BF0572252C6741DC9A52F5044487FEF2A93B811CDEDFAD7624CC283B7CDD5\n_25._tcp.mail.example.tld. 2 1 1 F1440A9B76E1E41E53A4CB461329BF6337B419726BE513E42E19F1C691C5D4B2\n_465._tcp.mail.example.tld. 2 1 1 CBBC559B44D524D6A132BDAC672744DA3407F12AAE5D5F722C5F6C7913871C75\n_465._tcp.mail.example.tld. 2 1 1 885BF0572252C6741DC9A52F5044487FEF2A93B811CDEDFAD7624CC283B7CDD5\n_465._tcp.mail.example.tld. 2 1 1 F1440A9B76E1E41E53A4CB461329BF6337B419726BE513E42E19F1C691C5D4B2\n_587._tcp.mail.example.tld. 2 1 1 CBBC559B44D524D6A132BDAC672744DA3407F12AAE5D5F722C5F6C7913871C75\n_587._tcp.mail.example.tld. 2 1 1 885BF0572252C6741DC9A52F5044487FEF2A93B811CDEDFAD7624CC283B7CDD5\n_587._tcp.mail.example.tld. 2 1 1 F1440A9B76E1E41E53A4CB461329BF6337B419726BE513E42E19F1C691C5D4B2"
- },
- "status": "OK"
-}
diff --git a/providers/dns/artfiles/internal/fixtures/set_dns.json b/providers/dns/artfiles/internal/fixtures/set_dns.json
deleted file mode 100644
index 7cacb33e5..000000000
--- a/providers/dns/artfiles/internal/fixtures/set_dns.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "status": "OK",
- "error": ""
-}
diff --git a/providers/dns/artfiles/internal/fixtures/txt_record-multiple.txt b/providers/dns/artfiles/internal/fixtures/txt_record-multiple.txt
deleted file mode 100644
index 461489c77..000000000
--- a/providers/dns/artfiles/internal/fixtures/txt_record-multiple.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-_dmarc "v=DMARC1;p=reject;sp=reject;adkim=r;aspf=r;pct=100;rua=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;ri=86400;ruf=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;fo=1;rf=afrf"
-_mta-sts "v=STSv1;id=yyyymmddTHHMMSS;"
-_smtp._tls "v=TLSRPTv1;rua=mailto:someone@in.mailhardener.com"
-@ "v=spf1 a mx ~all"
-selector._domainkey "v=DKIM1;k=rsa;p=Base64Stuff" "MoreBase64Stuff" "Even++MoreBase64Stuff" "YesMoreBase64Stuff" "And+Yes+Even+MoreBase64Stuff" "Sure++MoreBase64Stuff" "LastBase64Stuff"
-selectorecc._domainkey "v=DKIM1;k=ed25519;p=Base64Stuff"
-_acme-challenge "xxx"
-_acme-challenge "yyy"
diff --git a/providers/dns/artfiles/internal/fixtures/txt_record.txt b/providers/dns/artfiles/internal/fixtures/txt_record.txt
deleted file mode 100644
index 5a6259b14..000000000
--- a/providers/dns/artfiles/internal/fixtures/txt_record.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-_dmarc "v=DMARC1;p=reject;sp=reject;adkim=r;aspf=r;pct=100;rua=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;ri=86400;ruf=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;fo=1;rf=afrf"
-_mta-sts "v=STSv1;id=yyyymmddTHHMMSS;"
-_smtp._tls "v=TLSRPTv1;rua=mailto:someone@in.mailhardener.com"
-@ "v=spf1 a mx ~all"
-selector._domainkey "v=DKIM1;k=rsa;p=Base64Stuff" "MoreBase64Stuff" "Even++MoreBase64Stuff" "YesMoreBase64Stuff" "And+Yes+Even+MoreBase64Stuff" "Sure++MoreBase64Stuff" "LastBase64Stuff"
-selectorecc._domainkey "v=DKIM1;k=ed25519;p=Base64Stuff"
-_acme-challenge "TheAcmeChallenge"
diff --git a/providers/dns/artfiles/internal/types.go b/providers/dns/artfiles/internal/types.go
deleted file mode 100644
index c70ab34da..000000000
--- a/providers/dns/artfiles/internal/types.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package internal
-
-import (
- "encoding/csv"
- "encoding/json"
- "errors"
- "io"
- "maps"
- "slices"
- "strconv"
- "strings"
- "unicode"
-)
-
-type Records struct {
- Data map[string]json.RawMessage `json:"data"`
- Status string `json:"status"`
-}
-
-type RecordValue map[string][]string
-
-func (r RecordValue) Set(key, value string) {
- r[key] = []string{strconv.Quote(value)}
-}
-
-func (r RecordValue) Add(key, value string) {
- r[key] = append(r[key], strconv.Quote(value))
-}
-
-func (r RecordValue) Delete(key string) {
- delete(r, key)
-}
-
-func (r RecordValue) RemoveValue(key, value string) {
- if len(r[key]) == 0 {
- return
- }
-
- quotedValue := strconv.Quote(value)
-
- var data []string
-
- for _, s := range r[key] {
- if s != quotedValue {
- data = append(data, s)
- }
- }
-
- r[key] = data
-
- if len(r[key]) == 0 {
- r.Delete(key)
- }
-}
-
-func (r RecordValue) String() string {
- var parts []string
-
- for _, key := range slices.Sorted(maps.Keys(r)) {
- for _, s := range r[key] {
- parts = append(parts, key+" "+s)
- }
- }
-
- return strings.Join(parts, "\n")
-}
-
-func ParseRecordValue(lines string) RecordValue {
- data := make(RecordValue)
-
- for line := range strings.Lines(lines) {
- line = strings.TrimSpace(line)
-
- idx := strings.IndexFunc(line, unicode.IsSpace)
-
- data[line[:idx]] = append(data[line[:idx]], line[idx+1:])
- }
-
- return data
-}
-
-func parseDomains(input string) ([]string, error) {
- reader := csv.NewReader(strings.NewReader(input))
- reader.Comma = '\t'
- reader.TrimLeadingSpace = true
- reader.LazyQuotes = true
-
- var data []string
-
- for {
- record, err := reader.Read()
- if errors.Is(err, io.EOF) {
- break
- }
-
- if err != nil {
- return nil, err
- }
-
- if len(record) < 1 {
- // Malformed line
- continue
- }
-
- data = append(data, record[0])
- }
-
- return data, nil
-}
diff --git a/providers/dns/artfiles/internal/types_test.go b/providers/dns/artfiles/internal/types_test.go
deleted file mode 100644
index 3b219f39f..000000000
--- a/providers/dns/artfiles/internal/types_test.go
+++ /dev/null
@@ -1,183 +0,0 @@
-package internal
-
-import (
- "os"
- "path/filepath"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestRecordValue_Set(t *testing.T) {
- rv := make(RecordValue)
-
- rv.Set("a", "1")
- rv.Set("b", "2")
- rv.Set("b", "3")
-
- assert.Equal(t, "a \"1\"\nb \"3\"", rv.String())
-}
-
-func TestRecordValue_Add(t *testing.T) {
- rv := make(RecordValue)
-
- rv.Add("a", "1")
- rv.Add("b", "2")
- rv.Add("b", "3")
-
- assert.Equal(t, "a \"1\"\nb \"2\"\nb \"3\"", rv.String())
-}
-
-func TestRecordValue_Delete(t *testing.T) {
- rv := make(RecordValue)
-
- rv.Set("a", "1")
- rv.Add("b", "2")
-
- rv.Delete("b")
-
- assert.Equal(t, "a \"1\"", rv.String())
-}
-
-func TestRecordValue_RemoveValue(t *testing.T) {
- testCases := []struct {
- desc string
- data map[string][]string
- toRemove map[string][]string
- expected string
- }{
- {
- desc: "remove the only value",
- data: map[string][]string{
- "a": {"1"},
- },
- toRemove: map[string][]string{
- "a": {"1"},
- },
- expected: ``,
- },
- {
- desc: "remove value in the middle",
- data: map[string][]string{
- "a": {"1", "2", "3"},
- },
- toRemove: map[string][]string{
- "a": {"2"},
- },
- expected: "a \"1\"\na \"3\"",
- },
- {
- desc: "remove value at the beginning",
- data: map[string][]string{
- "a": {"1", "2", "3"},
- },
- toRemove: map[string][]string{
- "a": {"1"},
- },
- expected: "a \"2\"\na \"3\"",
- },
- {
- desc: "remove value at the end",
- data: map[string][]string{
- "a": {"1", "2", "3"},
- },
- toRemove: map[string][]string{
- "a": {"3"},
- },
- expected: "a \"1\"\na \"2\"",
- },
- {
- desc: "remove all (delete)",
- data: map[string][]string{
- "a": {"1", "2", "3"},
- },
- toRemove: map[string][]string{
- "a": {"1", "2", "3"},
- },
- expected: ``,
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- rv := make(RecordValue)
-
- for k, values := range test.data {
- for _, v := range values {
- rv.Add(k, v)
- }
- }
-
- for k, values := range test.toRemove {
- for _, v := range values {
- rv.RemoveValue(k, v)
- }
- }
-
- assert.Equal(t, test.expected, rv.String())
- })
- }
-}
-
-func TestParseRecordValue(t *testing.T) {
- testCases := []struct {
- desc string
- filename string
- expected RecordValue
- }{
- {
- desc: "simple",
- filename: "txt_record.txt",
- expected: RecordValue{
- "@": []string{"\"v=spf1 a mx ~all\""},
- "_acme-challenge": []string{"\"TheAcmeChallenge\""},
- "_dmarc": []string{"\"v=DMARC1;p=reject;sp=reject;adkim=r;aspf=r;pct=100;rua=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;ri=86400;ruf=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;fo=1;rf=afrf\""},
- "_mta-sts": []string{"\"v=STSv1;id=yyyymmddTHHMMSS;\""},
- "_smtp._tls": []string{"\"v=TLSRPTv1;rua=mailto:someone@in.mailhardener.com\""},
- "selector._domainkey": []string{"\"v=DKIM1;k=rsa;p=Base64Stuff\" \"MoreBase64Stuff\" \"Even++MoreBase64Stuff\" \"YesMoreBase64Stuff\" \"And+Yes+Even+MoreBase64Stuff\" \"Sure++MoreBase64Stuff\" \"LastBase64Stuff\""},
- "selectorecc._domainkey": []string{"\"v=DKIM1;k=ed25519;p=Base64Stuff\""},
- },
- },
- {
- desc: "multiple values with the same key",
- filename: "txt_record-multiple.txt",
- expected: RecordValue{
- "@": []string{"\"v=spf1 a mx ~all\""},
- "_acme-challenge": []string{"\"xxx\"", "\"yyy\""},
- "_dmarc": []string{"\"v=DMARC1;p=reject;sp=reject;adkim=r;aspf=r;pct=100;rua=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;ri=86400;ruf=mailto:someone@in.mailhardener.com,mailto:postmaster@example.tld;fo=1;rf=afrf\""},
- "_mta-sts": []string{"\"v=STSv1;id=yyyymmddTHHMMSS;\""},
- "_smtp._tls": []string{"\"v=TLSRPTv1;rua=mailto:someone@in.mailhardener.com\""},
- "selector._domainkey": []string{"\"v=DKIM1;k=rsa;p=Base64Stuff\" \"MoreBase64Stuff\" \"Even++MoreBase64Stuff\" \"YesMoreBase64Stuff\" \"And+Yes+Even+MoreBase64Stuff\" \"Sure++MoreBase64Stuff\" \"LastBase64Stuff\""},
- "selectorecc._domainkey": []string{"\"v=DKIM1;k=ed25519;p=Base64Stuff\""},
- },
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- file, err := os.ReadFile(filepath.Join("fixtures", test.filename))
- require.NoError(t, err)
-
- data := ParseRecordValue(string(file))
-
- assert.Equal(t, test.expected, data)
- })
- }
-}
-
-func Test_parseDomains(t *testing.T) {
- file, err := os.ReadFile(filepath.FromSlash("./fixtures/domains.txt"))
- require.NoError(t, err)
-
- domains, err := parseDomains(string(file))
- require.NoError(t, err)
-
- expected := []string{"example.com", "example.org", "example.net"}
-
- assert.Equal(t, expected, domains)
-}
diff --git a/providers/dns/azure/azure.go b/providers/dns/azure/azure.go
index 8bfc6cfe1..fd00bcbe2 100644
--- a/providers/dns/azure/azure.go
+++ b/providers/dns/azure/azure.go
@@ -8,7 +8,6 @@ import (
"io"
"net/http"
"net/url"
- "strings"
"time"
"github.com/Azure/go-autorest/autorest"
@@ -38,8 +37,6 @@ const (
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
)
-const EnvLegoAzureBypassDeprecation = "LEGO_AZURE_BYPASS_DEPRECATION"
-
const defaultMetadataEndpoint = "http://169.254.169.254"
var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
@@ -136,18 +133,6 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
return nil, errors.New("azure: the configuration of the DNS provider is nil")
}
- if !env.GetOrDefaultBool(EnvLegoAzureBypassDeprecation, false) {
- var msg strings.Builder
-
- msg.WriteString("azure: ")
- msg.WriteString("The `azure` provider has been deprecated since 2023, and replaced by `azuredns` provider. ")
- msg.WriteString("It can be TEMPORARILY reactivated by using the environment variable `LEGO_AZURE_BYPASS_DEPRECATION=true`. ")
- msg.WriteString("The `azure` provider will be removed in a future release, please migrate to the `azuredns` provider. ")
- msg.WriteString("The documentation of the `azuredns` provider can be found at https://go-acme.github.io/lego/dns/azuredns/")
-
- return nil, errors.New(msg.String())
- }
-
if config.HTTPClient == nil {
config.HTTPClient = &http.Client{Timeout: 5 * time.Second}
}
diff --git a/providers/dns/azure/azure_test.go b/providers/dns/azure/azure_test.go
index c4fec4359..44fb81eef 100644
--- a/providers/dns/azure/azure_test.go
+++ b/providers/dns/azure/azure_test.go
@@ -14,7 +14,6 @@ import (
const envDomain = envNamespace + "DOMAIN"
var envTest = tester.NewEnvTest(
- EnvLegoAzureBypassDeprecation,
EnvEnvironment,
EnvClientID,
EnvClientSecret,
@@ -58,8 +57,6 @@ func TestNewDNSProvider(t *testing.T) {
envTest.ClearEnv()
- test.envVars[EnvLegoAzureBypassDeprecation] = "true"
-
envTest.Apply(test.envVars)
p, err := NewDNSProvider()
@@ -143,11 +140,6 @@ func TestNewDNSProviderConfig(t *testing.T) {
},
}
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
- envTest.Apply(map[string]string{EnvLegoAzureBypassDeprecation: "true"})
-
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
config := NewDefaultConfig()
diff --git a/providers/dns/binarylane/internal/types.go b/providers/dns/binarylane/internal/types.go
index 06d4be5c0..987e5c356 100644
--- a/providers/dns/binarylane/internal/types.go
+++ b/providers/dns/binarylane/internal/types.go
@@ -15,12 +15,12 @@ type APIError struct {
}
func (a *APIError) Error() string {
- msg := new(strings.Builder)
+ var msg strings.Builder
- _, _ = fmt.Fprintf(msg, "%d: %s: %s: %s: %s", a.Status, a.Type, a.Title, a.Detail, a.Instance)
+ msg.WriteString(fmt.Sprintf("%d: %s: %s: %s: %s", a.Status, a.Type, a.Title, a.Detail, a.Instance))
for s, values := range a.Errors {
- _, _ = fmt.Fprintf(msg, ": %s: %s", s, strings.Join(values, ", "))
+ msg.WriteString(fmt.Sprintf(": %s: %s", s, strings.Join(values, ", ")))
}
return msg.String()
diff --git a/providers/dns/bluecatv2/bluecatv2.go b/providers/dns/bluecatv2/bluecatv2.go
deleted file mode 100644
index 0efe99661..000000000
--- a/providers/dns/bluecatv2/bluecatv2.go
+++ /dev/null
@@ -1,249 +0,0 @@
-// Package bluecatv2 implements a DNS provider for solving the DNS-01 challenge using Bluecat v2.
-package bluecatv2
-
-import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "sync"
- "time"
-
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/platform/config/env"
- "github.com/go-acme/lego/v4/providers/dns/bluecatv2/internal"
- "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
-)
-
-// Environment variables names.
-const (
- envNamespace = "BLUECATV2_"
-
- EnvServerURL = envNamespace + "SERVER_URL"
- EnvUsername = envNamespace + "USERNAME"
- EnvPassword = envNamespace + "PASSWORD"
- EnvConfigName = envNamespace + "CONFIG_NAME"
- EnvViewName = envNamespace + "VIEW_NAME"
- EnvSkipDeploy = envNamespace + "SKIP_DEPLOY"
-
- EnvTTL = envNamespace + "TTL"
- EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
- EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
- EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
-)
-
-// Config is used to configure the creation of the DNSProvider.
-type Config struct {
- ServerURL string
- Username string
- Password string
- ConfigName string
- ViewName string
- SkipDeploy bool
-
- PropagationTimeout time.Duration
- PollingInterval time.Duration
- TTL int
- HTTPClient *http.Client
-}
-
-// NewDefaultConfig returns a default configuration for the DNSProvider.
-func NewDefaultConfig() *Config {
- return &Config{
- SkipDeploy: env.GetOrDefaultBool(EnvSkipDeploy, false),
-
- TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
- PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
- PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
- HTTPClient: &http.Client{
- Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
- },
- }
-}
-
-// DNSProvider implements the challenge.Provider interface.
-type DNSProvider struct {
- config *Config
- client *internal.Client
-
- zoneIDs map[string]int64
- recordIDs map[string]int64
- recordIDsMu sync.Mutex
-}
-
-// NewDNSProvider returns a DNSProvider instance configured for Bluecat v2.
-func NewDNSProvider() (*DNSProvider, error) {
- values, err := env.Get(EnvServerURL, EnvUsername, EnvPassword, EnvConfigName, EnvViewName)
- if err != nil {
- return nil, fmt.Errorf("bluecatv2: %w", err)
- }
-
- config := NewDefaultConfig()
- config.ServerURL = values[EnvServerURL]
- config.Username = values[EnvUsername]
- config.Password = values[EnvPassword]
- config.ConfigName = values[EnvConfigName]
- config.ViewName = values[EnvViewName]
-
- return NewDNSProviderConfig(config)
-}
-
-// NewDNSProviderConfig return a DNSProvider instance configured for Bluecat v2.
-func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
- if config == nil {
- return nil, errors.New("bluecatv2: the configuration of the DNS provider is nil")
- }
-
- if config.ServerURL == "" {
- return nil, errors.New("bluecatv2: missing server URL")
- }
-
- if config.ConfigName == "" {
- return nil, errors.New("bluecatv2: missing configuration name")
- }
-
- if config.ViewName == "" {
- return nil, errors.New("bluecatv2: missing view name")
- }
-
- client, err := internal.NewClient(config.ServerURL, config.Username, config.Password)
- if err != nil {
- return nil, fmt.Errorf("bluecatv2: %w", err)
- }
-
- if config.HTTPClient != nil {
- client.HTTPClient = config.HTTPClient
- }
-
- client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
-
- return &DNSProvider{
- config: config,
- client: client,
- recordIDs: make(map[string]int64),
- zoneIDs: make(map[string]int64),
- }, nil
-}
-
-// Present creates a TXT record using the specified parameters.
-func (d *DNSProvider) Present(domain, token, keyAuth string) error {
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- ctx, err := d.client.CreateAuthenticatedContext(context.Background())
- if err != nil {
- return fmt.Errorf("bluecatv2: %w", err)
- }
-
- zone, err := d.findZone(ctx, info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("bluecatv2: %w", err)
- }
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone.AbsoluteName)
- if err != nil {
- return fmt.Errorf("bluecatv2: %w", err)
- }
-
- record := internal.RecordTXT{
- CommonResource: internal.CommonResource{
- Type: "TXTRecord",
- Name: subDomain,
- },
- Text: info.Value,
- TTL: d.config.TTL,
- RecordType: "TXT",
- }
-
- newRecord, err := d.client.CreateZoneResourceRecord(ctx, zone.ID, record)
- if err != nil {
- return fmt.Errorf("bluecatv2: create resource record: %w", err)
- }
-
- d.recordIDsMu.Lock()
- d.zoneIDs[token] = zone.ID
- d.recordIDs[token] = newRecord.ID
- d.recordIDsMu.Unlock()
-
- if d.config.SkipDeploy {
- return nil
- }
-
- _, err = d.client.CreateZoneDeployment(ctx, zone.ID)
- if err != nil {
- return fmt.Errorf("bluecat: deploy zone: %w", err)
- }
-
- return nil
-}
-
-// CleanUp removes the TXT record matching the specified parameters.
-func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- d.recordIDsMu.Lock()
- recordID, recordOK := d.recordIDs[token]
- zoneID, zoneOK := d.zoneIDs[token]
- d.recordIDsMu.Unlock()
-
- if !recordOK {
- return fmt.Errorf("bluecatv2: unknown record ID for '%s' '%s'", info.EffectiveFQDN, token)
- }
-
- if !zoneOK {
- return fmt.Errorf("bluecatv2: unknown zone ID for '%s' '%s'", info.EffectiveFQDN, token)
- }
-
- ctx, err := d.client.CreateAuthenticatedContext(context.Background())
- if err != nil {
- return fmt.Errorf("bluecatv2: %w", err)
- }
-
- err = d.client.DeleteResourceRecord(ctx, recordID)
- if err != nil {
- return fmt.Errorf("bluecatv2: delete resource record: %w", err)
- }
-
- if d.config.SkipDeploy {
- return nil
- }
-
- _, err = d.client.CreateZoneDeployment(ctx, zoneID)
- if err != nil {
- return fmt.Errorf("bluecat: deploy zone: %w", err)
- }
-
- return nil
-}
-
-// Timeout returns the timeout and interval to use when checking for DNS propagation.
-// Adjusting here to cope with spikes in propagation times.
-func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
- return d.config.PropagationTimeout, d.config.PollingInterval
-}
-
-func (d *DNSProvider) findZone(ctx context.Context, fqdn string) (*internal.ZoneResource, error) {
- for name := range dns01.UnFqdnDomainsSeq(fqdn) {
- opts := &internal.CollectionOptions{
- Fields: "id,absoluteName,configuration.id,configuration.name,view.id,view.name",
- Filter: internal.And(
- internal.Eq("absoluteName", name),
- internal.Eq("configuration.name", d.config.ConfigName),
- internal.Eq("view.name", d.config.ViewName),
- ).String(),
- }
-
- zones, err := d.client.RetrieveZones(ctx, opts)
- if err != nil {
- // TODO(ldez) maybe add a log in v5.
- continue
- }
-
- for _, zone := range zones {
- if zone.AbsoluteName == name {
- return &zone, nil
- }
- }
- }
-
- return nil, fmt.Errorf("no zone found for fqdn: %s", fqdn)
-}
diff --git a/providers/dns/bluecatv2/bluecatv2.toml b/providers/dns/bluecatv2/bluecatv2.toml
deleted file mode 100644
index 6ec3781c6..000000000
--- a/providers/dns/bluecatv2/bluecatv2.toml
+++ /dev/null
@@ -1,33 +0,0 @@
-Name = "Bluecat v2"
-Description = ''''''
-URL = "https://www.bluecatnetworks.com"
-Code = "bluecatv2"
-Since = "v4.32.0"
-
-Example = '''
-BLUECATV2_SERVER_URL="https://example.com" \
-BLUECATV2_USERNAME="xxx" \
-BLUECATV2_PASSWORD="yyy" \
-BLUECATV2_CONFIG_NAME="myConfiguration" \
-BLUECATV2_VIEW_NAME="myView" \
-lego --dns bluecatv2 -d '*.example.com' -d example.com run
-'''
-
-[Configuration]
- [Configuration.Credentials]
- BLUECAT_SERVER_URL = "The server URL: it should have a scheme, hostname, and port (if required) of the authoritative Bluecat BAM serve"
- BLUECATV2_USERNAME = "API username"
- BLUECATV2_PASSWORD = "API password"
- BLUECATV2_CONFIG_NAME = "Configuration name"
- BLUECATV2_VIEW_NAME = "DNS View Name"
- [Configuration.Additional]
- BLUECATV2_SKIP_DEPLOY = "Skip quick deployements"
- BLUECATV2_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
- BLUECATV2_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 60)"
- BLUECATV2_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)"
- BLUECATV2_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
-
-[Links]
- API = "https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Introduction/9.6.0"
- Swagger = "http://{Address_Manager_IP}/api/openapi.json"
- SwaggerDump = "https://github.com/go-acme/lego/discussions/2218#discussioncomment-13060545"
diff --git a/providers/dns/bluecatv2/bluecatv2_test.go b/providers/dns/bluecatv2/bluecatv2_test.go
deleted file mode 100644
index d852f0e18..000000000
--- a/providers/dns/bluecatv2/bluecatv2_test.go
+++ /dev/null
@@ -1,414 +0,0 @@
-package bluecatv2
-
-import (
- "net/http"
- "net/http/httptest"
- "strings"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester"
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/go-acme/lego/v4/providers/dns/bluecatv2/internal"
- "github.com/stretchr/testify/require"
-)
-
-const envDomain = envNamespace + "DOMAIN"
-
-var envTest = tester.NewEnvTest(
- EnvServerURL,
- EnvUsername,
- EnvPassword,
- EnvConfigName,
- EnvViewName,
- EnvSkipDeploy,
-).WithDomain(envDomain)
-
-func TestNewDNSProvider(t *testing.T) {
- testCases := []struct {
- desc string
- envVars map[string]string
- expected string
- }{
- {
- desc: "success",
- envVars: map[string]string{
- EnvServerURL: "https://example.com/",
- EnvUsername: "userA",
- EnvPassword: "secret",
- EnvConfigName: "myConfig",
- EnvViewName: "myView",
- },
- },
- {
- desc: "missing server URL",
- envVars: map[string]string{
- EnvServerURL: "",
- EnvUsername: "userA",
- EnvPassword: "secret",
- EnvConfigName: "myConfig",
- EnvViewName: "myView",
- },
- expected: "bluecatv2: some credentials information are missing: BLUECATV2_SERVER_URL",
- },
- {
- desc: "missing username",
- envVars: map[string]string{
- EnvServerURL: "https://example.com/",
- EnvUsername: "",
- EnvPassword: "secret",
- EnvConfigName: "myConfig",
- EnvViewName: "myView",
- },
- expected: "bluecatv2: some credentials information are missing: BLUECATV2_USERNAME",
- },
- {
- desc: "missing password",
- envVars: map[string]string{
- EnvServerURL: "https://example.com/",
- EnvUsername: "userA",
- EnvPassword: "",
- EnvConfigName: "myConfig",
- EnvViewName: "myView",
- },
- expected: "bluecatv2: some credentials information are missing: BLUECATV2_PASSWORD",
- },
- {
- desc: "missing configuration name",
- envVars: map[string]string{
- EnvServerURL: "https://example.com/",
- EnvUsername: "userA",
- EnvPassword: "secret",
- EnvConfigName: "",
- EnvViewName: "myView",
- },
- expected: "bluecatv2: some credentials information are missing: BLUECATV2_CONFIG_NAME",
- },
- {
- desc: "missing view name",
- envVars: map[string]string{
- EnvServerURL: "https://example.com/",
- EnvUsername: "userA",
- EnvPassword: "secret",
- EnvConfigName: "myConfig",
- EnvViewName: "",
- },
- expected: "bluecatv2: some credentials information are missing: BLUECATV2_VIEW_NAME",
- },
- {
- desc: "missing credentials",
- envVars: map[string]string{},
- expected: "bluecatv2: some credentials information are missing: BLUECATV2_SERVER_URL,BLUECATV2_USERNAME,BLUECATV2_PASSWORD,BLUECATV2_CONFIG_NAME,BLUECATV2_VIEW_NAME",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(test.envVars)
-
- p, err := NewDNSProvider()
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestNewDNSProviderConfig(t *testing.T) {
- testCases := []struct {
- desc string
- serverURL string
- username string
- password string
- configName string
- viewName string
- expected string
- }{
- {
- desc: "success",
- serverURL: "https://example.com/",
- username: "userA",
- password: "secret",
- configName: "myConfig",
- viewName: "myView",
- },
- {
- desc: "missing server URL",
- username: "userA",
- password: "secret",
- configName: "myConfig",
- viewName: "myView",
- expected: "bluecatv2: missing server URL",
- },
- {
- desc: "missing username",
- serverURL: "https://example.com/",
- password: "secret",
- configName: "myConfig",
- viewName: "myView",
- expected: "bluecatv2: credentials missing",
- },
- {
- desc: "missing password",
- serverURL: "https://example.com/",
- username: "userA",
- configName: "myConfig",
- viewName: "myView",
- expected: "bluecatv2: credentials missing",
- },
- {
- desc: "missing configuration name",
- serverURL: "https://example.com/",
- username: "userA",
- password: "secret",
- viewName: "myView",
- expected: "bluecatv2: missing configuration name",
- },
- {
- desc: "missing view name",
- serverURL: "https://example.com/",
- username: "userA",
- password: "secret",
- configName: "myConfig",
- expected: "bluecatv2: missing view name",
- },
- {
- desc: "missing credentials",
- expected: "bluecatv2: missing server URL",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- config := NewDefaultConfig()
- config.ServerURL = test.serverURL
- config.Username = test.username
- config.Password = test.password
- config.ConfigName = test.configName
- config.ViewName = test.viewName
-
- p, err := NewDNSProviderConfig(config)
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestLivePresent(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.Present(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func TestLiveCleanUp(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func mockBuilder() *servermock.Builder[*DNSProvider] {
- return servermock.NewBuilder(
- func(server *httptest.Server) (*DNSProvider, error) {
- config := NewDefaultConfig()
-
- config.ServerURL = server.URL
- config.Username = "userA"
- config.Password = "secret"
- config.ConfigName = "myConfiguration"
- config.ViewName = "myView"
-
- config.HTTPClient = server.Client()
-
- p, err := NewDNSProviderConfig(config)
- if err != nil {
- return nil, err
- }
-
- return p, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders(),
- )
-}
-
-func TestDNSProvider_Present(t *testing.T) {
- provider := mockBuilder().
- Route("POST /api/v2/sessions",
- servermock.ResponseFromInternal("postSession.json"),
- servermock.CheckRequestJSONBodyFromInternal("postSession-request.json"),
- ).
- Route("GET /api/v2/configurations",
- servermock.ResponseFromInternal("configurations.json"),
- servermock.CheckQueryParameter().Strict().
- With("filter", "name:eq('myConfiguration')"),
- ).
- Route("GET /api/v2/configurations/12345/views",
- servermock.ResponseFromInternal("views.json"),
- servermock.CheckQueryParameter().Strict().
- With("filter", "name:eq('myView')"),
- ).
- Route("GET /api/v2/zones",
- http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
- filter := req.URL.Query().Get("filter")
-
- if strings.Contains(filter, internal.Eq("absoluteName", "example.com").String()) {
- servermock.ResponseFromInternal("zones.json").ServeHTTP(rw, req)
-
- return
- }
-
- servermock.ResponseFromInternal("error.json").
- WithStatusCode(http.StatusNotFound).ServeHTTP(rw, req)
- }),
- ).
- Route("POST /api/v2/zones/12345/resourceRecords",
- servermock.ResponseFromInternal("postZoneResourceRecord.json"),
- servermock.CheckRequestJSONBodyFromInternal("postZoneResourceRecord-request.json"),
- ).
- Route("POST /api/v2/zones/12345/deployments",
- servermock.ResponseFromInternal("postZoneDeployment.json").
- WithStatusCode(http.StatusCreated),
- servermock.CheckRequestJSONBodyFromInternal("postZoneDeployment-request.json"),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_Present_skipDeploy(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(map[string]string{
- EnvSkipDeploy: "true",
- })
-
- provider := mockBuilder().
- Route("POST /api/v2/sessions",
- servermock.ResponseFromInternal("postSession.json"),
- servermock.CheckRequestJSONBodyFromInternal("postSession-request.json"),
- ).
- Route("GET /api/v2/configurations",
- servermock.ResponseFromInternal("configurations.json"),
- servermock.CheckQueryParameter().Strict().
- With("filter", "name:eq('myConfiguration')"),
- ).
- Route("GET /api/v2/configurations/12345/views",
- servermock.ResponseFromInternal("views.json"),
- servermock.CheckQueryParameter().Strict().
- With("filter", "name:eq('myView')"),
- ).
- Route("GET /api/v2/zones",
- http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
- filter := req.URL.Query().Get("filter")
-
- if strings.Contains(filter, internal.Eq("absoluteName", "example.com").String()) {
- servermock.ResponseFromInternal("zones.json").ServeHTTP(rw, req)
-
- return
- }
-
- servermock.ResponseFromInternal("error.json").
- WithStatusCode(http.StatusNotFound).ServeHTTP(rw, req)
- }),
- ).
- Route("POST /api/v2/zones/12345/resourceRecords",
- servermock.ResponseFromInternal("postZoneResourceRecord.json"),
- servermock.CheckRequestJSONBodyFromInternal("postZoneResourceRecord-request.json"),
- ).
- Route("POST /api/v2/zones/456789/deployments",
- servermock.Noop().
- WithStatusCode(http.StatusUnauthorized),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp(t *testing.T) {
- provider := mockBuilder().
- Route("POST /api/v2/sessions",
- servermock.ResponseFromInternal("postSession.json"),
- servermock.CheckRequestJSONBodyFromInternal("postSession-request.json"),
- ).
- Route("DELETE /api/v2/resourceRecords/12345",
- servermock.ResponseFromInternal("deleteResourceRecord.json"),
- ).
- Route("POST /api/v2/zones/456789/deployments",
- servermock.ResponseFromInternal("postZoneDeployment.json").
- WithStatusCode(http.StatusCreated),
- servermock.CheckRequestJSONBodyFromInternal("postZoneDeployment-request.json"),
- ).
- Build(t)
-
- provider.zoneIDs["abc"] = 456789
- provider.recordIDs["abc"] = 12345
-
- err := provider.CleanUp("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp_skipDeploy(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(map[string]string{
- EnvSkipDeploy: "true",
- })
-
- provider := mockBuilder().
- Route("POST /api/v2/sessions",
- servermock.ResponseFromInternal("postSession.json"),
- servermock.CheckRequestJSONBodyFromInternal("postSession-request.json"),
- ).
- Route("DELETE /api/v2/resourceRecords/12345",
- servermock.ResponseFromInternal("deleteResourceRecord.json"),
- ).
- Route("POST /api/v2/zones/456789/deployments",
- servermock.Noop().
- WithStatusCode(http.StatusUnauthorized),
- ).
- Build(t)
-
- provider.zoneIDs["abc"] = 456789
- provider.recordIDs["abc"] = 12345
-
- err := provider.CleanUp("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
diff --git a/providers/dns/bluecatv2/internal/client.go b/providers/dns/bluecatv2/internal/client.go
deleted file mode 100644
index d3c801154..000000000
--- a/providers/dns/bluecatv2/internal/client.go
+++ /dev/null
@@ -1,221 +0,0 @@
-package internal
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strconv"
- "time"
-
- "github.com/go-acme/lego/v4/providers/dns/internal/errutils"
- "github.com/go-acme/lego/v4/providers/dns/internal/useragent"
- querystring "github.com/google/go-querystring/query"
-)
-
-// Client the Bluecat v2 API client.
-type Client struct {
- username string
- password string
-
- baseURL *url.URL
- HTTPClient *http.Client
-}
-
-// NewClient creates a new Client.
-func NewClient(serverURL, username, password string) (*Client, error) {
- if serverURL == "" {
- return nil, errors.New("server URL missing")
- }
-
- if username == "" || password == "" {
- return nil, errors.New("credentials missing")
- }
-
- baseURL, err := url.Parse(serverURL)
- if err != nil {
- return nil, err
- }
-
- return &Client{
- username: username,
- password: password,
- baseURL: baseURL,
- HTTPClient: &http.Client{Timeout: 10 * time.Second},
- }, nil
-}
-
-// RetrieveZones retrieves all zones.
-func (c *Client) RetrieveZones(ctx context.Context, opts *CollectionOptions) ([]ZoneResource, error) {
- endpoint := c.baseURL.JoinPath("api", "v2", "zones")
-
- collection, err := retrieveCollection[ZoneResource](ctx, c, endpoint, opts)
- if err != nil {
- return nil, err
- }
-
- return collection.Data, nil
-}
-
-// RetrieveZoneDeployments retrieves all deployments for a zone.
-func (c *Client) RetrieveZoneDeployments(ctx context.Context, zoneID int64, opts *CollectionOptions) ([]QuickDeployment, error) {
- endpoint := c.baseURL.JoinPath("api", "v2", "zones", strconv.FormatInt(zoneID, 10), "deployments")
-
- collection, err := retrieveCollection[QuickDeployment](ctx, c, endpoint, opts)
- if err != nil {
- return nil, err
- }
-
- return collection.Data, nil
-}
-
-// CreateZoneDeployment creates a new deployment for a zone.
-func (c *Client) CreateZoneDeployment(ctx context.Context, zoneID int64) (*QuickDeployment, error) {
- endpoint := c.baseURL.JoinPath("api", "v2", "zones", strconv.FormatInt(zoneID, 10), "deployments")
-
- payload := CommonResource{
- Type: "QuickDeployment",
- }
-
- req, err := newJSONRequest(ctx, http.MethodPost, endpoint, payload)
- if err != nil {
- return nil, err
- }
-
- result := new(QuickDeployment)
-
- err = c.doAuthenticated(ctx, req, result)
- if err != nil {
- return nil, err
- }
-
- return result, nil
-}
-
-// CreateZoneResourceRecord creates a new TXT record in a zone.
-func (c *Client) CreateZoneResourceRecord(ctx context.Context, zoneID int64, record RecordTXT) (*RecordTXT, error) {
- endpoint := c.baseURL.JoinPath("api", "v2", "zones", strconv.FormatInt(zoneID, 10), "resourceRecords")
-
- req, err := newJSONRequest(ctx, http.MethodPost, endpoint, record)
- if err != nil {
- return nil, err
- }
-
- result := new(RecordTXT)
-
- err = c.doAuthenticated(ctx, req, result)
- if err != nil {
- return nil, err
- }
-
- return result, nil
-}
-
-// DeleteResourceRecord deletes a resource record.
-func (c *Client) DeleteResourceRecord(ctx context.Context, recordID int64) error {
- endpoint := c.baseURL.JoinPath("api", "v2", "resourceRecords", strconv.FormatInt(recordID, 10))
-
- req, err := newJSONRequest(ctx, http.MethodDelete, endpoint, nil)
- if err != nil {
- return err
- }
-
- return c.doAuthenticated(ctx, req, nil)
-}
-
-func (c *Client) do(req *http.Request, result any) error {
- useragent.SetHeader(req.Header)
-
- resp, err := c.HTTPClient.Do(req)
- if err != nil {
- return errutils.NewHTTPDoError(req, err)
- }
-
- defer func() { _ = resp.Body.Close() }()
-
- if resp.StatusCode/100 != 2 {
- return parseError(req, resp)
- }
-
- if result == nil {
- return nil
- }
-
- raw, err := io.ReadAll(resp.Body)
- if err != nil {
- return errutils.NewReadResponseError(req, resp.StatusCode, err)
- }
-
- err = json.Unmarshal(raw, result)
- if err != nil {
- return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
- }
-
- return nil
-}
-
-func retrieveCollection[T any](ctx context.Context, client *Client, endpoint *url.URL, opts *CollectionOptions) (*Collection[T], error) {
- if opts != nil {
- values, err := querystring.Values(opts)
- if err != nil {
- return nil, err
- }
-
- endpoint.RawQuery = values.Encode()
- }
-
- req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
- if err != nil {
- return nil, err
- }
-
- result := &Collection[T]{}
-
- err = client.doAuthenticated(ctx, req, result)
- if err != nil {
- return nil, err
- }
-
- return result, nil
-}
-
-func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
- buf := new(bytes.Buffer)
-
- if payload != nil {
- err := json.NewEncoder(buf).Encode(payload)
- if err != nil {
- return nil, fmt.Errorf("failed to create request JSON body: %w", err)
- }
- }
-
- req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), buf)
- if err != nil {
- return nil, fmt.Errorf("unable to create request: %w", err)
- }
-
- req.Header.Set("Accept", "application/json")
-
- if payload != nil {
- req.Header.Set("Content-Type", "application/json")
- }
-
- return req, nil
-}
-
-func parseError(req *http.Request, resp *http.Response) error {
- raw, _ := io.ReadAll(resp.Body)
-
- var errAPI APIError
-
- err := json.Unmarshal(raw, &errAPI)
- if err != nil {
- return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
- }
-
- return &errAPI
-}
diff --git a/providers/dns/bluecatv2/internal/client_test.go b/providers/dns/bluecatv2/internal/client_test.go
deleted file mode 100644
index 2559af66e..000000000
--- a/providers/dns/bluecatv2/internal/client_test.go
+++ /dev/null
@@ -1,208 +0,0 @@
-package internal
-
-import (
- "net/http"
- "net/http/httptest"
- "net/url"
- "testing"
- "time"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func mockBuilderAuthenticated() *servermock.Builder[*Client] {
- return servermock.NewBuilder[*Client](
- func(server *httptest.Server) (*Client, error) {
- client, err := NewClient(server.URL, "userA", "secret")
- if err != nil {
- return nil, err
- }
-
- client.baseURL, _ = url.Parse(server.URL)
- client.HTTPClient = server.Client()
-
- return client, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders(),
- servermock.CheckHeader().
- WithAuthorization("Basic secretToken"),
- )
-}
-
-func TestClient_RetrieveZones(t *testing.T) {
- client := mockBuilderAuthenticated().
- Route("GET /api/v2/zones",
- servermock.ResponseFromFixture("zones.json"),
- servermock.CheckQueryParameter().Strict().
- With(
- "filter",
- "absoluteName:eq('example.com') and configuration.name:eq('myConfiguration') and view.name:eq('myView')",
- ),
- ).
- Build(t)
-
- opts := &CollectionOptions{
- Filter: And(
- Eq("absoluteName", "example.com"),
- Eq("configuration.name", "myConfiguration"),
- Eq("view.name", "myView"),
- ).String(),
- }
-
- result, err := client.RetrieveZones(mockToken(t.Context()), opts)
- require.NoError(t, err)
-
- expected := []ZoneResource{
- {
- CommonResource: CommonResource{ID: 12345, Type: "ENUMZone", Name: "5678"},
- AbsoluteName: "string",
- },
- {
- CommonResource: CommonResource{ID: 12345, Type: "ExternalHostsZone", Name: "name"},
- },
- {
- CommonResource: CommonResource{ID: 12345, Type: "InternalRootZone", Name: "name"},
- },
- {
- CommonResource: CommonResource{ID: 12345, Type: "ResponsePolicyZone", Name: "name"},
- },
- {
- CommonResource: CommonResource{ID: 12345, Type: "Zone", Name: "example.com"},
- AbsoluteName: "example.com",
- },
- }
-
- assert.Equal(t, expected, result)
-}
-
-func TestClient_RetrieveZones_error(t *testing.T) {
- client := mockBuilderAuthenticated().
- Route("GET /api/v2/zones",
- servermock.ResponseFromFixture("error.json").
- WithStatusCode(http.StatusUnauthorized),
- ).
- Build(t)
-
- opts := &CollectionOptions{
- Filter: And(
- Eq("absoluteName", "example.com"),
- Eq("configuration.name", "myConfiguration"),
- Eq("view.name", "myView"),
- ).String(),
- }
-
- _, err := client.RetrieveZones(mockToken(t.Context()), opts)
- require.EqualError(t, err, "401: Unauthorized: InvalidAuthorizationToken: The provided authorization token is invalid")
-}
-
-func TestClient_RetrieveZoneDeployments(t *testing.T) {
- client := mockBuilderAuthenticated().
- Route("GET /api/v2/zones/456789/deployments",
- servermock.ResponseFromFixture("getZoneDeployments.json"),
- servermock.CheckQueryParameter().Strict().
- With("filter", "id:eq('12345')"),
- ).
- Build(t)
-
- opts := &CollectionOptions{
- Filter: Eq("id", "12345").String(),
- }
-
- result, err := client.RetrieveZoneDeployments(mockToken(t.Context()), 456789, opts)
- require.NoError(t, err)
-
- expected := []QuickDeployment{
- {
- CommonResource: CommonResource{ID: 12345, Type: "QuickDeployment", Name: ""},
- State: "PENDING",
- Status: "CANCEL",
- Message: "string",
- PercentComplete: 50,
- CreationDateTime: time.Date(2022, time.November, 23, 2, 53, 0, 0, time.UTC),
- StartDateTime: time.Date(2022, time.November, 23, 2, 53, 3, 0, time.UTC),
- CompletionDateTime: time.Date(2022, time.November, 23, 2, 54, 5, 0, time.UTC),
- Method: "SCHEDULED",
- },
- }
-
- assert.Equal(t, expected, result)
-}
-
-func TestClient_CreateZoneDeployment(t *testing.T) {
- client := mockBuilderAuthenticated().
- Route("POST /api/v2/zones/12345/deployments",
- servermock.ResponseFromFixture("postZoneDeployment.json").
- WithStatusCode(http.StatusCreated),
- servermock.CheckRequestJSONBodyFromFixture("postZoneDeployment-request.json"),
- ).
- Build(t)
-
- quickDeployment, err := client.CreateZoneDeployment(mockToken(t.Context()), 12345)
- require.NoError(t, err)
-
- expected := &QuickDeployment{
- CommonResource: CommonResource{ID: 12345, Type: "QuickDeployment"},
- State: "PENDING",
- Status: "CANCEL",
- Message: "string",
- PercentComplete: 50,
- CreationDateTime: time.Date(2022, time.November, 23, 2, 53, 0, 0, time.UTC),
- StartDateTime: time.Date(2022, time.November, 23, 2, 53, 3, 0, time.UTC),
- CompletionDateTime: time.Date(2022, time.November, 23, 2, 54, 5, 0, time.UTC),
- Method: "SCHEDULED",
- }
-
- assert.Equal(t, expected, quickDeployment)
-}
-
-func TestClient_CreateZoneResourceRecord(t *testing.T) {
- client := mockBuilderAuthenticated().
- Route("POST /api/v2/zones/12345/resourceRecords",
- servermock.ResponseFromFixture("postZoneResourceRecord.json"),
- servermock.CheckRequestJSONBodyFromFixture("postZoneResourceRecord-request.json"),
- ).
- Build(t)
-
- record := RecordTXT{
- CommonResource: CommonResource{
- Type: "TXTRecord",
- Name: "_acme-challenge",
- },
- Text: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 120,
- RecordType: "TXT",
- }
-
- result, err := client.CreateZoneResourceRecord(mockToken(t.Context()), 12345, record)
- require.NoError(t, err)
-
- expected := &RecordTXT{
- CommonResource: CommonResource{
- ID: 12345,
- Type: "ResourceRecord",
- Name: "name",
- },
- TTL: 3600,
- AbsoluteName: "host1.example.com",
- Comment: "Sample comment.",
- Dynamic: true,
- RecordType: "CNAME",
- Text: "",
- }
-
- assert.Equal(t, expected, result)
-}
-
-func TestClient_DeleteResourceRecord(t *testing.T) {
- client := mockBuilderAuthenticated().
- Route("DELETE /api/v2/resourceRecords/12345",
- servermock.ResponseFromFixture("deleteResourceRecord.json"),
- ).
- Build(t)
-
- err := client.DeleteResourceRecord(mockToken(t.Context()), 12345)
- require.NoError(t, err)
-}
diff --git a/providers/dns/bluecatv2/internal/fixtures/deleteResourceRecord.json b/providers/dns/bluecatv2/internal/fixtures/deleteResourceRecord.json
deleted file mode 100644
index 38ae2db6e..000000000
--- a/providers/dns/bluecatv2/internal/fixtures/deleteResourceRecord.json
+++ /dev/null
@@ -1,75 +0,0 @@
-{
- "id": 12345,
- "type": "WorkflowRequest",
- "state": "APPROVED",
- "operation": "ADD_ALIAS_RECORD",
- "creator": {
- "id": 103307,
- "type": "User",
- "name": "admin",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "authenticator": {
- "id": 12345,
- "type": "Authenticator",
- "name": "LDAP authenticator"
- },
- "email": "user@example.com",
- "phoneNumber": "555-1234",
- "securityPrivilege": "NO_ACCESS",
- "historyPrivilege": "HIDE",
- "accessType": "GUI",
- "passwordResetRequired": true,
- "accountLocked": true,
- "x509Required": true,
- "administrativeAccessRights": [
- {
- "resourceType": "Event",
- "accessLevel": "HIDE"
- }
- ]
- },
- "resourceId": 0,
- "resourceType": "ACL",
- "fieldUpdates": [
- {
- "name": "string",
- "value": {},
- "previousValue": {}
- }
- ],
- "dependentRequest": "string",
- "modifier": {
- "id": 103307,
- "type": "User",
- "name": "admin",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "authenticator": {
- "id": 12345,
- "type": "Authenticator",
- "name": "LDAP authenticator"
- },
- "email": "user@example.com",
- "phoneNumber": "555-1234",
- "securityPrivilege": "NO_ACCESS",
- "historyPrivilege": "HIDE",
- "accessType": "GUI",
- "passwordResetRequired": true,
- "accountLocked": true,
- "x509Required": true,
- "administrativeAccessRights": [
- {
- "resourceType": "Event",
- "accessLevel": "HIDE"
- }
- ]
- },
- "creationDateTime": "2022-10-17T19:11:45Z",
- "modificationDateTime": "2022-10-18T19:11:45Z",
- "comment": "Sample comment."
-}
diff --git a/providers/dns/bluecatv2/internal/fixtures/error.json b/providers/dns/bluecatv2/internal/fixtures/error.json
deleted file mode 100644
index d3d2b8b5f..000000000
--- a/providers/dns/bluecatv2/internal/fixtures/error.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "status": 401,
- "reason": "Unauthorized",
- "code": "InvalidAuthorizationToken",
- "message": "The provided authorization token is invalid"
-}
diff --git a/providers/dns/bluecatv2/internal/fixtures/getZoneDeployments.json b/providers/dns/bluecatv2/internal/fixtures/getZoneDeployments.json
deleted file mode 100644
index b1a4938ad..000000000
--- a/providers/dns/bluecatv2/internal/fixtures/getZoneDeployments.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "count": 0,
- "totalCount": 0,
- "data": [
- {
- "id": 12345,
- "type": "QuickDeployment",
- "state": "PENDING",
- "status": "CANCEL",
- "message": "string",
- "percentComplete": 50,
- "creationDateTime": "2022-11-23T02:53:00Z",
- "startDateTime": "2022-11-23T02:53:03Z",
- "completionDateTime": "2022-11-23T02:54:05Z",
- "user": {
- "id": 103307,
- "type": "User",
- "name": "admin",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "authenticator": {
- "id": 12345,
- "type": "Authenticator",
- "name": "LDAP authenticator"
- },
- "email": "user@example.com",
- "phoneNumber": "555-1234",
- "securityPrivilege": "NO_ACCESS",
- "historyPrivilege": "HIDE",
- "accessType": "GUI",
- "passwordResetRequired": true,
- "accountLocked": true,
- "x509Required": true,
- "administrativeAccessRights": [
- {
- "resourceType": "Event",
- "accessLevel": "HIDE"
- }
- ]
- },
- "method": "SCHEDULED"
- }
- ]
-}
diff --git a/providers/dns/bluecatv2/internal/fixtures/postSession-request.json b/providers/dns/bluecatv2/internal/fixtures/postSession-request.json
deleted file mode 100644
index e62048eb9..000000000
--- a/providers/dns/bluecatv2/internal/fixtures/postSession-request.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "username": "userA",
- "password": "secret"
-}
diff --git a/providers/dns/bluecatv2/internal/fixtures/postSession.json b/providers/dns/bluecatv2/internal/fixtures/postSession.json
deleted file mode 100644
index 4599ad0ad..000000000
--- a/providers/dns/bluecatv2/internal/fixtures/postSession.json
+++ /dev/null
@@ -1,50 +0,0 @@
-{
- "id": 12345,
- "type": "UserSession",
- "apiToken": "VZoO2Z0BjBaJyvuhE4vNJRWqI9upwDHk70UNi0Ez",
- "apiTokenExpirationDateTime": "2022-09-15T17:52:07Z",
- "basicAuthenticationCredentials": "YXBpOlQ0OExOT3VIRGhDcnVBNEo1bGFES3JuS3hTZC9QK3pjczZXTzBJMDY=",
- "remoteAddress": "192.168.1.1",
- "readOnly": true,
- "loginDateTime": "2022-09-14T17:45:03Z",
- "logoutDateTime": "2022-09-14T19:45:03Z",
- "state": "LOGGED_IN",
- "response": "Authentication Error: Ensure that your username and password are correct.",
- "user": {
- "id": 103307,
- "type": "User",
- "name": "admin",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "authenticator": {
- "id": 12345,
- "type": "Authenticator",
- "name": "LDAP authenticator"
- },
- "email": "user@example.com",
- "phoneNumber": "555-1234",
- "securityPrivilege": "NO_ACCESS",
- "historyPrivilege": "HIDE",
- "accessType": "GUI",
- "passwordResetRequired": true,
- "accountLocked": true,
- "x509Required": true,
- "administrativeAccessRights": [
- {
- "resourceType": "Event",
- "accessLevel": "HIDE"
- }
- ]
- },
- "authenticator": {
- "id": 12345,
- "type": "Authenticator",
- "name": "LDAP authenticator",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- }
- }
-}
diff --git a/providers/dns/bluecatv2/internal/fixtures/postZoneDeployment-request.json b/providers/dns/bluecatv2/internal/fixtures/postZoneDeployment-request.json
deleted file mode 100644
index 099573a84..000000000
--- a/providers/dns/bluecatv2/internal/fixtures/postZoneDeployment-request.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "type": "QuickDeployment"
-}
diff --git a/providers/dns/bluecatv2/internal/fixtures/postZoneDeployment.json b/providers/dns/bluecatv2/internal/fixtures/postZoneDeployment.json
deleted file mode 100644
index fd26781fb..000000000
--- a/providers/dns/bluecatv2/internal/fixtures/postZoneDeployment.json
+++ /dev/null
@@ -1,40 +0,0 @@
-{
- "id": 12345,
- "type": "QuickDeployment",
- "state": "PENDING",
- "status": "CANCEL",
- "message": "string",
- "percentComplete": 50,
- "creationDateTime": "2022-11-23T02:53:00Z",
- "startDateTime": "2022-11-23T02:53:03Z",
- "completionDateTime": "2022-11-23T02:54:05Z",
- "user": {
- "id": 103307,
- "type": "User",
- "name": "admin",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "authenticator": {
- "id": 12345,
- "type": "Authenticator",
- "name": "LDAP authenticator"
- },
- "email": "user@example.com",
- "phoneNumber": "555-1234",
- "securityPrivilege": "NO_ACCESS",
- "historyPrivilege": "HIDE",
- "accessType": "GUI",
- "passwordResetRequired": true,
- "accountLocked": true,
- "x509Required": true,
- "administrativeAccessRights": [
- {
- "resourceType": "Event",
- "accessLevel": "HIDE"
- }
- ]
- },
- "method": "SCHEDULED"
-}
diff --git a/providers/dns/bluecatv2/internal/fixtures/postZoneResourceRecord-request.json b/providers/dns/bluecatv2/internal/fixtures/postZoneResourceRecord-request.json
deleted file mode 100644
index 2de733c71..000000000
--- a/providers/dns/bluecatv2/internal/fixtures/postZoneResourceRecord-request.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "type": "TXTRecord",
- "name": "_acme-challenge",
- "ttl": 120,
- "recordType": "TXT",
- "text": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY"
-}
diff --git a/providers/dns/bluecatv2/internal/fixtures/postZoneResourceRecord.json b/providers/dns/bluecatv2/internal/fixtures/postZoneResourceRecord.json
deleted file mode 100644
index 78d028ee3..000000000
--- a/providers/dns/bluecatv2/internal/fixtures/postZoneResourceRecord.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "id": 12345,
- "type": "ResourceRecord",
- "name": "name",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "configuration": {
- "id": 12345,
- "type": "Configuration",
- "name": "name"
- },
- "ttl": 3600,
- "absoluteName": "host1.example.com",
- "comment": "Sample comment.",
- "dynamic": true,
- "recordType": "CNAME",
- "linkedRecord": {
- "id": 12345,
- "type": "ResourceRecord",
- "name": "name",
- "absoluteName": "host1.example.com"
- }
-}
diff --git a/providers/dns/bluecatv2/internal/fixtures/zones.json b/providers/dns/bluecatv2/internal/fixtures/zones.json
deleted file mode 100644
index b9f2dfa8f..000000000
--- a/providers/dns/bluecatv2/internal/fixtures/zones.json
+++ /dev/null
@@ -1,185 +0,0 @@
-{
- "count": 0,
- "totalCount": 0,
- "data": [
- {
- "id": 12345,
- "type": "ENUMZone",
- "name": "5678",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "configuration": {
- "id": 12345,
- "type": "Configuration",
- "name": "name"
- },
- "view": {
- "id": 12345,
- "type": "View",
- "name": "default",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "configuration": {
- "id": 12345,
- "type": "Configuration",
- "name": "name"
- },
- "deviceRegistrationEnabled": true,
- "deviceRegistrationPortalAddress": "10.10.10.10"
- },
- "deploymentEnabled": true,
- "absoluteName": "string"
- },
- {
- "id": 12345,
- "type": "ExternalHostsZone",
- "name": "name",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "configuration": {
- "id": 12345,
- "type": "Configuration",
- "name": "name"
- },
- "view": {
- "id": 12345,
- "type": "View",
- "name": "default",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "configuration": {
- "id": 12345,
- "type": "Configuration",
- "name": "name"
- },
- "deviceRegistrationEnabled": true,
- "deviceRegistrationPortalAddress": "10.10.10.10"
- }
- },
- {
- "id": 12345,
- "type": "InternalRootZone",
- "name": "name",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "configuration": {
- "id": 12345,
- "type": "Configuration",
- "name": "name"
- },
- "view": {
- "id": 12345,
- "type": "View",
- "name": "default",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "configuration": {
- "id": 12345,
- "type": "Configuration",
- "name": "name"
- },
- "deviceRegistrationEnabled": true,
- "deviceRegistrationPortalAddress": "10.10.10.10"
- },
- "deploymentEnabled": true
- },
- {
- "id": 12345,
- "type": "ResponsePolicyZone",
- "name": "name",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "configuration": {
- "id": 12345,
- "type": "Configuration",
- "name": "name"
- },
- "view": {
- "id": 12345,
- "type": "View",
- "name": "default",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "configuration": {
- "id": 12345,
- "type": "Configuration",
- "name": "name"
- },
- "deviceRegistrationEnabled": true,
- "deviceRegistrationPortalAddress": "10.10.10.10"
- },
- "responsePolicyZoneType": "LOCAL",
- "responsePolicy": {
- "id": 12345,
- "type": "ResponsePolicy",
- "name": "Block Response Policy"
- },
- "overridePolicyType": "ALLOWLIST",
- "overrideRefreshTime": "string",
- "redirectTarget": "string",
- "feedCategories": [
- "string"
- ]
- },
- {
- "id": 12345,
- "type": "Zone",
- "name": "example.com",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "configuration": {
- "id": 12345,
- "type": "Configuration",
- "name": "name"
- },
- "view": {
- "id": 12345,
- "type": "View",
- "name": "default",
- "userDefinedFields": {
- "udf1": "value1",
- "udf2": "value2"
- },
- "configuration": {
- "id": 12345,
- "type": "Configuration",
- "name": "name"
- },
- "deviceRegistrationEnabled": true,
- "deviceRegistrationPortalAddress": "10.10.10.10"
- },
- "deploymentEnabled": true,
- "dynamicUpdateEnabled": true,
- "template": {
- "id": 12345,
- "type": "ZoneTemplate",
- "name": "name"
- },
- "signed": true,
- "signingPolicy": {
- "id": 12345,
- "type": "DNSSECSigningPolicy",
- "name": "name"
- },
- "absoluteName": "example.com"
- }
- ]
-}
diff --git a/providers/dns/bluecatv2/internal/identity.go b/providers/dns/bluecatv2/internal/identity.go
deleted file mode 100644
index af9355ab2..000000000
--- a/providers/dns/bluecatv2/internal/identity.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package internal
-
-import (
- "context"
- "fmt"
- "net/http"
-)
-
-type token string
-
-const tokenKey token = "token"
-
-const authorizationHeader = "Authorization"
-
-// CreateSession creates a new session.
-func (c *Client) CreateSession(ctx context.Context, info LoginInfo) (*Session, error) {
- endpoint := c.baseURL.JoinPath("api", "v2", "sessions")
-
- req, err := newJSONRequest(ctx, http.MethodPost, endpoint, info)
- if err != nil {
- return nil, err
- }
-
- result := new(Session)
-
- err = c.do(req, result)
- if err != nil {
- return nil, err
- }
-
- return result, nil
-}
-
-// CreateAuthenticatedContext creates a new authenticated context.
-func (c *Client) CreateAuthenticatedContext(ctx context.Context) (context.Context, error) {
- tok, err := c.CreateSession(ctx, LoginInfo{Username: c.username, Password: c.password})
- if err != nil {
- return nil, fmt.Errorf("create session: %w", err)
- }
-
- return context.WithValue(ctx, tokenKey, tok.BasicAuthenticationCredentials), nil
-}
-
-func (c *Client) doAuthenticated(ctx context.Context, req *http.Request, result any) error {
- tok := getToken(ctx)
- if tok != "" {
- req.Header.Set(authorizationHeader, "Basic "+tok)
- }
-
- return c.do(req, result)
-}
-
-func getToken(ctx context.Context) string {
- tok, ok := ctx.Value(tokenKey).(string)
- if !ok {
- return ""
- }
-
- return tok
-}
diff --git a/providers/dns/bluecatv2/internal/identity_test.go b/providers/dns/bluecatv2/internal/identity_test.go
deleted file mode 100644
index 3a1c4d2a2..000000000
--- a/providers/dns/bluecatv2/internal/identity_test.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package internal
-
-import (
- "context"
- "net/http/httptest"
- "net/url"
- "testing"
- "time"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func mockBuilder() *servermock.Builder[*Client] {
- return servermock.NewBuilder[*Client](
- func(server *httptest.Server) (*Client, error) {
- client, err := NewClient(server.URL, "userA", "secret")
- if err != nil {
- return nil, err
- }
-
- client.baseURL, _ = url.Parse(server.URL)
- client.HTTPClient = server.Client()
-
- return client, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders(),
- )
-}
-
-func mockToken(ctx context.Context) context.Context {
- return context.WithValue(ctx, tokenKey, "secretToken")
-}
-
-func TestClient_CreateSession(t *testing.T) {
- client := mockBuilder().
- Route("POST /api/v2/sessions",
- servermock.ResponseFromFixture("postSession.json"),
- servermock.CheckRequestJSONBodyFromFixture("postSession-request.json"),
- ).
- Build(t)
-
- info := LoginInfo{
- Username: "userA",
- Password: "secret",
- }
-
- result, err := client.CreateSession(mockToken(t.Context()), info)
- require.NoError(t, err)
-
- expected := &Session{
- ID: 12345,
- Type: "UserSession",
- APIToken: "VZoO2Z0BjBaJyvuhE4vNJRWqI9upwDHk70UNi0Ez",
- APITokenExpirationDateTime: time.Date(2022, time.September, 15, 17, 52, 7, 0, time.UTC),
- BasicAuthenticationCredentials: "YXBpOlQ0OExOT3VIRGhDcnVBNEo1bGFES3JuS3hTZC9QK3pjczZXTzBJMDY=",
- RemoteAddress: "192.168.1.1",
- ReadOnly: true,
- LoginDateTime: time.Date(2022, time.September, 14, 17, 45, 3, 0, time.UTC),
- LogoutDateTime: time.Date(2022, time.September, 14, 19, 45, 3, 0, time.UTC),
- State: "LOGGED_IN",
- Response: "Authentication Error: Ensure that your username and password are correct.",
- }
-
- assert.Equal(t, expected, result)
-}
-
-func TestClient_CreateAuthenticatedContext(t *testing.T) {
- client := mockBuilder().
- Route("POST /api/v2/sessions",
- servermock.ResponseFromFixture("postSession.json"),
- servermock.CheckRequestJSONBodyFromFixture("postSession-request.json"),
- ).
- Build(t)
-
- ctx, err := client.CreateAuthenticatedContext(t.Context())
- require.NoError(t, err)
-
- assert.Equal(t, "YXBpOlQ0OExOT3VIRGhDcnVBNEo1bGFES3JuS3hTZC9QK3pjczZXTzBJMDY=", getToken(ctx))
-}
diff --git a/providers/dns/bluecatv2/internal/predicates.go b/providers/dns/bluecatv2/internal/predicates.go
deleted file mode 100644
index 8ed6f714b..000000000
--- a/providers/dns/bluecatv2/internal/predicates.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package internal
-
-import (
- "fmt"
- "strings"
-)
-
-type Predicate struct {
- field string
- operator string
- values []string
-}
-
-func (p *Predicate) String() string {
- var values []string
- for _, v := range p.values {
- values = append(values, fmt.Sprintf("'%s'", v))
- }
-
- return fmt.Sprintf("%s:%s(%s)", p.field, p.operator, strings.Join(values, ", "))
-}
-
-func Eq(field, value string) *Predicate {
- return &Predicate{field: field, operator: "eq", values: []string{value}}
-}
-
-func Contains(field, value string) *Predicate {
- return &Predicate{field: field, operator: "contains", values: []string{value}}
-}
-
-func StartsWith(field, value string) *Predicate {
- return &Predicate{field: field, operator: "startsWith", values: []string{value}}
-}
-
-func EndsWith(field, value string) *Predicate {
- return &Predicate{field: field, operator: "endsWith", values: []string{value}}
-}
-
-func In(field string, values ...string) *Predicate {
- return &Predicate{field: field, operator: "in", values: values}
-}
-
-type Combined struct {
- predicates []*Predicate
- operator string
-}
-
-func (o *Combined) String() string {
- var parts []string
-
- for _, predicate := range o.predicates {
- parts = append(parts, predicate.String())
- }
-
- return strings.Join(parts, " "+o.operator+" ")
-}
-
-func And(predicates ...*Predicate) *Combined {
- return &Combined{predicates: predicates, operator: "and"}
-}
-
-func Or(predicates ...*Predicate) *Combined {
- return &Combined{predicates: predicates, operator: "or"}
-}
diff --git a/providers/dns/bluecatv2/internal/predicates_test.go b/providers/dns/bluecatv2/internal/predicates_test.go
deleted file mode 100644
index 6913e8729..000000000
--- a/providers/dns/bluecatv2/internal/predicates_test.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package internal
-
-import (
- "fmt"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestPredicate(t *testing.T) {
- testCases := []struct {
- desc string
- predicate fmt.Stringer
- expected string
- }{
- {
- desc: "Equals",
- predicate: Eq("foo", "bar"),
- expected: "foo:eq('bar')",
- },
- {
- desc: "Contains",
- predicate: Contains("foo", "bar"),
- expected: "foo:contains('bar')",
- },
- {
- desc: "Starts with",
- predicate: StartsWith("foo", "bar"),
- expected: "foo:startsWith('bar')",
- },
- {
- desc: "Ends with",
- predicate: EndsWith("foo", "bar"),
- expected: "foo:endsWith('bar')",
- },
- {
- desc: "Match a list of values",
- predicate: In("foo", "bar", "bir"),
- expected: "foo:in('bar', 'bir')",
- },
- {
- desc: "Combined: and",
- predicate: And(Eq("foo", "bar"), Eq("fii", "bir")),
- expected: "foo:eq('bar') and fii:eq('bir')",
- },
- {
- desc: "Combined: multiple and",
- predicate: And(
- Eq("foo", "bar"),
- Eq("fii", "bir"),
- Eq("fuu", "bur"),
- ),
- expected: "foo:eq('bar') and fii:eq('bir') and fuu:eq('bur')",
- },
- {
- desc: "Combined: or",
- predicate: Or(Eq("foo", "bar"), Eq("foo", "bir")),
- expected: "foo:eq('bar') or foo:eq('bir')",
- },
- {
- desc: "Combined: multiple or",
- predicate: Or(
- Eq("foo", "bar"),
- Eq("foo", "bir"),
- Eq("foo", "bur"),
- ),
- expected: "foo:eq('bar') or foo:eq('bir') or foo:eq('bur')",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- assert.Equal(t, test.expected, test.predicate.String())
- })
- }
-}
diff --git a/providers/dns/bluecatv2/internal/types.go b/providers/dns/bluecatv2/internal/types.go
deleted file mode 100644
index 562fd60b0..000000000
--- a/providers/dns/bluecatv2/internal/types.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package internal
-
-import (
- "fmt"
- "time"
-)
-
-// Quick deployment states.
-//
-//nolint:misspell // US vs UK
-const (
- QDStatePending = "PENDING"
- QDStateQueued = "QUEUED"
- QDStateRunning = "RUNNING"
- QDStateCancelled = "CANCELLED"
- QDStateCancelling = "CANCELLING"
- QDStateCompleted = "COMPLETED"
- QDStateCompletedWithErrors = "COMPLETED_WITH_ERRORS"
- QDStateCompletedWithWarnings = "COMPLETED_WITH_WARNINGS"
- QDStateFailed = "FAILED"
- QDStateUnknown = "UNKNOWN"
-)
-
-// APIError represents an error.
-// https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Errors/9.6.0
-type APIError struct {
- Status int `json:"status"`
- Reason string `json:"reason"`
- Code string `json:"code"`
- Message string `json:"message"`
-}
-
-func (a *APIError) Error() string {
- return fmt.Sprintf("%d: %s: %s: %s", a.Status, a.Reason, a.Code, a.Message)
-}
-
-// CommonResource represents the common resource fields.
-// https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Resources/9.6.0
-type CommonResource struct {
- ID int64 `json:"id,omitempty"`
- Type string `json:"type,omitempty"`
- Name string `json:"name,omitempty"`
-}
-
-// Collection represents a collection of resources.
-// https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Collections/9.6.0
-type Collection[T any] struct {
- Count int64 `json:"count"`
- TotalCount int64 `json:"totalCount"`
- Data []T `json:"data"`
-}
-
-type CollectionOptions struct {
- // https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Fields/9.6.0
- Fields string `url:"fields,omitempty"`
-
- // https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Pagination/9.6.0
- Limit int `url:"limit,omitempty"`
- Offset int `url:"offset,omitempty"`
-
- // https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Filter/9.6.0
- Filter string `url:"filter,omitempty"`
-
- // https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Ordering/9.6.0
- OrderBy string `url:"orderBy,omitempty"`
-
- // Should return or not the total number of resources matching the query.
- Total bool `url:"total,omitempty"`
-}
-
-type RecordTXT struct {
- CommonResource
-
- TTL int `json:"ttl,omitempty"`
- AbsoluteName string `json:"absoluteName,omitempty"`
- Comment string `json:"comment,omitempty"`
- Dynamic bool `json:"dynamic,omitempty"`
- RecordType string `json:"recordType,omitempty"`
- Text string `json:"text,omitempty"`
-}
-
-type ZoneResource struct {
- CommonResource
-
- AbsoluteName string `json:"absoluteName,omitempty"`
-}
-
-type QuickDeployment struct {
- CommonResource
-
- State string `json:"state,omitempty"`
- Status string `json:"status,omitempty"`
- Message string `json:"message,omitempty"`
- PercentComplete int `json:"percentComplete,omitempty"`
- CreationDateTime time.Time `json:"creationDateTime,omitzero"`
- StartDateTime time.Time `json:"startDateTime,omitzero"`
- CompletionDateTime time.Time `json:"completionDateTime,omitzero"`
- Method string `json:"method,omitempty"`
-}
-
-// LoginInfo represents the login information.
-// https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Creating-an-API-session/9.6.0
-type LoginInfo struct {
- Username string `json:"username"`
- Password string `json:"password"`
-}
-
-// Session represents the session.
-// https://docs.bluecatnetworks.com/r/Address-Manager-RESTful-v2-API-Guide/Creating-an-API-session/9.6.0
-type Session struct {
- ID int `json:"id"`
- Type string `json:"type"`
- APIToken string `json:"apiToken"`
- APITokenExpirationDateTime time.Time `json:"apiTokenExpirationDateTime"`
- BasicAuthenticationCredentials string `json:"basicAuthenticationCredentials"`
- RemoteAddress string `json:"remoteAddress"`
- ReadOnly bool `json:"readOnly"`
- LoginDateTime time.Time `json:"loginDateTime"`
- LogoutDateTime time.Time `json:"logoutDateTime"`
- State string `json:"state"`
- Response string `json:"response"`
-}
diff --git a/providers/dns/cloudflare/internal/types.go b/providers/dns/cloudflare/internal/types.go
index 50a7bbbf9..4a7f9e031 100644
--- a/providers/dns/cloudflare/internal/types.go
+++ b/providers/dns/cloudflare/internal/types.go
@@ -42,13 +42,13 @@ type ErrorChain struct {
type Errors []Message
func (e Errors) Error() string {
- msg := new(strings.Builder)
+ var msg strings.Builder
for _, item := range e {
- _, _ = fmt.Fprintf(msg, "%d: %s", item.Code, item.Message)
+ msg.WriteString(fmt.Sprintf("%d: %s", item.Code, item.Message))
for _, link := range item.ErrorChain {
- _, _ = fmt.Fprintf(msg, "; %d: %s", link.Code, link.Message)
+ msg.WriteString(fmt.Sprintf("; %d: %s", link.Code, link.Message))
}
}
diff --git a/providers/dns/czechia/czechia.go b/providers/dns/czechia/czechia.go
deleted file mode 100644
index 3ff397c35..000000000
--- a/providers/dns/czechia/czechia.go
+++ /dev/null
@@ -1,159 +0,0 @@
-// Package czechia implements a DNS provider for solving the DNS-01 challenge using Czechia.
-package czechia
-
-import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "time"
-
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/platform/config/env"
- "github.com/go-acme/lego/v4/providers/dns/czechia/internal"
- "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
-)
-
-// Environment variables names.
-const (
- envNamespace = "CZECHIA_"
-
- EnvToken = envNamespace + "TOKEN"
-
- EnvTTL = envNamespace + "TTL"
- EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
- EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
- EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
-)
-
-// Config is used to configure the creation of the DNSProvider.
-type Config struct {
- Token string
-
- PropagationTimeout time.Duration
- PollingInterval time.Duration
- TTL int
- HTTPClient *http.Client
-}
-
-// NewDefaultConfig returns a default configuration for the DNSProvider.
-func NewDefaultConfig() *Config {
- return &Config{
- TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
- PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
- PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
- HTTPClient: &http.Client{
- Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
- },
- }
-}
-
-// DNSProvider implements the challenge.Provider interface.
-type DNSProvider struct {
- config *Config
- client *internal.Client
-}
-
-// NewDNSProvider returns a DNSProvider instance configured for Czechia.
-func NewDNSProvider() (*DNSProvider, error) {
- values, err := env.Get(EnvToken)
- if err != nil {
- return nil, fmt.Errorf("czechia: %w", err)
- }
-
- config := NewDefaultConfig()
- config.Token = values[EnvToken]
-
- return NewDNSProviderConfig(config)
-}
-
-// NewDNSProviderConfig return a DNSProvider instance configured for Czechia.
-func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
- if config == nil {
- return nil, errors.New("czechia: the configuration of the DNS provider is nil")
- }
-
- client, err := internal.NewClient(config.Token)
- if err != nil {
- return nil, fmt.Errorf("czechia: %w", err)
- }
-
- if config.HTTPClient != nil {
- client.HTTPClient = config.HTTPClient
- }
-
- client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
-
- return &DNSProvider{
- config: config,
- client: client,
- }, nil
-}
-
-// Present creates a TXT record using the specified parameters.
-func (d *DNSProvider) Present(domain, token, keyAuth string) error {
- ctx := context.Background()
-
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("czechia: could not find zone for domain %q: %w", domain, err)
- }
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
- if err != nil {
- return fmt.Errorf("czechia: %w", err)
- }
-
- record := internal.TXTRecord{
- Hostname: subDomain,
- Text: info.Value,
- TTL: d.config.TTL,
- PublishZone: 1,
- }
-
- err = d.client.AddTXTRecord(ctx, dns01.UnFqdn(authZone), record)
- if err != nil {
- return fmt.Errorf("czechia: add TXT record: %w", err)
- }
-
- return nil
-}
-
-// CleanUp removes the TXT record matching the specified parameters.
-func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
- ctx := context.Background()
-
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("czechia: could not find zone for domain %q: %w", domain, err)
- }
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
- if err != nil {
- return fmt.Errorf("czechia: %w", err)
- }
-
- record := internal.TXTRecord{
- Hostname: subDomain,
- Text: info.Value,
- TTL: d.config.TTL,
- PublishZone: 1,
- }
-
- err = d.client.DeleteTXTRecord(ctx, dns01.UnFqdn(authZone), record)
- if err != nil {
- return fmt.Errorf("czechia: delete TXT record: %w", err)
- }
-
- return nil
-}
-
-// Timeout returns the timeout and interval to use when checking for DNS propagation.
-// Adjusting here to cope with spikes in propagation times.
-func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
- return d.config.PropagationTimeout, d.config.PollingInterval
-}
diff --git a/providers/dns/czechia/czechia.toml b/providers/dns/czechia/czechia.toml
deleted file mode 100644
index 2a66d2054..000000000
--- a/providers/dns/czechia/czechia.toml
+++ /dev/null
@@ -1,22 +0,0 @@
-Name = "Czechia"
-Description = ''''''
-URL = "https://www.czechia.com/"
-Code = "czechia"
-Since = "v4.33.0"
-
-Example = '''
-CZECHIA_TOKEN="xxxxxxxxxxxxxxxxxxxxx" \
-lego --dns czechia -d '*.example.com' -d example.com run
-'''
-
-[Configuration]
- [Configuration.Credentials]
- CZECHIA_TOKEN = "Authorization token"
- [Configuration.Additional]
- CZECHIA_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
- CZECHIA_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 60)"
- CZECHIA_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)"
- CZECHIA_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
-
-[Links]
- API = "https://api.czechia.com/swagger/index.html"
diff --git a/providers/dns/czechia/czechia_test.go b/providers/dns/czechia/czechia_test.go
deleted file mode 100644
index 7d9a2676c..000000000
--- a/providers/dns/czechia/czechia_test.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package czechia
-
-import (
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester"
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/require"
-)
-
-const envDomain = envNamespace + "DOMAIN"
-
-var envTest = tester.NewEnvTest(EnvToken).WithDomain(envDomain)
-
-func TestNewDNSProvider(t *testing.T) {
- testCases := []struct {
- desc string
- envVars map[string]string
- expected string
- }{
- {
- desc: "success",
- envVars: map[string]string{
- EnvToken: "secret",
- },
- },
- {
- desc: "missing credentials",
- envVars: map[string]string{},
- expected: "czechia: some credentials information are missing: CZECHIA_TOKEN",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(test.envVars)
-
- p, err := NewDNSProvider()
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestNewDNSProviderConfig(t *testing.T) {
- testCases := []struct {
- desc string
- token string
- expected string
- }{
- {
- desc: "success",
- token: "secret",
- },
- {
- desc: "missing credentials",
- expected: "czechia: credentials missing",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- config := NewDefaultConfig()
- config.Token = test.token
-
- p, err := NewDNSProviderConfig(config)
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestLivePresent(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.Present(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func TestLiveCleanUp(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func mockBuilder() *servermock.Builder[*DNSProvider] {
- return servermock.NewBuilder(
- func(server *httptest.Server) (*DNSProvider, error) {
- config := NewDefaultConfig()
- config.Token = "secret"
- config.HTTPClient = server.Client()
-
- p, err := NewDNSProviderConfig(config)
- if err != nil {
- return nil, err
- }
-
- p.client.BaseURL, _ = url.Parse(server.URL)
-
- return p, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders().
- With("AuthorizationToken", "secret"),
- )
-}
-
-func TestDNSProvider_Present(t *testing.T) {
- provider := mockBuilder().
- Route("POST /DNS/example.com/TXT",
- servermock.Noop(),
- servermock.CheckRequestJSONBodyFromInternal("add_txt_record-request.json"),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp(t *testing.T) {
- provider := mockBuilder().
- Route("DELETE /DNS/example.com/TXT",
- servermock.Noop(),
- servermock.CheckRequestJSONBodyFromInternal("add_txt_record-request.json"),
- ).
- Build(t)
-
- err := provider.CleanUp("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
diff --git a/providers/dns/czechia/internal/client.go b/providers/dns/czechia/internal/client.go
deleted file mode 100644
index f3e0e462e..000000000
--- a/providers/dns/czechia/internal/client.go
+++ /dev/null
@@ -1,124 +0,0 @@
-package internal
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "time"
-
- "github.com/go-acme/lego/v4/providers/dns/internal/errutils"
- "github.com/go-acme/lego/v4/providers/dns/internal/useragent"
-)
-
-const defaultBaseURL = "https://api.czechia.com/api"
-
-const authorizationTokenHeader = "AuthorizationToken"
-
-// Client the Czechia API client.
-type Client struct {
- token string
-
- BaseURL *url.URL
- HTTPClient *http.Client
-}
-
-// NewClient creates a new Client.
-func NewClient(token string) (*Client, error) {
- if token == "" {
- return nil, errors.New("credentials missing")
- }
-
- baseURL, _ := url.Parse(defaultBaseURL)
-
- return &Client{
- token: token,
- BaseURL: baseURL,
- HTTPClient: &http.Client{Timeout: 10 * time.Second},
- }, nil
-}
-
-func (c *Client) AddTXTRecord(ctx context.Context, domain string, record TXTRecord) error {
- endpoint := c.BaseURL.JoinPath("DNS", domain, "TXT")
-
- req, err := newJSONRequest(ctx, http.MethodPost, endpoint, record)
- if err != nil {
- return err
- }
-
- return c.do(req, nil)
-}
-
-func (c *Client) DeleteTXTRecord(ctx context.Context, domain string, record TXTRecord) error {
- endpoint := c.BaseURL.JoinPath("DNS", domain, "TXT")
-
- req, err := newJSONRequest(ctx, http.MethodDelete, endpoint, record)
- if err != nil {
- return err
- }
-
- return c.do(req, nil)
-}
-
-func (c *Client) do(req *http.Request, result any) error {
- useragent.SetHeader(req.Header)
-
- req.Header.Set(authorizationTokenHeader, c.token)
-
- resp, err := c.HTTPClient.Do(req)
- if err != nil {
- return errutils.NewHTTPDoError(req, err)
- }
-
- defer func() { _ = resp.Body.Close() }()
-
- if resp.StatusCode/100 != 2 {
- raw, _ := io.ReadAll(resp.Body)
-
- return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
- }
-
- if result == nil {
- return nil
- }
-
- raw, err := io.ReadAll(resp.Body)
- if err != nil {
- return errutils.NewReadResponseError(req, resp.StatusCode, err)
- }
-
- err = json.Unmarshal(raw, result)
- if err != nil {
- return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
- }
-
- return nil
-}
-
-func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
- buf := new(bytes.Buffer)
-
- if payload != nil {
- err := json.NewEncoder(buf).Encode(payload)
- if err != nil {
- return nil, fmt.Errorf("failed to create request JSON body: %w", err)
- }
- }
-
- req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), buf)
- if err != nil {
- return nil, fmt.Errorf("unable to create request: %w", err)
- }
-
- req.Header.Set("Accept", "application/json")
-
- if payload != nil {
- req.Header.Set("Content-Type", "application/json")
- }
-
- return req, nil
-}
diff --git a/providers/dns/czechia/internal/client_test.go b/providers/dns/czechia/internal/client_test.go
deleted file mode 100644
index c6f1141c5..000000000
--- a/providers/dns/czechia/internal/client_test.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package internal
-
-import (
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/require"
-)
-
-func mockBuilder() *servermock.Builder[*Client] {
- return servermock.NewBuilder[*Client](
- func(server *httptest.Server) (*Client, error) {
- client, err := NewClient("secret")
- if err != nil {
- return nil, err
- }
-
- client.BaseURL, _ = url.Parse(server.URL)
- client.HTTPClient = server.Client()
-
- return client, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders().
- With(authorizationTokenHeader, "secret"),
- )
-}
-
-func TestClient_AddTXTRecord(t *testing.T) {
- client := mockBuilder().
- Route("POST /DNS/example.com/TXT",
- servermock.Noop(),
- servermock.CheckRequestJSONBodyFromFixture("add_txt_record-request.json"),
- ).
- Build(t)
-
- record := TXTRecord{
- Hostname: "_acme-challenge",
- Text: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 120,
- PublishZone: 1,
- }
-
- err := client.AddTXTRecord(t.Context(), "example.com", record)
- require.NoError(t, err)
-}
-
-func TestClient_DeleteTXTRecord(t *testing.T) {
- client := mockBuilder().
- Route("DELETE /DNS/example.com/TXT",
- servermock.Noop(),
- servermock.CheckRequestJSONBodyFromFixture("add_txt_record-request.json"),
- ).
- Build(t)
-
- record := TXTRecord{
- Hostname: "_acme-challenge",
- Text: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 120,
- PublishZone: 1,
- }
-
- err := client.DeleteTXTRecord(t.Context(), "example.com", record)
- require.NoError(t, err)
-}
diff --git a/providers/dns/czechia/internal/fixtures/add_txt_record-request.json b/providers/dns/czechia/internal/fixtures/add_txt_record-request.json
deleted file mode 100644
index ed5830093..000000000
--- a/providers/dns/czechia/internal/fixtures/add_txt_record-request.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "hostName": "_acme-challenge",
- "text": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- "ttl": 120,
- "publishZone": 1
-}
diff --git a/providers/dns/czechia/internal/fixtures/delete_txt_record-request.json b/providers/dns/czechia/internal/fixtures/delete_txt_record-request.json
deleted file mode 100644
index ed5830093..000000000
--- a/providers/dns/czechia/internal/fixtures/delete_txt_record-request.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "hostName": "_acme-challenge",
- "text": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- "ttl": 120,
- "publishZone": 1
-}
diff --git a/providers/dns/czechia/internal/types.go b/providers/dns/czechia/internal/types.go
deleted file mode 100644
index f4a9bfef7..000000000
--- a/providers/dns/czechia/internal/types.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package internal
-
-type TXTRecord struct {
- Hostname string `json:"hostName,omitempty"`
- Text string `json:"text,omitempty"`
- TTL int `json:"ttl,omitempty"`
- PublishZone int `json:"publishZone,omitempty"`
-}
diff --git a/providers/dns/ddnss/ddnss.go b/providers/dns/ddnss/ddnss.go
deleted file mode 100644
index 381151c55..000000000
--- a/providers/dns/ddnss/ddnss.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// Package ddnss implements a DNS provider for solving the DNS-01 challenge using DynDNS Service.
-package ddnss
-
-import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "time"
-
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/platform/config/env"
- "github.com/go-acme/lego/v4/providers/dns/ddnss/internal"
- "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
-)
-
-// Environment variables names.
-const (
- envNamespace = "DDNSS_"
-
- EnvKey = envNamespace + "KEY"
-
- EnvTTL = envNamespace + "TTL"
- EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
- EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
- EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
- EnvSequenceInterval = envNamespace + "SEQUENCE_INTERVAL"
-)
-
-// Config is used to configure the creation of the DNSProvider.
-type Config struct {
- Key string
-
- PropagationTimeout time.Duration
- PollingInterval time.Duration
- SequenceInterval time.Duration
- TTL int
- HTTPClient *http.Client
-}
-
-// NewDefaultConfig returns a default configuration for the DNSProvider.
-func NewDefaultConfig() *Config {
- return &Config{
- TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
- PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
- PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
- SequenceInterval: env.GetOrDefaultSecond(EnvSequenceInterval, dns01.DefaultPropagationTimeout),
- HTTPClient: &http.Client{
- Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
- },
- }
-}
-
-// DNSProvider implements the challenge.Provider interface.
-type DNSProvider struct {
- config *Config
- client *internal.Client
-}
-
-// NewDNSProvider returns a DNSProvider instance configured for DynDNS Service.
-func NewDNSProvider() (*DNSProvider, error) {
- values, err := env.Get(EnvKey)
- if err != nil {
- return nil, fmt.Errorf("ddnss: %w", err)
- }
-
- config := NewDefaultConfig()
- config.Key = values[EnvKey]
-
- return NewDNSProviderConfig(config)
-}
-
-// NewDNSProviderConfig return a DNSProvider instance configured for DynDNS Service.
-func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
- if config == nil {
- return nil, errors.New("ddnss: the configuration of the DNS provider is nil")
- }
-
- client, err := internal.NewClient(&internal.Authentication{Key: config.Key})
- if err != nil {
- return nil, fmt.Errorf("ddnss: %w", err)
- }
-
- if config.HTTPClient != nil {
- client.HTTPClient = config.HTTPClient
- }
-
- client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
-
- return &DNSProvider{
- config: config,
- client: client,
- }, nil
-}
-
-// Present creates a TXT record using the specified parameters.
-func (d *DNSProvider) Present(domain, token, keyAuth string) error {
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- err := d.client.AddTXTRecord(context.Background(), dns01.UnFqdn(info.EffectiveFQDN), info.Value)
- if err != nil {
- return fmt.Errorf("ddnss: add TXT record: %w", err)
- }
-
- return nil
-}
-
-// CleanUp removes the TXT record matching the specified parameters.
-func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- err := d.client.RemoveTXTRecord(context.Background(), dns01.UnFqdn(info.EffectiveFQDN))
- if err != nil {
- return fmt.Errorf("ddnss: remove TXT record: %w", err)
- }
-
- return nil
-}
-
-// Timeout returns the timeout and interval to use when checking for DNS propagation.
-// Adjusting here to cope with spikes in propagation times.
-func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
- return d.config.PropagationTimeout, d.config.PollingInterval
-}
-
-// Sequential All DNS challenges for this provider will be resolved sequentially.
-// Returns the interval between each iteration.
-func (d *DNSProvider) Sequential() time.Duration {
- return d.config.SequenceInterval
-}
diff --git a/providers/dns/ddnss/ddnss.toml b/providers/dns/ddnss/ddnss.toml
deleted file mode 100644
index 0d0a7132c..000000000
--- a/providers/dns/ddnss/ddnss.toml
+++ /dev/null
@@ -1,23 +0,0 @@
-Name = "DDnss (DynDNS Service)"
-Description = ''''''
-URL = "https://ddnss.de/"
-Code = "ddnss"
-Since = "v4.32.0"
-
-Example = '''
-DDNSS_KEY="xxxxxxxxxxxxxxxxxxxxx" \
-lego --dns ddnss -d '*.example.com' -d example.com run
-'''
-
-[Configuration]
- [Configuration.Credentials]
- DDNSS_KEY = "Update key"
- [Configuration.Additional]
- DDNSS_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
- DDNSS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 60)"
- DDNSS_SEQUENCE_INTERVAL = "Time between sequential requests in seconds (Default: 60)"
- DDNSS_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)"
- DDNSS_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
-
-[Links]
- API = "https://ddnss.de/info.php"
diff --git a/providers/dns/ddnss/ddnss_test.go b/providers/dns/ddnss/ddnss_test.go
deleted file mode 100644
index 5b1d7df58..000000000
--- a/providers/dns/ddnss/ddnss_test.go
+++ /dev/null
@@ -1,168 +0,0 @@
-package ddnss
-
-import (
- "net/http/httptest"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester"
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/require"
-)
-
-const envDomain = envNamespace + "DOMAIN"
-
-var envTest = tester.NewEnvTest(EnvKey).WithDomain(envDomain)
-
-func TestNewDNSProvider(t *testing.T) {
- testCases := []struct {
- desc string
- envVars map[string]string
- expected string
- }{
- {
- desc: "success",
- envVars: map[string]string{
- EnvKey: "secret",
- },
- },
- {
- desc: "missing credentials",
- envVars: map[string]string{},
- expected: "ddnss: some credentials information are missing: DDNSS_KEY",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(test.envVars)
-
- p, err := NewDNSProvider()
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestNewDNSProviderConfig(t *testing.T) {
- testCases := []struct {
- desc string
- Key string
- expected string
- }{
- {
- desc: "success",
- Key: "secret",
- },
- {
- desc: "missing credentials",
- expected: "ddnss: missing credentials",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- config := NewDefaultConfig()
- config.Key = test.Key
-
- p, err := NewDNSProviderConfig(config)
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestLivePresent(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.Present(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func TestLiveCleanUp(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func mockBuilder() *servermock.Builder[*DNSProvider] {
- return servermock.NewBuilder(
- func(server *httptest.Server) (*DNSProvider, error) {
- config := NewDefaultConfig()
- config.Key = "secret"
- config.HTTPClient = server.Client()
-
- p, err := NewDNSProviderConfig(config)
- if err != nil {
- return nil, err
- }
-
- p.client.BaseURL = server.URL
-
- return p, nil
- },
- )
-}
-
-func TestDNSProvider_Present(t *testing.T) {
- provider := mockBuilder().
- Route("GET /",
- servermock.ResponseFromInternal("success.html"),
- servermock.CheckQueryParameter().Strict().
- With("host", "_acme-challenge.example.com").
- With("key", "secret").
- With("txt", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY").
- With("txtm", "1"),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp(t *testing.T) {
- provider := mockBuilder().
- Route("GET /",
- servermock.ResponseFromInternal("success.html"),
- servermock.CheckQueryParameter().Strict().
- With("host", "_acme-challenge.example.com").
- With("key", "secret").
- With("txtm", "2"),
- ).
- Build(t)
-
- err := provider.CleanUp("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
diff --git a/providers/dns/ddnss/internal/client.go b/providers/dns/ddnss/internal/client.go
deleted file mode 100644
index a0cf4b4a6..000000000
--- a/providers/dns/ddnss/internal/client.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package internal
-
-import (
- "context"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strings"
- "time"
-
- "github.com/go-acme/lego/v4/providers/dns/internal/errutils"
- "github.com/go-acme/lego/v4/providers/dns/internal/useragent"
- "golang.org/x/net/html"
-)
-
-const defaultBaseURL = "https://ddnss.de/upd.php"
-
-// Client the DDns API client.
-type Client struct {
- auth *Authentication
-
- BaseURL string
- HTTPClient *http.Client
-}
-
-// NewClient creates a new Client.
-func NewClient(auth *Authentication) (*Client, error) {
- if auth == nil {
- return nil, errors.New("credentials missing")
- }
-
- err := auth.validate()
- if err != nil {
- return nil, err
- }
-
- return &Client{
- auth: auth,
- BaseURL: defaultBaseURL,
- HTTPClient: &http.Client{Timeout: 10 * time.Second},
- }, nil
-}
-
-func (c *Client) AddTXTRecord(ctx context.Context, host, value string) error {
- return c.update(ctx, map[string]string{
- "host": host,
- "txt": value,
- "txtm": "1",
- })
-}
-
-func (c *Client) RemoveTXTRecord(ctx context.Context, host string) error {
- return c.update(ctx, map[string]string{
- "host": host,
- "txtm": "2",
- })
-}
-
-func (c *Client) update(ctx context.Context, params map[string]string) error {
- endpoint, err := url.Parse(c.BaseURL)
- if err != nil {
- return err
- }
-
- query := endpoint.Query()
-
- for k, v := range params {
- query.Set(k, v)
- }
-
- c.auth.set(query)
-
- endpoint.RawQuery = query.Encode()
-
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
- if err != nil {
- return fmt.Errorf("unable to create request: %w", err)
- }
-
- useragent.SetHeader(req.Header)
-
- resp, err := c.HTTPClient.Do(req)
- if err != nil {
- return errutils.NewHTTPDoError(req, err)
- }
-
- defer func() { _ = resp.Body.Close() }()
-
- if resp.StatusCode/100 != 2 {
- raw, _ := io.ReadAll(resp.Body)
-
- return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
- }
-
- raw, err := io.ReadAll(resp.Body)
- if err != nil {
- return errutils.NewReadResponseError(req, resp.StatusCode, err)
- }
-
- content, err := readPage(raw)
- if err != nil {
- return err
- }
-
- if strings.Contains(content, "Updated 1 hostname.") {
- return nil
- }
-
- return fmt.Errorf("unexpected response: %s", content)
-}
-
-func readPage(raw []byte) (string, error) {
- page, err := html.Parse(strings.NewReader(string(raw)))
- if err != nil {
- return "", err
- }
-
- var b strings.Builder
- extractText(page, &b)
-
- return strings.TrimSpace(b.String()), nil
-}
-
-func extractText(n *html.Node, b *strings.Builder) {
- if n.Type == html.TextNode {
- text := strings.TrimSpace(n.Data)
- if text != "" {
- b.WriteString(text + " ")
- }
- }
-
- for c := n.FirstChild; c != nil; c = c.NextSibling {
- extractText(c, b)
- }
-}
diff --git a/providers/dns/ddnss/internal/client_test.go b/providers/dns/ddnss/internal/client_test.go
deleted file mode 100644
index 3faddded0..000000000
--- a/providers/dns/ddnss/internal/client_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package internal
-
-import (
- "net/http/httptest"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/require"
-)
-
-func mockBuilder() *servermock.Builder[*Client] {
- return servermock.NewBuilder[*Client](
- func(server *httptest.Server) (*Client, error) {
- client, err := NewClient(&Authentication{Key: "secret"})
- if err != nil {
- return nil, err
- }
-
- client.BaseURL = server.URL
- client.HTTPClient = server.Client()
-
- return client, nil
- },
- )
-}
-
-func TestClient_AddTXTRecord(t *testing.T) {
- client := mockBuilder().
- Route("GET /",
- servermock.ResponseFromFixture("success.html"),
- servermock.CheckQueryParameter().Strict().
- With("host", "_acme-challenge.example.com").
- With("key", "secret").
- With("txt", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY").
- With("txtm", "1"),
- ).
- Build(t)
-
- err := client.AddTXTRecord(t.Context(), "_acme-challenge.example.com", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY")
- require.NoError(t, err)
-}
-
-func TestClient_RemoveTXTRecord(t *testing.T) {
- client := mockBuilder().
- Route("GET /",
- servermock.ResponseFromFixture("success.html"),
- servermock.CheckQueryParameter().Strict().
- With("host", "_acme-challenge.example.com").
- With("key", "secret").
- With("txtm", "2"),
- ).
- Build(t)
-
- err := client.RemoveTXTRecord(t.Context(), "_acme-challenge.example.com")
- require.NoError(t, err)
-}
diff --git a/providers/dns/ddnss/internal/fixtures/error.html b/providers/dns/ddnss/internal/fixtures/error.html
deleted file mode 100644
index f0599ad9a..000000000
--- a/providers/dns/ddnss/internal/fixtures/error.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
- DDNSS - Kostenloser DynDNS Service : Re-ProutDNS v5.01v
-
-
-
-Error Occurred While Processing Request :
-
- - badysys : Der System Parameter ist ungültig.
- - badauth : Die Authorisation ist fehlgeschlagen. Die Parameter username und/oder password sind falsch.
- - notfqdn : Hostname fehlt oder ist falsch.
-
diff --git a/providers/dns/ddnss/internal/fixtures/success.html b/providers/dns/ddnss/internal/fixtures/success.html
deleted file mode 100644
index f51957334..000000000
--- a/providers/dns/ddnss/internal/fixtures/success.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
- DDNSS - Kostenloser DynDNS Service : Re-ProutDNS v5.01v
-
-
-
-Updated 1 hostname.
-
diff --git a/providers/dns/ddnss/internal/types.go b/providers/dns/ddnss/internal/types.go
deleted file mode 100644
index 37d41e076..000000000
--- a/providers/dns/ddnss/internal/types.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package internal
-
-import (
- "errors"
- "net/url"
-)
-
-type Authentication struct {
- Username string `url:"user,omitempty"`
- Password string `url:"pwd,omitempty"`
- Key string `url:"key,omitempty"`
-}
-
-func (a *Authentication) validate() error {
- if a.Username == "" && a.Password == "" && a.Key == "" {
- return errors.New("missing credentials")
- }
-
- if a.Username != "" && a.Password != "" && a.Key != "" {
- return errors.New("only one of username, password or key can be set")
- }
-
- if (a.Username != "" && a.Password == "") || a.Username == "" && a.Password != "" {
- return errors.New("username and password must be set together")
- }
-
- return nil
-}
-
-func (a *Authentication) set(query url.Values) {
- if a.Key != "" {
- query.Set("key", a.Key)
-
- return
- }
-
- query.Set("user", a.Username)
- query.Set("pwd", a.Password)
-}
diff --git a/providers/dns/dnsexit/dnsexit.go b/providers/dns/dnsexit/dnsexit.go
deleted file mode 100644
index ce9373a50..000000000
--- a/providers/dns/dnsexit/dnsexit.go
+++ /dev/null
@@ -1,163 +0,0 @@
-// Package dnsexit implements a DNS provider for solving the DNS-01 challenge using DNSExit.
-package dnsexit
-
-import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "time"
-
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/platform/config/env"
- "github.com/go-acme/lego/v4/providers/dns/dnsexit/internal"
- "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
-)
-
-// Environment variables names.
-const (
- envNamespace = "DNSEXIT_"
-
- EnvAPIKey = envNamespace + "API_KEY"
-
- EnvTTL = envNamespace + "TTL"
- EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
- EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
- EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
-)
-
-// Config is used to configure the creation of the DNSProvider.
-type Config struct {
- APIKey string
-
- PropagationTimeout time.Duration
- PollingInterval time.Duration
- TTL int
- HTTPClient *http.Client
-}
-
-// NewDefaultConfig returns a default configuration for the DNSProvider.
-func NewDefaultConfig() *Config {
- return &Config{
- TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
- PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 5*time.Minute),
- PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 10*time.Second),
- HTTPClient: &http.Client{
- Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
- },
- }
-}
-
-// DNSProvider implements the challenge.Provider interface.
-type DNSProvider struct {
- config *Config
- client *internal.Client
-}
-
-// NewDNSProvider returns a DNSProvider instance configured for DNSExit.
-func NewDNSProvider() (*DNSProvider, error) {
- values, err := env.Get(EnvAPIKey)
- if err != nil {
- return nil, fmt.Errorf("dnsexit: %w", err)
- }
-
- config := NewDefaultConfig()
- config.APIKey = values[EnvAPIKey]
-
- return NewDNSProviderConfig(config)
-}
-
-// NewDNSProviderConfig return a DNSProvider instance configured for DNSExit.
-func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
- if config == nil {
- return nil, errors.New("dnsexit: the configuration of the DNS provider is nil")
- }
-
- client, err := internal.NewClient(config.APIKey)
- if err != nil {
- return nil, fmt.Errorf("dnsexit: %w", err)
- }
-
- if config.HTTPClient != nil {
- client.HTTPClient = config.HTTPClient
- }
-
- client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
-
- return &DNSProvider{
- config: config,
- client: client,
- }, nil
-}
-
-// Present creates a TXT record using the specified parameters.
-func (d *DNSProvider) Present(domain, token, keyAuth string) error {
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("dnsexit: could not find zone for domain %q: %w", domain, err)
- }
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
- if err != nil {
- return fmt.Errorf("dnsexit: %w", err)
- }
-
- record := internal.Record{
- Type: "TXT",
- Name: subDomain,
- Content: info.Value,
- TTL: toMinutes(d.config.TTL),
- }
-
- err = d.client.AddRecord(context.Background(), dns01.UnFqdn(authZone), record)
- if err != nil {
- return fmt.Errorf("dnsexit: add record: %w", err)
- }
-
- return nil
-}
-
-// CleanUp removes the TXT record matching the specified parameters.
-func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("dnsexit: could not find zone for domain %q: %w", domain, err)
- }
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
- if err != nil {
- return fmt.Errorf("dnsexit: %w", err)
- }
-
- record := internal.Record{
- Type: "TXT",
- Name: subDomain,
- Content: info.Value,
- }
-
- err = d.client.DeleteRecord(context.Background(), dns01.UnFqdn(authZone), record)
- if err != nil {
- return fmt.Errorf("dnsexit: add record: %w", err)
- }
-
- return nil
-}
-
-// Timeout returns the timeout and interval to use when checking for DNS propagation.
-// Adjusting here to cope with spikes in propagation times.
-func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
- return d.config.PropagationTimeout, d.config.PollingInterval
-}
-
-func toMinutes(seconds int) int {
- i := seconds / 60
- if seconds%60 > 0 {
- i++
- }
-
- return i
-}
diff --git a/providers/dns/dnsexit/dnsexit.toml b/providers/dns/dnsexit/dnsexit.toml
deleted file mode 100644
index 0d5321835..000000000
--- a/providers/dns/dnsexit/dnsexit.toml
+++ /dev/null
@@ -1,22 +0,0 @@
-Name = "DNSExit"
-Description = ''''''
-URL = "https://dnsexit.com"
-Code = "dnsexit"
-Since = "v4.32.0"
-
-Example = '''
-DNSEXIT_API_KEY="xxxxxxxxxxxxxxxxxxxxx" \
-lego --dns dnsexit -d '*.example.com' -d example.com run
-'''
-
-[Configuration]
- [Configuration.Credentials]
- DNSEXIT_API_KEY = "API key"
- [Configuration.Additional]
- DNSEXIT_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 10)"
- DNSEXIT_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 300)"
- DNSEXIT_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)"
- DNSEXIT_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
-
-[Links]
- API = "https://dnsexit.com/dns/dns-api/"
diff --git a/providers/dns/dnsexit/dnsexit_test.go b/providers/dns/dnsexit/dnsexit_test.go
deleted file mode 100644
index 31fe61497..000000000
--- a/providers/dns/dnsexit/dnsexit_test.go
+++ /dev/null
@@ -1,165 +0,0 @@
-package dnsexit
-
-import (
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester"
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/require"
-)
-
-const envDomain = envNamespace + "DOMAIN"
-
-var envTest = tester.NewEnvTest(EnvAPIKey).WithDomain(envDomain)
-
-func TestNewDNSProvider(t *testing.T) {
- testCases := []struct {
- desc string
- envVars map[string]string
- expected string
- }{
- {
- desc: "success",
- envVars: map[string]string{
- EnvAPIKey: "key",
- },
- },
- {
- desc: "missing credentials",
- envVars: map[string]string{},
- expected: "dnsexit: some credentials information are missing: DNSEXIT_API_KEY",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(test.envVars)
-
- p, err := NewDNSProvider()
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestNewDNSProviderConfig(t *testing.T) {
- testCases := []struct {
- desc string
- apiKey string
- expected string
- }{
- {
- desc: "success",
- apiKey: "key",
- },
- {
- desc: "missing credentials",
- expected: "dnsexit: credentials missing",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- config := NewDefaultConfig()
- config.APIKey = test.apiKey
-
- p, err := NewDNSProviderConfig(config)
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestLivePresent(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.Present(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func TestLiveCleanUp(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func mockBuilder() *servermock.Builder[*DNSProvider] {
- return servermock.NewBuilder(
- func(server *httptest.Server) (*DNSProvider, error) {
- config := NewDefaultConfig()
- config.APIKey = "secret"
- config.HTTPClient = server.Client()
-
- p, err := NewDNSProviderConfig(config)
- if err != nil {
- return nil, err
- }
-
- p.client.BaseURL, _ = url.Parse(server.URL)
-
- return p, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders().
- With("apikey", "secret"),
- )
-}
-
-func TestDNSProvider_Present(t *testing.T) {
- provider := mockBuilder().
- Route("POST /",
- servermock.ResponseFromInternal("success.json"),
- servermock.CheckRequestJSONBodyFromInternal("add_record-request.json"),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp(t *testing.T) {
- provider := mockBuilder().
- Route("POST /",
- servermock.ResponseFromInternal("success.json"),
- servermock.CheckRequestJSONBodyFromInternal("delete_record-request.json"),
- ).
- Build(t)
-
- err := provider.CleanUp("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
diff --git a/providers/dns/dnsexit/internal/client.go b/providers/dns/dnsexit/internal/client.go
deleted file mode 100644
index 9b0164846..000000000
--- a/providers/dns/dnsexit/internal/client.go
+++ /dev/null
@@ -1,156 +0,0 @@
-package internal
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "time"
-
- "github.com/go-acme/lego/v4/providers/dns/internal/errutils"
- "github.com/go-acme/lego/v4/providers/dns/internal/useragent"
-)
-
-const defaultBaseURL = "https://api.dnsexit.com/dns/"
-
-// Client the DNSExit API client.
-type Client struct {
- apiKey string
-
- BaseURL *url.URL
- HTTPClient *http.Client
-}
-
-// NewClient creates a new Client.
-func NewClient(apiKey string) (*Client, error) {
- if apiKey == "" {
- return nil, errors.New("credentials missing")
- }
-
- baseURL, _ := url.Parse(defaultBaseURL)
-
- return &Client{
- apiKey: apiKey,
- BaseURL: baseURL,
- HTTPClient: &http.Client{Timeout: 10 * time.Second},
- }, nil
-}
-
-// AddRecord adds a record.
-// https://dnsexit.com/dns/dns-api/#example-add-spf
-// https://dnsexit.com/dns/dns-api/#example-lse
-func (c *Client) AddRecord(ctx context.Context, domain string, record Record) error {
- payload := APIRequest{
- Domain: domain,
- Add: []Record{record},
- }
-
- req, err := newJSONRequest(ctx, http.MethodPost, c.BaseURL, payload)
- if err != nil {
- return err
- }
-
- err = c.do(req)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// DeleteRecord deletes a record.
-// https://dnsexit.com/dns/dns-api/#delete-a-record
-func (c *Client) DeleteRecord(ctx context.Context, domain string, record Record) error {
- payload := APIRequest{
- Domain: domain,
- Delete: []Record{record},
- }
-
- req, err := newJSONRequest(ctx, http.MethodPost, c.BaseURL, payload)
- if err != nil {
- return err
- }
-
- err = c.do(req)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (c *Client) do(req *http.Request) error {
- useragent.SetHeader(req.Header)
-
- req.Header.Set("apikey", c.apiKey)
-
- resp, err := c.HTTPClient.Do(req)
- if err != nil {
- return errutils.NewHTTPDoError(req, err)
- }
-
- defer func() { _ = resp.Body.Close() }()
-
- if resp.StatusCode > http.StatusBadRequest {
- return parseError(req, resp)
- }
-
- raw, err := io.ReadAll(resp.Body)
- if err != nil {
- return errutils.NewReadResponseError(req, resp.StatusCode, err)
- }
-
- result := &APIResponse{}
-
- err = json.Unmarshal(raw, result)
- if err != nil {
- return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
- }
-
- if result.Code != 0 {
- return result
- }
-
- return nil
-}
-
-func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
- buf := new(bytes.Buffer)
-
- if payload != nil {
- err := json.NewEncoder(buf).Encode(payload)
- if err != nil {
- return nil, fmt.Errorf("failed to create request JSON body: %w", err)
- }
- }
-
- req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), buf)
- if err != nil {
- return nil, fmt.Errorf("unable to create request: %w", err)
- }
-
- req.Header.Set("Accept", "application/json")
-
- if payload != nil {
- req.Header.Set("Content-Type", "application/json")
- }
-
- return req, nil
-}
-
-func parseError(req *http.Request, resp *http.Response) error {
- raw, _ := io.ReadAll(resp.Body)
-
- var errAPI APIResponse
-
- err := json.Unmarshal(raw, &errAPI)
- if err != nil {
- return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
- }
-
- return &errAPI
-}
diff --git a/providers/dns/dnsexit/internal/client_test.go b/providers/dns/dnsexit/internal/client_test.go
deleted file mode 100644
index 26ea01203..000000000
--- a/providers/dns/dnsexit/internal/client_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-package internal
-
-import (
- "context"
- "net/http"
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/require"
-)
-
-func mockBuilder() *servermock.Builder[*Client] {
- return servermock.NewBuilder(
- func(server *httptest.Server) (*Client, error) {
- client, err := NewClient("secret")
- if err != nil {
- return nil, err
- }
-
- client.HTTPClient = server.Client()
- client.BaseURL, _ = url.Parse(server.URL)
-
- return client, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders().
- With("apikey", "secret"),
- )
-}
-
-func TestClient_AddRecord(t *testing.T) {
- client := mockBuilder().
- Route("POST /",
- servermock.ResponseFromFixture("success.json"),
- servermock.CheckRequestJSONBodyFromFixture("add_record-request.json"),
- ).
- Build(t)
-
- record := Record{
- Type: "TXT",
- Name: "_acme-challenge",
- Content: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 2,
- }
-
- err := client.AddRecord(context.Background(), "example.com", record)
- require.NoError(t, err)
-}
-
-func TestClient_AddRecord_error(t *testing.T) {
- client := mockBuilder().
- Route("POST /",
- servermock.ResponseFromFixture("error.json").
- WithStatusCode(http.StatusBadRequest),
- ).
- Build(t)
-
- record := Record{
- Type: "TXT",
- Name: "_acme-challenge",
- Content: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 480,
- Overwrite: true,
- }
-
- err := client.AddRecord(context.Background(), "example.com", record)
- require.Error(t, err)
-
- require.EqualError(t, err, "JSON Defined Record Type not Supported (code=6)")
-}
-
-func TestClient_DeleteRecord(t *testing.T) {
- client := mockBuilder().
- Route("POST /",
- servermock.ResponseFromFixture("success.json"),
- servermock.CheckRequestJSONBodyFromFixture("delete_record-request.json"),
- ).
- Build(t)
-
- record := Record{
- Type: "TXT",
- Name: "_acme-challenge",
- Content: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- }
-
- err := client.DeleteRecord(context.Background(), "example.com", record)
- require.NoError(t, err)
-}
-
-func TestClient_DeleteRecord_error(t *testing.T) {
- client := mockBuilder().
- Route("POST /",
- servermock.ResponseFromFixture("error.json").
- WithStatusCode(http.StatusBadRequest),
- ).
- Build(t)
-
- record := Record{
- Type: "TXT",
- Name: "foo",
- Content: "txtTXTtxt",
- }
-
- err := client.DeleteRecord(context.Background(), "example.com", record)
-
- require.Error(t, err)
-
- require.EqualError(t, err, "JSON Defined Record Type not Supported (code=6)")
-}
diff --git a/providers/dns/dnsexit/internal/fixtures/add_record-request.json b/providers/dns/dnsexit/internal/fixtures/add_record-request.json
deleted file mode 100644
index 6e5e2b520..000000000
--- a/providers/dns/dnsexit/internal/fixtures/add_record-request.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "domain": "example.com",
- "add": [
- {
- "type": "TXT",
- "name": "_acme-challenge",
- "content": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- "ttl": 2
- }
- ]
-}
diff --git a/providers/dns/dnsexit/internal/fixtures/delete_record-request.json b/providers/dns/dnsexit/internal/fixtures/delete_record-request.json
deleted file mode 100644
index dcfef9cdf..000000000
--- a/providers/dns/dnsexit/internal/fixtures/delete_record-request.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "domain": "example.com",
- "delete": [
- {
- "type": "TXT",
- "name": "_acme-challenge",
- "content": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY"
- }
- ]
-}
diff --git a/providers/dns/dnsexit/internal/fixtures/error.json b/providers/dns/dnsexit/internal/fixtures/error.json
deleted file mode 100644
index 9ba835895..000000000
--- a/providers/dns/dnsexit/internal/fixtures/error.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "code": 6,
- "message": "JSON Defined Record Type not Supported"
-}
diff --git a/providers/dns/dnsexit/internal/fixtures/success.json b/providers/dns/dnsexit/internal/fixtures/success.json
deleted file mode 100644
index 3af47a936..000000000
--- a/providers/dns/dnsexit/internal/fixtures/success.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "code": 0,
- "details": [
- "UPDATE Record A example.com. TTL(hh:mm) 08:00 IP 1.1.1.10"
- ],
- "message": "Success"
-}
diff --git a/providers/dns/dnsexit/internal/types.go b/providers/dns/dnsexit/internal/types.go
deleted file mode 100644
index db254549f..000000000
--- a/providers/dns/dnsexit/internal/types.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package internal
-
-import (
- "fmt"
- "strings"
-)
-
-type Record struct {
- Type string `json:"type,omitempty"`
- Name string `json:"name,omitempty"`
- Content string `json:"content,omitempty"`
- TTL int `json:"ttl,omitempty"` // NOTE: ttl value is in minutes.
- Overwrite bool `json:"overwrite,omitempty"`
-}
-
-type APIRequest struct {
- Domain string `json:"domain,omitempty"`
- Add []Record `json:"add,omitempty"`
- Delete []Record `json:"delete,omitempty"`
- Update []Record `json:"update,omitempty"`
-}
-
-// https://dnsexit.com/dns/dns-api/#server-reply
-
-type APIResponse struct {
- Code int `json:"code,omitempty"`
- Details []string `json:"details,omitempty"`
- Message string `json:"message,omitempty"`
-}
-
-func (a APIResponse) Error() string {
- msg := new(strings.Builder)
-
- _, _ = fmt.Fprintf(msg, "%s (code=%d)", a.Message, a.Code)
-
- for _, detail := range a.Details {
- _, _ = fmt.Fprintf(msg, ", %s", detail)
- }
-
- return msg.String()
-}
diff --git a/providers/dns/eurodns/eurodns.go b/providers/dns/eurodns/eurodns.go
deleted file mode 100644
index 21ff3c3a9..000000000
--- a/providers/dns/eurodns/eurodns.go
+++ /dev/null
@@ -1,197 +0,0 @@
-// Package eurodns implements a DNS provider for solving the DNS-01 challenge using EuroDNS.
-package eurodns
-
-import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "time"
-
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/platform/config/env"
- "github.com/go-acme/lego/v4/providers/dns/eurodns/internal"
- "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
-)
-
-// Environment variables names.
-const (
- envNamespace = "EURODNS_"
-
- EnvApplicationID = envNamespace + "APP_ID"
- EnvAPIKey = envNamespace + "API_KEY"
-
- EnvTTL = envNamespace + "TTL"
- EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
- EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
- EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
-)
-
-// Config is used to configure the creation of the DNSProvider.
-type Config struct {
- ApplicationID string
- APIKey string
-
- PropagationTimeout time.Duration
- PollingInterval time.Duration
- TTL int
- HTTPClient *http.Client
-}
-
-// NewDefaultConfig returns a default configuration for the DNSProvider.
-func NewDefaultConfig() *Config {
- return &Config{
- TTL: env.GetOrDefaultInt(EnvTTL, internal.DefaultTTL),
- PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
- PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
- HTTPClient: &http.Client{
- Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
- },
- }
-}
-
-// DNSProvider implements the challenge.Provider interface.
-type DNSProvider struct {
- config *Config
- client *internal.Client
-}
-
-// NewDNSProvider returns a DNSProvider instance configured for EuroDNS.
-func NewDNSProvider() (*DNSProvider, error) {
- values, err := env.Get(EnvApplicationID, EnvAPIKey)
- if err != nil {
- return nil, fmt.Errorf("eurodns: %w", err)
- }
-
- config := NewDefaultConfig()
- config.ApplicationID = values[EnvApplicationID]
- config.APIKey = values[EnvAPIKey]
-
- return NewDNSProviderConfig(config)
-}
-
-// NewDNSProviderConfig return a DNSProvider instance configured for EuroDNS.
-func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
- if config == nil {
- return nil, errors.New("eurodns: the configuration of the DNS provider is nil")
- }
-
- client, err := internal.NewClient(config.ApplicationID, config.APIKey)
- if err != nil {
- return nil, fmt.Errorf("eurodns: %w", err)
- }
-
- if config.HTTPClient != nil {
- client.HTTPClient = config.HTTPClient
- }
-
- client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
-
- return &DNSProvider{
- config: config,
- client: client,
- }, nil
-}
-
-// Present creates a TXT record using the specified parameters.
-func (d *DNSProvider) Present(domain, token, keyAuth string) error {
- ctx := context.Background()
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("eurodns: could not find zone for domain %q: %w", domain, err)
- }
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
- if err != nil {
- return fmt.Errorf("eurodns: %w", err)
- }
-
- authZone = dns01.UnFqdn(authZone)
-
- zone, err := d.client.GetZone(ctx, authZone)
- if err != nil {
- return fmt.Errorf("eurodns: get zone: %w", err)
- }
-
- zone.Records = append(zone.Records, internal.Record{
- Type: "TXT",
- Host: subDomain,
- TTL: internal.TTLRounder(d.config.TTL),
- RData: info.Value,
- })
-
- validation, err := d.client.ValidateZone(ctx, authZone, zone)
- if err != nil {
- return fmt.Errorf("eurodns: validate zone: %w", err)
- }
-
- if validation.Report != nil && !validation.Report.IsValid {
- return fmt.Errorf("eurodns: validation report: %w", validation.Report)
- }
-
- err = d.client.SaveZone(ctx, authZone, zone)
- if err != nil {
- return fmt.Errorf("eurodns: save zone: %w", err)
- }
-
- return nil
-}
-
-// CleanUp removes the TXT record matching the specified parameters.
-func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
- ctx := context.Background()
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("eurodns: could not find zone for domain %q: %w", domain, err)
- }
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
- if err != nil {
- return fmt.Errorf("eurodns: %w", err)
- }
-
- authZone = dns01.UnFqdn(authZone)
-
- zone, err := d.client.GetZone(ctx, authZone)
- if err != nil {
- return fmt.Errorf("eurodns: get zone: %w", err)
- }
-
- var recordsToKeep []internal.Record
-
- for _, record := range zone.Records {
- if record.Type == "TXT" && record.Host == subDomain && record.RData == info.Value {
- continue
- }
-
- recordsToKeep = append(recordsToKeep, record)
- }
-
- zone.Records = recordsToKeep
-
- validation, err := d.client.ValidateZone(ctx, authZone, zone)
- if err != nil {
- return fmt.Errorf("eurodns: validate zone: %w", err)
- }
-
- if validation.Report != nil && !validation.Report.IsValid {
- return fmt.Errorf("eurodns: validation report: %w", validation.Report)
- }
-
- err = d.client.SaveZone(ctx, authZone, zone)
- if err != nil {
- return fmt.Errorf("eurodns: save zone: %w", err)
- }
-
- return nil
-}
-
-// Timeout returns the timeout and interval to use when checking for DNS propagation.
-// Adjusting here to cope with spikes in propagation times.
-func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
- return d.config.PropagationTimeout, d.config.PollingInterval
-}
diff --git a/providers/dns/eurodns/eurodns.toml b/providers/dns/eurodns/eurodns.toml
deleted file mode 100644
index 302b15d00..000000000
--- a/providers/dns/eurodns/eurodns.toml
+++ /dev/null
@@ -1,24 +0,0 @@
-Name = "EuroDNS"
-Description = ''''''
-URL = "https://www.eurodns.com/"
-Code = "eurodns"
-Since = "v4.33.0"
-
-Example = '''
-EURODNS_APP_ID="xxx" \
-EURODNS_API_KEY="yyy" \
-lego --dns eurodns -d '*.example.com' -d example.com run
-'''
-
-[Configuration]
- [Configuration.Credentials]
- EURODNS_APP_ID = "Application ID"
- EURODNS_API_KEY = "API key"
- [Configuration.Additional]
- EURODNS_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
- EURODNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 60)"
- EURODNS_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 600)"
- EURODNS_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
-
-[Links]
- API = "https://docapi.eurodns.com/"
diff --git a/providers/dns/eurodns/eurodns_test.go b/providers/dns/eurodns/eurodns_test.go
deleted file mode 100644
index abbb4717e..000000000
--- a/providers/dns/eurodns/eurodns_test.go
+++ /dev/null
@@ -1,215 +0,0 @@
-package eurodns
-
-import (
- "net/http"
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester"
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/go-acme/lego/v4/providers/dns/eurodns/internal"
- "github.com/stretchr/testify/require"
-)
-
-const envDomain = envNamespace + "DOMAIN"
-
-var envTest = tester.NewEnvTest(EnvApplicationID, EnvAPIKey).WithDomain(envDomain)
-
-func TestNewDNSProvider(t *testing.T) {
- testCases := []struct {
- desc string
- envVars map[string]string
- expected string
- }{
- {
- desc: "success",
- envVars: map[string]string{
- EnvApplicationID: "abc",
- EnvAPIKey: "secret",
- },
- },
- {
- desc: "missing application ID",
- envVars: map[string]string{
- EnvApplicationID: "",
- EnvAPIKey: "secret",
- },
- expected: "eurodns: some credentials information are missing: EURODNS_APP_ID",
- },
- {
- desc: "missing API secret",
- envVars: map[string]string{
- EnvApplicationID: "",
- EnvAPIKey: "secret",
- },
- expected: "eurodns: some credentials information are missing: EURODNS_APP_ID",
- },
- {
- desc: "missing credentials",
- envVars: map[string]string{},
- expected: "eurodns: some credentials information are missing: EURODNS_APP_ID,EURODNS_API_KEY",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(test.envVars)
-
- p, err := NewDNSProvider()
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestNewDNSProviderConfig(t *testing.T) {
- testCases := []struct {
- desc string
- appID string
- apiKey string
- expected string
- }{
- {
- desc: "success",
- appID: "abc",
- apiKey: "secret",
- },
- {
- desc: "missing application ID",
- expected: "eurodns: credentials missing",
- apiKey: "secret",
- },
- {
- desc: "missing API secret",
- expected: "eurodns: credentials missing",
- appID: "abc",
- },
- {
- desc: "missing credentials",
- expected: "eurodns: credentials missing",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- config := NewDefaultConfig()
- config.ApplicationID = test.appID
- config.APIKey = test.apiKey
-
- p, err := NewDNSProviderConfig(config)
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestLivePresent(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.Present(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func TestLiveCleanUp(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func mockBuilder() *servermock.Builder[*DNSProvider] {
- return servermock.NewBuilder(
- func(server *httptest.Server) (*DNSProvider, error) {
- config := NewDefaultConfig()
- config.APIKey = "secret"
- config.ApplicationID = "abc"
- config.HTTPClient = server.Client()
-
- provider, err := NewDNSProviderConfig(config)
- if err != nil {
- return nil, err
- }
-
- provider.client.BaseURL, _ = url.Parse(server.URL)
-
- return provider, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders().
- With(internal.HeaderAppID, "abc").
- With(internal.HeaderAPIKey, "secret"),
- )
-}
-
-func TestDNSProvider_Present(t *testing.T) {
- provider := mockBuilder().
- Route("GET /example.com",
- servermock.ResponseFromInternal("zone_get.json"),
- ).
- Route("POST /example.com/check",
- servermock.ResponseFromInternal("zone_add_validate_ok.json"),
- servermock.CheckRequestJSONBodyFromInternal("zone_add.json"),
- ).
- Route("PUT /example.com",
- servermock.Noop().
- WithStatusCode(http.StatusNoContent),
- servermock.CheckRequestJSONBodyFromInternal("zone_add.json"),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp(t *testing.T) {
- provider := mockBuilder().
- Route("GET /example.com",
- servermock.ResponseFromInternal("zone_add.json"),
- ).
- Route("POST /example.com/check",
- servermock.ResponseFromInternal("zone_remove.json"),
- servermock.CheckRequestJSONBodyFromInternal("zone_remove.json"),
- ).
- Route("PUT /example.com",
- servermock.Noop().
- WithStatusCode(http.StatusNoContent),
- servermock.CheckRequestJSONBodyFromInternal("zone_remove.json"),
- ).
- Build(t)
-
- err := provider.CleanUp("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
diff --git a/providers/dns/eurodns/internal/client.go b/providers/dns/eurodns/internal/client.go
deleted file mode 100644
index 1ebf8d143..000000000
--- a/providers/dns/eurodns/internal/client.go
+++ /dev/null
@@ -1,199 +0,0 @@
-package internal
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "time"
-
- "github.com/go-acme/lego/v4/providers/dns/internal/errutils"
-)
-
-const defaultBaseURL = "https://rest-api.eurodns.com/dns-zones/"
-
-const (
- HeaderAppID = "X-APP-ID"
- HeaderAPIKey = "X-API-KEY"
-)
-
-// Client the EuroDNS API client.
-type Client struct {
- appID string
- apiKey string
-
- BaseURL *url.URL
- HTTPClient *http.Client
-}
-
-// NewClient creates a new Client.
-func NewClient(appID, apiKey string) (*Client, error) {
- if appID == "" || apiKey == "" {
- return nil, errors.New("credentials missing")
- }
-
- baseURL, _ := url.Parse(defaultBaseURL)
-
- return &Client{
- appID: appID,
- apiKey: apiKey,
- BaseURL: baseURL,
- HTTPClient: &http.Client{Timeout: 10 * time.Second},
- }, nil
-}
-
-// GetZone gets a DNS Zone.
-// https://docapi.eurodns.com/#/dnsprovider/getdnszone
-func (c *Client) GetZone(ctx context.Context, domain string) (*Zone, error) {
- endpoint := c.BaseURL.JoinPath(domain)
-
- req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
- if err != nil {
- return nil, err
- }
-
- result := &Zone{}
-
- err = c.do(req, result)
- if err != nil {
- return nil, err
- }
-
- return result, nil
-}
-
-// SaveZone saves a DNS Zone.
-// https://docapi.eurodns.com/#/dnsprovider/savednszone
-func (c *Client) SaveZone(ctx context.Context, domain string, zone *Zone) error {
- endpoint := c.BaseURL.JoinPath(domain)
-
- if len(zone.URLForwards) == 0 {
- zone.URLForwards = make([]URLForward, 0)
- }
-
- if len(zone.MailForwards) == 0 {
- zone.MailForwards = make([]MailForward, 0)
- }
-
- req, err := newJSONRequest(ctx, http.MethodPut, endpoint, zone)
- if err != nil {
- return err
- }
-
- return c.do(req, nil)
-}
-
-// ValidateZone validates DNS Zone.
-// https://docapi.eurodns.com/#/dnsprovider/checkdnszone
-func (c *Client) ValidateZone(ctx context.Context, domain string, zone *Zone) (*Zone, error) {
- endpoint := c.BaseURL.JoinPath(domain, "check")
-
- if len(zone.URLForwards) == 0 {
- zone.URLForwards = make([]URLForward, 0)
- }
-
- if len(zone.MailForwards) == 0 {
- zone.MailForwards = make([]MailForward, 0)
- }
-
- req, err := newJSONRequest(ctx, http.MethodPost, endpoint, zone)
- if err != nil {
- return nil, err
- }
-
- result := &Zone{}
-
- err = c.do(req, result)
- if err != nil {
- return nil, err
- }
-
- return result, nil
-}
-
-func (c *Client) do(req *http.Request, result any) error {
- req.Header.Set(HeaderAppID, c.appID)
- req.Header.Set(HeaderAPIKey, c.apiKey)
-
- resp, err := c.HTTPClient.Do(req)
- if err != nil {
- return errutils.NewHTTPDoError(req, err)
- }
-
- defer func() { _ = resp.Body.Close() }()
-
- if resp.StatusCode/100 != 2 {
- return parseError(req, resp)
- }
-
- if result == nil {
- return nil
- }
-
- raw, err := io.ReadAll(resp.Body)
- if err != nil {
- return errutils.NewReadResponseError(req, resp.StatusCode, err)
- }
-
- err = json.Unmarshal(raw, result)
- if err != nil {
- return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
- }
-
- return nil
-}
-
-func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
- buf := new(bytes.Buffer)
-
- if payload != nil {
- err := json.NewEncoder(buf).Encode(payload)
- if err != nil {
- return nil, fmt.Errorf("failed to create request JSON body: %w", err)
- }
- }
-
- req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), buf)
- if err != nil {
- return nil, fmt.Errorf("unable to create request: %w", err)
- }
-
- req.Header.Set("Accept", "application/json")
-
- if payload != nil {
- req.Header.Set("Content-Type", "application/json")
- }
-
- return req, nil
-}
-
-func parseError(req *http.Request, resp *http.Response) error {
- raw, _ := io.ReadAll(resp.Body)
-
- var errAPI APIError
-
- err := json.Unmarshal(raw, &errAPI)
- if err != nil {
- return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
- }
-
- return fmt.Errorf("%d: %w", resp.StatusCode, &errAPI)
-}
-
-const DefaultTTL = 600
-
-// TTLRounder rounds the given TTL in seconds to the next accepted value.
-// Accepted TTL values are: 600, 900, 1800,3600, 7200, 14400, 21600, 43200, 86400, 172800, 432000, 604800.
-func TTLRounder(ttl int) int {
- for _, validTTL := range []int{DefaultTTL, 900, 1800, 3600, 7200, 14400, 21600, 43200, 86400, 172800, 432000, 604800} {
- if ttl <= validTTL {
- return validTTL
- }
- }
-
- return DefaultTTL
-}
diff --git a/providers/dns/eurodns/internal/client_test.go b/providers/dns/eurodns/internal/client_test.go
deleted file mode 100644
index 68d1fda84..000000000
--- a/providers/dns/eurodns/internal/client_test.go
+++ /dev/null
@@ -1,310 +0,0 @@
-package internal
-
-import (
- "context"
- "net/http"
- "net/http/httptest"
- "net/url"
- "slices"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/go-acme/lego/v4/providers/dns/internal/ptr"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func mockBuilder() *servermock.Builder[*Client] {
- return servermock.NewBuilder[*Client](
- func(server *httptest.Server) (*Client, error) {
- client, err := NewClient("abc", "secret")
- if err != nil {
- return nil, err
- }
-
- client.HTTPClient = server.Client()
- client.BaseURL, _ = url.Parse(server.URL)
-
- return client, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders().
- With(HeaderAppID, "abc").
- With(HeaderAPIKey, "secret"),
- )
-}
-
-func TestClient_GetZone(t *testing.T) {
- client := mockBuilder().
- Route("GET /example.com",
- servermock.ResponseFromFixture("zone_get.json"),
- ).
- Build(t)
-
- zone, err := client.GetZone(context.Background(), "example.com")
- require.NoError(t, err)
-
- expected := &Zone{
- Name: "example.com",
- DomainConnect: true,
- Records: slices.Concat([]Record{fakeARecord()}),
- URLForwards: []URLForward{fakeURLForward()},
- MailForwards: []MailForward{fakeMailForward()},
- }
-
- assert.Equal(t, expected, zone)
-}
-
-func TestClient_GetZone_error(t *testing.T) {
- client := mockBuilder().
- Route("GET /example.com",
- servermock.ResponseFromFixture("error.json").
- WithStatusCode(http.StatusUnauthorized),
- ).
- Build(t)
-
- _, err := client.GetZone(context.Background(), "example.com")
- require.Error(t, err)
-
- require.EqualError(t, err, "401: INVALID_API_KEY: Invalid API Key")
-}
-
-func TestClient_SaveZone(t *testing.T) {
- client := mockBuilder().
- Route("PUT /example.com",
- servermock.Noop().
- WithStatusCode(http.StatusNoContent),
- servermock.CheckRequestJSONBodyFromFixture("zone_add.json"),
- ).
- Build(t)
-
- record := Record{
- Type: "TXT",
- Host: "_acme-challenge",
- RData: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 600,
- }
-
- zone := &Zone{
- Name: "example.com",
- DomainConnect: true,
- Records: []Record{fakeARecord(), record},
- URLForwards: []URLForward{fakeURLForward()},
- MailForwards: []MailForward{fakeMailForward()},
- }
-
- err := client.SaveZone(context.Background(), "example.com", zone)
- require.NoError(t, err)
-}
-
-func TestClient_SaveZone_emptyForwards(t *testing.T) {
- client := mockBuilder().
- Route("PUT /example.com",
- servermock.Noop().
- WithStatusCode(http.StatusNoContent),
- servermock.CheckRequestJSONBodyFromFixture("zone_add_empty_forwards.json"),
- ).
- Build(t)
-
- record := Record{
- Type: "TXT",
- Host: "_acme-challenge",
- RData: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 600,
- }
-
- zone := &Zone{
- Name: "example.com",
- DomainConnect: true,
- Records: slices.Concat([]Record{fakeARecord(), record}),
- }
-
- err := client.SaveZone(context.Background(), "example.com", zone)
- require.NoError(t, err)
-}
-
-func TestClient_SaveZone_error(t *testing.T) {
- client := mockBuilder().
- Route("PUT /example.com",
- servermock.ResponseFromFixture("error.json").
- WithStatusCode(http.StatusUnauthorized),
- ).
- Build(t)
-
- zone := &Zone{
- Name: "example.com",
- DomainConnect: true,
- Records: []Record{fakeARecord()},
- URLForwards: []URLForward{fakeURLForward()},
- MailForwards: []MailForward{fakeMailForward()},
- }
-
- err := client.SaveZone(context.Background(), "example.com", zone)
- require.Error(t, err)
-
- require.EqualError(t, err, "401: INVALID_API_KEY: Invalid API Key")
-}
-
-func TestClient_ValidateZone(t *testing.T) {
- client := mockBuilder().
- Route("POST /example.com/check",
- servermock.ResponseFromFixture("zone_add_validate_ok.json"),
- servermock.CheckRequestJSONBodyFromFixture("zone_add.json"),
- ).
- Build(t)
-
- record := Record{
- Type: "TXT",
- Host: "_acme-challenge",
- RData: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 600,
- }
-
- zone := &Zone{
- Name: "example.com",
- DomainConnect: true,
- Records: []Record{fakeARecord(), record},
- URLForwards: []URLForward{fakeURLForward()},
- MailForwards: []MailForward{fakeMailForward()},
- }
-
- zone, err := client.ValidateZone(context.Background(), "example.com", zone)
- require.NoError(t, err)
-
- expected := &Zone{
- Name: "example.com",
- DomainConnect: true,
- Records: []Record{fakeARecord(), record},
- URLForwards: []URLForward{fakeURLForward()},
- MailForwards: []MailForward{fakeMailForward()},
- Report: &Report{IsValid: true},
- }
-
- assert.Equal(t, expected, zone)
-}
-
-func TestClient_ValidateZone_report(t *testing.T) {
- client := mockBuilder().
- Route("POST /example.com/check",
- servermock.ResponseFromFixture("zone_add_validate_ko.json"),
- servermock.CheckRequestJSONBodyFromFixture("zone_add.json"),
- ).
- Build(t)
-
- record := Record{
- Type: "TXT",
- Host: "_acme-challenge",
- RData: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 600,
- }
-
- zone := &Zone{
- Name: "example.com",
- DomainConnect: true,
- Records: []Record{fakeARecord(), record},
- URLForwards: []URLForward{fakeURLForward()},
- MailForwards: []MailForward{fakeMailForward()},
- }
-
- zone, err := client.ValidateZone(context.Background(), "example.com", zone)
- require.NoError(t, err)
-
- expected := &Zone{
- Name: "example.com",
- DomainConnect: true,
- Records: []Record{fakeARecord(), record},
- URLForwards: []URLForward{fakeURLForward()},
- MailForwards: []MailForward{fakeMailForward()},
- Report: fakeReport(),
- }
-
- assert.EqualError(t, zone.Report, `record error (ERROR): "120" is not a valid TTL, URL forward error (ERROR): string, mail forward error (ERROR): string, zone error (ERROR): string`)
-
- assert.Equal(t, expected, zone)
-}
-
-func TestClient_ValidateZone_error(t *testing.T) {
- client := mockBuilder().
- Route("POST /example.com/check",
- servermock.ResponseFromFixture("error.json").
- WithStatusCode(http.StatusUnauthorized),
- ).
- Build(t)
-
- zone := &Zone{
- Name: "example.com",
- DomainConnect: true,
- Records: []Record{fakeARecord()},
- URLForwards: []URLForward{fakeURLForward()},
- MailForwards: []MailForward{fakeMailForward()},
- }
-
- _, err := client.ValidateZone(context.Background(), "example.com", zone)
- require.Error(t, err)
-
- require.EqualError(t, err, "401: INVALID_API_KEY: Invalid API Key")
-}
-
-func fakeARecord() Record {
- return Record{
- ID: 1000,
- Type: "A",
- Host: "@",
- TTL: 600,
- RData: "string",
- Updated: ptr.Pointer(true),
- Locked: ptr.Pointer(true),
- IsDynDNS: ptr.Pointer(true),
- Proxy: "ON",
- }
-}
-
-func fakeURLForward() URLForward {
- return URLForward{
- ID: 2000,
- ForwardType: "FRAME",
- Host: "string",
- URL: "string",
- Title: "string",
- Keywords: "string",
- Description: "string",
- Updated: ptr.Pointer(true),
- }
-}
-
-func fakeMailForward() MailForward {
- return MailForward{
- ID: 3000,
- Source: "string",
- Destination: "string",
- Updated: ptr.Pointer(true),
- }
-}
-
-func fakeReport() *Report {
- return &Report{
- IsValid: false,
- RecordErrors: []RecordError{{
- Messages: []string{`"120" is not a valid TTL`},
- Severity: "ERROR",
- Record: fakeARecord(),
- }},
- URLForwardErrors: []URLForwardError{{
- Messages: []string{"string"},
- Severity: "ERROR",
- URLForward: fakeURLForward(),
- }},
- MailForwardErrors: []MailForwardError{{
- Messages: []string{"string"},
- MailForward: fakeMailForward(),
- Severity: "ERROR",
- }},
- ZoneErrors: []ZoneError{{
- Message: "string",
- Severity: "ERROR",
- Records: []Record{fakeARecord()},
- URLForwards: []URLForward{fakeURLForward()},
- MailForwards: []MailForward{fakeMailForward()},
- }},
- }
-}
diff --git a/providers/dns/eurodns/internal/fixtures/error.json b/providers/dns/eurodns/internal/fixtures/error.json
deleted file mode 100644
index 82a334598..000000000
--- a/providers/dns/eurodns/internal/fixtures/error.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "errors": [
- {
- "code": "INVALID_API_KEY",
- "title": "Invalid API Key"
- }
- ]
-}
diff --git a/providers/dns/eurodns/internal/fixtures/zone_add.json b/providers/dns/eurodns/internal/fixtures/zone_add.json
deleted file mode 100644
index db8142357..000000000
--- a/providers/dns/eurodns/internal/fixtures/zone_add.json
+++ /dev/null
@@ -1,46 +0,0 @@
-{
- "name": "example.com",
- "domainConnect": true,
- "records": [
- {
- "id": 1000,
- "type": "A",
- "host": "@",
- "ttl": 600,
- "rdata": "string",
- "updated": true,
- "locked": true,
- "isDynDns": true,
- "proxy": "ON"
- },
- {
- "type": "TXT",
- "host": "_acme-challenge",
- "ttl": 600,
- "rdata": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- "updated": null,
- "locked": null,
- "isDynDns": null
- }
- ],
- "urlForwards": [
- {
- "id": 2000,
- "forwardType": "FRAME",
- "host": "string",
- "url": "string",
- "title": "string",
- "keywords": "string",
- "description": "string",
- "updated": true
- }
- ],
- "mailForwards": [
- {
- "id": 3000,
- "source": "string",
- "destination": "string",
- "updated": true
- }
- ]
-}
diff --git a/providers/dns/eurodns/internal/fixtures/zone_add_empty_forwards.json b/providers/dns/eurodns/internal/fixtures/zone_add_empty_forwards.json
deleted file mode 100644
index 64f8530c9..000000000
--- a/providers/dns/eurodns/internal/fixtures/zone_add_empty_forwards.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "name": "example.com",
- "domainConnect": true,
- "records": [
- {
- "id": 1000,
- "type": "A",
- "host": "@",
- "ttl": 600,
- "rdata": "string",
- "updated": true,
- "locked": true,
- "isDynDns": true,
- "proxy": "ON"
- },
- {
- "type": "TXT",
- "host": "_acme-challenge",
- "ttl": 600,
- "rdata": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- "updated": null,
- "locked": null,
- "isDynDns": null
- }
- ],
- "urlForwards": [],
- "mailForwards": []
-}
diff --git a/providers/dns/eurodns/internal/fixtures/zone_add_validate_ko.json b/providers/dns/eurodns/internal/fixtures/zone_add_validate_ko.json
deleted file mode 100644
index e07d42299..000000000
--- a/providers/dns/eurodns/internal/fixtures/zone_add_validate_ko.json
+++ /dev/null
@@ -1,139 +0,0 @@
-{
- "name": "example.com",
- "domainConnect": true,
- "records": [
- {
- "id": 1000,
- "type": "A",
- "host": "@",
- "ttl": 600,
- "rdata": "string",
- "updated": true,
- "locked": true,
- "isDynDns": true,
- "proxy": "ON"
- },
- {
- "type": "TXT",
- "host": "_acme-challenge",
- "ttl": 600,
- "rdata": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- "updated": null,
- "locked": null,
- "isDynDns": null
- }
- ],
- "urlForwards": [
- {
- "id": 2000,
- "forwardType": "FRAME",
- "host": "string",
- "url": "string",
- "title": "string",
- "keywords": "string",
- "description": "string",
- "updated": true
- }
- ],
- "mailForwards": [
- {
- "id": 3000,
- "source": "string",
- "destination": "string",
- "updated": true
- }
- ],
- "report": {
- "isValid": false,
- "recordErrors": [
- {
- "messages": [
- "\"120\" is not a valid TTL"
- ],
- "record": {
- "id": 1000,
- "type": "A",
- "host": "@",
- "ttl": 600,
- "rdata": "string",
- "updated": true,
- "locked": true,
- "isDynDns": true,
- "proxy": "ON"
- },
- "severity": "ERROR"
- }
- ],
- "urlForwardErrors": [
- {
- "messages": [
- "string"
- ],
- "urlForward": {
- "id": 2000,
- "forwardType": "FRAME",
- "host": "string",
- "url": "string",
- "title": "string",
- "keywords": "string",
- "description": "string",
- "updated": true
- },
- "severity": "ERROR"
- }
- ],
- "mailForwardErrors": [
- {
- "messages": [
- "string"
- ],
- "mailForward": {
- "id": 3000,
- "source": "string",
- "destination": "string",
- "updated": true
- },
- "severity": "ERROR"
- }
- ],
- "zoneErrors": [
- {
- "message": "string",
- "records": [
- {
- "id": 1000,
- "type": "A",
- "host": "@",
- "ttl": 600,
- "rdata": "string",
- "updated": true,
- "locked": true,
- "isDynDns": true,
- "proxy": "ON"
- }
- ],
- "urlForwards": [
- {
- "id": 2000,
- "forwardType": "FRAME",
- "host": "string",
- "url": "string",
- "title": "string",
- "keywords": "string",
- "description": "string",
- "updated": true
- }
- ],
- "mailForwards": [
- {
- "id": 3000,
- "source": "string",
- "destination": "string",
- "updated": true
- }
- ],
- "severity": "ERROR"
- }
- ]
- }
-}
diff --git a/providers/dns/eurodns/internal/fixtures/zone_add_validate_ok.json b/providers/dns/eurodns/internal/fixtures/zone_add_validate_ok.json
deleted file mode 100644
index ba0ddfefb..000000000
--- a/providers/dns/eurodns/internal/fixtures/zone_add_validate_ok.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "name": "example.com",
- "domainConnect": true,
- "records": [
- {
- "id": 1000,
- "type": "A",
- "host": "@",
- "ttl": 600,
- "rdata": "string",
- "updated": true,
- "locked": true,
- "isDynDns": true,
- "proxy": "ON"
- },
- {
- "type": "TXT",
- "host": "_acme-challenge",
- "ttl": 600,
- "rdata": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- "updated": null,
- "locked": null,
- "isDynDns": null
- }
- ],
- "urlForwards": [
- {
- "id": 2000,
- "forwardType": "FRAME",
- "host": "string",
- "url": "string",
- "title": "string",
- "keywords": "string",
- "description": "string",
- "updated": true
- }
- ],
- "mailForwards": [
- {
- "id": 3000,
- "source": "string",
- "destination": "string",
- "updated": true
- }
- ],
- "report": {
- "isValid": true
- }
-}
diff --git a/providers/dns/eurodns/internal/fixtures/zone_get.json b/providers/dns/eurodns/internal/fixtures/zone_get.json
deleted file mode 100644
index ebbc8593e..000000000
--- a/providers/dns/eurodns/internal/fixtures/zone_get.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
- "name": "example.com",
- "domainConnect": true,
- "records": [
- {
- "id": 1000,
- "type": "A",
- "host": "@",
- "ttl": 600,
- "rdata": "string",
- "updated": true,
- "locked": true,
- "isDynDns": true,
- "proxy": "ON"
- }
- ],
- "urlForwards": [
- {
- "id": 2000,
- "forwardType": "FRAME",
- "host": "string",
- "url": "string",
- "title": "string",
- "keywords": "string",
- "description": "string",
- "updated": true
- }
- ],
- "mailForwards": [
- {
- "id": 3000,
- "source": "string",
- "destination": "string",
- "updated": true
- }
- ]
-}
diff --git a/providers/dns/eurodns/internal/fixtures/zone_remove.json b/providers/dns/eurodns/internal/fixtures/zone_remove.json
deleted file mode 100644
index ebbc8593e..000000000
--- a/providers/dns/eurodns/internal/fixtures/zone_remove.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
- "name": "example.com",
- "domainConnect": true,
- "records": [
- {
- "id": 1000,
- "type": "A",
- "host": "@",
- "ttl": 600,
- "rdata": "string",
- "updated": true,
- "locked": true,
- "isDynDns": true,
- "proxy": "ON"
- }
- ],
- "urlForwards": [
- {
- "id": 2000,
- "forwardType": "FRAME",
- "host": "string",
- "url": "string",
- "title": "string",
- "keywords": "string",
- "description": "string",
- "updated": true
- }
- ],
- "mailForwards": [
- {
- "id": 3000,
- "source": "string",
- "destination": "string",
- "updated": true
- }
- ]
-}
diff --git a/providers/dns/eurodns/internal/types.go b/providers/dns/eurodns/internal/types.go
deleted file mode 100644
index 891b02e14..000000000
--- a/providers/dns/eurodns/internal/types.go
+++ /dev/null
@@ -1,136 +0,0 @@
-package internal
-
-import (
- "fmt"
- "strings"
-)
-
-type APIError struct {
- Errors []Error `json:"errors"`
-}
-
-func (a *APIError) Error() string {
- var msg []string
-
- for _, e := range a.Errors {
- msg = append(msg, fmt.Sprintf("%s: %s", e.Code, e.Title))
- }
-
- return strings.Join(msg, ", ")
-}
-
-type Error struct {
- Code string `json:"code"`
- Title string `json:"title"`
-}
-
-type Zone struct {
- Name string `json:"name,omitempty"`
- DomainConnect bool `json:"domainConnect,omitempty"`
- Records []Record `json:"records"`
- URLForwards []URLForward `json:"urlForwards"`
- MailForwards []MailForward `json:"mailForwards"`
- Report *Report `json:"report,omitempty"`
-}
-
-type Record struct {
- ID int `json:"id,omitempty"`
- Type string `json:"type,omitempty"`
- Host string `json:"host,omitempty"`
- TTL int `json:"ttl,omitempty"`
- RData string `json:"rdata,omitempty"`
- Updated *bool `json:"updated"`
- Locked *bool `json:"locked"`
- IsDynDNS *bool `json:"isDynDns"`
- Proxy string `json:"proxy,omitempty"`
-}
-
-type URLForward struct {
- ID int `json:"id,omitempty"`
- ForwardType string `json:"forwardType,omitempty"`
- Host string `json:"host,omitempty"`
- URL string `json:"url,omitempty"`
- Title string `json:"title,omitempty"`
- Keywords string `json:"keywords,omitempty"`
- Description string `json:"description,omitempty"`
- Updated *bool `json:"updated,omitempty"`
-}
-
-type MailForward struct {
- ID int `json:"id,omitempty"`
- Source string `json:"source,omitempty"`
- Destination string `json:"destination,omitempty"`
- Updated *bool `json:"updated,omitempty"`
-}
-
-type Report struct {
- IsValid bool `json:"isValid,omitempty"`
- RecordErrors []RecordError `json:"recordErrors,omitempty"`
- URLForwardErrors []URLForwardError `json:"urlForwardErrors,omitempty"`
- MailForwardErrors []MailForwardError `json:"mailForwardErrors,omitempty"`
- ZoneErrors []ZoneError `json:"zoneErrors,omitempty"`
-}
-
-func (r *Report) Error() string {
- var msg []string
-
- for _, e := range r.RecordErrors {
- msg = append(msg, e.Error())
- }
-
- for _, e := range r.URLForwardErrors {
- msg = append(msg, e.Error())
- }
-
- for _, e := range r.MailForwardErrors {
- msg = append(msg, e.Error())
- }
-
- for _, e := range r.ZoneErrors {
- msg = append(msg, e.Error())
- }
-
- return strings.Join(msg, ", ")
-}
-
-type RecordError struct {
- Messages []string `json:"messages,omitempty"`
- Record Record `json:"record"`
- Severity string `json:"severity,omitempty"`
-}
-
-func (e *RecordError) Error() string {
- return fmt.Sprintf("record error (%s): %s", e.Severity, strings.Join(e.Messages, ", "))
-}
-
-type URLForwardError struct {
- Messages []string `json:"messages,omitempty"`
- URLForward URLForward `json:"urlForward"`
- Severity string `json:"severity,omitempty"`
-}
-
-func (e *URLForwardError) Error() string {
- return fmt.Sprintf("URL forward error (%s): %s", e.Severity, strings.Join(e.Messages, ", "))
-}
-
-type MailForwardError struct {
- Messages []string `json:"messages,omitempty"`
- MailForward MailForward `json:"mailForward"`
- Severity string `json:"severity,omitempty"`
-}
-
-func (e *MailForwardError) Error() string {
- return fmt.Sprintf("mail forward error (%s): %s", e.Severity, strings.Join(e.Messages, ", "))
-}
-
-type ZoneError struct {
- Message string `json:"message,omitempty"`
- Records []Record `json:"records,omitempty"`
- URLForwards []URLForward `json:"urlForwards,omitempty"`
- MailForwards []MailForward `json:"mailForwards,omitempty"`
- Severity string `json:"severity,omitempty"`
-}
-
-func (e *ZoneError) Error() string {
- return fmt.Sprintf("zone error (%s): %s", e.Severity, e.Message)
-}
diff --git a/providers/dns/excedo/excedo.go b/providers/dns/excedo/excedo.go
deleted file mode 100644
index ae9128b94..000000000
--- a/providers/dns/excedo/excedo.go
+++ /dev/null
@@ -1,176 +0,0 @@
-// Package excedo implements a DNS provider for solving the DNS-01 challenge using Excedo.
-package excedo
-
-import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "strconv"
- "sync"
- "time"
-
- "github.com/go-acme/lego/v4/challenge"
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/platform/config/env"
- "github.com/go-acme/lego/v4/providers/dns/excedo/internal"
- "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
-)
-
-// Environment variables names.
-const (
- envNamespace = "EXCEDO_"
-
- EnvAPIURL = envNamespace + "API_URL"
- EnvAPIKey = envNamespace + "API_KEY"
-
- EnvTTL = envNamespace + "TTL"
- EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
- EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
- EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
-)
-
-var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
-
-// Config is used to configure the creation of the DNSProvider.
-type Config struct {
- APIURL string
- APIKey string
-
- PropagationTimeout time.Duration
- PollingInterval time.Duration
- TTL int
- HTTPClient *http.Client
-}
-
-// NewDefaultConfig returns a default configuration for the DNSProvider.
-func NewDefaultConfig() *Config {
- return &Config{
- TTL: env.GetOrDefaultInt(EnvTTL, 60),
- PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 5*time.Minute),
- PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 10*time.Second),
- HTTPClient: &http.Client{
- Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
- },
- }
-}
-
-// DNSProvider implements the challenge.Provider interface.
-type DNSProvider struct {
- config *Config
- client *internal.Client
-
- recordsMu sync.Mutex
- records map[string]int64
-}
-
-// NewDNSProvider returns a DNSProvider instance configured for Excedo.
-func NewDNSProvider() (*DNSProvider, error) {
- values, err := env.Get(EnvAPIURL, EnvAPIKey)
- if err != nil {
- return nil, fmt.Errorf("excedo: %w", err)
- }
-
- config := NewDefaultConfig()
- config.APIURL = values[EnvAPIURL]
- config.APIKey = values[EnvAPIKey]
-
- return NewDNSProviderConfig(config)
-}
-
-// NewDNSProviderConfig return a DNSProvider instance configured for Excedo.
-func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
- if config == nil {
- return nil, errors.New("excedo: the configuration of the DNS provider is nil")
- }
-
- client, err := internal.NewClient(config.APIURL, config.APIKey)
- if err != nil {
- return nil, fmt.Errorf("excedo: %w", err)
- }
-
- if config.HTTPClient != nil {
- client.HTTPClient = config.HTTPClient
- }
-
- client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
-
- return &DNSProvider{
- config: config,
- client: client,
- records: make(map[string]int64),
- }, nil
-}
-
-// Present creates a TXT record using the specified parameters.
-func (d *DNSProvider) Present(domain, token, keyAuth string) error {
- ctx := context.Background()
-
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("excedo: could not find zone for domain %q: %w", domain, err)
- }
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
- if err != nil {
- return fmt.Errorf("excedo: %w", err)
- }
-
- record := internal.Record{
- DomainName: dns01.UnFqdn(authZone),
- Name: subDomain,
- Type: "TXT",
- Content: info.Value,
- TTL: strconv.Itoa(d.config.TTL),
- }
-
- recordID, err := d.client.AddRecord(ctx, record)
- if err != nil {
- return fmt.Errorf("excedo: add record: %w", err)
- }
-
- d.recordsMu.Lock()
- d.records[token] = recordID
- d.recordsMu.Unlock()
-
- return nil
-}
-
-// CleanUp removes the TXT record matching the specified parameters.
-func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
- ctx := context.Background()
-
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("excedo: could not find zone for domain %q: %w", domain, err)
- }
-
- d.recordsMu.Lock()
- recordID, ok := d.records[token]
- d.recordsMu.Unlock()
-
- if !ok {
- return fmt.Errorf("excedo: unknown record ID for '%s'", info.EffectiveFQDN)
- }
-
- err = d.client.DeleteRecord(ctx, dns01.UnFqdn(authZone), strconv.FormatInt(recordID, 10))
- if err != nil {
- return fmt.Errorf("excedo: delete record: %w", err)
- }
-
- d.recordsMu.Lock()
- delete(d.records, token)
- d.recordsMu.Unlock()
-
- return nil
-}
-
-// Timeout returns the timeout and interval to use when checking for DNS propagation.
-// Adjusting here to cope with spikes in propagation times.
-func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
- return d.config.PropagationTimeout, d.config.PollingInterval
-}
diff --git a/providers/dns/excedo/excedo.toml b/providers/dns/excedo/excedo.toml
deleted file mode 100644
index 9f9874c62..000000000
--- a/providers/dns/excedo/excedo.toml
+++ /dev/null
@@ -1,24 +0,0 @@
-Name = "Excedo"
-Description = ''''''
-URL = "https://excedo.se/"
-Code = "excedo"
-Since = "v4.33.0"
-
-Example = '''
-EXCEDO_API_KEY=your-api-key \
-EXCEDO_API_URL=your-base-url \
-lego --dns excedo -d '*.example.com' -d example.com run
-'''
-
-[Configuration]
- [Configuration.Credentials]
- EXCEDO_API_KEY = "API key"
- EXCEDO_API_URL = "API base URL"
- [Configuration.Additional]
- EXCEDO_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 10)"
- EXCEDO_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 300)"
- EXCEDO_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 60)"
- EXCEDO_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
-
-[Links]
- API = "none"
diff --git a/providers/dns/excedo/excedo_test.go b/providers/dns/excedo/excedo_test.go
deleted file mode 100644
index f2350c035..000000000
--- a/providers/dns/excedo/excedo_test.go
+++ /dev/null
@@ -1,210 +0,0 @@
-package excedo
-
-import (
- "net/http/httptest"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester"
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/require"
-)
-
-const envDomain = envNamespace + "DOMAIN"
-
-var envTest = tester.NewEnvTest(EnvAPIURL, EnvAPIKey).WithDomain(envDomain)
-
-func TestNewDNSProvider(t *testing.T) {
- testCases := []struct {
- desc string
- envVars map[string]string
- expected string
- }{
- {
- desc: "success",
- envVars: map[string]string{
- EnvAPIURL: "https://example.com",
- EnvAPIKey: "secret",
- },
- },
- {
- desc: "missing the API key",
- envVars: map[string]string{
- EnvAPIURL: "https://example.com",
- EnvAPIKey: "",
- },
- expected: "excedo: some credentials information are missing: EXCEDO_API_KEY",
- },
- {
- desc: "missing the API URL",
- envVars: map[string]string{
- EnvAPIURL: "",
- EnvAPIKey: "secret",
- },
- expected: "excedo: some credentials information are missing: EXCEDO_API_URL",
- },
- {
- desc: "missing credentials",
- envVars: map[string]string{},
- expected: "excedo: some credentials information are missing: EXCEDO_API_URL,EXCEDO_API_KEY",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(test.envVars)
-
- p, err := NewDNSProvider()
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestNewDNSProviderConfig(t *testing.T) {
- testCases := []struct {
- desc string
- apiURL string
- apiKey string
- expected string
- }{
- {
- desc: "success",
- apiURL: "https://example.com",
- apiKey: "secret",
- },
- {
- desc: "missing the API key",
- apiURL: "https://example.com",
- expected: "excedo: credentials missing",
- },
- {
- desc: "missing the API URL",
- apiKey: "secret",
- expected: "excedo: credentials missing",
- },
- {
- desc: "missing credentials",
- expected: "excedo: credentials missing",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- config := NewDefaultConfig()
- config.APIURL = test.apiURL
- config.APIKey = test.apiKey
-
- p, err := NewDNSProviderConfig(config)
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestLivePresent(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.Present(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func TestLiveCleanUp(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func mockBuilder() *servermock.Builder[*DNSProvider] {
- return servermock.NewBuilder(
- func(server *httptest.Server) (*DNSProvider, error) {
- config := NewDefaultConfig()
- config.APIURL = server.URL
- config.APIKey = "secret"
- config.HTTPClient = server.Client()
-
- p, err := NewDNSProviderConfig(config)
- if err != nil {
- return nil, err
- }
-
- return p, nil
- },
- )
-}
-
-func TestDNSProvider_Present(t *testing.T) {
- provider := mockBuilder().
- Route("GET /authenticate/login/",
- servermock.ResponseFromInternal("login.json"),
- servermock.CheckHeader().
- WithAuthorization("Bearer secret"),
- ).
- Route("POST /dns/addrecord/",
- servermock.ResponseFromInternal("addrecord.json"),
- servermock.CheckHeader().
- WithAuthorization("Bearer session-token"),
- servermock.CheckForm().Strict().
- With("content", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY").
- With("domainName", "example.com").
- With("name", "_acme-challenge").
- With("ttl", "60").
- With("type", "TXT"),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp(t *testing.T) {
- provider := mockBuilder().
- Route("GET /authenticate/login/",
- servermock.ResponseFromInternal("login.json"),
- servermock.CheckHeader().
- WithAuthorization("Bearer secret"),
- ).
- Route("POST /dns/deleterecord/",
- servermock.ResponseFromInternal("deleterecord.json"),
- servermock.CheckHeader().
- WithAuthorization("Bearer session-token"),
- ).
- Build(t)
-
- provider.records["abc"] = 19695822
-
- err := provider.CleanUp("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
diff --git a/providers/dns/excedo/internal/client.go b/providers/dns/excedo/internal/client.go
deleted file mode 100644
index a5d8be88b..000000000
--- a/providers/dns/excedo/internal/client.go
+++ /dev/null
@@ -1,205 +0,0 @@
-package internal
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "mime/multipart"
- "net/http"
- "net/url"
- "sync"
- "time"
-
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/providers/dns/internal/errutils"
- "github.com/go-acme/lego/v4/providers/dns/internal/useragent"
- querystring "github.com/google/go-querystring/query"
-)
-
-type responseChecker interface {
- Check() error
-}
-
-// Client the Excedo API client.
-type Client struct {
- apiKey string
-
- baseURL *url.URL
- HTTPClient *http.Client
-
- token *ExpirableToken
- muToken sync.Mutex
-}
-
-// NewClient creates a new Client.
-func NewClient(apiURL, apiKey string) (*Client, error) {
- if apiURL == "" || apiKey == "" {
- return nil, errors.New("credentials missing")
- }
-
- baseURL, err := url.Parse(apiURL)
- if err != nil {
- return nil, err
- }
-
- return &Client{
- apiKey: apiKey,
- baseURL: baseURL,
- HTTPClient: &http.Client{Timeout: 10 * time.Second},
- }, nil
-}
-
-func (c *Client) AddRecord(ctx context.Context, record Record) (int64, error) {
- payload, err := querystring.Values(record)
- if err != nil {
- return 0, err
- }
-
- endpoint := c.baseURL.JoinPath("/dns/addrecord/")
-
- req, err := newFormRequest(ctx, http.MethodPost, endpoint, payload)
- if err != nil {
- return 0, err
- }
-
- result := new(AddRecordResponse)
-
- err = c.doAuthenticated(ctx, req, result)
- if err != nil {
- return 0, err
- }
-
- return result.RecordID, nil
-}
-
-func (c *Client) DeleteRecord(ctx context.Context, zone, recordID string) error {
- endpoint := c.baseURL.JoinPath("/dns/deleterecord/")
-
- data := map[string]string{
- "domainname": dns01.UnFqdn(zone),
- "recordid": recordID,
- }
-
- req, err := newMultipartRequest(ctx, http.MethodPost, endpoint, data)
- if err != nil {
- return err
- }
-
- result := new(BaseResponse)
-
- err = c.doAuthenticated(ctx, req, result)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (c *Client) GetRecords(ctx context.Context, zone string) (map[string]Zone, error) {
- endpoint := c.baseURL.JoinPath("/dns/getrecords/")
-
- query := endpoint.Query()
- query.Set("domainname", zone)
-
- endpoint.RawQuery = query.Encode()
-
- req, err := newFormRequest(ctx, http.MethodGet, endpoint, nil)
- if err != nil {
- return nil, err
- }
-
- result := new(GetRecordsResponse)
-
- err = c.doAuthenticated(ctx, req, result)
- if err != nil {
- return nil, err
- }
-
- return result.DNS, nil
-}
-
-func (c *Client) do(req *http.Request, result responseChecker) error {
- useragent.SetHeader(req.Header)
-
- resp, err := c.HTTPClient.Do(req)
- if err != nil {
- return errutils.NewHTTPDoError(req, err)
- }
-
- defer func() { _ = resp.Body.Close() }()
-
- if resp.StatusCode/100 != 2 {
- raw, _ := io.ReadAll(resp.Body)
-
- return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
- }
-
- if result == nil {
- return nil
- }
-
- raw, err := io.ReadAll(resp.Body)
- if err != nil {
- return errutils.NewReadResponseError(req, resp.StatusCode, err)
- }
-
- err = json.Unmarshal(raw, result)
- if err != nil {
- return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
- }
-
- return result.Check()
-}
-
-func newMultipartRequest(ctx context.Context, method string, endpoint *url.URL, data map[string]string) (*http.Request, error) {
- buf := new(bytes.Buffer)
-
- writer := multipart.NewWriter(buf)
-
- for k, v := range data {
- err := writer.WriteField(k, v)
- if err != nil {
- return nil, err
- }
- }
-
- err := writer.Close()
- if err != nil {
- return nil, err
- }
-
- body := bytes.NewReader(buf.Bytes())
-
- req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), body)
- if err != nil {
- return nil, fmt.Errorf("unable to create request: %w", err)
- }
-
- req.Header.Set("Content-Type", writer.FormDataContentType())
-
- return req, nil
-}
-
-func newFormRequest(ctx context.Context, method string, endpoint *url.URL, form url.Values) (*http.Request, error) {
- var body io.Reader
-
- if len(form) > 0 {
- body = bytes.NewReader([]byte(form.Encode()))
- } else {
- body = http.NoBody
- }
-
- req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), body)
- if err != nil {
- return nil, fmt.Errorf("unable to create request: %w", err)
- }
-
- if method == http.MethodPost {
- req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- }
-
- return req, nil
-}
diff --git a/providers/dns/excedo/internal/client_test.go b/providers/dns/excedo/internal/client_test.go
deleted file mode 100644
index f4fd52c00..000000000
--- a/providers/dns/excedo/internal/client_test.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package internal
-
-import (
- "net/http/httptest"
- "testing"
- "time"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func mockBuilder() *servermock.Builder[*Client] {
- return servermock.NewBuilder[*Client](
- func(server *httptest.Server) (*Client, error) {
- client, err := NewClient(server.URL, "secret")
- if err != nil {
- return nil, err
- }
-
- client.HTTPClient = server.Client()
-
- return client, nil
- },
- )
-}
-
-func TestClient_AddRecord(t *testing.T) {
- client := mockBuilder().
- Route("POST /dns/addrecord/",
- servermock.ResponseFromFixture("addrecord.json"),
- servermock.CheckHeader().
- WithAuthorization("Bearer session-token"),
- servermock.CheckForm().Strict().
- With("content", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY").
- With("domainName", "example.com").
- With("name", "_acme-challenge").
- With("ttl", "60").
- With("type", "TXT"),
- ).
- Build(t)
-
- client.token = &ExpirableToken{
- Token: "session-token",
- Expires: time.Now().Add(6 * time.Hour),
- }
-
- record := Record{
- DomainName: "example.com",
- Name: "_acme-challenge",
- Type: "TXT",
- Content: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: "60",
- }
-
- recordID, err := client.AddRecord(t.Context(), record)
- require.NoError(t, err)
-
- assert.EqualValues(t, 19695822, recordID)
-}
-
-func TestClient_AddRecord_error(t *testing.T) {
- client := mockBuilder().
- Route("POST /dns/addrecord/",
- servermock.ResponseFromFixture("error.json"),
- ).
- Build(t)
-
- client.token = &ExpirableToken{
- Token: "session-token",
- Expires: time.Now().Add(6 * time.Hour),
- }
-
- record := Record{
- DomainName: "example.com",
- Name: "_acme-challenge",
- Type: "TXT",
- Content: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: "60",
- }
-
- _, err := client.AddRecord(t.Context(), record)
- require.EqualError(t, err, "2003: Required parameter missing")
-}
-
-func TestClient_DeleteRecord(t *testing.T) {
- client := mockBuilder().
- Route("POST /dns/deleterecord/",
- servermock.ResponseFromFixture("deleterecord.json"),
- servermock.CheckHeader().
- WithAuthorization("Bearer session-token"),
- ).
- Build(t)
-
- client.token = &ExpirableToken{
- Token: "session-token",
- Expires: time.Now().Add(6 * time.Hour),
- }
-
- err := client.DeleteRecord(t.Context(), "example.com", "19695822")
- require.NoError(t, err)
-}
-
-func TestClient_GetRecords(t *testing.T) {
- client := mockBuilder().
- Route("GET /dns/getrecords/",
- servermock.ResponseFromFixture("getrecords.json"),
- servermock.CheckHeader().
- WithAuthorization("Bearer session-token"),
- servermock.CheckQueryParameter().Strict().
- With("domainname", "example.com"),
- ).
- Build(t)
-
- client.token = &ExpirableToken{
- Token: "session-token",
- Expires: time.Now().Add(6 * time.Hour),
- }
-
- zones, err := client.GetRecords(t.Context(), "example.com")
- require.NoError(t, err)
-
- expected := map[string]Zone{
- "example.com": {
- DNSType: "type",
- Records: []Record{{
- RecordID: "1234",
- Name: "_acme-challenge.example.com",
- Type: "TXT",
- Content: "txt-value",
- TTL: "60",
- }},
- },
- }
-
- assert.Equal(t, expected, zones)
-}
diff --git a/providers/dns/excedo/internal/fixtures/addrecord.json b/providers/dns/excedo/internal/fixtures/addrecord.json
deleted file mode 100644
index f1f7bf958..000000000
--- a/providers/dns/excedo/internal/fixtures/addrecord.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "code": 1000,
- "desc": "Command completed successfully",
- "recordid": 19695822,
- "session": {
- "accID": "1234",
- "usrID": "1234",
- "status": "active",
- "expire": {
- "date": "2026-03-10 19:03:18",
- "seconds": 5678
- }
- },
- "runtime": 0.2852
-}
diff --git a/providers/dns/excedo/internal/fixtures/deleterecord.json b/providers/dns/excedo/internal/fixtures/deleterecord.json
deleted file mode 100644
index 5c2431b1c..000000000
--- a/providers/dns/excedo/internal/fixtures/deleterecord.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "code": 1000,
- "desc": "Command completed successfully",
- "session": {
- "accID": "1234",
- "usrID": "1234",
- "status": "active",
- "expire": {
- "date": "2026-03-10 19:03:18",
- "seconds": 5678
- }
- },
- "runtime": 0.2852
-}
diff --git a/providers/dns/excedo/internal/fixtures/error.json b/providers/dns/excedo/internal/fixtures/error.json
deleted file mode 100644
index 5a24ec247..000000000
--- a/providers/dns/excedo/internal/fixtures/error.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "code": 2003,
- "desc": "Required parameter missing",
- "missing": [
- "domainname",
- "recordid"
- ],
- "session": {
- "accID": "1234",
- "usrID": "1234",
- "status": "active",
- "expire": {
- "date": "2026-03-10 19:03:18",
- "seconds": 5485
- }
- },
- "runtime": 0.0534
-}
diff --git a/providers/dns/excedo/internal/fixtures/getrecords.json b/providers/dns/excedo/internal/fixtures/getrecords.json
deleted file mode 100644
index 215a8abb2..000000000
--- a/providers/dns/excedo/internal/fixtures/getrecords.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "code": 1000,
- "desc": "Command completed successfully",
- "dns": {
- "example.com": {
- "dnstype": "type",
- "recordusage": {
- "used": 74
- },
- "records": [
- {
- "recordid": "1234",
- "name": "_acme-challenge.example.com",
- "type": "TXT",
- "content": "txt-value",
- "ttl": "60",
- "prio": null,
- "change_date": null
- }
- ]
- }
- }
-}
diff --git a/providers/dns/excedo/internal/fixtures/login.json b/providers/dns/excedo/internal/fixtures/login.json
deleted file mode 100644
index 2defb9843..000000000
--- a/providers/dns/excedo/internal/fixtures/login.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "code": 1000,
- "desc": "Command completed successfully",
- "parameters": {
- "token": "session-token"
- }
-}
diff --git a/providers/dns/excedo/internal/identity.go b/providers/dns/excedo/internal/identity.go
deleted file mode 100644
index 5c9ca119d..000000000
--- a/providers/dns/excedo/internal/identity.go
+++ /dev/null
@@ -1,75 +0,0 @@
-package internal
-
-import (
- "context"
- "fmt"
- "net/http"
- "time"
-)
-
-type ExpirableToken struct {
- Token string
- Expires time.Time
-}
-
-func (t *ExpirableToken) IsExpired() bool {
- return time.Now().After(t.Expires)
-}
-
-func (c *Client) Login(ctx context.Context) (string, error) {
- endpoint := c.baseURL.JoinPath("/authenticate/login/")
-
- req, err := newFormRequest(ctx, http.MethodGet, endpoint, nil)
- if err != nil {
- return "", err
- }
-
- req.Header.Set("Authorization", "Bearer "+c.apiKey)
-
- result := new(LoginResponse)
-
- err = c.do(req, result)
- if err != nil {
- return "", err
- }
-
- if result.Code != 1000 && result.Code != 1300 {
- return "", fmt.Errorf("%d: %s", result.Code, result.Description)
- }
-
- return result.Parameters.Token, nil
-}
-
-func (c *Client) authenticate(ctx context.Context) (string, error) {
- c.muToken.Lock()
- defer c.muToken.Unlock()
-
- if c.token == nil || c.token.IsExpired() {
- token, err := c.Login(ctx)
- if err != nil {
- return "", err
- }
-
- c.token = &ExpirableToken{
- Token: token,
- Expires: time.Now().Add(2*time.Hour - time.Minute),
- }
-
- return token, nil
- }
-
- return c.token.Token, nil
-}
-
-func (c *Client) doAuthenticated(ctx context.Context, req *http.Request, result responseChecker) error {
- token, err := c.authenticate(ctx)
- if err != nil {
- return err
- }
-
- if token != "" {
- req.Header.Set("Authorization", "Bearer "+token)
- }
-
- return c.do(req, result)
-}
diff --git a/providers/dns/excedo/internal/identity_test.go b/providers/dns/excedo/internal/identity_test.go
deleted file mode 100644
index 86b7eb9d8..000000000
--- a/providers/dns/excedo/internal/identity_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package internal
-
-import (
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestClient_Login(t *testing.T) {
- client := mockBuilder().
- Route("GET /authenticate/login/",
- servermock.ResponseFromFixture("login.json"),
- servermock.CheckHeader().
- WithAuthorization("Bearer secret"),
- ).
- Build(t)
-
- token, err := client.Login(t.Context())
- require.NoError(t, err)
-
- assert.Equal(t, "session-token", token)
-}
-
-func TestClient_Login_error(t *testing.T) {
- client := mockBuilder().
- Route("GET /authenticate/login/",
- servermock.ResponseFromFixture("error.json"),
- ).
- Build(t)
-
- _, err := client.Login(t.Context())
- require.EqualError(t, err, "2003: Required parameter missing")
-}
diff --git a/providers/dns/excedo/internal/types.go b/providers/dns/excedo/internal/types.go
deleted file mode 100644
index eb6ce8462..000000000
--- a/providers/dns/excedo/internal/types.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package internal
-
-import "fmt"
-
-type BaseResponse struct {
- Code int `json:"code"`
- Description string `json:"desc"`
-}
-
-func (r BaseResponse) Check() error {
- // Response codes:
- // - 1000: Command completed successfully
- // - 1300: Command completed successfully; no messages
- // - 2001: Command syntax error
- // - 2002: Command use error
- // - 2003: Required parameter missing
- // - 2004: Parameter value range error
- // - 2104: Billing failure
- // - 2200: Authentication error
- // - 2201: Authorization error
- // - 2303: Object does not exist
- // - 2304: Object status prohibits operation
- // - 2309: Object duplicate found
- // - 2400: Command failed
- // - 2500: Command failed; server closing connection
- if r.Code != 1000 && r.Code != 1300 {
- return fmt.Errorf("%d: %s", r.Code, r.Description)
- }
-
- return nil
-}
-
-type GetRecordsResponse struct {
- BaseResponse
-
- DNS map[string]Zone `json:"dns"`
-}
-
-type Zone struct {
- DNSType string `json:"dnstype"`
- Records []Record `json:"records"`
-}
-
-type Record struct {
- DomainName string `json:"domainName,omitempty" url:"domainName,omitempty"`
- RecordID string `json:"recordid,omitempty" url:"recordid,omitempty"`
- Name string `json:"name,omitempty" url:"name,omitempty"`
- Type string `json:"type,omitempty" url:"type,omitempty"`
- Content string `json:"content,omitempty" url:"content,omitempty"`
- TTL string `json:"ttl,omitempty" url:"ttl,omitempty"`
-}
-
-type AddRecordResponse struct {
- BaseResponse
-
- RecordID int64 `json:"recordid"`
-}
-
-type LoginResponse struct {
- BaseResponse
-
- Parameters struct {
- Token string `json:"token"`
- } `json:"parameters"`
-}
diff --git a/providers/dns/gigahostno/internal/client_test.go b/providers/dns/gigahostno/internal/client_test.go
index 8d1298947..aac65bceb 100644
--- a/providers/dns/gigahostno/internal/client_test.go
+++ b/providers/dns/gigahostno/internal/client_test.go
@@ -38,25 +38,55 @@ func TestClient_GetZones(t *testing.T) {
expected := []Zone{
{
- ID: "123",
- Name: "example.com",
- NameDisplay: "example.com",
- Type: "NATIVE",
- Active: "1",
+ ID: "123",
+ Name: "example.com",
+ NameDisplay: "example.com",
+ Type: "NATIVE",
+ Active: "1",
+ Protected: "1",
+ IsRegistered: "1",
+ Updated: false,
+ CustomerID: "16030",
+ DomainRegistrar: "norid",
+ DomainStatus: "active",
+ DomainExpiryDate: "2026-11-23 15:17:38",
+ DomainAutoRenew: "1",
+ ExternalDNS: "0",
+ RecordCount: 4,
},
{
- ID: "226",
- Name: "example.org",
- NameDisplay: "example.org",
- Type: "NATIVE",
- Active: "1",
+ ID: "226",
+ Name: "example.org",
+ NameDisplay: "example.org",
+ Type: "NATIVE",
+ Active: "1",
+ Protected: "1",
+ IsRegistered: "1",
+ Updated: false,
+ CustomerID: "16030",
+ DomainRegistrar: "norid",
+ DomainStatus: "active",
+ DomainExpiryDate: "2026-11-23 14:15:01",
+ DomainAutoRenew: "1",
+ ExternalDNS: "0",
+ RecordCount: 5,
},
{
- ID: "229",
- Name: "example.xn--zckzah",
- NameDisplay: "example.テスト",
- Type: "NATIVE",
- Active: "1",
+ ID: "229",
+ Name: "example.xn--zckzah",
+ NameDisplay: "example.テスト",
+ Type: "NATIVE",
+ Active: "1",
+ Protected: "1",
+ IsRegistered: "1",
+ Updated: false,
+ CustomerID: "16030",
+ DomainRegistrar: "norid",
+ DomainStatus: "active",
+ DomainExpiryDate: "2026-12-01 12:40:48",
+ DomainAutoRenew: "1",
+ ExternalDNS: "0",
+ RecordCount: 4,
},
}
diff --git a/providers/dns/gigahostno/internal/fixtures/zones.json b/providers/dns/gigahostno/internal/fixtures/zones.json
index d45b0ac49..f4d927335 100644
--- a/providers/dns/gigahostno/internal/fixtures/zones.json
+++ b/providers/dns/gigahostno/internal/fixtures/zones.json
@@ -30,7 +30,7 @@
"domain_dnssec_data": null,
"domain_protected_email": null,
"zone_created": "2025-11-23 16:17:29",
- "zone_updated": 1700000000,
+ "zone_updated": false,
"external_dns": "0",
"record_count": 4,
"zone_name_display": "example.com"
@@ -59,7 +59,7 @@
"domain_dnssec_data": null,
"domain_protected_email": null,
"zone_created": "2025-11-23 15:13:27",
- "zone_updated": 1700000000,
+ "zone_updated": false,
"external_dns": "0",
"record_count": 5,
"zone_name_display": "example.org"
@@ -88,7 +88,7 @@
"domain_dnssec_data": null,
"domain_protected_email": null,
"zone_created": "2025-11-23 16:37:15",
- "zone_updated": 1700000000,
+ "zone_updated": false,
"external_dns": "0",
"record_count": 4,
"zone_name_display": "example.\u30C6\u30B9\u30C8"
diff --git a/providers/dns/gigahostno/internal/types.go b/providers/dns/gigahostno/internal/types.go
index e998dc084..cbb7b8b23 100644
--- a/providers/dns/gigahostno/internal/types.go
+++ b/providers/dns/gigahostno/internal/types.go
@@ -26,11 +26,21 @@ type APIResponse[T any] struct {
}
type Zone struct {
- ID string `json:"zone_id,omitempty"`
- Name string `json:"zone_name,omitempty"`
- NameDisplay string `json:"zone_name_display,omitempty"`
- Type string `json:"zone_type,omitempty"`
- Active string `json:"zone_active,omitempty"`
+ ID string `json:"zone_id,omitempty"`
+ Name string `json:"zone_name,omitempty"`
+ NameDisplay string `json:"zone_name_display,omitempty"`
+ Type string `json:"zone_type,omitempty"`
+ Active string `json:"zone_active,omitempty"`
+ Protected string `json:"zone_protected,omitempty"`
+ IsRegistered string `json:"zone_is_registered,omitempty"`
+ Updated bool `json:"zone_updated,omitempty"`
+ CustomerID string `json:"cust_id,omitempty"`
+ DomainRegistrar string `json:"domain_registrar,omitempty"`
+ DomainStatus string `json:"domain_status,omitempty"`
+ DomainExpiryDate string `json:"domain_expiry_date,omitempty"`
+ DomainAutoRenew string `json:"domain_auto_renew,omitempty"`
+ ExternalDNS string `json:"external_dns,omitempty"`
+ RecordCount int `json:"record_count,omitempty"`
}
type Record struct {
diff --git a/providers/dns/godaddy/internal/types.go b/providers/dns/godaddy/internal/types.go
index 3bd5c9560..c1e6d6638 100644
--- a/providers/dns/godaddy/internal/types.go
+++ b/providers/dns/godaddy/internal/types.go
@@ -26,9 +26,9 @@ type APIError struct {
}
func (a APIError) Error() string {
- msg := new(strings.Builder)
+ var msg strings.Builder
- _, _ = fmt.Fprintf(msg, "%s: %s", a.Code, a.Message)
+ msg.WriteString(fmt.Sprintf("%s: %s", a.Code, a.Message))
for _, field := range a.Fields {
msg.WriteString(" ")
diff --git a/providers/dns/gravity/internal/types.go b/providers/dns/gravity/internal/types.go
index 872bc070f..cb6e99083 100644
--- a/providers/dns/gravity/internal/types.go
+++ b/providers/dns/gravity/internal/types.go
@@ -13,17 +13,17 @@ type APIError struct {
}
func (a *APIError) Error() string {
- msg := new(strings.Builder)
+ var msg strings.Builder
- _, _ = fmt.Fprintf(msg, "status: %s, error: %s", a.Status, a.ErrorMsg)
+ msg.WriteString(fmt.Sprintf("status: %s, error: %s", a.Status, a.ErrorMsg))
if a.Code != 0 {
- _, _ = fmt.Fprintf(msg, ", code: %d", a.Code)
+ msg.WriteString(fmt.Sprintf(", code: %d", a.Code))
}
if len(a.Context) != 0 {
for k, v := range a.Context {
- _, _ = fmt.Fprintf(msg, ", %s: %s", k, v)
+ msg.WriteString(fmt.Sprintf(", %s: %s", k, v))
}
}
diff --git a/providers/dns/hetzner/internal/hetznerv1/internal/types.go b/providers/dns/hetzner/internal/hetznerv1/internal/types.go
index 2b38a8a8c..47e8a3f91 100644
--- a/providers/dns/hetzner/internal/hetznerv1/internal/types.go
+++ b/providers/dns/hetzner/internal/hetznerv1/internal/types.go
@@ -16,20 +16,20 @@ type ErrorInfo struct {
}
func (i *ErrorInfo) Error() string {
- msg := new(strings.Builder)
+ var msg strings.Builder
- _, _ = fmt.Fprintf(msg, "%s: %s", i.Code, i.Message)
+ msg.WriteString(fmt.Sprintf("%s: %s", i.Code, i.Message))
if i.Details.Announcement != "" {
- _, _ = fmt.Fprintf(msg, ": %s", i.Details.Announcement)
+ msg.WriteString(fmt.Sprintf(": %s", i.Details.Announcement))
}
for _, limit := range i.Details.Limits {
- _, _ = fmt.Fprintf(msg, "limit: %s", limit.Name)
+ msg.WriteString(fmt.Sprintf("limit: %s", limit.Name))
}
for _, field := range i.Details.Fields {
- _, _ = fmt.Fprintf(msg, "field: %s: %s", field.Name, strings.Join(field.Messages, ", "))
+ msg.WriteString(fmt.Sprintf("field: %s: %s", field.Name, strings.Join(field.Messages, ", ")))
}
return msg.String()
diff --git a/providers/dns/hostinger/internal/types.go b/providers/dns/hostinger/internal/types.go
index c1a02ff8c..534dfa5e5 100644
--- a/providers/dns/hostinger/internal/types.go
+++ b/providers/dns/hostinger/internal/types.go
@@ -12,12 +12,12 @@ type APIError struct {
}
func (a *APIError) Error() string {
- msg := new(strings.Builder)
+ var msg strings.Builder
- _, _ = fmt.Fprintf(msg, "%s: %s", a.CorrelationID, a.Message)
+ msg.WriteString(fmt.Sprintf("%s: %s", a.CorrelationID, a.Message))
for field, values := range a.Errors {
- _, _ = fmt.Fprintf(msg, ": %s: %s", field, strings.Join(values, ", "))
+ msg.WriteString(fmt.Sprintf(": %s: %s", field, strings.Join(values, ", ")))
}
return msg.String()
diff --git a/providers/dns/hosttech/internal/types.go b/providers/dns/hosttech/internal/types.go
index a4b5b564d..854fc4883 100644
--- a/providers/dns/hosttech/internal/types.go
+++ b/providers/dns/hosttech/internal/types.go
@@ -16,12 +16,12 @@ type APIError struct {
}
func (a APIError) Error() string {
- msg := new(strings.Builder)
+ var msg strings.Builder
- _, _ = fmt.Fprintf(msg, "%d: %s", a.StatusCode, a.Message)
+ msg.WriteString(fmt.Sprintf("%d: %s", a.StatusCode, a.Message))
for k, v := range a.Errors {
- _, _ = fmt.Fprintf(msg, " %s: %v", k, v)
+ msg.WriteString(fmt.Sprintf(" %s: %v", k, v))
}
return msg.String()
diff --git a/providers/dns/internal/useragent/useragent.go b/providers/dns/internal/useragent/useragent.go
index 090c9109a..480c35af1 100644
--- a/providers/dns/internal/useragent/useragent.go
+++ b/providers/dns/internal/useragent/useragent.go
@@ -10,12 +10,12 @@ import (
const (
// ourUserAgent is the User-Agent of this underlying library package.
- ourUserAgent = "goacme-lego/4.32.0"
+ ourUserAgent = "goacme-lego/4.31.0"
// ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package.
// values: detach|release
// NOTE: Update this with each tagged release.
- ourUserAgentComment = "detach"
+ ourUserAgentComment = "release"
)
// Get builds and returns the User-Agent string.
diff --git a/providers/dns/leaseweb/internal/client.go b/providers/dns/leaseweb/internal/client.go
deleted file mode 100644
index 01619d49b..000000000
--- a/providers/dns/leaseweb/internal/client.go
+++ /dev/null
@@ -1,216 +0,0 @@
-package internal
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strconv"
- "time"
-
- "github.com/go-acme/lego/v4/providers/dns/internal/errutils"
- "github.com/go-acme/lego/v4/providers/dns/internal/useragent"
-)
-
-const defaultBaseURL = "https://api.leaseweb.com/hosting/v2"
-
-const AuthHeader = "X-LSW-Auth"
-
-// Client the Leaseweb API client.
-type Client struct {
- apiKey string
-
- BaseURL *url.URL
- HTTPClient *http.Client
-}
-
-// NewClient creates a new Client.
-func NewClient(apiKey string) (*Client, error) {
- if apiKey == "" {
- return nil, errors.New("credentials missing")
- }
-
- baseURL, _ := url.Parse(defaultBaseURL)
-
- return &Client{
- apiKey: apiKey,
- BaseURL: baseURL,
- HTTPClient: &http.Client{Timeout: 10 * time.Second},
- }, nil
-}
-
-// CreateRRSet creates a resource record set.
-// https://developer.leaseweb.com/docs/#tag/DNS/operation/createResourceRecordSet
-func (c *Client) CreateRRSet(ctx context.Context, domainName string, rrset RRSet) (*RRSet, error) {
- endpoint := c.BaseURL.JoinPath("domains", domainName, "resourceRecordSets")
-
- req, err := newJSONRequest(ctx, http.MethodPost, endpoint, rrset)
- if err != nil {
- return nil, err
- }
-
- result := &RRSet{}
-
- err = c.do(req, result)
- if err != nil {
- return nil, err
- }
-
- return result, nil
-}
-
-// GetRRSet gets a resource record set.
-// https://developer.leaseweb.com/docs/#tag/DNS/operation/getResourceRecordSet
-func (c *Client) GetRRSet(ctx context.Context, domainName, name, rType string) (*RRSet, error) {
- endpoint := c.BaseURL.JoinPath("domains", domainName, "resourceRecordSets", name, rType)
-
- req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
- if err != nil {
- return nil, err
- }
-
- result := &RRSet{}
-
- err = c.do(req, result)
- if err != nil {
- return nil, err
- }
-
- return result, nil
-}
-
-// UpdateRRSet updates a resource record set.
-// https://developer.leaseweb.com/docs/#tag/DNS/operation/updateResourceRecordSet
-func (c *Client) UpdateRRSet(ctx context.Context, domainName string, rrset RRSet) (*RRSet, error) {
- endpoint := c.BaseURL.JoinPath("domains", domainName, "resourceRecordSets", rrset.Name, rrset.Type)
-
- // Reset values that are not allowed to be updated.
- rrset.Name = ""
- rrset.Type = ""
- rrset.Editable = false
-
- req, err := newJSONRequest(ctx, http.MethodPut, endpoint, rrset)
- if err != nil {
- return nil, err
- }
-
- result := &RRSet{}
-
- err = c.do(req, result)
- if err != nil {
- return nil, err
- }
-
- return result, nil
-}
-
-// DeleteRRSet deletes a resource record set.
-// https://developer.leaseweb.com/docs/#tag/DNS/operation/deleteResourceRecordSet
-func (c *Client) DeleteRRSet(ctx context.Context, domainName, name, rType string) error {
- endpoint := c.BaseURL.JoinPath("domains", domainName, "resourceRecordSets", name, rType)
-
- req, err := newJSONRequest(ctx, http.MethodDelete, endpoint, nil)
- if err != nil {
- return err
- }
-
- return c.do(req, nil)
-}
-
-func (c *Client) do(req *http.Request, result any) error {
- useragent.SetHeader(req.Header)
-
- req.Header.Add(AuthHeader, c.apiKey)
-
- resp, err := c.HTTPClient.Do(req)
- if err != nil {
- return errutils.NewHTTPDoError(req, err)
- }
-
- defer func() { _ = resp.Body.Close() }()
-
- if resp.StatusCode/100 != 2 {
- return parseError(req, resp)
- }
-
- if result == nil {
- return nil
- }
-
- raw, err := io.ReadAll(resp.Body)
- if err != nil {
- return errutils.NewReadResponseError(req, resp.StatusCode, err)
- }
-
- err = json.Unmarshal(raw, result)
- if err != nil {
- return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
- }
-
- return nil
-}
-
-func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
- buf := new(bytes.Buffer)
-
- if payload != nil {
- err := json.NewEncoder(buf).Encode(payload)
- if err != nil {
- return nil, fmt.Errorf("failed to create request JSON body: %w", err)
- }
- }
-
- req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), buf)
- if err != nil {
- return nil, fmt.Errorf("unable to create request: %w", err)
- }
-
- req.Header.Set("Accept", "application/json")
-
- if payload != nil {
- req.Header.Set("Content-Type", "application/json")
- }
-
- return req, nil
-}
-
-func parseError(req *http.Request, resp *http.Response) error {
- raw, _ := io.ReadAll(resp.Body)
-
- var errAPI APIError
-
- err := json.Unmarshal(raw, &errAPI)
- if err != nil {
- if resp.StatusCode == http.StatusNotFound {
- return &NotFoundError{APIError{
- CorrelationID: resp.Header.Get("Correlation-Id"),
- ErrorCode: strconv.Itoa(http.StatusNotFound),
- ErrorMessage: string(raw),
- }}
- }
-
- return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
- }
-
- if errAPI.ErrorCode == strconv.Itoa(http.StatusNotFound) {
- return &NotFoundError{APIError: errAPI}
- }
-
- return &errAPI
-}
-
-// TTLRounder rounds the given TTL in seconds to the next accepted value.
-// Accepted TTL values are: 60, 300, 1800, 3600, 14400, 28800, 43200, 86400.
-func TTLRounder(ttl int) int {
- for _, validTTL := range []int{60, 300, 1800, 3600, 14400, 28800, 43200, 86400} {
- if ttl <= validTTL {
- return validTTL
- }
- }
-
- return 3600
-}
diff --git a/providers/dns/leaseweb/internal/client_test.go b/providers/dns/leaseweb/internal/client_test.go
deleted file mode 100644
index 5762aad4b..000000000
--- a/providers/dns/leaseweb/internal/client_test.go
+++ /dev/null
@@ -1,149 +0,0 @@
-package internal
-
-import (
- "net/http"
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func mockBuilder() *servermock.Builder[*Client] {
- return servermock.NewBuilder[*Client](
- func(server *httptest.Server) (*Client, error) {
- client, err := NewClient("secret")
- if err != nil {
- return nil, err
- }
-
- client.BaseURL, _ = url.Parse(server.URL)
- client.HTTPClient = server.Client()
-
- return client, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders().
- With(AuthHeader, "secret"),
- )
-}
-
-func TestClient_CreateRRSet(t *testing.T) {
- client := mockBuilder().
- Route("POST /domains/example.com/resourceRecordSets",
- servermock.ResponseFromFixture("createResourceRecordSet.json"),
- servermock.CheckRequestJSONBodyFromFixture("createResourceRecordSet-request.json"),
- ).
- Build(t)
-
- rrset := RRSet{
- Content: []string{"ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY"},
- Name: "_acme-challenge.example.com.",
- TTL: 300,
- Type: "TXT",
- }
-
- result, err := client.CreateRRSet(t.Context(), "example.com", rrset)
- require.NoError(t, err)
-
- expected := &RRSet{
- Content: []string{"ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY"},
- Name: "_acme-challenge.example.com.",
- Editable: true,
- TTL: 300,
- Type: "TXT",
- }
-
- assert.Equal(t, expected, result)
-}
-
-func TestClient_GetRRSet(t *testing.T) {
- client := mockBuilder().
- Route("GET /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.ResponseFromFixture("getResourceRecordSet.json"),
- ).
- Build(t)
-
- result, err := client.GetRRSet(t.Context(), "example.com", "_acme-challenge.example.com.", "TXT")
- require.NoError(t, err)
-
- expected := &RRSet{
- Content: []string{"foo", "Now36o-3BmlB623-0c1qCIUmgWVVmDJb88KGl24pqpo"},
- Name: "_acme-challenge.example.com.",
- Editable: true,
- TTL: 3600,
- Type: "TXT",
- }
-
- assert.Equal(t, expected, result)
-}
-
-func TestClient_GetRRSet_error_404(t *testing.T) {
- client := mockBuilder().
- Route("GET /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.ResponseFromFixture("error_404.json").
- WithStatusCode(http.StatusNotFound),
- ).
- Build(t)
-
- _, err := client.GetRRSet(t.Context(), "example.com", "_acme-challenge.example.com.", "TXT")
- require.EqualError(t, err, "404: Resource not found (289346a1-3eaf-4da4-b707-62ef12eb08be)")
-
- target := &NotFoundError{}
- require.ErrorAs(t, err, &target)
-}
-
-func TestClient_UpdateRRSet(t *testing.T) {
- client := mockBuilder().
- Route("PUT /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.ResponseFromFixture("updateResourceRecordSet.json"),
- servermock.CheckRequestJSONBodyFromFixture("updateResourceRecordSet-request.json"),
- ).
- Build(t)
-
- rrset := RRSet{
- Content: []string{"foo", "Now36o-3BmlB623-0c1qCIUmgWVVmDJb88KGl24pqpo", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY"},
- Name: "_acme-challenge.example.com.",
- TTL: 3600,
- Type: "TXT",
- }
-
- result, err := client.UpdateRRSet(t.Context(), "example.com", rrset)
- require.NoError(t, err)
-
- expected := &RRSet{
- Content: []string{"foo", "Now36o-3BmlB623-0c1qCIUmgWVVmDJb88KGl24pqpo", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY"},
- Name: "_acme-challenge.example.com.",
- Editable: true,
- TTL: 3600,
- Type: "TXT",
- }
-
- assert.Equal(t, expected, result)
-}
-
-func TestClient_DeleteRRSet(t *testing.T) {
- client := mockBuilder().
- Route("DELETE /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.Noop().
- WithStatusCode(http.StatusNoContent),
- ).
- Build(t)
-
- err := client.DeleteRRSet(t.Context(), "example.com", "_acme-challenge.example.com.", "TXT")
- require.NoError(t, err)
-}
-
-func TestClient_DeleteRRSet_error(t *testing.T) {
- client := mockBuilder().
- Route("DELETE /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.ResponseFromFixture("error_401.json").
- WithStatusCode(http.StatusUnauthorized),
- ).
- Build(t)
-
- err := client.DeleteRRSet(t.Context(), "example.com", "_acme-challenge.example.com.", "TXT")
- require.EqualError(t, err, "401: You are not authorized to view this resource. (289346a1-3eaf-4da4-b707-62ef12eb08be)")
-}
diff --git a/providers/dns/leaseweb/internal/fixtures/createResourceRecordSet-request.json b/providers/dns/leaseweb/internal/fixtures/createResourceRecordSet-request.json
deleted file mode 100644
index af53fcf04..000000000
--- a/providers/dns/leaseweb/internal/fixtures/createResourceRecordSet-request.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "content": [
- "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY"
- ],
- "name": "_acme-challenge.example.com.",
- "ttl": 300,
- "type": "TXT"
-}
diff --git a/providers/dns/leaseweb/internal/fixtures/createResourceRecordSet.json b/providers/dns/leaseweb/internal/fixtures/createResourceRecordSet.json
deleted file mode 100644
index 8ca040d63..000000000
--- a/providers/dns/leaseweb/internal/fixtures/createResourceRecordSet.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "_links": {
- "self": {
- "href": "/domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT"
- },
- "collection": {
- "href": "/domains/example.com/resourceRecordSets"
- }
- },
- "content": [
- "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY"
- ],
- "editable": true,
- "name": "_acme-challenge.example.com.",
- "ttl": 300,
- "type": "TXT"
-}
diff --git a/providers/dns/leaseweb/internal/fixtures/error_400.json b/providers/dns/leaseweb/internal/fixtures/error_400.json
deleted file mode 100644
index 1a980b6bb..000000000
--- a/providers/dns/leaseweb/internal/fixtures/error_400.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "correlationId": "289346a1-3eaf-4da4-b707-62ef12eb08be",
- "errorCode": "400",
- "errorDetails": {},
- "errorMessage": "The API could not interpret your request correctly."
-}
diff --git a/providers/dns/leaseweb/internal/fixtures/error_401.json b/providers/dns/leaseweb/internal/fixtures/error_401.json
deleted file mode 100644
index 47d8a311d..000000000
--- a/providers/dns/leaseweb/internal/fixtures/error_401.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "correlationId": "289346a1-3eaf-4da4-b707-62ef12eb08be",
- "errorCode": "401",
- "errorMessage": "You are not authorized to view this resource."
-}
diff --git a/providers/dns/leaseweb/internal/fixtures/error_404.json b/providers/dns/leaseweb/internal/fixtures/error_404.json
deleted file mode 100644
index 1deaf5606..000000000
--- a/providers/dns/leaseweb/internal/fixtures/error_404.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "correlationId": "289346a1-3eaf-4da4-b707-62ef12eb08be",
- "errorCode": "404",
- "errorMessage": "Resource not found"
-}
diff --git a/providers/dns/leaseweb/internal/fixtures/getResourceRecordSet.json b/providers/dns/leaseweb/internal/fixtures/getResourceRecordSet.json
deleted file mode 100644
index fd48f60c6..000000000
--- a/providers/dns/leaseweb/internal/fixtures/getResourceRecordSet.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
- "_links": {
- "self": {
- "href": "/domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT"
- },
- "collection": {
- "href": "/domains/example.com/resourceRecordSets"
- }
- },
- "content": [
- "foo",
- "Now36o-3BmlB623-0c1qCIUmgWVVmDJb88KGl24pqpo"
- ],
- "editable": true,
- "name": "_acme-challenge.example.com.",
- "ttl": 3600,
- "type": "TXT"
-}
diff --git a/providers/dns/leaseweb/internal/fixtures/getResourceRecordSet2.json b/providers/dns/leaseweb/internal/fixtures/getResourceRecordSet2.json
deleted file mode 100644
index abf3fb4c3..000000000
--- a/providers/dns/leaseweb/internal/fixtures/getResourceRecordSet2.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "_links": {
- "self": {
- "href": "/domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT"
- },
- "collection": {
- "href": "/domains/example.com/resourceRecordSets"
- }
- },
- "content": [
- "Now36o-3BmlB623-0c1qCIUmgWVVmDJb88KGl24pqpo"
- ],
- "editable": true,
- "name": "_acme-challenge.example.com.",
- "ttl": 3600,
- "type": "TXT"
-}
diff --git a/providers/dns/leaseweb/internal/fixtures/updateResourceRecordSet-request.json b/providers/dns/leaseweb/internal/fixtures/updateResourceRecordSet-request.json
deleted file mode 100644
index e781958c8..000000000
--- a/providers/dns/leaseweb/internal/fixtures/updateResourceRecordSet-request.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "content": [
- "foo",
- "Now36o-3BmlB623-0c1qCIUmgWVVmDJb88KGl24pqpo",
- "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY"
- ],
- "ttl": 3600
-}
diff --git a/providers/dns/leaseweb/internal/fixtures/updateResourceRecordSet-request2.json b/providers/dns/leaseweb/internal/fixtures/updateResourceRecordSet-request2.json
deleted file mode 100644
index 0acc314de..000000000
--- a/providers/dns/leaseweb/internal/fixtures/updateResourceRecordSet-request2.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "content": [
- "foo"
- ],
- "ttl": 3600
-}
diff --git a/providers/dns/leaseweb/internal/fixtures/updateResourceRecordSet.json b/providers/dns/leaseweb/internal/fixtures/updateResourceRecordSet.json
deleted file mode 100644
index 2b877982c..000000000
--- a/providers/dns/leaseweb/internal/fixtures/updateResourceRecordSet.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "_links": {
- "self": {
- "href": "/domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT"
- },
- "collection": {
- "href": "/domains/example.com/resourceRecordSets"
- }
- },
- "content": [
- "foo",
- "Now36o-3BmlB623-0c1qCIUmgWVVmDJb88KGl24pqpo",
- "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY"
- ],
- "editable": true,
- "name": "_acme-challenge.example.com.",
- "ttl": 3600,
- "type": "TXT"
-}
diff --git a/providers/dns/leaseweb/internal/types.go b/providers/dns/leaseweb/internal/types.go
deleted file mode 100644
index 7a4547584..000000000
--- a/providers/dns/leaseweb/internal/types.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package internal
-
-import (
- "encoding/json"
- "fmt"
-)
-
-type NotFoundError struct {
- APIError
-}
-
-type APIError struct {
- CorrelationID string `json:"correlationId,omitempty"`
- ErrorCode string `json:"errorCode,omitempty"`
- ErrorMessage string `json:"errorMessage,omitempty"`
- ErrorDetails json.RawMessage `json:"errorDetails,omitempty"`
-}
-
-func (a *APIError) Error() string {
- msg := fmt.Sprintf("%s: %s (%s)", a.ErrorCode, a.ErrorMessage, a.CorrelationID)
-
- if len(a.ErrorDetails) > 0 {
- msg += fmt.Sprintf(": %s", string(a.ErrorDetails))
- }
-
- return msg
-}
-
-type RRSet struct {
- Content []string `json:"content,omitempty"`
- Name string `json:"name,omitempty"`
- Editable bool `json:"editable,omitempty"`
- TTL int `json:"ttl,omitempty"`
- Type string `json:"type,omitempty"`
-}
diff --git a/providers/dns/leaseweb/leaseweb.go b/providers/dns/leaseweb/leaseweb.go
deleted file mode 100644
index fafaf1c4d..000000000
--- a/providers/dns/leaseweb/leaseweb.go
+++ /dev/null
@@ -1,187 +0,0 @@
-// Package leaseweb implements a DNS provider for solving the DNS-01 challenge using Leaseweb.
-package leaseweb
-
-import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "time"
-
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/platform/config/env"
- "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
- "github.com/go-acme/lego/v4/providers/dns/leaseweb/internal"
-)
-
-// Environment variables names.
-const (
- envNamespace = "LEASEWEB_"
-
- EnvAPIKey = envNamespace + "API_KEY"
-
- EnvTTL = envNamespace + "TTL"
- EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
- EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
- EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
-)
-
-// Config is used to configure the creation of the DNSProvider.
-type Config struct {
- APIKey string
-
- PropagationTimeout time.Duration
- PollingInterval time.Duration
- TTL int
- HTTPClient *http.Client
-}
-
-// NewDefaultConfig returns a default configuration for the DNSProvider.
-func NewDefaultConfig() *Config {
- return &Config{
- TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL),
- PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
- PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
- HTTPClient: &http.Client{
- Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
- },
- }
-}
-
-// DNSProvider implements the challenge.Provider interface.
-type DNSProvider struct {
- config *Config
- client *internal.Client
-}
-
-// NewDNSProvider returns a DNSProvider instance configured for Leaseweb.
-func NewDNSProvider() (*DNSProvider, error) {
- values, err := env.Get(EnvAPIKey)
- if err != nil {
- return nil, fmt.Errorf("leaseweb: %w", err)
- }
-
- config := NewDefaultConfig()
- config.APIKey = values[EnvAPIKey]
-
- return NewDNSProviderConfig(config)
-}
-
-// NewDNSProviderConfig return a DNSProvider instance configured for Leaseweb.
-func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
- if config == nil {
- return nil, errors.New("leaseweb: the configuration of the DNS provider is nil")
- }
-
- client, err := internal.NewClient(config.APIKey)
- if err != nil {
- return nil, fmt.Errorf("leaseweb: %w", err)
- }
-
- if config.HTTPClient != nil {
- client.HTTPClient = config.HTTPClient
- }
-
- client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
-
- return &DNSProvider{
- config: config,
- client: client,
- }, nil
-}
-
-// Present creates a TXT record using the specified parameters.
-func (d *DNSProvider) Present(domain, token, keyAuth string) error {
- ctx := context.Background()
-
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("leaseweb: could not find zone for domain %q: %w", domain, err)
- }
-
- existingRRSet, err := d.client.GetRRSet(ctx, dns01.UnFqdn(authZone), info.EffectiveFQDN, "TXT")
- if err != nil {
- notfoundErr := &internal.NotFoundError{}
- if !errors.As(err, ¬foundErr) {
- return fmt.Errorf("leaseweb: get RRSet: %w", err)
- }
-
- // Create the RRSet.
-
- rrset := internal.RRSet{
- Content: []string{info.Value},
- Name: info.EffectiveFQDN,
- TTL: internal.TTLRounder(d.config.TTL),
- Type: "TXT",
- }
-
- _, err = d.client.CreateRRSet(ctx, dns01.UnFqdn(authZone), rrset)
- if err != nil {
- return fmt.Errorf("leaseweb: create RRSet: %w", err)
- }
-
- return nil
- }
-
- // Update the RRSet.
-
- existingRRSet.Content = append(existingRRSet.Content, info.Value)
-
- _, err = d.client.UpdateRRSet(ctx, dns01.UnFqdn(authZone), *existingRRSet)
- if err != nil {
- return fmt.Errorf("leaseweb: update RRSet: %w", err)
- }
-
- return nil
-}
-
-// CleanUp removes the TXT record matching the specified parameters.
-func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
- ctx := context.Background()
-
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("leaseweb: could not find zone for domain %q: %w", domain, err)
- }
-
- existingRRSet, err := d.client.GetRRSet(ctx, dns01.UnFqdn(authZone), info.EffectiveFQDN, "TXT")
- if err != nil {
- return fmt.Errorf("leaseweb: get RRSet: %w", err)
- }
-
- var content []string
-
- for _, s := range existingRRSet.Content {
- if s != info.Value {
- content = append(content, s)
- }
- }
-
- if len(content) == 0 {
- err = d.client.DeleteRRSet(ctx, dns01.UnFqdn(authZone), info.EffectiveFQDN, "TXT")
- if err != nil {
- return fmt.Errorf("leaseweb: delete RRSet: %w", err)
- }
-
- return nil
- }
-
- existingRRSet.Content = content
-
- _, err = d.client.UpdateRRSet(ctx, dns01.UnFqdn(authZone), *existingRRSet)
- if err != nil {
- return fmt.Errorf("leaseweb: update RRSet: %w", err)
- }
-
- return nil
-}
-
-// Timeout returns the timeout and interval to use when checking for DNS propagation.
-// Adjusting here to cope with spikes in propagation times.
-func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
- return d.config.PropagationTimeout, d.config.PollingInterval
-}
diff --git a/providers/dns/leaseweb/leaseweb.toml b/providers/dns/leaseweb/leaseweb.toml
deleted file mode 100644
index 2c3503291..000000000
--- a/providers/dns/leaseweb/leaseweb.toml
+++ /dev/null
@@ -1,22 +0,0 @@
-Name = "Leaseweb"
-Description = ''''''
-URL = "https://www.leaseweb.com/en/"
-Code = "leaseweb"
-Since = "v4.32.0"
-
-Example = '''
-LEASEWEB_API_KEY="xxxxxxxxxxxxxxxxxxxxx" \
-lego --dns leaseweb -d '*.example.com' -d example.com run
-'''
-
-[Configuration]
- [Configuration.Credentials]
- LEASEWEB_API_KEY = "API key"
- [Configuration.Additional]
- LEASEWEB_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
- LEASEWEB_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 60)"
- LEASEWEB_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 120)"
- LEASEWEB_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
-
-[Links]
- API = "https://developer.leaseweb.com/docs/#tag/DNS"
diff --git a/providers/dns/leaseweb/leaseweb_test.go b/providers/dns/leaseweb/leaseweb_test.go
deleted file mode 100644
index 0450cd2c2..000000000
--- a/providers/dns/leaseweb/leaseweb_test.go
+++ /dev/null
@@ -1,204 +0,0 @@
-package leaseweb
-
-import (
- "net/http"
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester"
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/go-acme/lego/v4/providers/dns/leaseweb/internal"
- "github.com/stretchr/testify/require"
-)
-
-const envDomain = envNamespace + "DOMAIN"
-
-var envTest = tester.NewEnvTest(EnvAPIKey).WithDomain(envDomain)
-
-func TestNewDNSProvider(t *testing.T) {
- testCases := []struct {
- desc string
- envVars map[string]string
- expected string
- }{
- {
- desc: "success",
- envVars: map[string]string{
- EnvAPIKey: "secret",
- },
- },
- {
- desc: "missing credentials",
- envVars: map[string]string{},
- expected: "leaseweb: some credentials information are missing: LEASEWEB_API_KEY",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(test.envVars)
-
- p, err := NewDNSProvider()
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestNewDNSProviderConfig(t *testing.T) {
- testCases := []struct {
- desc string
- apiKey string
- expected string
- }{
- {
- desc: "success",
- apiKey: "secret",
- },
- {
- desc: "missing credentials",
- expected: "leaseweb: credentials missing",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- config := NewDefaultConfig()
- config.APIKey = test.apiKey
-
- p, err := NewDNSProviderConfig(config)
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestLivePresent(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.Present(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func TestLiveCleanUp(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func mockBuilder() *servermock.Builder[*DNSProvider] {
- return servermock.NewBuilder(
- func(server *httptest.Server) (*DNSProvider, error) {
- config := NewDefaultConfig()
- config.APIKey = "secret"
- config.HTTPClient = server.Client()
-
- p, err := NewDNSProviderConfig(config)
- if err != nil {
- return nil, err
- }
-
- p.client.BaseURL, _ = url.Parse(server.URL)
-
- return p, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders().
- With(internal.AuthHeader, "secret"),
- )
-}
-
-func TestDNSProvider_Present_create(t *testing.T) {
- provider := mockBuilder().
- Route("GET /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.ResponseFromInternal("error_404.json").
- WithStatusCode(http.StatusNotFound),
- ).
- Route("POST /domains/example.com/resourceRecordSets",
- servermock.ResponseFromInternal("createResourceRecordSet.json"),
- servermock.CheckRequestJSONBodyFromInternal("createResourceRecordSet-request.json"),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_Present_update(t *testing.T) {
- provider := mockBuilder().
- Route("GET /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.ResponseFromInternal("getResourceRecordSet.json"),
- ).
- Route("PUT /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.ResponseFromInternal("updateResourceRecordSet.json"),
- servermock.CheckRequestJSONBodyFromInternal("updateResourceRecordSet-request.json"),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp_delete(t *testing.T) {
- provider := mockBuilder().
- Route("GET /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.ResponseFromInternal("getResourceRecordSet2.json"),
- ).
- Route("DELETE /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.Noop().
- WithStatusCode(http.StatusNoContent),
- ).
- Build(t)
-
- err := provider.CleanUp("example.com", "abc", "1234d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp_update(t *testing.T) {
- provider := mockBuilder().
- Route("GET /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.ResponseFromInternal("getResourceRecordSet.json"),
- ).
- Route("PUT /domains/example.com/resourceRecordSets/_acme-challenge.example.com./TXT",
- servermock.ResponseFromInternal("updateResourceRecordSet.json"),
- servermock.CheckRequestJSONBodyFromInternal("updateResourceRecordSet-request2.json"),
- ).
- Build(t)
-
- err := provider.CleanUp("example.com", "abc", "1234d==")
- require.NoError(t, err)
-}
diff --git a/providers/dns/liara/internal/client.go b/providers/dns/liara/internal/client.go
index 95c39695b..93cdcf7c8 100644
--- a/providers/dns/liara/internal/client.go
+++ b/providers/dns/liara/internal/client.go
@@ -20,23 +20,17 @@ const defaultBaseURL = "https://dns-service.iran.liara.ir"
type Client struct {
baseURL *url.URL
httpClient *http.Client
-
- teamID string
}
// NewClient creates a new Client.
-func NewClient(hc *http.Client, teamID string) *Client {
+func NewClient(hc *http.Client) *Client {
baseURL, _ := url.Parse(defaultBaseURL)
if hc == nil {
hc = &http.Client{Timeout: 10 * time.Second}
}
- return &Client{
- httpClient: hc,
- baseURL: baseURL,
- teamID: teamID,
- }
+ return &Client{httpClient: hc, baseURL: baseURL}
}
// GetRecords gets the records of a domain.
@@ -44,7 +38,7 @@ func NewClient(hc *http.Client, teamID string) *Client {
func (c *Client) GetRecords(ctx context.Context, domainName string) ([]Record, error) {
endpoint := c.baseURL.JoinPath("api", "v1", "zones", domainName, "dns-records")
- req, err := c.newJSONRequest(ctx, http.MethodGet, endpoint, nil)
+ req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
}
@@ -79,7 +73,7 @@ func (c *Client) GetRecords(ctx context.Context, domainName string) ([]Record, e
func (c *Client) CreateRecord(ctx context.Context, domainName string, record Record) (*Record, error) {
endpoint := c.baseURL.JoinPath("api", "v1", "zones", domainName, "dns-records")
- req, err := c.newJSONRequest(ctx, http.MethodPost, endpoint, record)
+ req, err := newJSONRequest(ctx, http.MethodPost, endpoint, record)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
}
@@ -114,7 +108,7 @@ func (c *Client) CreateRecord(ctx context.Context, domainName string, record Rec
func (c *Client) GetRecord(ctx context.Context, domainName, recordID string) (*Record, error) {
endpoint := c.baseURL.JoinPath("api", "v1", "zones", domainName, "dns-records", recordID)
- req, err := c.newJSONRequest(ctx, http.MethodGet, endpoint, nil)
+ req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, fmt.Errorf("create request: %w", err)
}
@@ -149,7 +143,7 @@ func (c *Client) GetRecord(ctx context.Context, domainName, recordID string) (*R
func (c *Client) DeleteRecord(ctx context.Context, domainName, recordID string) error {
endpoint := c.baseURL.JoinPath("api", "v1", "zones", domainName, "dns-records", recordID)
- req, err := c.newJSONRequest(ctx, http.MethodDelete, endpoint, nil)
+ req, err := newJSONRequest(ctx, http.MethodDelete, endpoint, nil)
if err != nil {
return fmt.Errorf("create request: %w", err)
}
@@ -168,14 +162,7 @@ func (c *Client) DeleteRecord(ctx context.Context, domainName, recordID string)
return nil
}
-func (c *Client) newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
- if c.teamID != "" {
- query := endpoint.Query()
- query.Set("teamID", c.teamID)
-
- endpoint.RawQuery = query.Encode()
- }
-
+func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
buf := new(bytes.Buffer)
if payload != nil {
diff --git a/providers/dns/liara/internal/client_test.go b/providers/dns/liara/internal/client_test.go
index b6d007046..57ac7e8b3 100644
--- a/providers/dns/liara/internal/client_test.go
+++ b/providers/dns/liara/internal/client_test.go
@@ -13,10 +13,10 @@ import (
const apiKey = "key"
-func mockBuilder(teamID string) *servermock.Builder[*Client] {
+func mockBuilder() *servermock.Builder[*Client] {
return servermock.NewBuilder[*Client](
func(server *httptest.Server) (*Client, error) {
- client := NewClient(OAuthStaticAccessToken(server.Client(), apiKey), teamID)
+ client := NewClient(OAuthStaticAccessToken(server.Client(), apiKey))
client.baseURL, _ = url.Parse(server.URL)
return client, nil
@@ -26,7 +26,7 @@ func mockBuilder(teamID string) *servermock.Builder[*Client] {
}
func TestClient_GetRecords(t *testing.T) {
- client := mockBuilder("").
+ client := mockBuilder().
Route("GET /api/v1/zones/example.com/dns-records", servermock.ResponseFromFixture("RecordsResponse.json")).
Build(t)
@@ -50,7 +50,7 @@ func TestClient_GetRecords(t *testing.T) {
}
func TestClient_GetRecord(t *testing.T) {
- client := mockBuilder("").
+ client := mockBuilder().
Route("GET /api/v1/zones/example.com/dns-records/123", servermock.ResponseFromFixture("RecordResponse.json")).
Build(t)
@@ -72,7 +72,7 @@ func TestClient_GetRecord(t *testing.T) {
}
func TestClient_CreateRecord(t *testing.T) {
- client := mockBuilder("").
+ client := mockBuilder().
Route("POST /api/v1/zones/example.com/dns-records",
servermock.ResponseFromFixture("RecordResponse.json").
WithStatusCode(http.StatusCreated),
@@ -108,47 +108,8 @@ func TestClient_CreateRecord(t *testing.T) {
assert.Equal(t, expected, record)
}
-func TestClient_CreateRecord_withTeamID(t *testing.T) {
- client := mockBuilder("123").
- Route("POST /api/v1/zones/example.com/dns-records",
- servermock.ResponseFromFixture("RecordResponse.json").
- WithStatusCode(http.StatusCreated),
- servermock.CheckRequestJSONBody(`{"name":"string","type":"string","ttl":3600,"contents":[{"text":"string"}]}`),
- servermock.CheckQueryParameter().Strict().With("teamID", "123"),
- ).
- Build(t)
-
- data := Record{
- Type: "string",
- Name: "string",
- Contents: []Content{
- {
- Text: "string",
- },
- },
- TTL: 3600,
- }
-
- record, err := client.CreateRecord(t.Context(), "example.com", data)
- require.NoError(t, err)
-
- expected := &Record{
- ID: "string",
- Type: "string",
- Name: "string",
- Contents: []Content{
- {
- Text: "string",
- },
- },
- TTL: 3600,
- }
-
- assert.Equal(t, expected, record)
-}
-
func TestClient_DeleteRecord(t *testing.T) {
- client := mockBuilder("").
+ client := mockBuilder().
Route("DELETE /api/v1/zones/example.com/dns-records/123",
servermock.Noop().
WithStatusCode(http.StatusNoContent)).
@@ -159,7 +120,7 @@ func TestClient_DeleteRecord(t *testing.T) {
}
func TestClient_DeleteRecord_NotFound_Response(t *testing.T) {
- client := mockBuilder("").
+ client := mockBuilder().
Route("DELETE /api/v1/zones/example.com/dns-records/123",
servermock.Noop().
WithStatusCode(http.StatusNotFound)).
@@ -170,7 +131,7 @@ func TestClient_DeleteRecord_NotFound_Response(t *testing.T) {
}
func TestClient_DeleteRecord_error(t *testing.T) {
- client := mockBuilder("").
+ client := mockBuilder().
Route("DELETE /api/v1/zones/example.com/dns-records/123",
servermock.ResponseFromFixture("error.json").
WithStatusCode(http.StatusUnauthorized)).
diff --git a/providers/dns/liara/liara.go b/providers/dns/liara/liara.go
index c7e403eed..b91b004cc 100644
--- a/providers/dns/liara/liara.go
+++ b/providers/dns/liara/liara.go
@@ -23,7 +23,6 @@ const (
envNamespace = "LIARA_"
EnvAPIKey = envNamespace + "API_KEY"
- EnvTeamID = envNamespace + "TEAM_ID"
EnvTTL = envNamespace + "TTL"
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
@@ -40,9 +39,7 @@ var _ challenge.ProviderTimeout = (*DNSProvider)(nil)
// Config is used to configure the creation of the DNSProvider.
type Config struct {
- APIKey string
- TeamID string
-
+ APIKey string
TTL int
PropagationTimeout time.Duration
PollingInterval time.Duration
@@ -80,7 +77,6 @@ func NewDNSProvider() (*DNSProvider, error) {
config := NewDefaultConfig()
config.APIKey = values[EnvAPIKey]
- config.TeamID = env.GetOrFile(EnvTeamID)
return NewDNSProviderConfig(config)
}
@@ -116,7 +112,6 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
clientdebug.Wrap(
internal.OAuthStaticAccessToken(retryClient.StandardClient(), config.APIKey),
),
- config.TeamID,
)
return &DNSProvider{
diff --git a/providers/dns/liara/liara.toml b/providers/dns/liara/liara.toml
index f471de04e..4ed53ec75 100644
--- a/providers/dns/liara/liara.toml
+++ b/providers/dns/liara/liara.toml
@@ -13,7 +13,6 @@ lego --dns liara -d '*.example.com' -d example.com run
[Configuration.Credentials]
LIARA_API_KEY = "The API key"
[Configuration.Additional]
- LIARA_TEAM_ID = "The team ID to access services in a team"
LIARA_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
LIARA_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 60)"
LIARA_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 3600)"
diff --git a/providers/dns/manual/manual.toml b/providers/dns/manual/manual.toml
index fc47a8fae..aca67536d 100644
--- a/providers/dns/manual/manual.toml
+++ b/providers/dns/manual/manual.toml
@@ -31,13 +31,13 @@ If you accept the linked Terms of Service, hit `Enter`.
[INFO] acme: Registering account for you@example.com
!!!! HEADS UP !!!!
-Your account credentials have been saved in your
-configuration directory at "./.lego/accounts".
+ Your account credentials have been saved in your Let's Encrypt
+ configuration directory at "./.lego/accounts".
-You should make a secure backup of this folder now. This
-configuration directory will also contain private keys
-generated by lego and certificates obtained from the ACME
-server. Making regular backups of this folder is ideal.
+ You should make a secure backup of this folder now. This
+ configuration directory will also contain certificates and
+ private keys obtained from Let's Encrypt so making regular
+ backups of this folder is ideal.
[INFO] [example.com] acme: Obtaining bundled SAN certificate
[INFO] [example.com] AuthURL: https://acme-v02.api.letsencrypt.org/acme/authz-v3/2345678901
[INFO] [example.com] acme: Could not find solver for: tls-alpn-01
diff --git a/providers/dns/mittwald/internal/types.go b/providers/dns/mittwald/internal/types.go
index 86cdf065c..ce49cb820 100644
--- a/providers/dns/mittwald/internal/types.go
+++ b/providers/dns/mittwald/internal/types.go
@@ -61,14 +61,14 @@ type APIError struct {
}
func (a APIError) Error() string {
- msg := new(strings.Builder)
+ var msg strings.Builder
- _, _ = fmt.Fprintf(msg, "%s: %s", a.Type, a.Message)
+ msg.WriteString(fmt.Sprintf("%s: %s", a.Type, a.Message))
if len(a.ValidationErrors) > 0 {
for _, validationError := range a.ValidationErrors {
- _, _ = fmt.Fprintf(msg, " [%s: %s (%s, %s)]",
- validationError.Type, validationError.Message, validationError.Path, validationError.Context.Format)
+ msg.WriteString(fmt.Sprintf(" [%s: %s (%s, %s)]",
+ validationError.Type, validationError.Message, validationError.Path, validationError.Context.Format))
}
}
diff --git a/providers/dns/namesurfer/internal/client.go b/providers/dns/namesurfer/internal/client.go
deleted file mode 100644
index e40a7988c..000000000
--- a/providers/dns/namesurfer/internal/client.go
+++ /dev/null
@@ -1,226 +0,0 @@
-package internal
-
-import (
- "bytes"
- "context"
- "crypto/hmac"
- "crypto/sha256"
- "encoding/hex"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "slices"
- "strconv"
- "strings"
- "time"
-
- "github.com/go-acme/lego/v4/providers/dns/internal/errutils"
-)
-
-type Client struct {
- apiKey string
- apiSecret string
-
- BaseURL *url.URL
- HTTPClient *http.Client
-}
-
-func NewClient(baseURL, apiKey, apiSecret string) (*Client, error) {
- if apiKey == "" || apiSecret == "" {
- return nil, errors.New("credentials missing")
- }
-
- if baseURL == "" {
- return nil, errors.New("base URL missing")
- }
-
- apiEndpoint, err := url.Parse(baseURL)
- if err != nil {
- return nil, err
- }
-
- return &Client{
- apiKey: apiKey,
- apiSecret: apiSecret,
- BaseURL: apiEndpoint.JoinPath("jsonrpc10"),
- HTTPClient: &http.Client{
- Timeout: 5 * time.Second,
- },
- }, nil
-}
-
-// AddDNSRecord adds a DNS record.
-// http://95.128.3.201:8053/API/NSService_10#addDNSRecord
-func (d *Client) AddDNSRecord(ctx context.Context, zoneName, viewName string, record DNSNode) error {
- digest := d.computeDigest(
- zoneName,
- viewName,
- record.Name,
- record.Type,
- strconv.Itoa(record.TTL),
- record.Data,
- )
-
- // JSON-RPC 1.0 requires positional parameters array
- params := []any{
- digest,
- zoneName,
- viewName,
- record,
- }
-
- var ok bool
-
- err := d.doRequest(ctx, "addDNSRecord", params, &ok)
- if err != nil {
- return err
- }
-
- if !ok {
- return errors.New("addDNSRecord failed")
- }
-
- return nil
-}
-
-// UpdateDNSHost updates a DNS host record.
-// Passing an empty newNode removes the oldNode.
-// http://95.128.3.201:8053/API/NSService_10#updateDNSHost
-func (d *Client) UpdateDNSHost(ctx context.Context, zoneName, viewName string, oldNode, newNode DNSNode) error {
- digest := d.computeDigest(zoneName, viewName)
-
- // JSON-RPC 1.0 requires positional parameters array
- params := []any{
- digest,
- zoneName,
- viewName,
- oldNode,
- newNode,
- }
-
- var ok bool
-
- err := d.doRequest(ctx, "updateDNSHost", params, &ok)
- if err != nil {
- return err
- }
-
- if !ok {
- return errors.New("updateDNSHost failed")
- }
-
- return nil
-}
-
-// SearchDNSHosts searches for DNS host records.
-// http://95.128.3.201:8053/API/NSService_10#searchDNSHosts
-func (d *Client) SearchDNSHosts(ctx context.Context, pattern string) ([]DNSNode, error) {
- digest := d.computeDigest(pattern)
-
- // JSON-RPC 1.0 requires positional parameters array
- params := []any{
- digest,
- pattern,
- }
-
- var nodes []DNSNode
-
- err := d.doRequest(ctx, "searchDNSHosts", params, &nodes)
- if err != nil {
- return nil, err
- }
-
- return nodes, nil
-}
-
-// ListZones lists DNS zones.
-// http://95.128.3.201:8053/API/NSService_10#listZones
-func (d *Client) ListZones(ctx context.Context, mode string) ([]DNSZone, error) {
- digest := d.computeDigest()
-
- // JSON-RPC 1.0 requires positional parameters array
- params := []any{
- digest,
- mode,
- }
-
- var zones []DNSZone
-
- err := d.doRequest(ctx, "listZones", params, &zones)
- if err != nil {
- return nil, err
- }
-
- return zones, nil
-}
-
-func (d *Client) doRequest(ctx context.Context, method string, params []any, result any) error {
- payload := APIRequest{
- ID: 1,
- Method: method,
- Params: slices.Concat([]any{d.apiKey}, params),
- }
-
- buf := new(bytes.Buffer)
-
- err := json.NewEncoder(buf).Encode(payload)
- if err != nil {
- return fmt.Errorf("failed to create request JSON body: %w", err)
- }
-
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, d.BaseURL.String(), buf)
- if err != nil {
- return fmt.Errorf("unable to create request: %w", err)
- }
-
- req.Header.Set("Content-Type", "application/json")
- req.Header.Set("Accept", "application/json")
-
- resp, err := d.HTTPClient.Do(req)
- if err != nil {
- return errutils.NewHTTPDoError(req, err)
- }
-
- defer func() { _ = resp.Body.Close() }()
-
- raw, err := io.ReadAll(resp.Body)
- if err != nil {
- return errutils.NewReadResponseError(req, resp.StatusCode, err)
- }
-
- if resp.StatusCode/100 != 2 {
- return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
- }
-
- var rpcResp APIResponse
-
- err = json.Unmarshal(raw, &rpcResp)
- if err != nil {
- return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
- }
-
- if rpcResp.Error != nil {
- return rpcResp.Error
- }
-
- err = json.Unmarshal(rpcResp.Result, result)
- if err != nil {
- return fmt.Errorf("unable to unmarshal response: %w: %s", err, rpcResp.Result)
- }
-
- return nil
-}
-
-func (d *Client) computeDigest(parts ...string) string {
- params := []string{d.apiKey}
- params = append(params, parts...)
- params = append(params, d.apiSecret)
-
- mac := hmac.New(sha256.New, []byte(d.apiSecret))
- mac.Write([]byte(strings.Join(params, "&")))
-
- return hex.EncodeToString(mac.Sum(nil))
-}
diff --git a/providers/dns/namesurfer/internal/client_test.go b/providers/dns/namesurfer/internal/client_test.go
deleted file mode 100644
index 9e8f917bc..000000000
--- a/providers/dns/namesurfer/internal/client_test.go
+++ /dev/null
@@ -1,158 +0,0 @@
-package internal
-
-import (
- "net/http/httptest"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func mockBuilder() *servermock.Builder[*Client] {
- return servermock.NewBuilder[*Client](
- func(server *httptest.Server) (*Client, error) {
- client, err := NewClient(server.URL, "user", "secret")
- if err != nil {
- return nil, err
- }
-
- client.HTTPClient = server.Client()
-
- return client, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders(),
- )
-}
-
-func TestClient_AddDNSRecord(t *testing.T) {
- client := mockBuilder().
- Route("POST /jsonrpc10",
- servermock.ResponseFromFixture("addDNSRecord.json"),
- servermock.CheckRequestJSONBodyFromFixture("addDNSRecord-request.json"),
- ).
- Build(t)
-
- record := DNSNode{
- Name: "_acme-challenge",
- Type: "TXT",
- Data: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 300,
- }
-
- err := client.AddDNSRecord(t.Context(), "example.com", "viewA", record)
- require.NoError(t, err)
-}
-
-func TestClient_AddDNSRecord_error(t *testing.T) {
- client := mockBuilder().
- Route("POST /jsonrpc10",
- servermock.ResponseFromFixture("error.json"),
- ).
- Build(t)
-
- record := DNSNode{
- Name: "_acme-challenge",
- Type: "TXT",
- Data: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 300,
- }
-
- err := client.AddDNSRecord(t.Context(), "example.com", "viewA", record)
- require.EqualError(t, err, "code: Server.Keyfailure, "+
- "filename: service, line: 13, "+
- "message: Unknown keyname user, "+
- `detail: Traceback (most recent call last): File "/usr/local/namesurfer/python/lib/python2.6/site-packages/ladon/server/dispatcher.py", line 159, in dispatch_request result = self.call_method(method,req_dict,tc,export_dict,log_line) File "/usr/local/namesurfer/python/lib/python2.6/site-packages/ladon/server/dispatcher.py", line 96, in call_method result = getattr(service_class_instance,req_dict['methodname'])(*args) File "/usr/local/namesurfer/python/lib/python2.6/site-packages/ladon/ladonizer/decorator.py", line 77, in injector res = f(*args,**kw) File "/usr/local/namesurfer/webui2/webui/service/service10/NSService_10.py", line 502, in addDNSRecord key = validate_key(keyname, digest, [zonename, viewname, record.name, record.type, str(record.ttl), record.data]) File "/usr/local/namesurfer/webui2/webui/service/base/implementation.py", line 63, in validate_key raise ApiFault('Server.Keyfailure', 'Unknown keyname %s' % keyname) ApiFault: service(13): Unknown keyname user `)
-}
-
-func TestClient_UpdateDNSHost(t *testing.T) {
- client := mockBuilder().
- Route("POST /jsonrpc10",
- servermock.ResponseFromFixture("updateDNSHost.json"),
- servermock.CheckRequestJSONBodyFromFixture("updateDNSHost-request.json"),
- ).
- Build(t)
-
- record := DNSNode{
- Name: "_acme-challenge",
- Type: "TXT",
- Data: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: 300,
- }
-
- err := client.UpdateDNSHost(t.Context(), "example.com", "viewA", record, DNSNode{})
- require.NoError(t, err)
-}
-
-func TestClient_SearchDNSHosts(t *testing.T) {
- client := mockBuilder().
- Route("POST /jsonrpc10",
- servermock.ResponseFromFixture("searchDNSHosts.json"),
- servermock.CheckRequestJSONBodyFromFixture("searchDNSHosts-request.json"),
- ).
- Build(t)
-
- records, err := client.SearchDNSHosts(t.Context(), "value")
- require.NoError(t, err)
-
- expected := []DNSNode{
- {Name: "foo", Type: "TXT", Data: "xxx", TTL: 300},
- {Name: "_acme-challenge", Type: "TXT", Data: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY", TTL: 300},
- {Name: "bar", Type: "A", Data: "yyy", TTL: 300},
- }
-
- assert.Equal(t, expected, records)
-}
-
-func TestClient_ListZones(t *testing.T) {
- client := mockBuilder().
- Route("POST /jsonrpc10",
- servermock.ResponseFromFixture("listZones.json"),
- servermock.CheckRequestJSONBodyFromFixture("listZones-request.json"),
- ).
- Build(t)
-
- zones, err := client.ListZones(t.Context(), "value")
- require.NoError(t, err)
-
- expected := []DNSZone{
- {Name: "example.com", View: "viewA"},
- {Name: "example.org", View: "viewB"},
- {Name: "example.net", View: "viewC"},
- }
-
- assert.Equal(t, expected, zones)
-}
-
-func TestClient_computeDigest(t *testing.T) {
- client, err := NewClient("https://test.example.com", "testkey", "testsecret")
- require.NoError(t, err)
-
- testCases := []struct {
- desc string
- parts []string
- expected string
- }{
- {
- desc: "no parts",
- parts: []string{},
- expected: "99b5dcdc19bfc0ce2af3fe848f4bcb6f7beb352e9599e8ba50544d86de567282",
- },
- {
- desc: "parts",
- parts: []string{"zone.example.com", "default"},
- expected: "94efef76383889b1ae620582a25d1c3aa9bd9ba9ac4bdccdf4aefbc3ae6e8329",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- t.Parallel()
-
- digest := client.computeDigest(test.parts...)
-
- assert.Equal(t, test.expected, digest)
- })
- }
-}
diff --git a/providers/dns/namesurfer/internal/fixtures/addDNSRecord-request.json b/providers/dns/namesurfer/internal/fixtures/addDNSRecord-request.json
deleted file mode 100644
index 660109aae..000000000
--- a/providers/dns/namesurfer/internal/fixtures/addDNSRecord-request.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "id": 1,
- "method": "addDNSRecord",
- "params": [
- "user",
- "4fcc5fa29531709b0381c8debea127a6a26e71cb9491727876819cf5805c4990",
- "example.com",
- "viewA",
- {
- "name": "_acme-challenge",
- "type": "TXT",
- "data": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- "ttl": 300
- }
- ]
-}
diff --git a/providers/dns/namesurfer/internal/fixtures/addDNSRecord.json b/providers/dns/namesurfer/internal/fixtures/addDNSRecord.json
deleted file mode 100644
index f41779e30..000000000
--- a/providers/dns/namesurfer/internal/fixtures/addDNSRecord.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "id": 1,
- "result": true
-}
diff --git a/providers/dns/namesurfer/internal/fixtures/error.json b/providers/dns/namesurfer/internal/fixtures/error.json
deleted file mode 100644
index 8ddf8df25..000000000
--- a/providers/dns/namesurfer/internal/fixtures/error.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "result": null,
- "error": {
- "filename": "service",
- "lineno": 13,
- "code": "Server.Keyfailure",
- "string": "Unknown keyname user",
- "detail": [
- "Traceback (most recent call last):",
- " File \"/usr/local/namesurfer/python/lib/python2.6/site-packages/ladon/server/dispatcher.py\", line 159, in dispatch_request",
- " result = self.call_method(method,req_dict,tc,export_dict,log_line)",
- " File \"/usr/local/namesurfer/python/lib/python2.6/site-packages/ladon/server/dispatcher.py\", line 96, in call_method",
- " result = getattr(service_class_instance,req_dict['methodname'])(*args)",
- " File \"/usr/local/namesurfer/python/lib/python2.6/site-packages/ladon/ladonizer/decorator.py\", line 77, in injector",
- " res = f(*args,**kw)",
- " File \"/usr/local/namesurfer/webui2/webui/service/service10/NSService_10.py\", line 502, in addDNSRecord",
- " key = validate_key(keyname, digest, [zonename, viewname, record.name, record.type, str(record.ttl), record.data])",
- " File \"/usr/local/namesurfer/webui2/webui/service/base/implementation.py\", line 63, in validate_key",
- " raise ApiFault('Server.Keyfailure', 'Unknown keyname %s' % keyname)",
- "ApiFault: service(13): Unknown keyname user",
- ""
- ]
- }
-}
diff --git a/providers/dns/namesurfer/internal/fixtures/listZones-request.json b/providers/dns/namesurfer/internal/fixtures/listZones-request.json
deleted file mode 100644
index 06689de7a..000000000
--- a/providers/dns/namesurfer/internal/fixtures/listZones-request.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "id": 1,
- "method": "listZones",
- "params": [
- "user",
- "2739461ea1a3dc51302993f724f40228409c53b78025d8d7b1d7bba3c1bf2d66",
- "value"
- ]
-}
diff --git a/providers/dns/namesurfer/internal/fixtures/listZones.json b/providers/dns/namesurfer/internal/fixtures/listZones.json
deleted file mode 100644
index 37fa2053b..000000000
--- a/providers/dns/namesurfer/internal/fixtures/listZones.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "id": 1,
- "result": [
- {
- "name": "example.com",
- "view": "viewA"
- },
- {
- "name": "example.org",
- "view": "viewB"
- },
- {
- "name": "example.net",
- "view": "viewC"
- }
- ]
-}
diff --git a/providers/dns/namesurfer/internal/fixtures/searchDNSHosts-request.json b/providers/dns/namesurfer/internal/fixtures/searchDNSHosts-request.json
deleted file mode 100644
index 4a88340e2..000000000
--- a/providers/dns/namesurfer/internal/fixtures/searchDNSHosts-request.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "id": 1,
- "method": "searchDNSHosts",
- "params": [
- "user",
- "02cf1a2f6e124507d16738d595f583932185313fc96afc2d8404960acaec29b4",
- "value"
- ]
-}
diff --git a/providers/dns/namesurfer/internal/fixtures/searchDNSHosts.json b/providers/dns/namesurfer/internal/fixtures/searchDNSHosts.json
deleted file mode 100644
index 822459148..000000000
--- a/providers/dns/namesurfer/internal/fixtures/searchDNSHosts.json
+++ /dev/null
@@ -1,23 +0,0 @@
-{
- "id": 1,
- "result": [
- {
- "name": "foo",
- "type": "TXT",
- "data": "xxx",
- "ttl": 300
- },
- {
- "name": "_acme-challenge",
- "type": "TXT",
- "data": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- "ttl": 300
- },
- {
- "name": "bar",
- "type": "A",
- "data": "yyy",
- "ttl": 300
- }
- ]
-}
diff --git a/providers/dns/namesurfer/internal/fixtures/updateDNSHost-request.json b/providers/dns/namesurfer/internal/fixtures/updateDNSHost-request.json
deleted file mode 100644
index 494de20c6..000000000
--- a/providers/dns/namesurfer/internal/fixtures/updateDNSHost-request.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "id": 1,
- "method": "updateDNSHost",
- "params": [
- "user",
- "510e63288ac874c1d5ba313a9411591daa346e5621fb0153263adc278794e378",
- "example.com",
- "viewA",
- {
- "name": "_acme-challenge",
- "type": "TXT",
- "data": "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- "ttl": 300
- },
- {
- "name": "",
- "type": "",
- "data": "",
- "ttl": 0
- }
- ]
-}
diff --git a/providers/dns/namesurfer/internal/fixtures/updateDNSHost.json b/providers/dns/namesurfer/internal/fixtures/updateDNSHost.json
deleted file mode 100644
index f41779e30..000000000
--- a/providers/dns/namesurfer/internal/fixtures/updateDNSHost.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "id": 1,
- "result": true
-}
diff --git a/providers/dns/namesurfer/internal/types.go b/providers/dns/namesurfer/internal/types.go
deleted file mode 100644
index d364c1876..000000000
--- a/providers/dns/namesurfer/internal/types.go
+++ /dev/null
@@ -1,72 +0,0 @@
-package internal
-
-import (
- "encoding/json"
- "fmt"
- "strings"
-)
-
-// DNSNode represents a DNS record.
-// http://95.128.3.201:8053/API/NSService_10#DNSNode
-type DNSNode struct {
- Name string `json:"name"`
- Type string `json:"type"`
- Data string `json:"data"`
- TTL int `json:"ttl"`
-}
-
-// DNSZone represents a DNS zone.
-// http://95.128.3.201:8053/API/NSService_10#DNSZone
-type DNSZone struct {
- Name string `json:"name,omitempty"`
- View string `json:"view,omitempty"`
-}
-
-// APIRequest represents a JSON-RPC request.
-// https://www.jsonrpc.org/specification_v1#a1.1Requestmethodinvocation
-type APIRequest struct {
- ID any `json:"id"` // Can be int or string depending on API
- Method string `json:"method"`
- Params []any `json:"params"`
-}
-
-// APIResponse represents a JSON-RPC response.
-// https://www.jsonrpc.org/specification_v1#a1.2Response
-type APIResponse struct {
- ID any `json:"id"` // Can be int or string depending on API
- Result json.RawMessage `json:"result"`
- Error *APIError `json:"error"`
-}
-
-// APIError represents an error.
-type APIError struct {
- Code any `json:"code"` // Can be int or string depending on API
- Filename string `json:"filename"`
- LineNumber int `json:"lineno"`
- Message string `json:"string"`
- Detail []string `json:"detail"`
-}
-
-func (e *APIError) Error() string {
- msg := new(strings.Builder)
-
- _, _ = fmt.Fprintf(msg, "code: %v", e.Code)
-
- if e.Filename != "" {
- _, _ = fmt.Fprintf(msg, ", filename: %s", e.Filename)
- }
-
- if e.LineNumber > 0 {
- _, _ = fmt.Fprintf(msg, ", line: %d", e.LineNumber)
- }
-
- if e.Message != "" {
- _, _ = fmt.Fprintf(msg, ", message: %s", e.Message)
- }
-
- if len(e.Detail) > 0 {
- _, _ = fmt.Fprintf(msg, ", detail: %v", strings.Join(e.Detail, " "))
- }
-
- return msg.String()
-}
diff --git a/providers/dns/namesurfer/namesurfer.go b/providers/dns/namesurfer/namesurfer.go
deleted file mode 100644
index 6b7f48402..000000000
--- a/providers/dns/namesurfer/namesurfer.go
+++ /dev/null
@@ -1,214 +0,0 @@
-// Package namesurfer implements a DNS provider for solving the DNS-01 challenge using FusionLayer NameSurfer API.
-package namesurfer
-
-import (
- "context"
- "crypto/tls"
- "errors"
- "fmt"
- "net/http"
- "strings"
- "sync"
- "time"
-
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/platform/config/env"
- "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
- "github.com/go-acme/lego/v4/providers/dns/namesurfer/internal"
-)
-
-// Environment variables names.
-const (
- envNamespace = "NAMESURFER_"
-
- EnvBaseURL = envNamespace + "BASE_URL"
- EnvAPIKey = envNamespace + "API_KEY"
- EnvAPISecret = envNamespace + "API_SECRET"
- EnvView = envNamespace + "VIEW"
-
- EnvTTL = envNamespace + "TTL"
- EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
- EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
- EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
- EnvInsecureSkipVerify = envNamespace + "INSECURE_SKIP_VERIFY"
-)
-
-// Config is used to configure the creation of the DNSProvider.
-type Config struct {
- BaseURL string
- APIKey string
- APISecret string
- View string
-
- PropagationTimeout time.Duration
- PollingInterval time.Duration
- TTL int
- HTTPClient *http.Client
-}
-
-// NewDefaultConfig returns a default configuration for the DNSProvider.
-func NewDefaultConfig() *Config {
- return &Config{
- TTL: env.GetOrDefaultInt(EnvTTL, 300),
- PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
- PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
- HTTPClient: &http.Client{
- Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
- },
- }
-}
-
-// DNSProvider implements the challenge.Provider interface.
-type DNSProvider struct {
- config *Config
- client *internal.Client
-
- zones map[string]string
- zonesMu sync.Mutex
-}
-
-// NewDNSProvider returns a DNSProvider instance configured for FusionLayer NameSurfer.
-func NewDNSProvider() (*DNSProvider, error) {
- values, err := env.Get(EnvBaseURL, EnvAPIKey, EnvAPISecret)
- if err != nil {
- return nil, fmt.Errorf("namesurfer: %w", err)
- }
-
- config := NewDefaultConfig()
- config.BaseURL = values[EnvBaseURL]
- config.APIKey = values[EnvAPIKey]
- config.APISecret = values[EnvAPISecret]
- config.View = env.GetOrDefaultString(EnvView, "")
-
- if env.GetOrDefaultBool(EnvInsecureSkipVerify, false) {
- config.HTTPClient.Transport = &http.Transport{
- TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
- }
- }
-
- return NewDNSProviderConfig(config)
-}
-
-// NewDNSProviderConfig return a DNSProvider instance configured for FusionLayer NameSurfer.
-func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
- if config == nil {
- return nil, errors.New("namesurfer: the configuration of the DNS provider is nil")
- }
-
- client, err := internal.NewClient(config.BaseURL, config.APIKey, config.APISecret)
- if err != nil {
- return nil, fmt.Errorf("namesurfer: %w", err)
- }
-
- if config.HTTPClient != nil {
- client.HTTPClient = config.HTTPClient
- }
-
- client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
-
- return &DNSProvider{
- config: config,
- client: client,
- zones: make(map[string]string),
- }, nil
-}
-
-// Present creates a TXT record using the specified parameters.
-func (d *DNSProvider) Present(domain, token, keyAuth string) error {
- ctx := context.Background()
-
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- zone, err := d.findZone(ctx, info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("namesurfer: %w", err)
- }
-
- d.zonesMu.Lock()
- d.zones[token] = zone
- d.zonesMu.Unlock()
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone)
- if err != nil {
- return fmt.Errorf("namesurfer: %w", err)
- }
-
- record := internal.DNSNode{
- Name: subDomain,
- Type: "TXT",
- TTL: d.config.TTL,
- Data: info.Value,
- }
-
- err = d.client.AddDNSRecord(ctx, zone, d.config.View, record)
- if err != nil {
- return fmt.Errorf("namesurfer: add DNS record: %w", err)
- }
-
- return nil
-}
-
-// CleanUp removes the TXT record matching the specified parameters.
-func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
- ctx := context.Background()
-
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- d.zonesMu.Lock()
- zone, ok := d.zones[token]
- d.zonesMu.Unlock()
-
- if !ok {
- return fmt.Errorf("namesurfer: unknown zone for '%s'", info.EffectiveFQDN)
- }
-
- d.zonesMu.Lock()
- delete(d.zones, token)
- d.zonesMu.Unlock()
-
- existing, err := d.client.SearchDNSHosts(ctx, dns01.UnFqdn(info.EffectiveFQDN))
- if err != nil {
- return fmt.Errorf("namesurfer: search DNS hosts: %w", err)
- }
-
- for _, node := range existing {
- if node.Type != "TXT" || node.Data != info.Value {
- continue
- }
-
- err = d.client.UpdateDNSHost(ctx, zone, d.config.View, node, internal.DNSNode{})
- if err != nil {
- return fmt.Errorf("namesurfer: update DNS host: %w", err)
- }
- }
-
- return nil
-}
-
-// Timeout returns the timeout and interval to use when checking for DNS propagation.
-func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
- return d.config.PropagationTimeout, d.config.PollingInterval
-}
-
-func (d *DNSProvider) findZone(ctx context.Context, fqdn string) (string, error) {
- zones, err := d.client.ListZones(ctx, "forward")
- if err != nil {
- return "", fmt.Errorf("list zones: %w", err)
- }
-
- domain := dns01.UnFqdn(fqdn)
-
- var zoneName string
-
- for _, zone := range zones {
- if strings.HasSuffix(domain, zone.Name) && len(zone.Name) > len(zoneName) {
- zoneName = zone.Name
- }
- }
-
- if zoneName == "" {
- return "", fmt.Errorf("no zone found for %s", fqdn)
- }
-
- return zoneName, nil
-}
diff --git a/providers/dns/namesurfer/namesurfer.toml b/providers/dns/namesurfer/namesurfer.toml
deleted file mode 100644
index fd914ec0c..000000000
--- a/providers/dns/namesurfer/namesurfer.toml
+++ /dev/null
@@ -1,28 +0,0 @@
-Name = "FusionLayer NameSurfer"
-Description = ''''''
-URL = "https://www.fusionlayer.com/"
-Code = "namesurfer"
-Since = "v4.32.0"
-
-Example = '''
-NAMESURFER_BASE_URL=https://foo.example.com:8443/API/NSService_10 \
-NAMESURFER_API_KEY=xxx \
-NAMESURFER_API_SECRET=yyy \
-lego --dns namesurfer -d '*.example.com' -d example.com run
-'''
-
-[Configuration]
- [Configuration.Credentials]
- NAMESURFER_BASE_URL = "The base URL of NameSurfer API (jsonrpc10) endpoint URL (e.g., https://foo.example.com:8443/API/NSService_10)"
- NAMESURFER_API_KEY = "API key name"
- NAMESURFER_API_SECRET = "API secret"
- [Configuration.Additional]
- NAMESURFER_VIEW = "DNS view name (optional, default: empty string)"
- NAMESURFER_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
- NAMESURFER_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 120)"
- NAMESURFER_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 300)"
- NAMESURFER_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
- NAMESURFER_INSECURE_SKIP_VERIFY = "Whether to verify the API certificate"
-
-[Links]
- API = "https://web.archive.org/web/20260213170737/http://95.128.3.201:8053/API/NSService_10"
diff --git a/providers/dns/namesurfer/namesurfer_test.go b/providers/dns/namesurfer/namesurfer_test.go
deleted file mode 100644
index ce3aa37af..000000000
--- a/providers/dns/namesurfer/namesurfer_test.go
+++ /dev/null
@@ -1,174 +0,0 @@
-package namesurfer
-
-import (
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester"
- "github.com/stretchr/testify/require"
-)
-
-const envDomain = envNamespace + "DOMAIN"
-
-var envTest = tester.NewEnvTest(
- EnvBaseURL,
- EnvAPIKey,
- EnvAPISecret,
- EnvView,
-).WithDomain(envDomain)
-
-func TestNewDNSProvider(t *testing.T) {
- testCases := []struct {
- desc string
- envVars map[string]string
- expected string
- }{
- {
- desc: "success",
- envVars: map[string]string{
- EnvBaseURL: "https://example.com",
- EnvAPIKey: "user",
- EnvAPISecret: "secret",
- },
- },
- {
- desc: "missing base URL",
- envVars: map[string]string{
- EnvBaseURL: "",
- EnvAPIKey: "user",
- EnvAPISecret: "secret",
- },
- expected: "namesurfer: some credentials information are missing: NAMESURFER_BASE_URL",
- },
- {
- desc: "missing API key",
- envVars: map[string]string{
- EnvBaseURL: "https://example.com",
- EnvAPIKey: "",
- EnvAPISecret: "secret",
- },
- expected: "namesurfer: some credentials information are missing: NAMESURFER_API_KEY",
- },
- {
- desc: "missing API secret",
- envVars: map[string]string{
- EnvBaseURL: "https://example.com",
- EnvAPIKey: "user",
- EnvAPISecret: "",
- },
- expected: "namesurfer: some credentials information are missing: NAMESURFER_API_SECRET",
- },
- {
- desc: "missing credentials",
- envVars: map[string]string{},
- expected: "namesurfer: some credentials information are missing: NAMESURFER_BASE_URL,NAMESURFER_API_KEY,NAMESURFER_API_SECRET",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(test.envVars)
-
- p, err := NewDNSProvider()
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestNewDNSProviderConfig(t *testing.T) {
- testCases := []struct {
- desc string
- baseURL string
- apiKey string
- apiSecret string
- expected string
- }{
- {
- desc: "success",
- baseURL: "https://example.com",
- apiKey: "user",
- apiSecret: "secret",
- },
- {
- desc: "missing base URL",
- apiKey: "user",
- apiSecret: "secret",
- expected: "namesurfer: base URL missing",
- },
- {
- desc: "missing API key",
- baseURL: "https://example.com",
- apiSecret: "secret",
- expected: "namesurfer: credentials missing",
- },
- {
- desc: "missing API secret",
- baseURL: "https://example.com",
- apiKey: "user",
- expected: "namesurfer: credentials missing",
- },
- {
- desc: "missing credentials",
- expected: "namesurfer: credentials missing",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- config := NewDefaultConfig()
- config.BaseURL = test.baseURL
- config.APIKey = test.apiKey
- config.APISecret = test.apiSecret
-
- p, err := NewDNSProviderConfig(config)
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestLivePresent(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.Present(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func TestLiveCleanUp(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
diff --git a/providers/dns/safedns/internal/client.go b/providers/dns/safedns/internal/client.go
index 628618032..51b12e99d 100644
--- a/providers/dns/safedns/internal/client.go
+++ b/providers/dns/safedns/internal/client.go
@@ -19,7 +19,7 @@ const defaultBaseURL = "https://api.ukfast.io/safedns/v1"
const authorizationHeader = "Authorization"
-// Client the ANS SafeDNS client.
+// Client the UKFast SafeDNS client.
type Client struct {
authToken string
diff --git a/providers/dns/safedns/safedns.go b/providers/dns/safedns/safedns.go
index 154cfc5ee..be8ca4fe6 100644
--- a/providers/dns/safedns/safedns.go
+++ b/providers/dns/safedns/safedns.go
@@ -1,4 +1,4 @@
-// Package safedns implements a DNS provider for solving the DNS-01 challenge using ANS SafeDNS.
+// Package safedns implements a DNS provider for solving the DNS-01 challenge using UKFast SafeDNS.
package safedns
import (
@@ -75,7 +75,7 @@ func NewDNSProvider() (*DNSProvider, error) {
return NewDNSProviderConfig(config)
}
-// NewDNSProviderConfig return a DNSProvider instance configured for ANS SafeDNS.
+// NewDNSProviderConfig return a DNSProvider instance configured for UKFast SafeDNS.
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
if config == nil {
return nil, errors.New("safedns: supplied configuration was nil")
diff --git a/providers/dns/safedns/safedns.toml b/providers/dns/safedns/safedns.toml
index f387f2535..188db66a4 100644
--- a/providers/dns/safedns/safedns.toml
+++ b/providers/dns/safedns/safedns.toml
@@ -1,6 +1,6 @@
-Name = "ANS SafeDNS"
+Name = "UKFast SafeDNS"
Description = ''''''
-URL = "https://www.ans.co.uk/"
+URL = "https://www.ukfast.co.uk/dns-hosting.html"
Code = "safedns"
Since = "v4.6.0"
diff --git a/providers/dns/timewebcloud/internal/types.go b/providers/dns/timewebcloud/internal/types.go
index 80cdb2c70..81da4df5c 100644
--- a/providers/dns/timewebcloud/internal/types.go
+++ b/providers/dns/timewebcloud/internal/types.go
@@ -3,11 +3,9 @@ package internal
import "fmt"
type DNSRecord struct {
- ID int `json:"id,omitempty"`
- Type string `json:"type,omitempty"`
- Value string `json:"value,omitempty"`
-
- // SubDomain is the full name of a subdomain (not only the subdomain label).
+ ID int `json:"id,omitempty"`
+ Type string `json:"type,omitempty"`
+ Value string `json:"value,omitempty"`
SubDomain string `json:"subdomain,omitempty"`
}
diff --git a/providers/dns/timewebcloud/timewebcloud.go b/providers/dns/timewebcloud/timewebcloud.go
index a599566e3..d71beea4b 100644
--- a/providers/dns/timewebcloud/timewebcloud.go
+++ b/providers/dns/timewebcloud/timewebcloud.go
@@ -110,10 +110,15 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
return fmt.Errorf("timewebcloud: could not find zone for domain %q: %w", domain, err)
}
+ subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
+ if err != nil {
+ return fmt.Errorf("timewebcloud: %w", err)
+ }
+
record := internal.DNSRecord{
Type: "TXT",
Value: info.Value,
- SubDomain: dns01.UnFqdn(info.EffectiveFQDN),
+ SubDomain: subDomain,
}
response, err := d.client.CreateRecord(context.Background(), authZone, record)
diff --git a/providers/dns/todaynic/internal/client.go b/providers/dns/todaynic/internal/client.go
deleted file mode 100644
index 2c537f4a7..000000000
--- a/providers/dns/todaynic/internal/client.go
+++ /dev/null
@@ -1,141 +0,0 @@
-package internal
-
-import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strconv"
- "time"
-
- "github.com/go-acme/lego/v4/providers/dns/internal/errutils"
- "github.com/go-acme/lego/v4/providers/dns/internal/useragent"
- querystring "github.com/google/go-querystring/query"
-)
-
-const defaultBaseURL = "https://todapi.now.cn:2443"
-
-// Client the TodayNIC API client.
-type Client struct {
- authUserID string
- apiKey string
-
- BaseURL *url.URL
- HTTPClient *http.Client
-}
-
-// NewClient creates a new Client.
-func NewClient(authUserID, apiKey string) (*Client, error) {
- if authUserID == "" || apiKey == "" {
- return nil, errors.New("credentials missing")
- }
-
- baseURL, _ := url.Parse(defaultBaseURL)
-
- return &Client{
- authUserID: authUserID,
- apiKey: apiKey,
- BaseURL: baseURL,
- HTTPClient: &http.Client{Timeout: 10 * time.Second},
- }, nil
-}
-
-func (c *Client) AddRecord(ctx context.Context, record Record) (int, error) {
- endpoint := c.BaseURL.JoinPath("api", "dns", "add-domain-record.json")
-
- query, err := querystring.Values(record)
- if err != nil {
- return 0, err
- }
-
- req, err := c.newRequest(ctx, endpoint, query)
- if err != nil {
- return 0, err
- }
-
- var result APIResponse
-
- err = c.do(req, &result)
- if err != nil {
- return 0, err
- }
-
- return result.ID, nil
-}
-
-func (c *Client) DeleteRecord(ctx context.Context, recordID int) error {
- endpoint := c.BaseURL.JoinPath("api", "dns", "delete-domain-record.json")
-
- query := endpoint.Query()
- query.Set("Id", strconv.Itoa(recordID))
-
- req, err := c.newRequest(ctx, endpoint, query)
- if err != nil {
- return err
- }
-
- return c.do(req, nil)
-}
-
-func (c *Client) do(req *http.Request, result any) error {
- useragent.SetHeader(req.Header)
-
- resp, err := c.HTTPClient.Do(req)
- if err != nil {
- return errutils.NewHTTPDoError(req, err)
- }
-
- defer func() { _ = resp.Body.Close() }()
-
- if resp.StatusCode/100 != 2 {
- return parseError(req, resp)
- }
-
- if result == nil {
- return nil
- }
-
- raw, err := io.ReadAll(resp.Body)
- if err != nil {
- return errutils.NewReadResponseError(req, resp.StatusCode, err)
- }
-
- err = json.Unmarshal(raw, result)
- if err != nil {
- return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
- }
-
- return nil
-}
-
-func (c *Client) newRequest(ctx context.Context, endpoint *url.URL, query url.Values) (*http.Request, error) {
- query.Set("auth-userid", c.authUserID)
- query.Set("api-key", c.apiKey)
-
- endpoint.RawQuery = query.Encode()
-
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
- if err != nil {
- return nil, fmt.Errorf("unable to create request: %w", err)
- }
-
- req.Header.Set("Accept", "application/json")
-
- return req, nil
-}
-
-func parseError(req *http.Request, resp *http.Response) error {
- raw, _ := io.ReadAll(resp.Body)
-
- var errAPI APIError
-
- err := json.Unmarshal(raw, &errAPI)
- if err != nil {
- return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
- }
-
- return &errAPI
-}
diff --git a/providers/dns/todaynic/internal/client_test.go b/providers/dns/todaynic/internal/client_test.go
deleted file mode 100644
index 71ee7f8b7..000000000
--- a/providers/dns/todaynic/internal/client_test.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package internal
-
-import (
- "net/http"
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func mockBuilder() *servermock.Builder[*Client] {
- return servermock.NewBuilder[*Client](
- func(server *httptest.Server) (*Client, error) {
- client, err := NewClient("user123", "secret")
- if err != nil {
- return nil, err
- }
-
- client.BaseURL, _ = url.Parse(server.URL)
- client.HTTPClient = server.Client()
-
- return client, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders(),
- )
-}
-
-func TestClient_AddRecord(t *testing.T) {
- client := mockBuilder().
- Route("GET /api/dns/add-domain-record.json",
- servermock.ResponseFromFixture("add_record.json"),
- servermock.CheckQueryParameter().Strict().
- With("Domain", "example.com").
- With("Host", "_acme-challenge").
- With("Type", "TXT").
- With("Value", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY").
- With("Ttl", "600").
- With("auth-userid", "user123").
- With("api-key", "secret"),
- ).
- Build(t)
-
- record := Record{
- Domain: "example.com",
- Host: "_acme-challenge",
- Type: "TXT",
- Value: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: "600",
- }
-
- recordID, err := client.AddRecord(t.Context(), record)
- require.NoError(t, err)
-
- assert.Equal(t, 11554102, recordID)
-}
-
-func TestClient_AddRecord_error(t *testing.T) {
- client := mockBuilder().
- Route("GET /api/dns/add-domain-record.json",
- servermock.ResponseFromFixture("error.json").
- WithStatusCode(http.StatusNotFound),
- ).
- Build(t)
-
- record := Record{
- Domain: "example.com",
- Host: "_acme-challenge",
- Type: "TXT",
- Value: "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY",
- TTL: "600",
- }
-
- _, err := client.AddRecord(t.Context(), record)
- require.EqualError(t, err, "host.repeat (2d5876b2-f272-43e9-acc1-4c6a3d3683b1)")
-}
-
-func TestClient_DeleteRecord(t *testing.T) {
- client := mockBuilder().
- Route("GET /api/dns/delete-domain-record.json",
- servermock.ResponseFromFixture("add_record.json"),
- servermock.CheckQueryParameter().Strict().
- With("Id", "123").
- With("auth-userid", "user123").
- With("api-key", "secret"),
- ).
- Build(t)
-
- err := client.DeleteRecord(t.Context(), 123)
- require.NoError(t, err)
-}
diff --git a/providers/dns/todaynic/internal/fixtures/add_record.json b/providers/dns/todaynic/internal/fixtures/add_record.json
deleted file mode 100644
index 27f34d71c..000000000
--- a/providers/dns/todaynic/internal/fixtures/add_record.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "RequestId": "f60ea4d9-67ef-49fa-bbae-06178a6e7293",
- "Id": 11554102
-}
diff --git a/providers/dns/todaynic/internal/fixtures/error.json b/providers/dns/todaynic/internal/fixtures/error.json
deleted file mode 100644
index 3ea9c9310..000000000
--- a/providers/dns/todaynic/internal/fixtures/error.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "RequestId": "2d5876b2-f272-43e9-acc1-4c6a3d3683b1",
- "error": "host.repeat"
-}
diff --git a/providers/dns/todaynic/internal/types.go b/providers/dns/todaynic/internal/types.go
deleted file mode 100644
index 0a15c7da8..000000000
--- a/providers/dns/todaynic/internal/types.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package internal
-
-import "fmt"
-
-type APIError struct {
- RequestID string `json:"RequestId"`
- Message string `json:"error"`
-}
-
-func (a *APIError) Error() string {
- return fmt.Sprintf("%s (%s)", a.Message, a.RequestID)
-}
-
-type Record struct {
- Domain string `url:"Domain,omitempty"`
- Host string `url:"Host,omitempty"`
- Type string `url:"Type,omitempty"`
- Value string `url:"Value,omitempty"`
- Mx string `url:"Mx,omitempty"`
- TTL string `url:"Ttl,omitempty"`
-}
-
-type APIResponse struct {
- RequestID string `json:"RequestId"`
- ID int `json:"Id"`
-}
diff --git a/providers/dns/todaynic/todaynic.go b/providers/dns/todaynic/todaynic.go
deleted file mode 100644
index 3a3734033..000000000
--- a/providers/dns/todaynic/todaynic.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// Package todaynic implements a DNS provider for solving the DNS-01 challenge using TodayNIC.
-package todaynic
-
-import (
- "context"
- "errors"
- "fmt"
- "net/http"
- "strconv"
- "sync"
- "time"
-
- "github.com/go-acme/lego/v4/challenge/dns01"
- "github.com/go-acme/lego/v4/platform/config/env"
- "github.com/go-acme/lego/v4/providers/dns/internal/clientdebug"
- "github.com/go-acme/lego/v4/providers/dns/todaynic/internal"
-)
-
-// Environment variables names.
-const (
- envNamespace = "TODAYNIC_"
-
- EnvAuthUserID = envNamespace + "AUTH_USER_ID"
- EnvAPIKey = envNamespace + "API_KEY"
-
- EnvTTL = envNamespace + "TTL"
- EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
- EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
- EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
-)
-
-// Config is used to configure the creation of the DNSProvider.
-type Config struct {
- AuthUserID string
- APIKey string
-
- PropagationTimeout time.Duration
- PollingInterval time.Duration
- TTL int
- HTTPClient *http.Client
-}
-
-// NewDefaultConfig returns a default configuration for the DNSProvider.
-func NewDefaultConfig() *Config {
- return &Config{
- TTL: env.GetOrDefaultInt(EnvTTL, 600),
- PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout),
- PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval),
- HTTPClient: &http.Client{
- Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
- },
- }
-}
-
-// DNSProvider implements the challenge.Provider interface.
-type DNSProvider struct {
- config *Config
- client *internal.Client
-
- recordIDs map[string]int
- recordIDsMu sync.Mutex
-}
-
-// NewDNSProvider returns a DNSProvider instance configured for TodayNIC.
-func NewDNSProvider() (*DNSProvider, error) {
- values, err := env.Get(EnvAuthUserID, EnvAPIKey)
- if err != nil {
- return nil, fmt.Errorf("todaynic: %w", err)
- }
-
- config := NewDefaultConfig()
- config.AuthUserID = values[EnvAuthUserID]
- config.APIKey = values[EnvAPIKey]
-
- return NewDNSProviderConfig(config)
-}
-
-// NewDNSProviderConfig return a DNSProvider instance configured for TodayNIC.
-func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
- if config == nil {
- return nil, errors.New("todaynic: the configuration of the DNS provider is nil")
- }
-
- client, err := internal.NewClient(config.AuthUserID, config.APIKey)
- if err != nil {
- return nil, fmt.Errorf("todaynic: %w", err)
- }
-
- if config.HTTPClient != nil {
- client.HTTPClient = config.HTTPClient
- }
-
- client.HTTPClient = clientdebug.Wrap(client.HTTPClient)
-
- return &DNSProvider{
- config: config,
- client: client,
- recordIDs: make(map[string]int),
- }, nil
-}
-
-// Present creates a TXT record using the specified parameters.
-func (d *DNSProvider) Present(domain, token, keyAuth string) error {
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN)
- if err != nil {
- return fmt.Errorf("todaynic: could not find zone for domain %q: %w", domain, err)
- }
-
- subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
- if err != nil {
- return fmt.Errorf("todaynic: %w", err)
- }
-
- record := internal.Record{
- Domain: dns01.UnFqdn(authZone),
- Host: subDomain,
- Type: "TXT",
- Value: info.Value,
- TTL: strconv.Itoa(d.config.TTL),
- }
-
- recordID, err := d.client.AddRecord(context.Background(), record)
- if err != nil {
- return fmt.Errorf("todaynic: add record: %w", err)
- }
-
- d.recordIDsMu.Lock()
- d.recordIDs[token] = recordID
- d.recordIDsMu.Unlock()
-
- return nil
-}
-
-// CleanUp removes the TXT record matching the specified parameters.
-func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
- info := dns01.GetChallengeInfo(domain, keyAuth)
-
- d.recordIDsMu.Lock()
- recordID, ok := d.recordIDs[token]
- d.recordIDsMu.Unlock()
-
- if !ok {
- return fmt.Errorf("todaynic: unknown record ID for '%s' '%s'", info.EffectiveFQDN, token)
- }
-
- err := d.client.DeleteRecord(context.Background(), recordID)
- if err != nil {
- return fmt.Errorf("todaynic: delete record: %w", err)
- }
-
- d.recordIDsMu.Lock()
- delete(d.recordIDs, token)
- d.recordIDsMu.Unlock()
-
- return nil
-}
-
-// Timeout returns the timeout and interval to use when checking for DNS propagation.
-// Adjusting here to cope with spikes in propagation times.
-func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
- return d.config.PropagationTimeout, d.config.PollingInterval
-}
diff --git a/providers/dns/todaynic/todaynic.toml b/providers/dns/todaynic/todaynic.toml
deleted file mode 100644
index 16d55ccc0..000000000
--- a/providers/dns/todaynic/todaynic.toml
+++ /dev/null
@@ -1,25 +0,0 @@
-Name = "TodayNIC/时代互联"
-Description = ''''''
-URL = "https://www.todaynic.com/"
-Code = "todaynic"
-Since = "v4.32.0"
-
-Example = '''
-TODAYNIC_AUTH_USER_ID="xxx" \
-TODAYNIC_API_KEY="yyy" \
-lego --dns todaynic -d '*.example.com' -d example.com run
-'''
-
-[Configuration]
- [Configuration.Credentials]
- TODAYNIC_AUTH_USER_ID = "account ID"
- TODAYNIC_API_KEY = "API key"
- [Configuration.Additional]
- TODAYNIC_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 2)"
- TODAYNIC_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 60)"
- TODAYNIC_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 600)"
- TODAYNIC_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)"
-
-[Links]
- API = "https://www.todaynic.com/partner/mode_Http_Api_detail.php"
- apipost = "https://docs.apipost.net/docs/detail/49dcef10a876000?target_id=0"
diff --git a/providers/dns/todaynic/todaynic_test.go b/providers/dns/todaynic/todaynic_test.go
deleted file mode 100644
index c73bf6cc5..000000000
--- a/providers/dns/todaynic/todaynic_test.go
+++ /dev/null
@@ -1,207 +0,0 @@
-package todaynic
-
-import (
- "net/http/httptest"
- "net/url"
- "testing"
-
- "github.com/go-acme/lego/v4/platform/tester"
- "github.com/go-acme/lego/v4/platform/tester/servermock"
- "github.com/stretchr/testify/require"
-)
-
-const envDomain = envNamespace + "DOMAIN"
-
-var envTest = tester.NewEnvTest(EnvAuthUserID, EnvAPIKey).WithDomain(envDomain)
-
-func TestNewDNSProvider(t *testing.T) {
- testCases := []struct {
- desc string
- envVars map[string]string
- expected string
- }{
- {
- desc: "success",
- envVars: map[string]string{
- EnvAuthUserID: "user123",
- EnvAPIKey: "secret",
- },
- },
- {
- desc: "missing user ID",
- envVars: map[string]string{
- EnvAuthUserID: "",
- EnvAPIKey: "secret",
- },
- expected: "todaynic: some credentials information are missing: TODAYNIC_AUTH_USER_ID",
- },
- {
- desc: "missing API key",
- envVars: map[string]string{
- EnvAuthUserID: "user123",
- EnvAPIKey: "",
- },
- expected: "todaynic: some credentials information are missing: TODAYNIC_API_KEY",
- },
- {
- desc: "missing credentials",
- envVars: map[string]string{},
- expected: "todaynic: some credentials information are missing: TODAYNIC_AUTH_USER_ID,TODAYNIC_API_KEY",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- defer envTest.RestoreEnv()
-
- envTest.ClearEnv()
-
- envTest.Apply(test.envVars)
-
- p, err := NewDNSProvider()
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestNewDNSProviderConfig(t *testing.T) {
- testCases := []struct {
- desc string
- authUserID string
- apiKey string
- expected string
- }{
- {
- desc: "success",
- authUserID: "user123",
- apiKey: "secret",
- },
- {
- desc: "missing user ID",
- apiKey: "secret",
- expected: "todaynic: credentials missing",
- },
- {
- desc: "missing API key",
- authUserID: "user123",
- expected: "todaynic: credentials missing",
- },
- {
- desc: "missing credentials",
- expected: "todaynic: credentials missing",
- },
- }
-
- for _, test := range testCases {
- t.Run(test.desc, func(t *testing.T) {
- config := NewDefaultConfig()
- config.AuthUserID = test.authUserID
- config.APIKey = test.apiKey
-
- p, err := NewDNSProviderConfig(config)
-
- if test.expected == "" {
- require.NoError(t, err)
- require.NotNil(t, p)
- require.NotNil(t, p.config)
- require.NotNil(t, p.client)
- } else {
- require.EqualError(t, err, test.expected)
- }
- })
- }
-}
-
-func TestLivePresent(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.Present(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func TestLiveCleanUp(t *testing.T) {
- if !envTest.IsLiveTest() {
- t.Skip("skipping live test")
- }
-
- envTest.RestoreEnv()
-
- provider, err := NewDNSProvider()
- require.NoError(t, err)
-
- err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
- require.NoError(t, err)
-}
-
-func mockBuilder() *servermock.Builder[*DNSProvider] {
- return servermock.NewBuilder(
- func(server *httptest.Server) (*DNSProvider, error) {
- config := NewDefaultConfig()
- config.AuthUserID = "user123"
- config.APIKey = "secret"
- config.HTTPClient = server.Client()
-
- p, err := NewDNSProviderConfig(config)
- if err != nil {
- return nil, err
- }
-
- p.client.BaseURL, _ = url.Parse(server.URL)
-
- return p, nil
- },
- servermock.CheckHeader().
- WithJSONHeaders(),
- )
-}
-
-func TestDNSProvider_Present(t *testing.T) {
- provider := mockBuilder().
- Route("GET /api/dns/add-domain-record.json",
- servermock.ResponseFromInternal("add_record.json"),
- servermock.CheckQueryParameter().Strict().
- With("Domain", "example.com").
- With("Host", "_acme-challenge").
- With("Type", "TXT").
- With("Value", "ADw2sEd82DUgXcQ9hNBZThJs7zVJkR5v9JeSbAb9mZY").
- With("Ttl", "600").
- With("auth-userid", "user123").
- With("api-key", "secret"),
- ).
- Build(t)
-
- err := provider.Present("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
-
-func TestDNSProvider_CleanUp(t *testing.T) {
- provider := mockBuilder().
- Route("GET /api/dns/delete-domain-record.json",
- servermock.ResponseFromInternal("add_record.json"),
- servermock.CheckQueryParameter().Strict().
- With("Id", "123").
- With("auth-userid", "user123").
- With("api-key", "secret"),
- ).
- Build(t)
-
- provider.recordIDs["abc"] = 123
-
- err := provider.CleanUp("example.com", "abc", "123d==")
- require.NoError(t, err)
-}
diff --git a/providers/dns/zz_gen_dns_providers.go b/providers/dns/zz_gen_dns_providers.go
index 9c4bc9e61..7099a3f4c 100644
--- a/providers/dns/zz_gen_dns_providers.go
+++ b/providers/dns/zz_gen_dns_providers.go
@@ -13,7 +13,6 @@ import (
"github.com/go-acme/lego/v4/providers/dns/allinkl"
"github.com/go-acme/lego/v4/providers/dns/alwaysdata"
"github.com/go-acme/lego/v4/providers/dns/anexia"
- "github.com/go-acme/lego/v4/providers/dns/artfiles"
"github.com/go-acme/lego/v4/providers/dns/arvancloud"
"github.com/go-acme/lego/v4/providers/dns/auroradns"
"github.com/go-acme/lego/v4/providers/dns/autodns"
@@ -26,7 +25,6 @@ import (
"github.com/go-acme/lego/v4/providers/dns/binarylane"
"github.com/go-acme/lego/v4/providers/dns/bindman"
"github.com/go-acme/lego/v4/providers/dns/bluecat"
- "github.com/go-acme/lego/v4/providers/dns/bluecatv2"
"github.com/go-acme/lego/v4/providers/dns/bookmyname"
"github.com/go-acme/lego/v4/providers/dns/brandit"
"github.com/go-acme/lego/v4/providers/dns/bunny"
@@ -43,14 +41,11 @@ import (
"github.com/go-acme/lego/v4/providers/dns/constellix"
"github.com/go-acme/lego/v4/providers/dns/corenetworks"
"github.com/go-acme/lego/v4/providers/dns/cpanel"
- "github.com/go-acme/lego/v4/providers/dns/czechia"
- "github.com/go-acme/lego/v4/providers/dns/ddnss"
"github.com/go-acme/lego/v4/providers/dns/derak"
"github.com/go-acme/lego/v4/providers/dns/desec"
"github.com/go-acme/lego/v4/providers/dns/designate"
"github.com/go-acme/lego/v4/providers/dns/digitalocean"
"github.com/go-acme/lego/v4/providers/dns/directadmin"
- "github.com/go-acme/lego/v4/providers/dns/dnsexit"
"github.com/go-acme/lego/v4/providers/dns/dnshomede"
"github.com/go-acme/lego/v4/providers/dns/dnsimple"
"github.com/go-acme/lego/v4/providers/dns/dnsmadeeasy"
@@ -68,8 +63,6 @@ import (
"github.com/go-acme/lego/v4/providers/dns/edgeone"
"github.com/go-acme/lego/v4/providers/dns/efficientip"
"github.com/go-acme/lego/v4/providers/dns/epik"
- "github.com/go-acme/lego/v4/providers/dns/eurodns"
- "github.com/go-acme/lego/v4/providers/dns/excedo"
"github.com/go-acme/lego/v4/providers/dns/exec"
"github.com/go-acme/lego/v4/providers/dns/exoscale"
"github.com/go-acme/lego/v4/providers/dns/f5xc"
@@ -109,7 +102,6 @@ import (
"github.com/go-acme/lego/v4/providers/dns/jdcloud"
"github.com/go-acme/lego/v4/providers/dns/joker"
"github.com/go-acme/lego/v4/providers/dns/keyhelp"
- "github.com/go-acme/lego/v4/providers/dns/leaseweb"
"github.com/go-acme/lego/v4/providers/dns/liara"
"github.com/go-acme/lego/v4/providers/dns/lightsail"
"github.com/go-acme/lego/v4/providers/dns/limacity"
@@ -130,7 +122,6 @@ import (
"github.com/go-acme/lego/v4/providers/dns/namecheap"
"github.com/go-acme/lego/v4/providers/dns/namedotcom"
"github.com/go-acme/lego/v4/providers/dns/namesilo"
- "github.com/go-acme/lego/v4/providers/dns/namesurfer"
"github.com/go-acme/lego/v4/providers/dns/nearlyfreespeech"
"github.com/go-acme/lego/v4/providers/dns/neodigit"
"github.com/go-acme/lego/v4/providers/dns/netcup"
@@ -172,7 +163,6 @@ import (
"github.com/go-acme/lego/v4/providers/dns/technitium"
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
"github.com/go-acme/lego/v4/providers/dns/timewebcloud"
- "github.com/go-acme/lego/v4/providers/dns/todaynic"
"github.com/go-acme/lego/v4/providers/dns/transip"
"github.com/go-acme/lego/v4/providers/dns/ultradns"
"github.com/go-acme/lego/v4/providers/dns/uniteddomains"
@@ -216,8 +206,6 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
return alwaysdata.NewDNSProvider()
case "anexia":
return anexia.NewDNSProvider()
- case "artfiles":
- return artfiles.NewDNSProvider()
case "arvancloud":
return arvancloud.NewDNSProvider()
case "auroradns":
@@ -242,8 +230,6 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
return bindman.NewDNSProvider()
case "bluecat":
return bluecat.NewDNSProvider()
- case "bluecatv2":
- return bluecatv2.NewDNSProvider()
case "bookmyname":
return bookmyname.NewDNSProvider()
case "brandit":
@@ -276,10 +262,6 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
return corenetworks.NewDNSProvider()
case "cpanel":
return cpanel.NewDNSProvider()
- case "czechia":
- return czechia.NewDNSProvider()
- case "ddnss":
- return ddnss.NewDNSProvider()
case "derak":
return derak.NewDNSProvider()
case "desec":
@@ -290,8 +272,6 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
return digitalocean.NewDNSProvider()
case "directadmin":
return directadmin.NewDNSProvider()
- case "dnsexit":
- return dnsexit.NewDNSProvider()
case "dnshomede":
return dnshomede.NewDNSProvider()
case "dnsimple":
@@ -326,10 +306,6 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
return efficientip.NewDNSProvider()
case "epik":
return epik.NewDNSProvider()
- case "eurodns":
- return eurodns.NewDNSProvider()
- case "excedo":
- return excedo.NewDNSProvider()
case "exec":
return exec.NewDNSProvider()
case "exoscale":
@@ -408,8 +384,6 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
return joker.NewDNSProvider()
case "keyhelp":
return keyhelp.NewDNSProvider()
- case "leaseweb":
- return leaseweb.NewDNSProvider()
case "liara":
return liara.NewDNSProvider()
case "lightsail":
@@ -450,8 +424,6 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
return namedotcom.NewDNSProvider()
case "namesilo":
return namesilo.NewDNSProvider()
- case "namesurfer":
- return namesurfer.NewDNSProvider()
case "nearlyfreespeech":
return nearlyfreespeech.NewDNSProvider()
case "neodigit":
@@ -534,8 +506,6 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
return tencentcloud.NewDNSProvider()
case "timewebcloud":
return timewebcloud.NewDNSProvider()
- case "todaynic":
- return todaynic.NewDNSProvider()
case "transip":
return transip.NewDNSProvider()
case "ultradns":