From ddce5cff4a7fb5a2fc1a0a2a319610f1fbfd8825 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Wed, 6 Aug 2025 17:00:24 +0200 Subject: [PATCH] Add DNS provider for Tencent EdgeOne (#2606) --- README.md | 14 +- cmd/zz_gen_cmd_dnshelp.go | 24 +++ docs/content/dns/zz_gen_edgeone.md | 72 +++++++ docs/content/dns/zz_gen_tencentcloud.md | 4 +- docs/data/zz_cli_help.toml | 2 +- go.mod | 3 +- go.sum | 6 +- providers/dns/edgeone/edgeone.go | 188 +++++++++++++++++++ providers/dns/edgeone/edgeone.toml | 27 +++ providers/dns/edgeone/edgeone_test.go | 147 +++++++++++++++ providers/dns/edgeone/wrapper.go | 50 +++++ providers/dns/tencentcloud/tencentcloud.toml | 2 +- providers/dns/zz_gen_dns_providers.go | 3 + 13 files changed, 528 insertions(+), 14 deletions(-) create mode 100644 docs/content/dns/zz_gen_edgeone.md create mode 100644 providers/dns/edgeone/edgeone.go create mode 100644 providers/dns/edgeone/edgeone.toml create mode 100644 providers/dns/edgeone/edgeone_test.go create mode 100644 providers/dns/edgeone/wrapper.go diff --git a/README.md b/README.md index 59b62a8de..66ed2e4d6 100644 --- a/README.md +++ b/README.md @@ -221,37 +221,37 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns). Stackpath Technitium Tencent Cloud DNS - Timeweb Cloud + Tencent EdgeOne + Timeweb Cloud TransIP UKFast SafeDNS Ultradns - Variomedia + Variomedia VegaDNS Vercel Versio.[nl|eu|uk] - VinylDNS + VinylDNS VK Cloud Volcano Engine/火山引擎 Vscale - Vultr + Vultr Webnames Websupport WEDOS - West.cn/西部数码 + West.cn/西部数码 Yandex 360 Yandex Cloud Yandex PDD - Zone.ee + Zone.ee ZoneEdit Zonomi - diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go index 73c162b21..1aacdc7af 100644 --- a/cmd/zz_gen_cmd_dnshelp.go +++ b/cmd/zz_gen_cmd_dnshelp.go @@ -60,6 +60,7 @@ func allDNSCodes() string { "dynu", "easydns", "edgedns", + "edgeone", "efficientip", "epik", "exec", @@ -1206,6 +1207,29 @@ func displayDNSHelp(w io.Writer, name string) error { ew.writeln() ew.writeln(`More information: https://go-acme.github.io/lego/dns/edgedns`) + case "edgeone": + // generated from: providers/dns/edgeone/edgeone.toml + ew.writeln(`Configuration for Tencent EdgeOne.`) + ew.writeln(`Code: 'edgeone'`) + ew.writeln(`Since: 'v4.26.0'`) + ew.writeln() + + ew.writeln(`Credentials:`) + ew.writeln(` - "EDGEONE_SECRET_ID": Access key ID`) + ew.writeln(` - "EDGEONE_SECRET_KEY": Access Key secret`) + ew.writeln() + + ew.writeln(`Additional Configuration:`) + ew.writeln(` - "EDGEONE_HTTP_TIMEOUT": API request timeout in seconds (Default: 30)`) + ew.writeln(` - "EDGEONE_POLLING_INTERVAL": Time between DNS propagation check in seconds (Default: 30)`) + ew.writeln(` - "EDGEONE_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation in seconds (Default: 1200)`) + ew.writeln(` - "EDGEONE_REGION": Region`) + ew.writeln(` - "EDGEONE_SESSION_TOKEN": Access Key token`) + ew.writeln(` - "EDGEONE_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/edgeone`) + case "efficientip": // generated from: providers/dns/efficientip/efficientip.toml ew.writeln(`Configuration for Efficient IP.`) diff --git a/docs/content/dns/zz_gen_edgeone.md b/docs/content/dns/zz_gen_edgeone.md new file mode 100644 index 000000000..b7b5b1eec --- /dev/null +++ b/docs/content/dns/zz_gen_edgeone.md @@ -0,0 +1,72 @@ +--- +title: "Tencent EdgeOne" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: edgeone +dnsprovider: + since: "v4.26.0" + code: "edgeone" + url: "https://edgeone.ai" +--- + + + + + + +Configuration for [Tencent EdgeOne](https://edgeone.ai). + + + + +- Code: `edgeone` +- Since: v4.26.0 + + +Here is an example bash command using the Tencent EdgeOne provider: + +```bash +EDGEONE_SECRET_ID=abcdefghijklmnopqrstuvwx \ +EDGEONE_SECRET_KEY=your-secret-key \ +lego --email you@example.com --dns edgeone -d '*.example.com' -d example.com run +``` + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `EDGEONE_SECRET_ID` | Access key ID | +| `EDGEONE_SECRET_KEY` | Access Key secret | + +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 | +|--------------------------------|-------------| +| `EDGEONE_HTTP_TIMEOUT` | API request timeout in seconds (Default: 30) | +| `EDGEONE_POLLING_INTERVAL` | Time between DNS propagation check in seconds (Default: 30) | +| `EDGEONE_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation in seconds (Default: 1200) | +| `EDGEONE_REGION` | Region | +| `EDGEONE_SESSION_TOKEN` | Access Key token | +| `EDGEONE_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](https://edgeone.ai/document/50454#dns-record-apis) +- [Go client](https://github.com/tencentcloud/tencentcloud-sdk-go) + + + + diff --git a/docs/content/dns/zz_gen_tencentcloud.md b/docs/content/dns/zz_gen_tencentcloud.md index bc08c43ce..ef1e6cdf8 100644 --- a/docs/content/dns/zz_gen_tencentcloud.md +++ b/docs/content/dns/zz_gen_tencentcloud.md @@ -6,7 +6,7 @@ slug: tencentcloud dnsprovider: since: "v4.6.0" code: "tencentcloud" - url: "https://cloud.tencent.com/product/cns" + url: "https://cloud.tencent.com/product/dns" --- @@ -14,7 +14,7 @@ dnsprovider: -Configuration for [Tencent Cloud DNS](https://cloud.tencent.com/product/cns). +Configuration for [Tencent Cloud DNS](https://cloud.tencent.com/product/dns). diff --git a/docs/data/zz_cli_help.toml b/docs/data/zz_cli_help.toml index 35f641cd4..62e422217 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, allinkl, arvancloud, auroradns, autodns, axelname, azion, azure, azuredns, baiducloud, bindman, bluecat, bookmyname, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, conohav3, constellix, corenetworks, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dyndnsfree, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, f5xc, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, metaregistrar, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nicru, nifcloud, njalla, nodion, ns1, 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, technitium, tencentcloud, timewebcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, volcengine, vscale, vultr, webnames, websupport, wedos, westcn, yandex, yandex360, yandexcloud, zoneedit, zoneee, zonomi + acme-dns, active24, alidns, allinkl, arvancloud, auroradns, autodns, axelname, azion, azure, azuredns, baiducloud, bindman, bluecat, bookmyname, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, conohav3, constellix, corenetworks, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dyndnsfree, dynu, easydns, edgedns, edgeone, efficientip, epik, exec, exoscale, f5xc, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, huaweicloud, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, limacity, linode, liquidweb, loopia, luadns, mailinabox, manageengine, manual, metaname, metaregistrar, mijnhost, mittwald, myaddr, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nicru, nifcloud, njalla, nodion, ns1, 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, technitium, tencentcloud, timewebcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, volcengine, vscale, vultr, webnames, 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 c5c328d41..47ce27ebe 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,7 @@ require ( github.com/exoscale/egoscale/v3 v3.1.24 github.com/go-acme/alidns-20150109/v4 v4.5.10 github.com/go-acme/tencentclouddnspod v1.0.1208 + github.com/go-acme/tencentedgdeone v1.0.1212 github.com/go-jose/go-jose/v4 v4.1.1 github.com/go-viper/mapstructure/v2 v2.4.0 github.com/google/go-cmp v0.7.0 @@ -76,7 +77,7 @@ require ( github.com/selectel/go-selvpcclient/v4 v4.1.0 github.com/softlayer/softlayer-go v1.1.7 github.com/stretchr/testify v1.10.0 - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1210 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1212 github.com/transip/gotransip/v6 v6.26.0 github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec github.com/urfave/cli/v2 v2.27.7 diff --git a/go.sum b/go.sum index f6b2aceee..f99d60e97 100644 --- a/go.sum +++ b/go.sum @@ -321,6 +321,8 @@ github.com/go-acme/alidns-20150109/v4 v4.5.10 h1:epLD0VaHR5XUpiM6mjm4MzQFICrk+zp github.com/go-acme/alidns-20150109/v4 v4.5.10/go.mod h1:qGRq8kD0xVgn82qRSQmhHwh/oWxKRjF4Db5OI4ScV5g= github.com/go-acme/tencentclouddnspod v1.0.1208 h1:xAVy1lmg2KcKKeYmFSBQUttwc1o1S++9QTjAotGC+BM= github.com/go-acme/tencentclouddnspod v1.0.1208/go.mod h1:yxG02mkbbVd7lTb97nOn7oj09djhm7hAwxNQw4B9dpQ= +github.com/go-acme/tencentedgdeone v1.0.1212 h1:H0HaYrkyX4htOXgusb6Mf8mKDIr/+CIqq5hbOdQB2EY= +github.com/go-acme/tencentedgdeone v1.0.1212/go.mod h1:IADOuBpaFM1IIKFedlJj7EfMqstThcm1g1g1uVejgxo= 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= @@ -895,8 +897,8 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 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.0.1208/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1210 h1:waSk2KyI2VvXtR+XQJm0v1lWfgbJg51iSWJh4hWnyeo= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1210/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1212 h1:61myGZ5h8YgmFEziBGuKPSnPNs75KlqDQwEeGC2dwyk= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1212/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= diff --git a/providers/dns/edgeone/edgeone.go b/providers/dns/edgeone/edgeone.go new file mode 100644 index 000000000..88a795c69 --- /dev/null +++ b/providers/dns/edgeone/edgeone.go @@ -0,0 +1,188 @@ +// Package edgeone implements a DNS provider for solving the DNS-01 challenge using Tencent EdgeOne. +package edgeone + +import ( + "context" + "errors" + "fmt" + "math" + "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/ptr" + teo "github.com/go-acme/tencentedgdeone/v20220901" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" + "golang.org/x/net/idna" +) + +// Environment variables names. +const ( + envNamespace = "EDGEONE_" + + EnvSecretID = envNamespace + "SECRET_ID" + EnvSecretKey = envNamespace + "SECRET_KEY" + EnvRegion = envNamespace + "REGION" + EnvSessionToken = envNamespace + "SESSION_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 { + SecretID string + SecretKey string + Region string + SessionToken string + + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int + HTTPTimeout time.Duration +} + +// NewDefaultConfig returns a default configuration for the DNSProvider. +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt(EnvTTL, 60), + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 20*time.Minute), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 30*time.Second), + HTTPTimeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), + } +} + +// DNSProvider implements the challenge.Provider interface. +type DNSProvider struct { + config *Config + client *teo.Client + + recordIDs map[string]*string + recordIDsMu sync.Mutex +} + +// NewDNSProvider returns a DNSProvider instance configured for Tencent EdgeOne. +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvSecretID, EnvSecretKey) + if err != nil { + return nil, fmt.Errorf("edgeone: %w", err) + } + + config := NewDefaultConfig() + config.SecretID = values[EnvSecretID] + config.SecretKey = values[EnvSecretKey] + config.Region = env.GetOrDefaultString(EnvRegion, "") + config.SessionToken = env.GetOrDefaultString(EnvSessionToken, "") + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Tencent EdgeOne. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("edgeone: the configuration of the DNS provider is nil") + } + + var credential *common.Credential + + switch { + case config.SecretID != "" && config.SecretKey != "" && config.SessionToken != "": + credential = common.NewTokenCredential(config.SecretID, config.SecretKey, config.SessionToken) + case config.SecretID != "" && config.SecretKey != "": + credential = common.NewCredential(config.SecretID, config.SecretKey) + default: + return nil, errors.New("edgeone: credentials missing") + } + + cpf := profile.NewClientProfile() + cpf.HttpProfile.Endpoint = "teo.intl.tencentcloudapi.com" + cpf.HttpProfile.ReqTimeout = int(math.Round(config.HTTPTimeout.Seconds())) + + client, err := teo.NewClient(credential, config.Region, cpf) + if err != nil { + return nil, fmt.Errorf("edgeone: %w", err) + } + + return &DNSProvider{ + config: config, + client: client, + recordIDs: map[string]*string{}, + recordIDsMu: sync.Mutex{}, + }, 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 := context.Background() + + zone, err := d.getHostedZone(ctx, info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("edgeone: failed to get hosted zone: %w", err) + } + + punnyCoded, err := idna.ToASCII(dns01.UnFqdn(info.EffectiveFQDN)) + if err != nil { + return fmt.Errorf("edgeone: fail to convert punycode: %w", err) + } + + request := teo.NewCreateDnsRecordRequest() + request.Name = ptr.Pointer(punnyCoded) + request.ZoneId = zone.ZoneId + request.Type = ptr.Pointer("TXT") + request.Content = ptr.Pointer(info.Value) + request.TTL = ptr.Pointer(int64(d.config.TTL)) + + nr, err := teo.CreateDnsRecordWithContext(ctx, d.client, request) + if err != nil { + return fmt.Errorf("edgeone: API call failed: %w", err) + } + + d.recordIDsMu.Lock() + d.recordIDs[token] = nr.Response.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) + + ctx := context.Background() + + zone, err := d.getHostedZone(ctx, info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("edgeone: failed to get hosted zone: %w", err) + } + + // get the record's unique ID from when we created it + d.recordIDsMu.Lock() + recordID, ok := d.recordIDs[token] + d.recordIDsMu.Unlock() + if !ok { + return fmt.Errorf("edgeone: unknown record ID for '%s'", info.EffectiveFQDN) + } + + request := teo.NewDeleteDnsRecordsRequest() + request.ZoneId = zone.ZoneId + request.RecordIds = []*string{recordID} + + _, err = teo.DeleteDnsRecordsWithContext(ctx, d.client, request) + if err != nil { + return fmt.Errorf("edgeone: delete record failed: %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/edgeone/edgeone.toml b/providers/dns/edgeone/edgeone.toml new file mode 100644 index 000000000..120756da6 --- /dev/null +++ b/providers/dns/edgeone/edgeone.toml @@ -0,0 +1,27 @@ +Name = "Tencent EdgeOne" +Description = '''''' +URL = "https://edgeone.ai" +Code = "edgeone" +Since = "v4.26.0" + +Example = ''' +EDGEONE_SECRET_ID=abcdefghijklmnopqrstuvwx \ +EDGEONE_SECRET_KEY=your-secret-key \ +lego --email you@example.com --dns edgeone -d '*.example.com' -d example.com run +''' + +[Configuration] + [Configuration.Credentials] + EDGEONE_SECRET_ID = "Access key ID" + EDGEONE_SECRET_KEY = "Access Key secret" + [Configuration.Additional] + EDGEONE_SESSION_TOKEN = "Access Key token" + EDGEONE_REGION = "Region" + EDGEONE_POLLING_INTERVAL = "Time between DNS propagation check in seconds (Default: 30)" + EDGEONE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation in seconds (Default: 1200)" + EDGEONE_TTL = "The TTL of the TXT record used for the DNS challenge in seconds (Default: 60)" + EDGEONE_HTTP_TIMEOUT = "API request timeout in seconds (Default: 30)" + +[Links] + API = "https://edgeone.ai/document/50454#dns-record-apis" + GoClient = "https://github.com/tencentcloud/tencentcloud-sdk-go" diff --git a/providers/dns/edgeone/edgeone_test.go b/providers/dns/edgeone/edgeone_test.go new file mode 100644 index 000000000..e1c004d67 --- /dev/null +++ b/providers/dns/edgeone/edgeone_test.go @@ -0,0 +1,147 @@ +package edgeone + +import ( + "testing" + + "github.com/go-acme/lego/v4/platform/tester" + "github.com/stretchr/testify/require" +) + +const envDomain = envNamespace + "DOMAIN" + +var envTest = tester.NewEnvTest(EnvSecretID, EnvSecretKey). + WithDomain(envDomain) + +func TestNewDNSProvider(t *testing.T) { + testCases := []struct { + desc string + envVars map[string]string + expected string + }{ + { + desc: "success", + envVars: map[string]string{ + EnvSecretID: "123", + EnvSecretKey: "456", + }, + }, + { + desc: "missing credentials", + envVars: map[string]string{ + EnvSecretID: "", + EnvSecretKey: "", + }, + expected: "edgeone: some credentials information are missing: EDGEONE_SECRET_ID,EDGEONE_SECRET_KEY", + }, + { + desc: "missing access id", + envVars: map[string]string{ + EnvSecretID: "", + EnvSecretKey: "456", + }, + expected: "edgeone: some credentials information are missing: EDGEONE_SECRET_ID", + }, + { + desc: "missing secret key", + envVars: map[string]string{ + EnvSecretID: "123", + EnvSecretKey: "", + }, + expected: "edgeone: some credentials information are missing: EDGEONE_SECRET_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 + secretID string + secretKey string + expected string + }{ + { + desc: "success", + secretID: "123", + secretKey: "456", + }, + { + desc: "missing credentials", + expected: "edgeone: credentials missing", + }, + { + desc: "missing secret id", + secretKey: "456", + expected: "edgeone: credentials missing", + }, + { + desc: "missing secret key", + secretID: "123", + expected: "edgeone: credentials missing", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + config := NewDefaultConfig() + config.SecretID = test.secretID + config.SecretKey = test.secretKey + + 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/edgeone/wrapper.go b/providers/dns/edgeone/wrapper.go new file mode 100644 index 000000000..0d207f030 --- /dev/null +++ b/providers/dns/edgeone/wrapper.go @@ -0,0 +1,50 @@ +package edgeone + +import ( + "context" + "fmt" + + "github.com/go-acme/lego/v4/challenge/dns01" + "github.com/go-acme/lego/v4/providers/dns/internal/ptr" + teo "github.com/go-acme/tencentedgdeone/v20220901" +) + +func (d *DNSProvider) getHostedZone(ctx context.Context, domain string) (*teo.Zone, error) { + request := teo.NewDescribeZonesRequest() + + var domains []*teo.Zone + + for { + response, err := teo.DescribeZonesWithContext(ctx, d.client, request) + if err != nil { + return nil, fmt.Errorf("API call failed: %w", err) + } + + domains = append(domains, response.Response.Zones...) + + if int64(len(domains)) >= ptr.Deref(response.Response.TotalCount) { + break + } + + request.Offset = ptr.Pointer(int64(len(domains))) + } + + authZone, err := dns01.FindZoneByFqdn(domain) + if err != nil { + return nil, fmt.Errorf("could not find zone: %w", err) + } + + var hostedZone *teo.Zone + for _, zone := range domains { + unfqdn := dns01.UnFqdn(authZone) + if ptr.Deref(zone.ZoneName) == unfqdn { + hostedZone = zone + } + } + + if hostedZone == nil { + return nil, fmt.Errorf("zone %s not found in dnspod for domain %s", authZone, domain) + } + + return hostedZone, nil +} diff --git a/providers/dns/tencentcloud/tencentcloud.toml b/providers/dns/tencentcloud/tencentcloud.toml index 75a950a49..7f06d9386 100644 --- a/providers/dns/tencentcloud/tencentcloud.toml +++ b/providers/dns/tencentcloud/tencentcloud.toml @@ -1,6 +1,6 @@ Name = "Tencent Cloud DNS" Description = '''''' -URL = "https://cloud.tencent.com/product/cns" +URL = "https://cloud.tencent.com/product/dns" Code = "tencentcloud" Since = "v4.6.0" diff --git a/providers/dns/zz_gen_dns_providers.go b/providers/dns/zz_gen_dns_providers.go index 59205f7f6..f3598b7b5 100644 --- a/providers/dns/zz_gen_dns_providers.go +++ b/providers/dns/zz_gen_dns_providers.go @@ -54,6 +54,7 @@ import ( "github.com/go-acme/lego/v4/providers/dns/dynu" "github.com/go-acme/lego/v4/providers/dns/easydns" "github.com/go-acme/lego/v4/providers/dns/edgedns" + "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/exec" @@ -265,6 +266,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { return easydns.NewDNSProvider() case "edgedns", "fastdns": return edgedns.NewDNSProvider() + case "edgeone": + return edgeone.NewDNSProvider() case "efficientip": return efficientip.NewDNSProvider() case "epik":