From afcb2bc6d1597eedefe19a08a057ccfba9c7cced Mon Sep 17 00:00:00 2001 From: AntonyRohithAkash <93250352+AntonyRohithAkash@users.noreply.github.com> Date: Wed, 11 Jan 2023 19:27:25 +0530 Subject: [PATCH] Add DNS provider for UltraDNS (#1806) Co-authored-by: Fernandez Ludovic --- README.md | 8 +- cmd/zz_gen_cmd_dnshelp.go | 22 +++ docs/content/dns/zz_gen_ultradns.md | 70 +++++++++ docs/data/zz_cli_help.toml | 2 +- go.mod | 14 +- go.sum | 27 ++-- providers/dns/dns_providers.go | 3 + providers/dns/ultradns/ultradns.go | 169 ++++++++++++++++++++ providers/dns/ultradns/ultradns.toml | 25 +++ providers/dns/ultradns/ultradns_test.go | 198 ++++++++++++++++++++++++ 10 files changed, 515 insertions(+), 23 deletions(-) create mode 100644 docs/content/dns/zz_gen_ultradns.md create mode 100644 providers/dns/ultradns/ultradns.go create mode 100644 providers/dns/ultradns/ultradns.toml create mode 100644 providers/dns/ultradns/ultradns_test.go diff --git a/README.md b/README.md index 0f4696ba..f4a5f97b 100644 --- a/README.md +++ b/README.md @@ -73,10 +73,10 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns). | [RFC2136](https://go-acme.github.io/lego/dns/rfc2136/) | [RimuHosting](https://go-acme.github.io/lego/dns/rimuhosting/) | [Sakura Cloud](https://go-acme.github.io/lego/dns/sakuracloud/) | [Scaleway](https://go-acme.github.io/lego/dns/scaleway/) | | [Selectel](https://go-acme.github.io/lego/dns/selectel/) | [Servercow](https://go-acme.github.io/lego/dns/servercow/) | [Simply.com](https://go-acme.github.io/lego/dns/simply/) | [Sonic](https://go-acme.github.io/lego/dns/sonic/) | | [Stackpath](https://go-acme.github.io/lego/dns/stackpath/) | [Tencent Cloud DNS](https://go-acme.github.io/lego/dns/tencentcloud/) | [TransIP](https://go-acme.github.io/lego/dns/transip/) | [UKFast SafeDNS](https://go-acme.github.io/lego/dns/safedns/) | -| [Variomedia](https://go-acme.github.io/lego/dns/variomedia/) | [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/) | [Vercel](https://go-acme.github.io/lego/dns/vercel/) | [Versio.[nl/eu/uk]](https://go-acme.github.io/lego/dns/versio/) | -| [VinylDNS](https://go-acme.github.io/lego/dns/vinyldns/) | [VK Cloud](https://go-acme.github.io/lego/dns/vkcloud/) | [Vscale](https://go-acme.github.io/lego/dns/vscale/) | [Vultr](https://go-acme.github.io/lego/dns/vultr/) | -| [WEDOS](https://go-acme.github.io/lego/dns/wedos/) | [Yandex Cloud](https://go-acme.github.io/lego/dns/yandexcloud/) | [Yandex PDD](https://go-acme.github.io/lego/dns/yandex/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | -| [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) | | | | +| [Ultradns](https://go-acme.github.io/lego/dns/ultradns/) | [Variomedia](https://go-acme.github.io/lego/dns/variomedia/) | [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/) | [Vercel](https://go-acme.github.io/lego/dns/vercel/) | +| [Versio.[nl/eu/uk]](https://go-acme.github.io/lego/dns/versio/) | [VinylDNS](https://go-acme.github.io/lego/dns/vinyldns/) | [VK Cloud](https://go-acme.github.io/lego/dns/vkcloud/) | [Vscale](https://go-acme.github.io/lego/dns/vscale/) | +| [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [WEDOS](https://go-acme.github.io/lego/dns/wedos/) | [Yandex Cloud](https://go-acme.github.io/lego/dns/yandexcloud/) | [Yandex PDD](https://go-acme.github.io/lego/dns/yandex/) | +| [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) | | | diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go index 52ab1892..a18feaae 100644 --- a/cmd/zz_gen_cmd_dnshelp.go +++ b/cmd/zz_gen_cmd_dnshelp.go @@ -109,6 +109,7 @@ func allDNSCodes() string { "stackpath", "tencentcloud", "transip", + "ultradns", "variomedia", "vegadns", "vercel", @@ -2158,6 +2159,27 @@ func displayDNSHelp(w io.Writer, name string) error { ew.writeln() ew.writeln(`More information: https://go-acme.github.io/lego/dns/transip`) + case "ultradns": + // generated from: providers/dns/ultradns/ultradns.toml + ew.writeln(`Configuration for Ultradns.`) + ew.writeln(`Code: 'ultradns'`) + ew.writeln(`Since: 'v4.10.0'`) + ew.writeln() + + ew.writeln(`Credentials:`) + ew.writeln(` - "ULTRADNS_PASSWORD": API Password`) + ew.writeln(` - "ULTRADNS_USERNAME": API Username`) + ew.writeln() + + ew.writeln(`Additional Configuration:`) + ew.writeln(` - "ULTRADNS_ENDPOINT": API endpoint URL, defaults to https://api.ultradns.com/`) + ew.writeln(` - "ULTRADNS_POLLING_INTERVAL": Time between DNS propagation check`) + ew.writeln(` - "ULTRADNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + ew.writeln(` - "ULTRADNS_TTL": The TTL of the TXT record used for the DNS challenge`) + + ew.writeln() + ew.writeln(`More information: https://go-acme.github.io/lego/dns/ultradns`) + case "variomedia": // generated from: providers/dns/variomedia/variomedia.toml ew.writeln(`Configuration for Variomedia.`) diff --git a/docs/content/dns/zz_gen_ultradns.md b/docs/content/dns/zz_gen_ultradns.md new file mode 100644 index 00000000..3274b12e --- /dev/null +++ b/docs/content/dns/zz_gen_ultradns.md @@ -0,0 +1,70 @@ +--- +title: "Ultradns" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: ultradns +dnsprovider: + since: "v4.10.0" + code: "ultradns" + url: "https://neustarsecurityservices.com/dns-services" +--- + + + + + + +Configuration for [Ultradns](https://neustarsecurityservices.com/dns-services). + + + + +- Code: `ultradns` +- Since: v4.10.0 + + +Here is an example bash command using the Ultradns provider: + +```bash +ULTRADNS_USERNAME=username \ +ULTRADNS_PASSWORD=password \ +lego --email you@example.com --dns ultradns --domains my.example.org run +``` + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `ULTRADNS_PASSWORD` | API Password | +| `ULTRADNS_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 | +|--------------------------------|-------------| +| `ULTRADNS_ENDPOINT` | API endpoint URL, defaults to https://api.ultradns.com/ | +| `ULTRADNS_POLLING_INTERVAL` | Time between DNS propagation check | +| `ULTRADNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `ULTRADNS_TTL` | The TTL of the TXT record used for the DNS challenge | + +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://ultra-portalstatic.ultradns.com/static/docs/REST-API_User_Guide.pdf) +- [Go client](https://github.com/ultradns/ultradns-go-sdk) + + + + diff --git a/docs/data/zz_cli_help.toml b/docs/data/zz_cli_help.toml index 14134118..87ced52f 100644 --- a/docs/data/zz_cli_help.toml +++ b/docs/data/zz_cli_help.toml @@ -125,7 +125,7 @@ To display the documentation for a specific DNS provider, run: $ lego dnshelp -c code Supported DNS providers: - acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, bindman, bluecat, checkdomain, civo, clouddns, cloudflare, cloudns, cloudxns, conoha, constellix, desec, designate, digitalocean, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, hetzner, hostingde, hosttech, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, manual, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, ns1, oraclecloud, otc, ovh, pdns, porkbun, rackspace, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, servercow, simply, sonic, stackpath, tencentcloud, transip, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, vscale, vultr, wedos, yandex, yandexcloud, zoneee, zonomi + acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, bindman, bluecat, checkdomain, civo, clouddns, cloudflare, cloudns, cloudxns, conoha, constellix, desec, designate, digitalocean, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, hetzner, hostingde, hosttech, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, manual, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, ns1, oraclecloud, otc, ovh, pdns, porkbun, rackspace, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, servercow, simply, sonic, stackpath, tencentcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, vscale, vultr, wedos, yandex, yandexcloud, zoneee, zonomi More information: https://go-acme.github.io/lego/dns """ diff --git a/go.mod b/go.mod index b67b7edf..efd0ec70 100644 --- a/go.mod +++ b/go.mod @@ -55,14 +55,15 @@ require ( github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 github.com/transip/gotransip/v6 v6.17.0 + github.com/ultradns/ultradns-go-sdk v1.4.0-20221107152238-f3f1d1d github.com/urfave/cli/v2 v2.14.0 github.com/vinyldns/go-vinyldns v0.9.16 github.com/vultr/govultr/v2 v2.17.2 github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 - golang.org/x/net v0.0.0-20220722155237-a158d28d115b - golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 + golang.org/x/net v0.1.0 + golang.org/x/oauth2 v0.1.0 golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 google.golang.org/api v0.20.0 gopkg.in/ns1/ns1-go.v2 v2.6.5 @@ -121,11 +122,10 @@ require ( github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.opencensus.io v0.22.3 // indirect go.uber.org/ratelimit v0.2.0 // indirect - golang.org/x/mod v0.4.2 // indirect - golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/sys v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect + golang.org/x/tools v0.1.12 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20211021150943-2b146023228c // indirect google.golang.org/grpc v1.41.0 // indirect diff --git a/go.sum b/go.sum index 227a25e7..2c0e9440 100644 --- a/go.sum +++ b/go.sum @@ -549,6 +549,8 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= +github.com/ultradns/ultradns-go-sdk v1.4.0-20221107152238-f3f1d1d h1:pLMpEtrkiaeA2NY6CzA2+K75YnY6c5ka02SbxQ9YgSo= +github.com/ultradns/ultradns-go-sdk v1.4.0-20221107152238-f3f1d1d/go.mod h1:IgdoVzrGYzq4H4IGI0DAVnM3CbcuQDSxEP4s/j6cztI= github.com/urfave/cli/v2 v2.14.0 h1:sFRL29Dm9JhXSMYb96raDeo/Q/JRyPXPs8u+4CkMlI8= github.com/urfave/cli/v2 v2.14.0/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= @@ -634,8 +636,9 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 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= @@ -671,15 +674,15 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= 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-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y= +golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= 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= @@ -688,8 +691,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -741,12 +744,13 @@ golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= 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= @@ -755,8 +759,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 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= @@ -801,12 +806,12 @@ golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= diff --git a/providers/dns/dns_providers.go b/providers/dns/dns_providers.go index 342770e0..e6d50312 100644 --- a/providers/dns/dns_providers.go +++ b/providers/dns/dns_providers.go @@ -100,6 +100,7 @@ import ( "github.com/go-acme/lego/v4/providers/dns/stackpath" "github.com/go-acme/lego/v4/providers/dns/tencentcloud" "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/variomedia" "github.com/go-acme/lego/v4/providers/dns/vegadns" "github.com/go-acme/lego/v4/providers/dns/vercel" @@ -310,6 +311,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { return tencentcloud.NewDNSProvider() case "transip": return transip.NewDNSProvider() + case "ultradns": + return ultradns.NewDNSProvider() case "variomedia": return variomedia.NewDNSProvider() case "vegadns": diff --git a/providers/dns/ultradns/ultradns.go b/providers/dns/ultradns/ultradns.go new file mode 100644 index 00000000..b9c9cbcf --- /dev/null +++ b/providers/dns/ultradns/ultradns.go @@ -0,0 +1,169 @@ +// Package ultradns implements a DNS provider for solving the DNS-01 challenge using ultradns. +package ultradns + +import ( + "errors" + "fmt" + "time" + + "github.com/go-acme/lego/v4/challenge/dns01" + "github.com/go-acme/lego/v4/platform/config/env" + "github.com/ultradns/ultradns-go-sdk/pkg/client" + "github.com/ultradns/ultradns-go-sdk/pkg/record" + "github.com/ultradns/ultradns-go-sdk/pkg/rrset" +) + +// Environment variables names. +const ( + envNamespace = "ULTRADNS_" + + EnvUsername = envNamespace + "USERNAME" + EnvPassword = envNamespace + "PASSWORD" + EnvEndpoint = envNamespace + "ENDPOINT" + + EnvTTL = envNamespace + "TTL" + EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" + EnvPollingInterval = envNamespace + "POLLING_INTERVAL" + + // Default variables names. + defaultEndpoint = "https://api.ultradns.com/" + defaultUserAgent = "lego-provider-ultradns" +) + +// DNSProvider implements the challenge.Provider interface. +type DNSProvider struct { + config *Config + client *client.Client +} + +// Config is used to configure the creation of the DNSProvider. +type Config struct { + Username string + Password string + Endpoint string + + TTL int + PropagationTimeout time.Duration + PollingInterval time.Duration +} + +// NewDefaultConfig returns a default configuration for the DNSProvider. +func NewDefaultConfig() *Config { + return &Config{ + Endpoint: env.GetOrDefaultString(EnvEndpoint, defaultEndpoint), + TTL: env.GetOrDefaultInt(EnvTTL, 120), + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 4*time.Second), + } +} + +// NewDNSProvider returns a DNSProvider instance configured for ultradns. +// Credentials must be passed in the environment variables: +// ULTRADNS_USERNAME and ULTRADNS_PASSWORD. +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvUsername, EnvPassword) + if err != nil { + return nil, fmt.Errorf("ultradns: %w", err) + } + + config := NewDefaultConfig() + config.Username = values[EnvUsername] + config.Password = values[EnvPassword] + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for ultradns. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("ultradns: the configuration of the DNS provider is nil") + } + + ultraConfig := client.Config{ + Username: config.Username, + Password: config.Password, + HostURL: config.Endpoint, + UserAgent: defaultUserAgent, + } + + uClient, err := client.NewClient(ultraConfig) + if err != nil { + return nil, fmt.Errorf("ultradns: %w", err) + } + + return &DNSProvider{config: config, client: uClient}, 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 +} + +// Present creates a TXT record using the specified parameters. +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + fqdn, value := dns01.GetRecord(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(fqdn) + if err != nil { + return fmt.Errorf("ultradns: %w", err) + } + + recordService, err := record.Get(d.client) + if err != nil { + return fmt.Errorf("ultradns: %w", err) + } + + rrSetKeyData := &rrset.RRSetKey{ + Owner: fqdn, + Zone: authZone, + RecordType: "TXT", + } + + res, _, _ := recordService.Read(rrSetKeyData) + + rrSetData := &rrset.RRSet{ + OwnerName: fqdn, + TTL: d.config.TTL, + RRType: "TXT", + RData: []string{value}, + } + + if res != nil && res.StatusCode == 200 { + _, err = recordService.Update(rrSetKeyData, rrSetData) + } else { + _, err = recordService.Create(rrSetKeyData, rrSetData) + } + if err != nil { + return fmt.Errorf("ultradns: %w", err) + } + + return nil +} + +// CleanUp removes the TXT record matching the specified parameters. +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + fqdn, _ := dns01.GetRecord(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(fqdn) + if err != nil { + return fmt.Errorf("ultradns: %w", err) + } + + recordService, err := record.Get(d.client) + if err != nil { + return fmt.Errorf("ultradns: %w", err) + } + + rrSetKeyData := &rrset.RRSetKey{ + Owner: fqdn, + Zone: authZone, + RecordType: "TXT", + } + + _, err = recordService.Delete(rrSetKeyData) + if err != nil { + return fmt.Errorf("ultradns: %w", err) + } + + return nil +} diff --git a/providers/dns/ultradns/ultradns.toml b/providers/dns/ultradns/ultradns.toml new file mode 100644 index 00000000..50cc1a62 --- /dev/null +++ b/providers/dns/ultradns/ultradns.toml @@ -0,0 +1,25 @@ +Name = "Ultradns" +Description = '''''' +URL = "https://neustarsecurityservices.com/dns-services" +Code = "ultradns" +Since = "v4.10.0" + +Example = ''' +ULTRADNS_USERNAME=username \ +ULTRADNS_PASSWORD=password \ +lego --email you@example.com --dns ultradns --domains my.example.org run +''' + +[Configuration] + [Configuration.Credentials] + ULTRADNS_USERNAME = "API Username" + ULTRADNS_PASSWORD = "API Password" + [Configuration.Additional] + ULTRADNS_ENDPOINT = "API endpoint URL, defaults to https://api.ultradns.com/" + ULTRADNS_TTL = "The TTL of the TXT record used for the DNS challenge" + ULTRADNS_POLLING_INTERVAL = "Time between DNS propagation check" + ULTRADNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + +[Links] + API = "https://ultra-portalstatic.ultradns.com/static/docs/REST-API_User_Guide.pdf" + GoClient = "https://github.com/ultradns/ultradns-go-sdk" diff --git a/providers/dns/ultradns/ultradns_test.go b/providers/dns/ultradns/ultradns_test.go new file mode 100644 index 00000000..08a3b6aa --- /dev/null +++ b/providers/dns/ultradns/ultradns_test.go @@ -0,0 +1,198 @@ +package ultradns + +import ( + "testing" + "time" + + "github.com/go-acme/lego/v4/platform/tester" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const envDomain = envNamespace + "DOMAIN" + +var envTest = tester.NewEnvTest( + EnvUsername, + EnvPassword, + EnvEndpoint, + EnvTTL, + EnvPropagationTimeout, + EnvPollingInterval). + WithDomain(envDomain) + +func TestNewDefaultConfig(t *testing.T) { + defer envTest.RestoreEnv() + + testCases := []struct { + desc string + envVars map[string]string + expected *Config + }{ + { + desc: "default configuration", + expected: &Config{ + Endpoint: "https://api.ultradns.com/", + TTL: 120, + PropagationTimeout: 2 * time.Minute, + PollingInterval: 4 * time.Second, + }, + }, + { + desc: "input configuration", + envVars: map[string]string{ + EnvEndpoint: "https://example.com/", + EnvTTL: "99", + EnvPropagationTimeout: "60", + EnvPollingInterval: "60", + }, + expected: &Config{ + Endpoint: "https://example.com/", + TTL: 99, + PropagationTimeout: 60 * time.Second, + PollingInterval: 60 * time.Second, + }, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + envTest.ClearEnv() + envTest.Apply(test.envVars) + + config := NewDefaultConfig() + + assert.Equal(t, test.expected, config) + }) + } +} + +func TestNewDNSProvider(t *testing.T) { + defer envTest.RestoreEnv() + + testCases := []struct { + desc string + envVars map[string]string + expected string + }{ + { + desc: "missing username and password", + expected: "ultradns: some credentials information are missing: ULTRADNS_USERNAME,ULTRADNS_PASSWORD", + }, + { + desc: "missing username", + envVars: map[string]string{ + EnvPassword: "password", + }, + expected: "ultradns: some credentials information are missing: ULTRADNS_USERNAME", + }, + { + desc: "missing password", + envVars: map[string]string{ + EnvUsername: "username", + }, + expected: "ultradns: some credentials information are missing: ULTRADNS_PASSWORD", + }, + { + desc: "success", + envVars: map[string]string{ + EnvUsername: "username", + EnvPassword: "password", + }, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + envTest.ClearEnv() + envTest.Apply(test.envVars) + + p, err := NewDNSProvider() + + if test.expected == "" { + require.NoError(t, err) + require.NotNil(t, p) + assert.NotNil(t, p.config) + assert.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: "api_username", + password: "api_password", + }, + { + desc: "missing credentials", + expected: "ultradns: config validation failure: username is missing", + }, + { + desc: "missing username", + username: "", + password: "api_password", + expected: "ultradns: config validation failure: username is missing", + }, + { + desc: "missing password", + username: "api_username", + password: "", + expected: "ultradns: config validation failure: password is 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) + } 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) +}