From dce3edfcead4e67f1c329832c4db2ab8b7826b90 Mon Sep 17 00:00:00 2001 From: vanbroup Date: Wed, 19 Jul 2023 07:05:32 +0000 Subject: [PATCH] Update DNS Providers --- go.mod | 95 ++-- go.sum | 224 +++++---- providers/arvancloud/arvancloud.go | 4 +- providers/autodns/autodns.go | 6 +- providers/azure/azure.go | 2 + providers/azure/azure.toml | 2 +- providers/azure/private.go | 2 +- providers/azure/public.go | 2 +- providers/azuredns/azuredns.go | 179 +++++++ providers/azuredns/azuredns.toml | 77 ++++ providers/azuredns/azuredns_test.go | 168 +++++++ providers/azuredns/private.go | 154 +++++++ providers/azuredns/public.go | 154 +++++++ providers/bunny/bunny.go | 4 +- providers/cloudflare/cloudflare.go | 10 +- providers/cloudflare/wrapper.go | 10 +- providers/designate/designate.toml | 2 +- providers/dns_providers.go | 12 + providers/dnsmadeeasy/internal/client.go | 3 +- providers/efficientip/efficientip.go | 152 ++++++ providers/efficientip/efficientip.toml | 26 ++ providers/efficientip/efficientip_test.go | 201 ++++++++ providers/efficientip/internal/client.go | 209 +++++++++ providers/efficientip/internal/client_test.go | 427 +++++++++++++++++ .../internal/fixtures/dns_rr_add.json | 5 + .../fixtures/dns_rr_delete-error.json | 6 + .../internal/fixtures/dns_rr_delete.json | 5 + .../internal/fixtures/dns_rr_info.json | 64 +++ .../internal/fixtures/dns_rr_list.json | 436 ++++++++++++++++++ providers/efficientip/internal/types.go | 109 +++++ providers/exec/exec.toml | 15 - providers/gcore/gcore.go | 4 +- providers/godaddy/godaddy.go | 4 +- providers/hetzner/hetzner.go | 4 +- providers/linode/linode.go | 4 +- providers/metaname/metaname.go | 159 +++++++ providers/metaname/metaname.toml | 24 + providers/metaname/metaname_test.go | 145 ++++++ providers/pdns/pdns.go | 6 +- providers/rcodezero/internal/client.go | 114 +++++ providers/rcodezero/internal/client_test.go | 96 ++++ .../rcodezero/internal/fixtures/error.json | 4 + .../internal/fixtures/rrsets-response.json | 4 + providers/rcodezero/internal/types.go | 25 + providers/rcodezero/rcodezero.go | 145 ++++++ providers/rcodezero/rcodezero.toml | 33 ++ providers/rcodezero/rcodezero_test.go | 105 +++++ providers/rfc2136/rfc2136.go | 1 - providers/route53/route53.go | 11 +- providers/route53/route53.toml | 3 +- providers/transip/fakeclient_test.go | 12 + providers/websupport/websupport.go | 14 +- 52 files changed, 3478 insertions(+), 204 deletions(-) create mode 100644 providers/azuredns/azuredns.go create mode 100644 providers/azuredns/azuredns.toml create mode 100644 providers/azuredns/azuredns_test.go create mode 100644 providers/azuredns/private.go create mode 100644 providers/azuredns/public.go create mode 100644 providers/efficientip/efficientip.go create mode 100644 providers/efficientip/efficientip.toml create mode 100644 providers/efficientip/efficientip_test.go create mode 100644 providers/efficientip/internal/client.go create mode 100644 providers/efficientip/internal/client_test.go create mode 100644 providers/efficientip/internal/fixtures/dns_rr_add.json create mode 100644 providers/efficientip/internal/fixtures/dns_rr_delete-error.json create mode 100644 providers/efficientip/internal/fixtures/dns_rr_delete.json create mode 100644 providers/efficientip/internal/fixtures/dns_rr_info.json create mode 100644 providers/efficientip/internal/fixtures/dns_rr_list.json create mode 100644 providers/efficientip/internal/types.go create mode 100644 providers/metaname/metaname.go create mode 100644 providers/metaname/metaname.toml create mode 100644 providers/metaname/metaname_test.go create mode 100644 providers/rcodezero/internal/client.go create mode 100644 providers/rcodezero/internal/client_test.go create mode 100644 providers/rcodezero/internal/fixtures/error.json create mode 100644 providers/rcodezero/internal/fixtures/rrsets-response.json create mode 100644 providers/rcodezero/internal/types.go create mode 100644 providers/rcodezero/rcodezero.go create mode 100644 providers/rcodezero/rcodezero.toml create mode 100644 providers/rcodezero/rcodezero_test.go diff --git a/go.mod b/go.mod index 36340a9..44c08e3 100644 --- a/go.mod +++ b/go.mod @@ -4,91 +4,98 @@ go 1.20 require ( cloud.google.com/go/compute/metadata v0.2.3 - github.com/Azure/azure-sdk-for-go v32.4.0+incompatible + github.com/Azure/azure-sdk-for-go v68.0.0+incompatible + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 + github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 github.com/Azure/go-autorest/autorest v0.11.24 - github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 + github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 github.com/Azure/go-autorest/autorest/to v0.4.0 github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 - github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1 + github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 github.com/aws/aws-sdk-go v1.39.0 - github.com/cenkalti/backoff/v4 v4.2.0 + github.com/cenkalti/backoff/v4 v4.2.1 github.com/civo/civogo v0.3.11 - github.com/cloudflare/cloudflare-go v0.49.0 + github.com/cloudflare/cloudflare-go v0.70.0 github.com/cpu/goacmedns v0.1.1 - github.com/dnsimple/dnsimple-go v0.71.1 + github.com/dnsimple/dnsimple-go v1.2.0 github.com/entrustcorporation/entrust v0.0.0-20230314134457-b6b1cf0dd3bb - github.com/exoscale/egoscale v0.90.0 - github.com/go-acme/lego/v4 v4.11.0 + github.com/exoscale/egoscale v0.100.1 + github.com/go-acme/lego/v4 v4.12.3 github.com/go-jose/go-jose/v3 v3.0.0 github.com/google/go-querystring v1.1.0 github.com/gophercloud/gophercloud v1.0.0 github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae - github.com/hashicorp/go-retryablehttp v0.7.1 + github.com/hashicorp/go-retryablehttp v0.7.4 github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df github.com/infobloxopen/infoblox-go-client v1.1.1 github.com/labbsr0x/bindman-dns-webhook v1.0.2 - github.com/linode/linodego v1.9.1 + github.com/linode/linodego v1.17.2 github.com/liquidweb/liquidweb-go v1.6.3 - github.com/miekg/dns v1.1.50 - github.com/mimuret/golang-iij-dpf v0.7.1 + github.com/miekg/dns v1.1.55 + github.com/mimuret/golang-iij-dpf v0.9.1 github.com/mitchellh/mapstructure v1.5.0 github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 github.com/nrdcg/auroradns v1.1.0 - github.com/nrdcg/desec v0.6.0 + github.com/nrdcg/desec v0.7.0 github.com/nrdcg/dnspod-go v0.4.0 github.com/nrdcg/freemyip v0.2.0 - github.com/nrdcg/goinwx v0.8.1 + github.com/nrdcg/goinwx v0.8.2 github.com/nrdcg/namesilo v0.2.1 github.com/nrdcg/nodion v0.1.0 - github.com/nrdcg/porkbun v0.1.1 + github.com/nrdcg/porkbun v0.2.0 + github.com/nzdjb/go-metaname v1.0.0 github.com/oracle/oci-go-sdk v24.3.0+incompatible - github.com/ovh/go-ovh v1.1.0 - github.com/pquerna/otp v1.3.0 - github.com/sacloud/api-client-go v0.2.1 - github.com/sacloud/iaas-api-go v1.3.2 - github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 + github.com/ovh/go-ovh v1.4.1 + github.com/pquerna/otp v1.4.0 + github.com/sacloud/api-client-go v0.2.8 + github.com/sacloud/iaas-api-go v1.11.1 + github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 - github.com/sirupsen/logrus v1.9.0 - github.com/softlayer/softlayer-go v1.0.6 - github.com/stretchr/testify v1.8.1 + github.com/sirupsen/logrus v1.9.3 + github.com/softlayer/softlayer-go v1.1.2 + github.com/stretchr/testify v1.8.4 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/transip/gotransip/v6 v6.20.0 + github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c github.com/vinyldns/go-vinyldns v0.9.16 github.com/vultr/govultr/v2 v2.17.2 - github.com/weppos/publicsuffix-go v0.30.0 + github.com/weppos/publicsuffix-go v0.30.1 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/net v0.8.0 - golang.org/x/oauth2 v0.6.0 + golang.org/x/net v0.12.0 + golang.org/x/oauth2 v0.9.0 golang.org/x/time v0.3.0 google.golang.org/api v0.111.0 - gopkg.in/ns1/ns1-go.v2 v2.6.5 + gopkg.in/ns1/ns1-go.v2 v2.7.6 gopkg.in/yaml.v2 v2.4.0 ) require ( cloud.google.com/go/compute v1.18.0 // indirect + github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/deepmap/oapi-codegen v1.9.1 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/fatih/structs v1.1.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-errors/errors v1.0.1 // indirect - github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 // indirect - github.com/golang-jwt/jwt/v4 v4.2.0 // indirect + github.com/go-resty/resty/v2 v2.7.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/uuid v1.3.0 // indirect @@ -100,34 +107,36 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect - github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect + github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/labbsr0x/goh v1.0.1 // indirect github.com/liquidweb/go-lwApi v0.0.5 // indirect github.com/liquidweb/liquidweb-cli v0.6.9 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/sacloud/go-http v0.1.2 // indirect - github.com/sacloud/packages-go v0.0.5 // indirect + github.com/sacloud/go-http v0.1.6 // indirect + github.com/sacloud/packages-go v0.0.9 // indirect github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/spf13/cast v1.3.1 // indirect github.com/stretchr/objx v0.5.0 // indirect go.opencensus.io v0.24.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/mod v0.8.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/text v0.8.0 // indirect - golang.org/x/tools v0.6.0 // indirect + golang.org/x/crypto v0.11.0 // indirect + golang.org/x/mod v0.11.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/text v0.11.0 // indirect + golang.org/x/tools v0.10.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 // indirect google.golang.org/grpc v1.53.0 // indirect google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/ini.v1 v1.66.6 // 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 f1d6c31..ac2120c 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,7 @@ cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -17,16 +18,30 @@ cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go v32.4.0+incompatible h1:1JP8SKfroEakYiQU2ZyPDosh8w2Tg9UopKt88VyQPt4= -github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYsMyFh9qoE= +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.6.0 h1:8kDqDngH+DmVBiCtIjCFTGa7MBnsIOkF9IccInFEbjk= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0 h1:8iR6OLffWWorFdzL2JFCab5xpD8VKEE2DUBBl+HNTDY= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.1.0/go.mod h1:copqlcjMWc/wgQ1N2fzsJFQxDdqKGg1EQt8T5wJMOGE= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0 h1:rR8ZW79lE/ppfXTfiYSnMFv5EzmVuY4pfZWIkscIJ64= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.1.0/go.mod h1:y2zXtLSMM/X5Mfawq0lOftpWn3f4V6OCsRdINsvWBPI= +github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.24 h1:1fIGgHKqVm54KIPT+q8Zmd1QlVsmHqeUGso5qm2BqqE= github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ= github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 h1:P6bYXFoao05z5uhOQzbC3Qd8JqF3jUoocoTeIxkp2cA= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk= +github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 h1:0W/yGmFdTIT77fvdlGZ0LMISoLHFJ7Tx4U0yeB+uFs4= github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= @@ -35,19 +50,20 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPu github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= -github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= -github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= -github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1 h1:5BIsppVPdWJA29Yb5cYawQYeh5geN413WxAgBZvEtdA= -github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1/go.mod h1:kX6YddBkXqqywAe8c9LyvgTCyFuZCTMF4cRPQhc3Fy8= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 h1:F1j7z+/DKEsYqZNoxC6wvfmaiDneLsQOFQmuq9NADSY= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2/go.mod h1:QlXr/TrICfQ/ANa76sLeQyhAJyNR9sEcfNuZBkY9jgY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 h1:J45/QHgrzUdqe/Vco/Vxk0wRvdS2nKUxmf/zLgvfass= @@ -67,10 +83,11 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw= github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= -github.com/cenkalti/backoff/v4 v4.2.0 h1:HN5dHm3WBOgndBH6E8V0q2jIYIR3s9yglV8k/+MN3u4= -github.com/cenkalti/backoff/v4 v4.2.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -80,8 +97,9 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/civo/civogo v0.3.11 h1:mON/fyrV946Sbk6paRtOSGsN+asCgCmHCgArf5xmGxM= github.com/civo/civogo v0.3.11/go.mod h1:7+GeeFwc4AYTULaEshpT2vIcl3Qq8HPoxA17viX3l6g= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.49.0 h1:KqJYk/YQ5ZhmyYz1oa4kGDskfF1gVuZfqesaJ/XDLto= -github.com/cloudflare/cloudflare-go v0.49.0/go.mod h1:h0QgcIZ3qEXwFiwfBO8sQxjVdYsLX+PfD7NFEnANaKg= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/cloudflare-go v0.70.0 h1:4opGbUygM8DjirUuaz23jn3akuAcnOCEx+0nQtQEcFo= +github.com/cloudflare/cloudflare-go v0.70.0/go.mod h1:VW6GuazkaZ4xEDkFt24lkXQUsE8q7BiGqDniC2s8WEM= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= @@ -107,8 +125,9 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/dnsimple/dnsimple-go v0.71.1 h1:1hGoBA3CIjpjZj5DM3081xfxr4e2jYmYnkO2VuBF8Qc= -github.com/dnsimple/dnsimple-go v0.71.1/go.mod h1:F9WHww9cC76hrnwGFfAfrqdW99j3MOYasQcIwTS/aUk= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= +github.com/dnsimple/dnsimple-go v1.2.0 h1:ddTGyLVKly5HKb5L65AkLqFqwZlWo3WnR0BlFZlIddM= +github.com/dnsimple/dnsimple-go v1.2.0/go.mod h1:z/cs26v/eiRvUyXsHQBLd8lWF8+cD6GbmkPH84plM4U= github.com/entrustcorporation/entrust v0.0.0-20230314134457-b6b1cf0dd3bb h1:xQBHq0AL4UXflRL7EH/t4FrVygprNA0C0X+BqWjK9i0= github.com/entrustcorporation/entrust v0.0.0-20230314134457-b6b1cf0dd3bb/go.mod h1:NyddGpZcZoMDrK0AfWPiFn74sGdwBfyjK3nqZYb/Q6w= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -118,8 +137,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/exoscale/egoscale v0.90.0 h1:DZBXVU3iHqu5Ju5lQ5jWVlPo0IpI98SUo8Aa1UQVrmo= -github.com/exoscale/egoscale v0.90.0/go.mod h1:wyXE5zrnFynMXA0jMhwQqSe24CfUhmBk2WI5wFZcq6Y= +github.com/exoscale/egoscale v0.100.1 h1:iXsV1Ei7daqe/6FYSCSDyrFs1iUG1l1X9qNh2uMw6z0= +github.com/exoscale/egoscale v0.100.1/go.mod h1:BAb9p4rmyU+Wl400CJZO5270H2sXtdsZjLcm5xMKkz4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -127,15 +146,15 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= -github.com/go-acme/lego/v4 v4.11.0 h1:oIPoU7zBJoTfoVrbqk62+/2NsGCSgCVK1JtZSZZ28SU= -github.com/go-acme/lego/v4 v4.11.0/go.mod h1:dENL0J3/WughN2NLy0T35otK5k1EWCmXTwCw0+X5ZaE= +github.com/go-acme/lego/v4 v4.12.3 h1:aWPYhBopAZXWBASPgvi1LnWGrr5YiXOsrpVaFaVJipo= +github.com/go-acme/lego/v4 v4.12.3/go.mod h1:UZoOlhVmUYP/N0z4tEbfUjoCNHRZNObzqWZtT76DIsc= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= 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= @@ -155,8 +174,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48 h1:JVrqSeQfdhYRFk24TvhTZWU0q8lfCojxZQFi3Ou7+uY= -github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= +github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= +github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA= @@ -168,8 +187,9 @@ github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptG github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= @@ -204,9 +224,11 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= +github.com/google/go-github/v50 v50.2.0/go.mod h1:VBY8FB6yPIjrtKhozXv4FQupxKLS6H4m6xFZlT43q8Q= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -255,9 +277,8 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= -github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ= -github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -279,9 +300,8 @@ github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhK github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/infobloxopen/infoblox-go-client v1.1.1 h1:728A6LbLjptj/7kZjHyIxQnm768PWHfGFm0HH8FnbtU= github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI= -github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= -github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k= github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= +github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -301,8 +321,8 @@ github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcM github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b h1:DzHy0GlWeF0KAglaTMY7Q+khIFoG8toHP+wLFBVBQJc= -github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -314,6 +334,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labbsr0x/bindman-dns-webhook v1.0.2 h1:I7ITbmQPAVwrDdhd6dHKi+MYJTJqPCK0jE6YNBAevnk= github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= github.com/labbsr0x/goh v1.0.1 h1:97aBJkDjpyBZGPbQuOK5/gHcSFbcr5aRsq3RSRJFpPk= @@ -329,8 +351,8 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++ github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/linode/linodego v1.9.1 h1:29UpEPpYcGFnbwiJW8mbk/bjBZpgd/pv68io2IKTo34= -github.com/linode/linodego v1.9.1/go.mod h1:h6AuFR/JpqwwM/vkj7s8KV3iGN8/jxn+zc437F8SZ8w= +github.com/linode/linodego v1.17.2 h1:b32dj4662PGG5P9qVa6nBezccWdqgukndlMIuPGq1CQ= +github.com/linode/linodego v1.17.2/go.mod h1:C2iyT3Vg2O2sPxkWka4XAQ5WSUtm5LmTZ3Adw43Ra7Q= github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= github.com/liquidweb/go-lwApi v0.0.5 h1:CT4cdXzJXmo0bon298kS7NeSk+Gt8/UHpWBBol1NGCA= github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= @@ -354,18 +376,19 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/mimuret/golang-iij-dpf v0.7.1 h1:MHEZKx6gNGTvq1+3PYUNfTZ/qtGNNK4+zo+0Rdo4jY4= -github.com/mimuret/golang-iij-dpf v0.7.1/go.mod h1:IXWYcQVIHYzuM+W7kDWX0mseHDfUoqMuarxMXHVTir0= +github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= +github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +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/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -393,23 +416,25 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uY github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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/desec v0.6.0 h1:kZ9JtsYEW3LNfuPIM+2tXoxoQlF9koWfQTWTQsA7Sr8= -github.com/nrdcg/desec v0.6.0/go.mod h1:wybWg5cRrNmtXLYpUCPCLvz4jfFNEGZQEnoUiX9WqcY= +github.com/nrdcg/desec v0.7.0 h1:iuGhi4pstF3+vJWwt292Oqe2+AsSPKDynQna/eu1fDs= +github.com/nrdcg/desec v0.7.0/go.mod h1:e1uRqqKv1mJdd5+SQROAhmy75lKMphLzWIuASLkpeFY= github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U= github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= github.com/nrdcg/freemyip v0.2.0 h1:/GscavT4GVqAY13HExl5UyoB4wlchv6Cg5NYDGsUoJ8= github.com/nrdcg/freemyip v0.2.0/go.mod h1:HjF0Yz0lSb37HD2ihIyGz9esyGcxbCrrGFLPpKevbx4= -github.com/nrdcg/goinwx v0.8.1 h1:20EQ/JaGFnSKwiDH2JzjIpicffl3cPk6imJBDqVBVtU= -github.com/nrdcg/goinwx v0.8.1/go.mod h1:tILVc10gieBp/5PMvbcYeXM6pVQ+c9jxDZnpaR1UW7c= +github.com/nrdcg/goinwx v0.8.2 h1:RmjiHlEA+lzi3toXyPSaE6hWnBQ0+G+1u7w8C6Fpp4g= +github.com/nrdcg/goinwx v0.8.2/go.mod h1:mnMSTi7CXBu2io4DzdOBoGFA1XclD0sEPWJaDhNgkA4= github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg= github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= 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/porkbun v0.1.1 h1:gxVzQYfFUGXhnBax/aVugoE3OIBAdHgrJgyMPyY5Sjo= -github.com/nrdcg/porkbun v0.1.1/go.mod h1:JWl/WKnguWos4mjfp4YizvvToigk9qpQwrodOk+CPoA= +github.com/nrdcg/porkbun v0.2.0 h1:ghaqPtIKcffba99epWFkK3VWf6TKJT9WMXMgaTqv95Y= +github.com/nrdcg/porkbun v0.2.0/go.mod h1:i0uLMn9ItFsLsSQIAeEu1wQ9/+6EvX1eQw15hulMMRw= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/nzdjb/go-metaname v1.0.0 h1:sNASlZC1RM3nSudtBTE1a3ZVTDyTpjqI5WXRPrdZ9Hg= +github.com/nzdjb/go-metaname v1.0.0/go.mod h1:0GR0LshZax1Lz4VrOrfNSE4dGvTp7HGjiemdczXT2H4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= @@ -425,13 +450,15 @@ github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/oracle/oci-go-sdk v24.3.0+incompatible h1:x4mcfb4agelf1O4/1/auGlZ1lr97jXRSSN5MxTgG/zU= github.com/oracle/oci-go-sdk v24.3.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= -github.com/ovh/go-ovh v1.1.0 h1:bHXZmw8nTgZin4Nv7JuaLs0KG5x54EQR7migYTd1zrk= -github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA= +github.com/ovh/go-ovh v1.4.1 h1:VBGa5wMyQtTP7Zb+w97zRCh9sLtM/2YKRyy+MEJmWaM= +github.com/ovh/go-ovh v1.4.1/go.mod h1:6bL6pPyUT7tBfI0pqOegJgRjgjuO+mOo+MyXd1EEC0M= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -442,8 +469,8 @@ github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= -github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= +github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -468,16 +495,16 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sacloud/api-client-go v0.2.1 h1:jl02ZG6cM+mcH4eDYg0cxCFFuTOVTOjUCLYL4UbP09U= -github.com/sacloud/api-client-go v0.2.1/go.mod h1:8fmYy5OpT3W8ltV5ZxF8evultNwKpduGN4YKmU9Af7w= -github.com/sacloud/go-http v0.1.2 h1:a84HkeDHxDD1vIA6HiOT72a3fwwJueZBwuGP6zVtEJU= -github.com/sacloud/go-http v0.1.2/go.mod h1:gvWaT8LFBFnSBFVrznOQXC62uad46bHZQM8w+xoH3eE= -github.com/sacloud/iaas-api-go v1.3.2 h1:03obrdVdv/bGHK9p6CV7Uzg+ot2gLsddUMevm9DDZqQ= -github.com/sacloud/iaas-api-go v1.3.2/go.mod h1:CoqpRYBG2NRB5xfqTfZNyh2lVLKyLkE/HV9ISqmbhGc= -github.com/sacloud/packages-go v0.0.5 h1:NXTQNyyp/3ugM4CANtLBJLejFESzfWu4GPUURN4NJrA= -github.com/sacloud/packages-go v0.0.5/go.mod h1:XWMBSNHT9YKY3lCh6yJsx1o1RRQQGpuhNqJA6bSHdD4= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 h1:0roa6gXKgyta64uqh52AQG3wzZXH21unn+ltzQSXML0= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= +github.com/sacloud/api-client-go v0.2.8 h1:tIY6PZNBX900K66TqEPa4d6UIbedUczfCBnPJkzi8kw= +github.com/sacloud/api-client-go v0.2.8/go.mod h1:0CV/kWNYlS1hCNdnk6Wx7Wdg8DPFCnv0zOIzdXjeAeY= +github.com/sacloud/go-http v0.1.6 h1:lJGXDt9xrxJiDszRPaN9NIP8MVj10YKMzmnyzdSfI8w= +github.com/sacloud/go-http v0.1.6/go.mod h1:oLAHoDJRkptf8sq4fE8oERLkdCh0kJWfWu+paoJY7I0= +github.com/sacloud/iaas-api-go v1.11.1 h1:2MsFZ4H1uRdRVx2nVXuERWQ3swoFc3XreIV5hJ3Nsws= +github.com/sacloud/iaas-api-go v1.11.1/go.mod h1:uBDSa06F/V0OnoR66jGdbH0PVnCJw+NeE9RVbVgMfss= +github.com/sacloud/packages-go v0.0.9 h1:GbinkBLC/eirFhHpLjoDW6JV7+95Rnd2d8RWj7Afeks= +github.com/sacloud/packages-go v0.0.9/go.mod h1:k+EEUMF2LlncjbNIJNOqLyZ9wjTESPIWIk1OA7x9j2Q= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17 h1:1WuWJu7/e8SqK+uQl7lfk/N/oMZTL2NE/TJsNKRNMc4= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.17/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/simplesurance/bunny-go v0.0.0-20221115111006-e11d9dc91f04 h1:ZTzdx88+AcnjqUfJwnz89UBrMSBQ1NEysg9u5d+dU9c= @@ -486,14 +513,16 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA= -github.com/softlayer/softlayer-go v1.0.6 h1:wMyWmnTm0y3iNwwUJLacgSpMjxAW42MaVqWW4CwYb3c= -github.com/softlayer/softlayer-go v1.0.6/go.mod h1:6HepcfAXROz0Rf63krk5hPZyHT6qyx2MNvYyHof7ik4= +github.com/softlayer/softlayer-go v1.1.2 h1:rUSSGCyaxymvTOsaFjwr+cGxA8muw3xg2LSrIMNcN/c= +github.com/softlayer/softlayer-go v1.1.2/go.mod h1:hvAbzGH4LRXA6yXY8BNx99yoqZ7urfDdtl9mvBf0G+g= github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e h1:3OgWYFw7jxCZPcvAg+4R8A50GZ+CCkARF10lxu2qDsQ= github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e/go.mod h1:fKZCUVdirrxrBpwd9wb+lSoVixvpwAu8eHzbQB2tums= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -523,32 +552,31 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 h1:mmz27tVi2r70JYnm5y0Zk8w0Qzsx+vfUw3oqSyrEfP8= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 h1:g9SWTaTy/rEuhMErC2jWq9Qt5ci+jBYSvXnJsLq4adg= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490/go.mod h1:l9q4vc1QiawUB1m3RU+87yLvrrxe54jc0w/kEl4DbSQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/transip/gotransip/v6 v6.17.0 h1:2RCyqYqz5+Ej8z96EyE4sf6tQrrfEBaFDO0LliSl6+8= -github.com/transip/gotransip/v6 v6.17.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g= +github.com/transip/gotransip/v6 v6.20.0 h1:AuvwyOZ51f2brzMbTqlRy/wmaM3kF7Vx5Wds8xcDflY= +github.com/transip/gotransip/v6 v6.20.0/go.mod h1:nzv9eN2tdsUrm5nG5ZX6AugYIU4qgsMwIn2c0EZLk8c= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 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/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c h1:mKnW6IGLw7uXu6DL6RitufZWcXS6hCnauXRUFof7rKM= +github.com/ultradns/ultradns-go-sdk v1.5.0-20230427130837-23c9b0c/go.mod h1:F4UyVEmq4/m5lAmx+GccrxyRCXmnBjzUL09JLTQFp94= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ= github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= -github.com/weppos/publicsuffix-go v0.12.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= -github.com/weppos/publicsuffix-go v0.30.0 h1:QHPZ2GRu/YE7cvejH9iyavPOkVCB4dNxp2ZvtT+vQLY= -github.com/weppos/publicsuffix-go v0.30.0/go.mod h1:kBi8zwYnR0zrbm8RcuN1o9Fzgpnnn+btVN8uWPMyXAY= -github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220927085643-dc0d00c92642/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE= +github.com/weppos/publicsuffix-go v0.30.1 h1:8q+QwBS1MY56Zjfk/50ycu33NN8aa1iCCEQwo/71Oos= +github.com/weppos/publicsuffix-go v0.30.1/go.mod h1:s41lQh6dIsDWIC1OWh7ChWJXLH0zkJ9KHZVqA7vHyuQ= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= @@ -557,7 +585,6 @@ github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f h1:cG+ehP github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE= github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 h1:2wzke3JH7OtN20WsNDZx2VH/TCmsbqtDEbXzjF+i05E= github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997/go.mod h1:2CHKs/YGbCcNn/BPaCkEBwKz/FNCELi+MLILjR9RaTA= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -591,8 +618,9 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -611,13 +639,13 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -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/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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= @@ -634,9 +662,6 @@ golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -646,18 +671,22 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211029224645-99673261e6eb/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/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= 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-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs= +golang.org/x/oauth2 v0.9.0/go.mod h1:qYgFZaFiu6Wg24azG8bdV52QJXJGbZzIIsRCdVKzbLw= 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= @@ -668,7 +697,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= 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= @@ -705,27 +735,31 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.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/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= 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= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -735,8 +769,10 @@ 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/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -762,14 +798,14 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 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/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= +golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= 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= @@ -826,6 +862,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -841,13 +878,12 @@ gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0= gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.6 h1:LATuAqN/shcYAOkv3wl2L4rkaKqkcgTBQjOyYDvcPKI= -gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ns1/ns1-go.v2 v2.6.5 h1:nzf3RXP4TEZLeZl7q9t6eav4htlNlWuYX+pXVUitlf0= -gopkg.in/ns1/ns1-go.v2 v2.6.5/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk= +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/ns1/ns1-go.v2 v2.7.6 h1:mCPl7q0jbIGACXvGBljAuuApmKZo3rRi4tlRIEbMvjA= +gopkg.in/ns1/ns1-go.v2 v2.7.6/go.mod h1:GMnKY+ZuoJ+lVLL+78uSTjwTz2jMazq6AfGKQOYhsPk= 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/arvancloud/arvancloud.go b/providers/arvancloud/arvancloud.go index 2e2eca8..214d4c8 100644 --- a/providers/arvancloud/arvancloud.go +++ b/providers/arvancloud/arvancloud.go @@ -99,8 +99,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { }, nil } -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. +// 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/autodns/autodns.go b/providers/autodns/autodns.go index 65568b9..660cf6b 100644 --- a/providers/autodns/autodns.go +++ b/providers/autodns/autodns.go @@ -122,8 +122,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { Value: info.Value, }} - // TODO(ldez) replace domain by FQDN to follow CNAME. - _, err := d.client.AddTxtRecords(context.Background(), domain, records) + _, err := d.client.AddTxtRecords(context.Background(), info.EffectiveFQDN, records) if err != nil { return fmt.Errorf("autodns: %w", err) } @@ -142,8 +141,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { Value: info.Value, }} - // TODO(ldez) replace domain by FQDN to follow CNAME. - if err := d.client.RemoveTXTRecords(context.Background(), domain, records); err != nil { + if err := d.client.RemoveTXTRecords(context.Background(), info.EffectiveFQDN, records); err != nil { return fmt.Errorf("autodns: %w", err) } diff --git a/providers/azure/azure.go b/providers/azure/azure.go index 502abc2..95e2ebd 100644 --- a/providers/azure/azure.go +++ b/providers/azure/azure.go @@ -84,6 +84,7 @@ type DNSProvider struct { // If the credentials are _not_ set via the environment, // then it will attempt to get a bearer token via the instance metadata service. // see: https://github.com/Azure/go-autorest/blob/v10.14.0/autorest/azure/auth/auth.go#L38-L42 +// Deprecated: use azuredns instead. func NewDNSProvider() (*DNSProvider, error) { config := NewDefaultConfig() @@ -118,6 +119,7 @@ func NewDNSProvider() (*DNSProvider, error) { } // NewDNSProviderConfig return a DNSProvider instance configured for Azure. +// Deprecated: use azuredns instead. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { return nil, errors.New("azure: the configuration of the DNS provider is nil") diff --git a/providers/azure/azure.toml b/providers/azure/azure.toml index 164512e..c4e3b67 100644 --- a/providers/azure/azure.toml +++ b/providers/azure/azure.toml @@ -1,4 +1,4 @@ -Name = "Azure" +Name = "Azure (deprecated)" Description = '''''' URL = "https://azure.microsoft.com/services/dns/" Code = "azure" diff --git a/providers/azure/private.go b/providers/azure/private.go index f936707..624897a 100644 --- a/providers/azure/private.go +++ b/providers/azure/private.go @@ -7,7 +7,7 @@ import ( "net/http" "time" - "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + "github.com/Azure/azure-sdk-for-go/profiles/latest/privatedns/mgmt/privatedns" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/to" "github.com/entrustcorporation/dv/dns01" diff --git a/providers/azure/public.go b/providers/azure/public.go index 4efcd0b..b5c4375 100644 --- a/providers/azure/public.go +++ b/providers/azure/public.go @@ -7,7 +7,7 @@ import ( "net/http" "time" - "github.com/Azure/azure-sdk-for-go/services/dns/mgmt/2017-09-01/dns" + "github.com/Azure/azure-sdk-for-go/profiles/latest/dns/mgmt/dns" "github.com/Azure/go-autorest/autorest" "github.com/Azure/go-autorest/autorest/to" "github.com/entrustcorporation/dv/dns01" diff --git a/providers/azuredns/azuredns.go b/providers/azuredns/azuredns.go new file mode 100644 index 0000000..0c56121 --- /dev/null +++ b/providers/azuredns/azuredns.go @@ -0,0 +1,179 @@ +// Package azuredns implements a DNS provider for solving the DNS-01 challenge using azure DNS. +// Azure doesn't like trailing dots on domain names, most of the acme code does. +package azuredns + +import ( + "errors" + "fmt" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" + "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/go-acme/lego/v4/challenge" + "github.com/go-acme/lego/v4/platform/config/env" +) + +// Environment variables names. +const ( + envNamespace = "AZURE_" + + EnvEnvironment = envNamespace + "ENVIRONMENT" + EnvSubscriptionID = envNamespace + "SUBSCRIPTION_ID" + EnvResourceGroup = envNamespace + "RESOURCE_GROUP" + EnvZoneName = envNamespace + "ZONE_NAME" + EnvPrivateZone = envNamespace + "PRIVATE_ZONE" + + EnvTenantID = envNamespace + "TENANT_ID" + EnvClientID = envNamespace + "CLIENT_ID" + EnvClientSecret = envNamespace + "CLIENT_SECRET" + + EnvTTL = envNamespace + "TTL" + EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" + EnvPollingInterval = envNamespace + "POLLING_INTERVAL" +) + +// Config is used to configure the creation of the DNSProvider. +type Config struct { + SubscriptionID string + ResourceGroup string + PrivateZone bool + + Environment cloud.Configuration + + // optional if using default Azure credentials + ClientID string + ClientSecret string + TenantID string + + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider. +func NewDefaultConfig() *Config { + return &Config{ + TTL: env.GetOrDefaultInt(EnvTTL, 60), + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 2*time.Second), + Environment: cloud.AzurePublic, + } +} + +// DNSProvider implements the challenge.Provider interface. +type DNSProvider struct { + provider challenge.ProviderTimeout +} + +// NewDNSProvider returns a DNSProvider instance configured for azuredns. +func NewDNSProvider() (*DNSProvider, error) { + config := NewDefaultConfig() + + environmentName := env.GetOrFile(EnvEnvironment) + if environmentName != "" { + switch environmentName { + case "china": + config.Environment = cloud.AzureChina + case "public": + config.Environment = cloud.AzurePublic + case "usgovernment": + config.Environment = cloud.AzureGovernment + default: + return nil, fmt.Errorf("azuredns: unknown environment %s", environmentName) + } + } else { + config.Environment = cloud.AzurePublic + } + + config.SubscriptionID = env.GetOrFile(EnvSubscriptionID) + config.ResourceGroup = env.GetOrFile(EnvResourceGroup) + config.PrivateZone = env.GetOrDefaultBool(EnvPrivateZone, false) + + config.ClientID = env.GetOrFile(EnvTenantID) + config.ClientSecret = env.GetOrFile(EnvClientID) + config.TenantID = env.GetOrFile(EnvClientSecret) + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Azure. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("azuredns: the configuration of the DNS provider is nil") + } + + var err error + var credentials azcore.TokenCredential + if config.ClientID != "" && config.ClientSecret != "" && config.TenantID != "" { + options := azidentity.ClientSecretCredentialOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: config.Environment, + }, + } + + credentials, err = azidentity.NewClientSecretCredential(config.TenantID, config.ClientID, config.ClientSecret, &options) + if err != nil { + return nil, fmt.Errorf("azuredns: %w", err) + } + } else { + options := azidentity.DefaultAzureCredentialOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: config.Environment, + }, + } + + credentials, err = azidentity.NewDefaultAzureCredential(&options) + if err != nil { + return nil, fmt.Errorf("azuredns: %w", err) + } + } + + if config.SubscriptionID == "" { + return nil, errors.New("azuredns: SubscriptionID is missing") + } + + if config.ResourceGroup == "" { + return nil, errors.New("azuredns: ResourceGroup is missing") + } + + var dnsProvider challenge.ProviderTimeout + if config.PrivateZone { + dnsProvider, err = NewDNSProviderPrivate(config, credentials) + if err != nil { + return nil, fmt.Errorf("azuredns: %w", err) + } + } else { + dnsProvider, err = NewDNSProviderPublic(config, credentials) + if err != nil { + return nil, fmt.Errorf("azuredns: %w", err) + } + } + + return &DNSProvider{provider: dnsProvider}, 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.provider.Timeout() +} + +// Present creates a TXT record to fulfill the dns-01 challenge. +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + return d.provider.Present(domain, token, keyAuth) +} + +// CleanUp removes the TXT record matching the specified parameters. +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + return d.provider.CleanUp(domain, token, keyAuth) +} + +func deref[T string | int | int32 | int64](v *T) T { + if v == nil { + var zero T + return zero + } + + return *v +} diff --git a/providers/azuredns/azuredns.toml b/providers/azuredns/azuredns.toml new file mode 100644 index 0000000..43bc27b --- /dev/null +++ b/providers/azuredns/azuredns.toml @@ -0,0 +1,77 @@ +Name = "AzureDNS" +Description = '''''' +URL = "https://azure.microsoft.com/services/dns/" +Code = "azuredns" +Since = "v0.1.0" + +Example = ''' +### Using client secret +AZURE_CLIENT_ID= \ +AZURE_TENANT_ID= \ +AZURE_CLIENT_SECRET= \ +lego --domains example.com --email your_example@email.com --dns azuredns run + +### Using client certificate +AZURE_CLIENT_ID= \ +AZURE_TENANT_ID= \ +AZURE_CLIENT_CERTIFICATE_PATH= \ +lego --domains example.com --email your_example@email.com --dns azuredns run + +### Using Azure CLI +az login \ +lego --domains example.com --email your_example@email.com --dns azuredns run +''' + +Additional = ''' +## Description + +Azure Credentials are automatically detected in the following locations and prioritized in the following order: + +1. Environment variables for client secret: `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_CLIENT_SECRET` +2. Environment variables for client certificate: `AZURE_CLIENT_ID`, `AZURE_TENANT_ID`, `AZURE_CLIENT_CERTIFICATE_PATH` +3. Workload identity for resources hosted in Azure environment (see below) +4. Shared credentials file (defaults to `~/.azure`), used by Azure CLI + +Link: +- [Azure Authentication](https://learn.microsoft.com/en-us/azure/developer/go/azure-sdk-authentication) + +### Workload identity + +#### Azure Managed Identity + +Azure managed identity service allows linking Azure AD identities to Azure resources. \ +Workloads running inside compute typed resource can inherit from this configuration to get rights on Azure resources. + +#### Workload identity for AKS + +Workload identity allows workloads running Azure Kubernetes Services (AKS) clusters to authenticate as an Azure AD application identity using federated credentials. \ +This must be configured in kubernetes workload deployment in one hand and on the Azure AD application registration in the other hand. \ + +Here is a summary of the steps to follow to use it : +* create a `ServiceAccount` resource, add following annotations to reference the targeted Azure AD application registration : `azure.workload.identity/client-id` and `azure.workload.identity/tenant-id`. \ +* on the `Deployment` resource you must reference the previous `ServiceAccount` and add the following label : `azure.workload.identity/use: "true"`. +* create a fedreated credentials of type `Kubernetes accessing Azure resources`, add the cluster issuer URL and add the namespace and name of your kubernetes service account. + +Link : +- [Azure AD Workload identity](https://azure.github.io/azure-workload-identity/docs/topics/service-account-labels-and-annotations.html) + +''' + +[Configuration] + [Configuration.Credentials] + AZURE_CLIENT_ID = "Client ID" + AZURE_CLIENT_SECRET = "Client secret" + AZURE_TENANT_ID = "Tenant ID" + AZURE_SUBSCRIPTION_ID = "DNS zone subscription ID" + AZURE_RESOURCE_GROUP = "DNS zone resource group" + [Configuration.Additional] + AZURE_ENVIRONMENT = "Azure environment, one of: public, usgovernment, and china" + AZURE_PRIVATE_ZONE = "Set to true to use Azure Private DNS Zones and not public" + AZURE_ZONE_NAME = "Zone name to use inside Azure DNS service to add the TXT record in" + AZURE_TTL = "The TTL of the TXT record used for the DNS challenge" + AZURE_POLLING_INTERVAL = "Time between DNS propagation check" + AZURE_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + +[Links] + API = "https://docs.microsoft.com/en-us/go/azure/" + GoClient = "https://github.com/Azure/azure-sdk-for-go" diff --git a/providers/azuredns/azuredns_test.go b/providers/azuredns/azuredns_test.go new file mode 100644 index 0000000..cf15055 --- /dev/null +++ b/providers/azuredns/azuredns_test.go @@ -0,0 +1,168 @@ +package azuredns + +import ( + "net/http" + "net/http/httptest" + "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( + EnvEnvironment, + EnvSubscriptionID, + EnvResourceGroup). + WithDomain(envDomain) + +func TestNewDNSProvider(t *testing.T) { + testCases := []struct { + desc string + envVars map[string]string + expected string + }{ + { + desc: "success", + envVars: map[string]string{ + EnvEnvironment: "", + EnvSubscriptionID: "A", + EnvResourceGroup: "B", + }, + }, + { + desc: "unknown environment", + envVars: map[string]string{ + EnvEnvironment: "test", + EnvSubscriptionID: "A", + EnvResourceGroup: "B", + }, + expected: "azuredns: unknown environment test", + }, + } + + 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.EqualError(t, err, test.expected) + return + } + + require.NoError(t, err) + require.NotNil(t, p) + require.NotNil(t, p.provider) + + assert.IsType(t, p.provider, new(DNSProviderPublic)) + }) + } +} + +func TestNewDNSProviderConfig(t *testing.T) { + testCases := []struct { + desc string + subscriptionID string + resourceGroup string + privateZone bool + handler func(w http.ResponseWriter, r *http.Request) + expected string + }{ + { + desc: "success (public)", + subscriptionID: "A", + resourceGroup: "B", + privateZone: false, + }, + { + desc: "success (private)", + subscriptionID: "A", + resourceGroup: "B", + privateZone: true, + }, + { + desc: "SubscriptionID missing", + subscriptionID: "", + resourceGroup: "", + expected: "azuredns: SubscriptionID is missing", + }, + { + desc: "ResourceGroup missing", + subscriptionID: "A", + resourceGroup: "", + expected: "azuredns: ResourceGroup is missing", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + config := NewDefaultConfig() + config.SubscriptionID = test.subscriptionID + config.ResourceGroup = test.resourceGroup + config.PrivateZone = test.privateZone + + mux := http.NewServeMux() + server := httptest.NewServer(mux) + t.Cleanup(server.Close) + + if test.handler == nil { + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {}) + } else { + mux.HandleFunc("/", test.handler) + } + + p, err := NewDNSProviderConfig(config) + + if test.expected != "" { + require.EqualError(t, err, test.expected) + return + } + + require.NoError(t, err) + require.NotNil(t, p) + require.NotNil(t, p.provider) + + if test.privateZone { + assert.IsType(t, p.provider, new(DNSProviderPrivate)) + } else { + assert.IsType(t, p.provider, new(DNSProviderPublic)) + } + }) + } +} + +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) + + time.Sleep(1 * time.Second) + + err = provider.CleanUp(envTest.GetDomain(), "", "123d==") + require.NoError(t, err) +} diff --git a/providers/azuredns/private.go b/providers/azuredns/private.go new file mode 100644 index 0000000..5317d64 --- /dev/null +++ b/providers/azuredns/private.go @@ -0,0 +1,154 @@ +package azuredns + +import ( + "context" + "errors" + "fmt" + "net/http" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns" + "github.com/entrustcorporation/dv/dns01" + "github.com/go-acme/lego/v4/platform/config/env" +) + +// DNSProviderPrivate implements the challenge.Provider interface for Azure Private Zone DNS. +type DNSProviderPrivate struct { + config *Config + zoneClient *armprivatedns.PrivateZonesClient + recordClient *armprivatedns.RecordSetsClient +} + +// NewDNSProviderPrivate creates a DNSProviderPrivate structure with initialized Azure clients. +func NewDNSProviderPrivate(config *Config, credentials azcore.TokenCredential) (*DNSProviderPrivate, error) { + options := arm.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: config.Environment, + }, + } + + zoneClient, err := armprivatedns.NewPrivateZonesClient(config.SubscriptionID, credentials, &options) + if err != nil { + return nil, err + } + + recordClient, err := armprivatedns.NewRecordSetsClient(config.SubscriptionID, credentials, &options) + if err != nil { + return nil, err + } + + return &DNSProviderPrivate{ + config: config, + zoneClient: zoneClient, + recordClient: recordClient, + }, 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 *DNSProviderPrivate) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +// Present creates a TXT record to fulfill the dns-01 challenge. +func (d *DNSProviderPrivate) Present(domain, _, keyAuth string) error { + ctx := context.Background() + info := dns01.GetChallengeInfo(domain, keyAuth) + + zone, err := d.getHostedZoneID(ctx, info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + // Get existing record set + rset, err := d.recordClient.Get(ctx, d.config.ResourceGroup, zone, armprivatedns.RecordTypeTXT, subDomain, nil) + if err != nil { + var respErr *azcore.ResponseError + if !errors.As(err, &respErr) || respErr.StatusCode != http.StatusNotFound { + return fmt.Errorf("azuredns: %w", err) + } + } + + // Construct unique TXT records using map + uniqRecords := map[string]struct{}{info.Value: {}} + if rset.RecordSet.Properties != nil && rset.RecordSet.Properties.TxtRecords != nil { + for _, txtRecord := range rset.RecordSet.Properties.TxtRecords { + // Assume Value doesn't contain multiple strings + if len(txtRecord.Value) > 0 { + uniqRecords[deref(txtRecord.Value[0])] = struct{}{} + } + } + } + + var txtRecords []*armprivatedns.TxtRecord + for txt := range uniqRecords { + txtRecord := txt + txtRecords = append(txtRecords, &armprivatedns.TxtRecord{Value: []*string{&txtRecord}}) + } + + ttlInt64 := int64(d.config.TTL) + rec := armprivatedns.RecordSet{ + Name: &subDomain, + Properties: &armprivatedns.RecordSetProperties{ + TTL: &ttlInt64, + TxtRecords: txtRecords, + }, + } + + _, err = d.recordClient.CreateOrUpdate(ctx, d.config.ResourceGroup, zone, armprivatedns.RecordTypeTXT, subDomain, rec, nil) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + return nil +} + +// CleanUp removes the TXT record matching the specified parameters. +func (d *DNSProviderPrivate) CleanUp(domain, _, keyAuth string) error { + ctx := context.Background() + info := dns01.GetChallengeInfo(domain, keyAuth) + + zone, err := d.getHostedZoneID(ctx, info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + _, err = d.recordClient.Delete(ctx, d.config.ResourceGroup, zone, armprivatedns.RecordTypeTXT, subDomain, nil) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + return nil +} + +// Checks that azure has a zone for this domain name. +func (d *DNSProviderPrivate) getHostedZoneID(ctx context.Context, fqdn string) (string, error) { + if zone := env.GetOrFile(EnvZoneName); zone != "" { + return zone, nil + } + + authZone, err := dns01.FindZoneByFqdn(fqdn) + if err != nil { + return "", err + } + + zone, err := d.zoneClient.Get(ctx, d.config.ResourceGroup, dns01.UnFqdn(authZone), nil) + if err != nil { + return "", err + } + + // zone.Name shouldn't have a trailing dot(.) + return dns01.UnFqdn(deref(zone.Name)), nil +} diff --git a/providers/azuredns/public.go b/providers/azuredns/public.go new file mode 100644 index 0000000..bcbdf2e --- /dev/null +++ b/providers/azuredns/public.go @@ -0,0 +1,154 @@ +package azuredns + +import ( + "context" + "errors" + "fmt" + "net/http" + "time" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns" + "github.com/entrustcorporation/dv/dns01" + "github.com/go-acme/lego/v4/platform/config/env" +) + +// DNSProviderPublic implements the challenge.Provider interface for Azure Public Zone DNS. +type DNSProviderPublic struct { + config *Config + zoneClient *armdns.ZonesClient + recordClient *armdns.RecordSetsClient +} + +// NewDNSProviderPublic creates a DNSProviderPublic structure with intialised Azure clients. +func NewDNSProviderPublic(config *Config, credentials azcore.TokenCredential) (*DNSProviderPublic, error) { + options := arm.ClientOptions{ + ClientOptions: azcore.ClientOptions{ + Cloud: config.Environment, + }, + } + + zoneClient, err := armdns.NewZonesClient(config.SubscriptionID, credentials, &options) + if err != nil { + return nil, err + } + + recordClient, err := armdns.NewRecordSetsClient(config.SubscriptionID, credentials, &options) + if err != nil { + return nil, err + } + + return &DNSProviderPublic{ + config: config, + zoneClient: zoneClient, + recordClient: recordClient, + }, 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 *DNSProviderPublic) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} + +// Present creates a TXT record to fulfill the dns-01 challenge. +func (d *DNSProviderPublic) Present(domain, _, keyAuth string) error { + ctx := context.Background() + info := dns01.GetChallengeInfo(domain, keyAuth) + + zone, err := d.getHostedZoneID(ctx, info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + // Get existing record set + rset, err := d.recordClient.Get(ctx, d.config.ResourceGroup, zone, subDomain, armdns.RecordTypeTXT, nil) + if err != nil { + var respErr *azcore.ResponseError + if !errors.As(err, &respErr) || respErr.StatusCode != http.StatusNotFound { + return fmt.Errorf("azuredns: %w", err) + } + } + + // Construct unique TXT records using map + uniqRecords := map[string]struct{}{info.Value: {}} + if rset.RecordSet.Properties != nil && rset.RecordSet.Properties.TxtRecords != nil { + for _, txtRecord := range rset.RecordSet.Properties.TxtRecords { + // Assume Value doesn't contain multiple strings + if len(txtRecord.Value) > 0 { + uniqRecords[deref(txtRecord.Value[0])] = struct{}{} + } + } + } + + var txtRecords []*armdns.TxtRecord + for txt := range uniqRecords { + txtRecord := txt + txtRecords = append(txtRecords, &armdns.TxtRecord{Value: []*string{&txtRecord}}) + } + + ttlInt64 := int64(d.config.TTL) + rec := armdns.RecordSet{ + Name: &subDomain, + Properties: &armdns.RecordSetProperties{ + TTL: &ttlInt64, + TxtRecords: txtRecords, + }, + } + + _, err = d.recordClient.CreateOrUpdate(ctx, d.config.ResourceGroup, zone, subDomain, armdns.RecordTypeTXT, rec, nil) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + return nil +} + +// CleanUp removes the TXT record matching the specified parameters. +func (d *DNSProviderPublic) CleanUp(domain, _, keyAuth string) error { + ctx := context.Background() + info := dns01.GetChallengeInfo(domain, keyAuth) + + zone, err := d.getHostedZoneID(ctx, info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, zone) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + _, err = d.recordClient.Delete(ctx, d.config.ResourceGroup, zone, subDomain, armdns.RecordTypeTXT, nil) + if err != nil { + return fmt.Errorf("azuredns: %w", err) + } + + return nil +} + +// Checks that azure has a zone for this domain name. +func (d *DNSProviderPublic) getHostedZoneID(ctx context.Context, fqdn string) (string, error) { + if zone := env.GetOrFile(EnvZoneName); zone != "" { + return zone, nil + } + + authZone, err := dns01.FindZoneByFqdn(fqdn) + if err != nil { + return "", err + } + + zone, err := d.zoneClient.Get(ctx, d.config.ResourceGroup, dns01.UnFqdn(authZone), nil) + if err != nil { + return "", err + } + + // zone.Name shouldn't have a trailing dot(.) + return dns01.UnFqdn(deref(zone.Name)), nil +} diff --git a/providers/bunny/bunny.go b/providers/bunny/bunny.go index 8719ea3..b0b1806 100644 --- a/providers/bunny/bunny.go +++ b/providers/bunny/bunny.go @@ -81,8 +81,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return &DNSProvider{config: config, client: client}, nil } -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. +// 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/cloudflare/cloudflare.go b/providers/cloudflare/cloudflare.go index 253890b..93ded52 100644 --- a/providers/cloudflare/cloudflare.go +++ b/providers/cloudflare/cloudflare.go @@ -134,7 +134,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("cloudflare: failed to find zone %s: %w", authZone, err) } - dnsRecord := cloudflare.DNSRecord{ + dnsRecord := cloudflare.CreateDNSRecordParams{ Type: "TXT", Name: dns01.UnFqdn(info.EffectiveFQDN), Content: info.Value, @@ -146,15 +146,11 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return fmt.Errorf("cloudflare: failed to create TXT record: %w", err) } - if !response.Success { - return fmt.Errorf("cloudflare: failed to create TXT record: %+v %+v", response.Errors, response.Messages) - } - d.recordIDsMu.Lock() - d.recordIDs[token] = response.Result.ID + d.recordIDs[token] = response.ID d.recordIDsMu.Unlock() - log.Infof("cloudflare: new record for %s, ID %s", domain, response.Result.ID) + log.Infof("cloudflare: new record for %s, ID %s", domain, response.ID) return nil } diff --git a/providers/cloudflare/wrapper.go b/providers/cloudflare/wrapper.go index 79cc41d..c92da5b 100644 --- a/providers/cloudflare/wrapper.go +++ b/providers/cloudflare/wrapper.go @@ -59,16 +59,16 @@ func newClient(config *Config) (*metaClient, error) { }, nil } -func (m *metaClient) CreateDNSRecord(ctx context.Context, zoneID string, rr cloudflare.DNSRecord) (*cloudflare.DNSRecordResponse, error) { - return m.clientEdit.CreateDNSRecord(ctx, zoneID, rr) +func (m *metaClient) CreateDNSRecord(ctx context.Context, zoneID string, rr cloudflare.CreateDNSRecordParams) (cloudflare.DNSRecord, error) { + return m.clientEdit.CreateDNSRecord(ctx, cloudflare.ZoneIdentifier(zoneID), rr) } -func (m *metaClient) DNSRecords(ctx context.Context, zoneID string, rr cloudflare.DNSRecord) ([]cloudflare.DNSRecord, error) { - return m.clientEdit.DNSRecords(ctx, zoneID, rr) +func (m *metaClient) DNSRecords(ctx context.Context, zoneID string, rr cloudflare.ListDNSRecordsParams) ([]cloudflare.DNSRecord, *cloudflare.ResultInfo, error) { + return m.clientEdit.ListDNSRecords(ctx, cloudflare.ZoneIdentifier(zoneID), rr) } func (m *metaClient) DeleteDNSRecord(ctx context.Context, zoneID, recordID string) error { - return m.clientEdit.DeleteDNSRecord(ctx, zoneID, recordID) + return m.clientEdit.DeleteDNSRecord(ctx, cloudflare.ZoneIdentifier(zoneID), recordID) } func (m *metaClient) ZoneIDByName(fdqn string) (string, error) { diff --git a/providers/designate/designate.toml b/providers/designate/designate.toml index b885999..045c235 100644 --- a/providers/designate/designate.toml +++ b/providers/designate/designate.toml @@ -65,4 +65,4 @@ For more information, you can read about the different methods of authentication [Links] API = "https://docs.openstack.org/designate/latest/" - GoClient = "https://godoc.org/github.com/gophercloud/gophercloud/openstack/dns/v2" + GoClient = "https://pkg.go.dev/github.com/gophercloud/gophercloud/openstack/dns/v2" diff --git a/providers/dns_providers.go b/providers/dns_providers.go index 0e43065..9e2d432 100644 --- a/providers/dns_providers.go +++ b/providers/dns_providers.go @@ -12,6 +12,7 @@ import ( "github.com/entrustcorporation/dv/providers/auroradns" "github.com/entrustcorporation/dv/providers/autodns" "github.com/entrustcorporation/dv/providers/azure" + "github.com/entrustcorporation/dv/providers/azuredns" "github.com/entrustcorporation/dv/providers/bindman" "github.com/entrustcorporation/dv/providers/bluecat" "github.com/entrustcorporation/dv/providers/brandit" @@ -40,6 +41,7 @@ import ( "github.com/entrustcorporation/dv/providers/dynu" "github.com/entrustcorporation/dv/providers/easydns" "github.com/entrustcorporation/dv/providers/edgedns" + "github.com/entrustcorporation/dv/providers/efficientip" "github.com/entrustcorporation/dv/providers/epik" "github.com/entrustcorporation/dv/providers/exec" "github.com/entrustcorporation/dv/providers/exoscale" @@ -73,6 +75,7 @@ import ( "github.com/entrustcorporation/dv/providers/liquidweb" "github.com/entrustcorporation/dv/providers/loopia" "github.com/entrustcorporation/dv/providers/luadns" + "github.com/entrustcorporation/dv/providers/metaname" "github.com/entrustcorporation/dv/providers/mydnsjp" "github.com/entrustcorporation/dv/providers/mythicbeasts" "github.com/entrustcorporation/dv/providers/namecheap" @@ -93,6 +96,7 @@ import ( "github.com/entrustcorporation/dv/providers/plesk" "github.com/entrustcorporation/dv/providers/porkbun" "github.com/entrustcorporation/dv/providers/rackspace" + "github.com/entrustcorporation/dv/providers/rcodezero" "github.com/entrustcorporation/dv/providers/regru" "github.com/entrustcorporation/dv/providers/rfc2136" "github.com/entrustcorporation/dv/providers/rimuhosting" @@ -137,6 +141,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { return arvancloud.NewDNSProvider() case "azure": return azure.NewDNSProvider() + case "azuredns": + return azuredns.NewDNSProvider() case "auroradns": return auroradns.NewDNSProvider() case "autodns": @@ -197,6 +203,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { return easydns.NewDNSProvider() case "edgedns", "fastdns": // "fastdns" is for compatibility with v3, must be dropped in v5 return edgedns.NewDNSProvider() + case "efficientip": + return efficientip.NewDNSProvider() case "epik": return epik.NewDNSProvider() case "exec": @@ -265,6 +273,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { return loopia.NewDNSProvider() case "manual": return dns01.NewDNSProviderManual() + case "metaname": + return metaname.NewDNSProvider() case "mydnsjp": return mydnsjp.NewDNSProvider() case "mythicbeasts": @@ -305,6 +315,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { return porkbun.NewDNSProvider() case "rackspace": return rackspace.NewDNSProvider() + case "rcodezero": + return rcodezero.NewDNSProvider() case "regru": return regru.NewDNSProvider() case "rfc2136": diff --git a/providers/dnsmadeeasy/internal/client.go b/providers/dnsmadeeasy/internal/client.go index fe3d364..42fa2da 100644 --- a/providers/dnsmadeeasy/internal/client.go +++ b/providers/dnsmadeeasy/internal/client.go @@ -84,6 +84,7 @@ func (c *Client) GetRecords(ctx context.Context, domain *Domain, recordName, rec query := endpoint.Query() query.Set("recordName", recordName) query.Set("type", recordType) + endpoint.RawQuery = query.Encode() req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil) if err != nil { @@ -113,7 +114,7 @@ func (c *Client) CreateRecord(ctx context.Context, domain *Domain, record *Recor // DeleteRecord deletes a TXT records. func (c *Client) DeleteRecord(ctx context.Context, record Record) error { - endpoint := c.BaseURL.JoinPath("/dns/managed", strconv.Itoa(record.SourceID), "records", strconv.Itoa(record.ID)) + endpoint := c.BaseURL.JoinPath("dns", "managed", strconv.Itoa(record.SourceID), "records", strconv.Itoa(record.ID)) req, err := newJSONRequest(ctx, http.MethodDelete, endpoint, nil) if err != nil { diff --git a/providers/efficientip/efficientip.go b/providers/efficientip/efficientip.go new file mode 100644 index 0000000..7d9c30e --- /dev/null +++ b/providers/efficientip/efficientip.go @@ -0,0 +1,152 @@ +// Package efficientip implements a DNS provider for solving the DNS-01 challenge using Efficient IP. +package efficientip + +import ( + "context" + "errors" + "fmt" + "net/http" + "time" + + "github.com/entrustcorporation/dv/dns01" + "github.com/go-acme/lego/v4/platform/config/env" + "github.com/entrustcorporation/dv/providers/efficientip/internal" +) + +// Environment variables names. +const ( + envNamespace = "EFFICIENTIP_" + + EnvUsername = envNamespace + "USERNAME" + EnvPassword = envNamespace + "PASSWORD" + EnvHostname = envNamespace + "HOSTNAME" + EnvDNSName = envNamespace + "DNS_NAME" + EnvViewName = envNamespace + "VIEW_NAME" + + 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 + Hostname string + DNSName string + ViewName 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, dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 10*time.Second), + }, + } +} + +// DNSProvider implements the challenge.Provider interface. +type DNSProvider struct { + config *Config + client *internal.Client +} + +// NewDNSProvider returns a new DNS provider +// using environment variable EFFICIENTIP_API_KEY for adding and removing the DNS record. +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvUsername, EnvPassword, EnvHostname, EnvDNSName) + if err != nil { + return nil, fmt.Errorf("efficientip: %w", err) + } + + config := NewDefaultConfig() + config.Username = values[EnvUsername] + config.Password = values[EnvPassword] + config.Hostname = values[EnvHostname] + config.DNSName = values[EnvDNSName] + config.ViewName = env.GetOrDefaultString(EnvViewName, "") + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Efficient IP. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("efficientip: the configuration of the DNS provider is nil") + } + + if config.Username == "" { + return nil, errors.New("efficientip: missing username") + } + if config.Password == "" { + return nil, errors.New("efficientip: missing password") + } + if config.Hostname == "" { + return nil, errors.New("efficientip: missing hostname") + } + if config.DNSName == "" { + return nil, errors.New("efficientip: missing dnsname") + } + + client := internal.NewClient(config.Hostname, config.Username, config.Password) + + if config.HTTPClient != nil { + client.HTTPClient = config.HTTPClient + } + + return &DNSProvider{config: config, client: client}, nil +} + +func (d *DNSProvider) Present(domain, _, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + ctx := context.Background() + + r := internal.ResourceRecord{ + RRName: dns01.UnFqdn(info.EffectiveFQDN), + RRType: "TXT", + Value1: info.Value, + DNSName: d.config.DNSName, + DNSViewName: d.config.ViewName, + } + + _, err := d.client.AddRecord(ctx, r) + if err != nil { + return fmt.Errorf("efficientip: add record: %w", err) + } + + return nil +} + +func (d *DNSProvider) CleanUp(domain, _, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + ctx := context.Background() + + params := internal.DeleteInputParameters{ + RRName: dns01.UnFqdn(info.EffectiveFQDN), + RRType: "TXT", + RRValue1: info.Value, + DNSName: d.config.DNSName, + DNSViewName: d.config.ViewName, + } + + _, err := d.client.DeleteRecord(ctx, params) + if err != nil { + return fmt.Errorf("efficientip: delete 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/efficientip/efficientip.toml b/providers/efficientip/efficientip.toml new file mode 100644 index 0000000..278701e --- /dev/null +++ b/providers/efficientip/efficientip.toml @@ -0,0 +1,26 @@ +Name = "Efficient IP" +Description = '''''' +URL = "https://efficientip.com/" +Code = "efficientip" +Since = "v4.13.0" + +Example = ''' +EFFICIENTIP_USERNAME="user" \ +EFFICIENTIP_PASSWORD="secret" \ +EFFICIENTIP_HOSTNAME="ipam.example.org" \ +EFFICIENTIP_DNS_NAME="dns.smart" \ +lego --email you@example.com --dns efficientip --domains my.example.org run +''' + +[Configuration] + [Configuration.Credentials] + EFFICIENTIP_USERNAME = "Username" + EFFICIENTIP_PASSWORD = "Password" + EFFICIENTIP_HOSTNAME = "Hostname (ex: foo.example.com)" + EFFICIENTIP_DNS_NAME = "DNS name (ex: dns.smart)" + [Configuration.Additional] + EFFICIENTIP_VIEW_NAME = "View name (ex: external)" + EFFICIENTIP_POLLING_INTERVAL = "Time between DNS propagation check" + EFFICIENTIP_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + EFFICIENTIP_TTL = "The TTL of the TXT record used for the DNS challenge" + EFFICIENTIP_HTTP_TIMEOUT = "API request timeout" diff --git a/providers/efficientip/efficientip_test.go b/providers/efficientip/efficientip_test.go new file mode 100644 index 0000000..3ee2da7 --- /dev/null +++ b/providers/efficientip/efficientip_test.go @@ -0,0 +1,201 @@ +package efficientip + +import ( + "testing" + "time" + + "github.com/go-acme/lego/v4/platform/tester" + "github.com/stretchr/testify/require" +) + +const envDomain = envNamespace + "DOMAIN" + +var envTest = tester.NewEnvTest( + EnvUsername, + EnvPassword, + EnvHostname, + EnvDNSName, + EnvViewName, +).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", + EnvHostname: "example.com", + EnvDNSName: "dns.smart", + }, + }, + { + desc: "missing username", + envVars: map[string]string{ + EnvUsername: "", + EnvPassword: "secret", + EnvHostname: "example.com", + EnvDNSName: "dns.smart", + }, + expected: "efficientip: some credentials information are missing: EFFICIENTIP_USERNAME", + }, + { + desc: "missing password", + envVars: map[string]string{ + EnvUsername: "user", + EnvPassword: "", + EnvHostname: "example.com", + EnvDNSName: "dns.smart", + }, + expected: "efficientip: some credentials information are missing: EFFICIENTIP_PASSWORD", + }, + { + desc: "missing hostname", + envVars: map[string]string{ + EnvUsername: "user", + EnvPassword: "secret", + EnvHostname: "", + EnvDNSName: "dns.smart", + }, + expected: "efficientip: some credentials information are missing: EFFICIENTIP_HOSTNAME", + }, + { + desc: "missing DNS name", + envVars: map[string]string{ + EnvUsername: "user", + EnvPassword: "secret", + EnvHostname: "example.com", + EnvDNSName: "", + }, + expected: "efficientip: some credentials information are missing: EFFICIENTIP_DNS_NAME", + }, + { + desc: "missing credentials", + envVars: map[string]string{}, + expected: "efficientip: some credentials information are missing: EFFICIENTIP_USERNAME,EFFICIENTIP_PASSWORD,EFFICIENTIP_HOSTNAME,EFFICIENTIP_DNS_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) + } else { + require.EqualError(t, err, test.expected) + } + }) + } +} + +func TestNewDNSProviderConfig(t *testing.T) { + testCases := []struct { + desc string + username string + password string + hostname string + dnsName string + expected string + }{ + { + desc: "success", + username: "user", + password: "secret", + hostname: "example.com", + dnsName: "dns.smart", + }, + { + desc: "missing username", + password: "secret", + hostname: "example.com", + dnsName: "dns.smart", + expected: "efficientip: missing username", + }, + { + desc: "missing password", + username: "user", + hostname: "example.com", + dnsName: "dns.smart", + expected: "efficientip: missing password", + }, + { + desc: "missing hostname", + username: "user", + password: "secret", + dnsName: "dns.smart", + expected: "efficientip: missing hostname", + }, + { + desc: "missing dnsName", + username: "user", + password: "secret", + hostname: "example.com", + expected: "efficientip: missing dnsname", + }, + { + desc: "missing all", + expected: "efficientip: missing username", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + config := NewDefaultConfig() + + config.Username = test.username + config.Password = test.password + config.Hostname = test.hostname + config.DNSName = test.dnsName + + 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) + + time.Sleep(1 * time.Second) + + err = provider.CleanUp(envTest.GetDomain(), "", "123d==") + require.NoError(t, err) +} diff --git a/providers/efficientip/internal/client.go b/providers/efficientip/internal/client.go new file mode 100644 index 0000000..38e8151 --- /dev/null +++ b/providers/efficientip/internal/client.go @@ -0,0 +1,209 @@ +package internal + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "time" + + "github.com/entrustcorporation/dv/providers/internal/errutils" + querystring "github.com/google/go-querystring/query" +) + +type Client struct { + baseURL *url.URL + HTTPClient *http.Client + + username string + password string +} + +func NewClient(hostname string, username string, password string) *Client { + baseURL, _ := url.Parse(fmt.Sprintf("https://%s/rest/", hostname)) + + return &Client{ + HTTPClient: &http.Client{Timeout: 5 * time.Second}, + baseURL: baseURL, + username: username, + password: password, + } +} + +func (c Client) ListRecords(ctx context.Context) ([]ResourceRecord, error) { + endpoint := c.baseURL.JoinPath("dns_rr_list") + + req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil) + if err != nil { + return nil, err + } + + var result []ResourceRecord + + err = c.do(req, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +func (c Client) GetRecord(ctx context.Context, id string) (*ResourceRecord, error) { + endpoint := c.baseURL.JoinPath("dns_rr_info") + + query := endpoint.Query() + query.Set("rr_id", id) + endpoint.RawQuery = query.Encode() + + req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil) + if err != nil { + return nil, err + } + + var result []ResourceRecord + + err = c.do(req, &result) + if err != nil { + return nil, err + } + + if len(result) == 0 { + return nil, nil + } + + return &result[0], nil +} + +func (c Client) AddRecord(ctx context.Context, record ResourceRecord) (*BaseOutput, error) { + endpoint := c.baseURL.JoinPath("dns_rr_add") + + req, err := newJSONRequest(ctx, http.MethodPost, endpoint, record) + if err != nil { + return nil, err + } + + var result []BaseOutput + + err = c.do(req, &result) + if err != nil { + return nil, err + } + + if len(result) == 0 { + return nil, nil + } + + return &result[0], nil +} + +func (c Client) DeleteRecord(ctx context.Context, params DeleteInputParameters) (*BaseOutput, error) { + endpoint := c.baseURL.JoinPath("dns_rr_delete") + + // (rr_id || (rr_name && (dns_id || dns_name || hostaddr))) + + v, err := querystring.Values(params) + if err != nil { + return nil, fmt.Errorf("query parameters: %w", err) + } + endpoint.RawQuery = v.Encode() + + req, err := newJSONRequest(ctx, http.MethodDelete, endpoint, nil) + if err != nil { + return nil, err + } + + var result []BaseOutput + + err = c.do(req, &result) + if err != nil { + return nil, err + } + + if len(result) == 0 { + return nil, nil + } + + return &result[0], nil +} + +func (c Client) do(req *http.Request, result any) error { + req.SetBasicAuth(c.username, c.password) + req.Header.Set("cache-control", "no-cache") + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return errutils.NewHTTPDoError(req, err) + } + + defer func() { _ = resp.Body.Close() }() + + switch req.Method { + case http.MethodPost: + if resp.StatusCode != http.StatusCreated { + return parseError(req, resp) + } + default: + if resp.StatusCode == http.StatusNoContent { + return nil + } + + if resp.StatusCode != http.StatusOK { + 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 response APIError + err := json.Unmarshal(raw, &response) + if err != nil { + return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw) + } + + return fmt.Errorf("[status code %d] %w", resp.StatusCode, response) +} diff --git a/providers/efficientip/internal/client_test.go b/providers/efficientip/internal/client_test.go new file mode 100644 index 0000000..a766c90 --- /dev/null +++ b/providers/efficientip/internal/client_test.go @@ -0,0 +1,427 @@ +package internal + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func setupTest(t *testing.T, method, pattern string, status int, file string) *Client { + t.Helper() + + mux := http.NewServeMux() + server := httptest.NewServer(mux) + t.Cleanup(server.Close) + + mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) { + if req.Method != method { + http.Error(rw, fmt.Sprintf("unsupported method %s", req.Method), http.StatusBadRequest) + return + } + + username, password, ok := req.BasicAuth() + if !ok { + http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) + return + } + + if username != "user" { + http.Error(rw, fmt.Sprintf("username: want %s got %s", username, "user"), http.StatusUnauthorized) + return + } + + if password != "secret" { + http.Error(rw, fmt.Sprintf("password: want %s got %s", password, "secret"), http.StatusUnauthorized) + return + } + + open, err := os.Open(filepath.Join("fixtures", file)) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + defer func() { _ = open.Close() }() + + rw.WriteHeader(status) + _, err = io.Copy(rw, open) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + }) + + srvURL, _ := url.Parse(server.URL) + + client := NewClient(srvURL.Host, "user", "secret") + client.HTTPClient = server.Client() + client.baseURL, _ = url.Parse(server.URL) + + return client +} + +func TestListRecords(t *testing.T) { + client := setupTest(t, http.MethodGet, "/dns_rr_list", http.StatusOK, "dns_rr_list.json") + + ctx := context.Background() + + records, err := client.ListRecords(ctx) + require.NoError(t, err) + + expected := []ResourceRecord{ + { + ErrorCode: "0", + DelayedCreateTime: "0", + DelayedDeleteTime: "0", + DelayedTime: "0", + DNSCloud: "0", + DNSID: "3", + DNSName: "dns.smart", + DNSType: "vdns", + DNSViewID: "0", + DNSViewName: "#", + DNSZoneID: "9", + DNSZoneIsReverse: "0", + DNSZoneIsRpz: "0", + DNSZoneName: "lego.example.com", + DNSZoneNameUTF: "lego.example.com", + DNSZoneSiteName: "#", + DNSZoneSortZone: "lego.example.com", + DNSZoneType: "master", + RRAllValue: "test1", + RRAuthGsstsig: "0", + RRFullName: "test.lego.example.com", + RRFullNameUTF: "test.lego.example.com", + RRGlue: "test", + RRGlueID: "21", + RRID: "239", + RRNameID: "26", + RRType: "TXT", + RRTypeID: "6", + RRValueID: "274", + TTL: "3600", + Value1: "test1", + VDNSParentID: "0", + VDNSParentName: "#", + }, + { + ErrorCode: "0", + DelayedCreateTime: "0", + DelayedDeleteTime: "0", + DelayedTime: "0", + DNSCloud: "0", + DNSID: "3", + DNSName: "dns.smart", + DNSType: "vdns", + DNSViewID: "0", + DNSViewName: "#", + DNSZoneID: "9", + DNSZoneIsReverse: "0", + DNSZoneIsRpz: "0", + DNSZoneName: "lego.example.com", + DNSZoneNameUTF: "lego.example.com", + DNSZoneSiteName: "#", + DNSZoneSortZone: "lego.example.com", + DNSZoneType: "master", + RRAllValue: "test2", + RRAuthGsstsig: "0", + RRFullName: "test.lego.example.com", + RRFullNameUTF: "test.lego.example.com", + RRGlue: "test", + RRGlueID: "21", + RRID: "241", + RRNameID: "26", + RRType: "TXT", + RRTypeID: "6", + RRValueID: "275", + TTL: "3600", + Value1: "test2", + VDNSParentID: "0", + VDNSParentName: "#", + }, + { + ErrorCode: "0", + DelayedCreateTime: "0", + DelayedDeleteTime: "0", + DelayedTime: "0", + DNSCloud: "0", + DNSID: "3", + DNSName: "dns.smart", + DNSType: "vdns", + DNSViewID: "0", + DNSViewName: "#", + DNSZoneID: "9", + DNSZoneIsReverse: "0", + DNSZoneIsRpz: "0", + DNSZoneName: "lego.example.com", + DNSZoneNameUTF: "lego.example.com", + DNSZoneSiteName: "#", + DNSZoneSortZone: "lego.example.com", + DNSZoneType: "master", + RRAllValue: "test1", + RRAuthGsstsig: "0", + RRFullName: "lego.example.com", + RRFullNameUTF: "lego.example.com", + RRGlue: ".", + RRGlueID: "3", + RRID: "245", + RRNameID: "21", + RRType: "TXT", + RRTypeID: "6", + RRValueID: "274", + TTL: "3600", + Value1: "test1", + VDNSParentID: "0", + VDNSParentName: "#", + }, + { + ErrorCode: "0", + DelayedCreateTime: "0", + DelayedDeleteTime: "0", + DelayedTime: "0", + DNSCloud: "0", + DNSID: "3", + DNSName: "dns.smart", + DNSType: "vdns", + DNSViewID: "0", + DNSViewName: "#", + DNSZoneID: "9", + DNSZoneIsReverse: "0", + DNSZoneIsRpz: "0", + DNSZoneName: "lego.example.com", + DNSZoneNameUTF: "lego.example.com", + DNSZoneSiteName: "#", + DNSZoneSortZone: "lego.example.com", + DNSZoneType: "master", + RRAllValue: "test2", + RRAuthGsstsig: "0", + RRFullName: "lego.example.com", + RRFullNameUTF: "lego.example.com", + RRGlue: ".", + RRGlueID: "3", + RRID: "247", + RRNameID: "21", + RRType: "TXT", + RRTypeID: "6", + RRValueID: "275", + TTL: "3600", + Value1: "test2", + VDNSParentID: "0", + VDNSParentName: "#", + }, + { + ErrorCode: "0", + DelayedCreateTime: "0", + DelayedDeleteTime: "0", + DelayedTime: "0", + DNSCloud: "0", + DNSID: "3", + DNSName: "dns.smart", + DNSType: "vdns", + DNSViewID: "0", + DNSViewName: "#", + DNSZoneID: "9", + DNSZoneIsReverse: "0", + DNSZoneIsRpz: "0", + DNSZoneName: "lego.example.com", + DNSZoneNameUTF: "lego.example.com", + DNSZoneSiteName: "#", + DNSZoneSortZone: "lego.example.com", + DNSZoneType: "master", + RRAllValue: "dns.smart, root@lego.example.com, 2023062719, 1200, 600, 1209600, 3600", + RRAuthGsstsig: "0", + RRFullName: "lego.example.com", + RRFullNameUTF: "lego.example.com", + RRGlue: ".", + RRGlueID: "3", + RRID: "201", + RRNameID: "21", + RRType: "SOA", + RRTypeID: "2", + RRValueID: "282", + TTL: "3600", + Value1: "dns.smart", + Value2: "root@lego.example.com", + Value3: "2023062719", + Value4: "1200", + Value5: "600", + Value6: "1209600", + Value7: "3600", + VDNSParentID: "0", + VDNSParentName: "#", + }, + { + ErrorCode: "0", + DelayedCreateTime: "0", + DelayedDeleteTime: "0", + DelayedTime: "0", + DNSCloud: "0", + DNSID: "3", + DNSName: "dns.smart", + DNSType: "vdns", + DNSViewID: "0", + DNSViewName: "#", + DNSZoneID: "9", + DNSZoneIsReverse: "0", + DNSZoneIsRpz: "0", + DNSZoneName: "lego.example.com", + DNSZoneNameUTF: "lego.example.com", + DNSZoneSiteName: "#", + DNSZoneSortZone: "lego.example.com", + DNSZoneType: "master", + RRAllValue: "dns.smart", + RRAuthGsstsig: "0", + RRFullName: "lego.example.com", + RRFullNameUTF: "lego.example.com", + RRGlue: ".", + RRGlueID: "3", + RRID: "200", + RRNameID: "21", + RRType: "NS", + RRTypeID: "1", + RRValueID: "10", + TTL: "3600", + Value1: "dns.smart", + VDNSParentID: "0", + VDNSParentName: "#", + }, + { + ErrorCode: "0", + DelayedCreateTime: "0", + DelayedDeleteTime: "0", + DelayedTime: "0", + DNSCloud: "0", + DNSID: "3", + DNSName: "dns.smart", + DNSType: "vdns", + DNSViewID: "0", + DNSViewName: "#", + DNSZoneID: "9", + DNSZoneIsReverse: "0", + DNSZoneIsRpz: "0", + DNSZoneName: "lego.example.com", + DNSZoneNameUTF: "lego.example.com", + DNSZoneSiteName: "#", + DNSZoneSortZone: "lego.example.com", + DNSZoneType: "master", + RRAllValue: "127.0.0.1", + RRAuthGsstsig: "0", + RRFullName: "loopback.lego.example.com", + RRFullNameUTF: "loopback.lego.example.com", + RRGlue: "loopback", + RRGlueID: "17", + RRID: "208", + RRNameID: "22", + RRType: "A", + RRTypeID: "3", + RRValueID: "237", + RRValueIP4Addr: "7f000001", + RRValueIPAddr: "7f000001", + TTL: "3600", + Value1: "127.0.0.1", + VDNSParentID: "0", + VDNSParentName: "#", + }, + } + + assert.Equal(t, expected, records) +} + +func TestGetRecord(t *testing.T) { + client := setupTest(t, http.MethodGet, "/dns_rr_info", http.StatusOK, "dns_rr_info.json") + + ctx := context.Background() + + record, err := client.GetRecord(ctx, "239") + require.NoError(t, err) + + expected := &ResourceRecord{ + ErrorCode: "0", + DelayedCreateTime: "0", + DelayedDeleteTime: "0", + DelayedTime: "0", + DNSCloud: "0", + DNSID: "3", + DNSName: "dns.smart", + DNSType: "vdns", + DNSViewID: "0", + DNSViewName: "#", + DNSZoneID: "9", + DNSZoneIsReverse: "0", + DNSZoneIsRpz: "0", + DNSZoneName: "lego.example.com", + DNSZoneNameUTF: "lego.example.com", + DNSZoneSiteName: "#", + DNSZoneSortZone: "lego.example.com", + DNSZoneType: "master", + RRAllValue: "test1", + RRAuthGsstsig: "0", + RRFullName: "test.lego.example.com", + RRFullNameUTF: "test.lego.example.com", + RRGlue: "test", + RRGlueID: "21", + RRID: "239", + RRNameID: "26", + RRType: "TXT", + RRTypeID: "6", + RRValueID: "274", + TTL: "3600", + Value1: "test1", + VDNSParentID: "0", + VDNSParentName: "#", + } + + assert.Equal(t, expected, record) +} + +func TestAddRecord(t *testing.T) { + client := setupTest(t, http.MethodPost, "/dns_rr_add", http.StatusCreated, "dns_rr_add.json") + + ctx := context.Background() + + r := ResourceRecord{ + RRName: "test.example.com", + RRType: "TXT", + Value1: "test", + DNSName: "dns.smart", + DNSViewName: "external", + } + + resp, err := client.AddRecord(ctx, r) + require.NoError(t, err) + + expected := &BaseOutput{RetOID: "239"} + + assert.Equal(t, expected, resp) +} + +func TestDeleteRecord(t *testing.T) { + client := setupTest(t, http.MethodDelete, "/dns_rr_delete", http.StatusOK, "dns_rr_delete.json") + + ctx := context.Background() + + resp, err := client.DeleteRecord(ctx, DeleteInputParameters{RRID: "251"}) + require.NoError(t, err) + + expected := &BaseOutput{RetOID: "251"} + + assert.Equal(t, expected, resp) +} + +func TestDeleteRecord_error(t *testing.T) { + client := setupTest(t, http.MethodDelete, "/dns_rr_delete", http.StatusBadRequest, "dns_rr_delete-error.json") + + ctx := context.Background() + + _, err := client.DeleteRecord(ctx, DeleteInputParameters{RRID: "251"}) + require.ErrorAs(t, err, &APIError{}) +} diff --git a/providers/efficientip/internal/fixtures/dns_rr_add.json b/providers/efficientip/internal/fixtures/dns_rr_add.json new file mode 100644 index 0000000..49932c2 --- /dev/null +++ b/providers/efficientip/internal/fixtures/dns_rr_add.json @@ -0,0 +1,5 @@ +[ + { + "ret_oid": "239" + } +] diff --git a/providers/efficientip/internal/fixtures/dns_rr_delete-error.json b/providers/efficientip/internal/fixtures/dns_rr_delete-error.json new file mode 100644 index 0000000..7eb0db6 --- /dev/null +++ b/providers/efficientip/internal/fixtures/dns_rr_delete-error.json @@ -0,0 +1,6 @@ +{ + "errno": "20117", + "errmsg": "This RR does not exist", + "severity": "error", + "category": "dns_rr_delete" +} diff --git a/providers/efficientip/internal/fixtures/dns_rr_delete.json b/providers/efficientip/internal/fixtures/dns_rr_delete.json new file mode 100644 index 0000000..d794fba --- /dev/null +++ b/providers/efficientip/internal/fixtures/dns_rr_delete.json @@ -0,0 +1,5 @@ +[ + { + "ret_oid": "251" + } +] diff --git a/providers/efficientip/internal/fixtures/dns_rr_info.json b/providers/efficientip/internal/fixtures/dns_rr_info.json new file mode 100644 index 0000000..8a2f6d0 --- /dev/null +++ b/providers/efficientip/internal/fixtures/dns_rr_info.json @@ -0,0 +1,64 @@ +[ + { + "errno": "0", + "rr_all_value": "test1", + "dnszone_sort_zone": "lego.example.com", + "dnszone_is_rpz": "0", + "dnszone_type": "master", + "rr_full_name": "test.lego.example.com", + "rr_full_name_utf": "test.lego.example.com", + "rr_name_ip_addr": "", + "rr_name_ip4_addr": "", + "rr_value_ip_addr": "", + "rr_value_ip4_addr": "", + "rr_glue": "test", + "rr_type": "TXT", + "ttl": "3600", + "delayed_time": "0", + "rr_class_name": "", + "value1": "test1", + "value2": "", + "value3": "", + "value4": "", + "value5": "", + "value6": "", + "value7": "", + "dnszone_id": "9", + "rr_id": "239", + "dns_id": "3", + "dnszone_name_utf": "lego.example.com", + "dnszone_name": "lego.example.com", + "dns_name": "dns.smart", + "dns_type": "vdns", + "dns_cloud": "0", + "vdns_parent_id": "0", + "dnsview_name": "#", + "dnsview_class_name": "", + "dnsview_id": "0", + "dnszone_site_name": "#", + "dnszone_is_reverse": "0", + "dnszone_masters": "", + "vdns_parent_name": "#", + "dnszone_forwarders": "", + "dns_class_name": "", + "dnszone_class_name": "", + "dns_version": "", + "dns_comment": "", + "delayed_create_time": "0", + "delayed_delete_time": "0", + "multistatus": "", + "rr_auth_gsstsig": "0", + "rr_last_update_time": "", + "rr_last_update_days": "", + "rr_name_id": "26", + "rr_value_id": "274", + "rr_type_id": "6", + "rr_glue_id": "21", + "dnsview_class_parameters": "", + "dnsview_class_parameters_properties": "", + "dnsview_class_parameters_inheritance_source": "", + "rr_class_parameters": "", + "rr_class_parameters_properties": "", + "rr_class_parameters_inheritance_source": "" + } +] diff --git a/providers/efficientip/internal/fixtures/dns_rr_list.json b/providers/efficientip/internal/fixtures/dns_rr_list.json new file mode 100644 index 0000000..2371eaf --- /dev/null +++ b/providers/efficientip/internal/fixtures/dns_rr_list.json @@ -0,0 +1,436 @@ +[ + { + "errno": "0", + "rr_all_value": "test1", + "dnszone_sort_zone": "lego.example.com", + "dnszone_is_rpz": "0", + "dnszone_type": "master", + "rr_full_name": "test.lego.example.com", + "rr_full_name_utf": "test.lego.example.com", + "rr_name_ip_addr": "", + "rr_name_ip4_addr": "", + "rr_value_ip_addr": "", + "rr_value_ip4_addr": "", + "rr_glue": "test", + "rr_type": "TXT", + "ttl": "3600", + "delayed_time": "0", + "rr_class_name": "", + "value1": "test1", + "value2": "", + "value3": "", + "value4": "", + "value5": "", + "value6": "", + "value7": "", + "dnszone_id": "9", + "rr_id": "239", + "dns_id": "3", + "dnszone_name_utf": "lego.example.com", + "dnszone_name": "lego.example.com", + "dns_name": "dns.smart", + "dns_type": "vdns", + "dns_cloud": "0", + "vdns_parent_id": "0", + "dnsview_name": "#", + "dnsview_class_name": "", + "dnsview_id": "0", + "dnszone_site_name": "#", + "dnszone_is_reverse": "0", + "dnszone_masters": "", + "vdns_parent_name": "#", + "dnszone_forwarders": "", + "dns_class_name": "", + "dnszone_class_name": "", + "dns_version": "", + "dns_comment": "", + "delayed_create_time": "0", + "delayed_delete_time": "0", + "multistatus": "", + "rr_auth_gsstsig": "0", + "rr_last_update_time": "", + "rr_last_update_days": "", + "rr_name_id": "26", + "rr_value_id": "274", + "rr_type_id": "6", + "rr_glue_id": "21", + "dnsview_class_parameters": "", + "dnsview_class_parameters_properties": "", + "dnsview_class_parameters_inheritance_source": "", + "rr_class_parameters": "", + "rr_class_parameters_properties": "", + "rr_class_parameters_inheritance_source": "" + }, + { + "errno": "0", + "rr_all_value": "test2", + "dnszone_sort_zone": "lego.example.com", + "dnszone_is_rpz": "0", + "dnszone_type": "master", + "rr_full_name": "test.lego.example.com", + "rr_full_name_utf": "test.lego.example.com", + "rr_name_ip_addr": "", + "rr_name_ip4_addr": "", + "rr_value_ip_addr": "", + "rr_value_ip4_addr": "", + "rr_glue": "test", + "rr_type": "TXT", + "ttl": "3600", + "delayed_time": "0", + "rr_class_name": "", + "value1": "test2", + "value2": "", + "value3": "", + "value4": "", + "value5": "", + "value6": "", + "value7": "", + "dnszone_id": "9", + "rr_id": "241", + "dns_id": "3", + "dnszone_name_utf": "lego.example.com", + "dnszone_name": "lego.example.com", + "dns_name": "dns.smart", + "dns_type": "vdns", + "dns_cloud": "0", + "vdns_parent_id": "0", + "dnsview_name": "#", + "dnsview_class_name": "", + "dnsview_id": "0", + "dnszone_site_name": "#", + "dnszone_is_reverse": "0", + "dnszone_masters": "", + "vdns_parent_name": "#", + "dnszone_forwarders": "", + "dns_class_name": "", + "dnszone_class_name": "", + "dns_version": "", + "dns_comment": "", + "delayed_create_time": "0", + "delayed_delete_time": "0", + "multistatus": "", + "rr_auth_gsstsig": "0", + "rr_last_update_time": "", + "rr_last_update_days": "", + "rr_name_id": "26", + "rr_value_id": "275", + "rr_type_id": "6", + "rr_glue_id": "21", + "dnsview_class_parameters": "", + "dnsview_class_parameters_properties": "", + "dnsview_class_parameters_inheritance_source": "", + "rr_class_parameters": "", + "rr_class_parameters_properties": "", + "rr_class_parameters_inheritance_source": "" + }, + { + "errno": "0", + "rr_all_value": "test1", + "dnszone_sort_zone": "lego.example.com", + "dnszone_is_rpz": "0", + "dnszone_type": "master", + "rr_full_name": "lego.example.com", + "rr_full_name_utf": "lego.example.com", + "rr_name_ip_addr": "", + "rr_name_ip4_addr": "", + "rr_value_ip_addr": "", + "rr_value_ip4_addr": "", + "rr_glue": ".", + "rr_type": "TXT", + "ttl": "3600", + "delayed_time": "0", + "rr_class_name": "", + "value1": "test1", + "value2": "", + "value3": "", + "value4": "", + "value5": "", + "value6": "", + "value7": "", + "dnszone_id": "9", + "rr_id": "245", + "dns_id": "3", + "dnszone_name_utf": "lego.example.com", + "dnszone_name": "lego.example.com", + "dns_name": "dns.smart", + "dns_type": "vdns", + "dns_cloud": "0", + "vdns_parent_id": "0", + "dnsview_name": "#", + "dnsview_class_name": "", + "dnsview_id": "0", + "dnszone_site_name": "#", + "dnszone_is_reverse": "0", + "dnszone_masters": "", + "vdns_parent_name": "#", + "dnszone_forwarders": "", + "dns_class_name": "", + "dnszone_class_name": "", + "dns_version": "", + "dns_comment": "", + "delayed_create_time": "0", + "delayed_delete_time": "0", + "multistatus": "", + "rr_auth_gsstsig": "0", + "rr_last_update_time": "", + "rr_last_update_days": "", + "rr_name_id": "21", + "rr_value_id": "274", + "rr_type_id": "6", + "rr_glue_id": "3", + "dnsview_class_parameters": "", + "dnsview_class_parameters_properties": "", + "dnsview_class_parameters_inheritance_source": "", + "rr_class_parameters": "", + "rr_class_parameters_properties": "", + "rr_class_parameters_inheritance_source": "" + }, + { + "errno": "0", + "rr_all_value": "test2", + "dnszone_sort_zone": "lego.example.com", + "dnszone_is_rpz": "0", + "dnszone_type": "master", + "rr_full_name": "lego.example.com", + "rr_full_name_utf": "lego.example.com", + "rr_name_ip_addr": "", + "rr_name_ip4_addr": "", + "rr_value_ip_addr": "", + "rr_value_ip4_addr": "", + "rr_glue": ".", + "rr_type": "TXT", + "ttl": "3600", + "delayed_time": "0", + "rr_class_name": "", + "value1": "test2", + "value2": "", + "value3": "", + "value4": "", + "value5": "", + "value6": "", + "value7": "", + "dnszone_id": "9", + "rr_id": "247", + "dns_id": "3", + "dnszone_name_utf": "lego.example.com", + "dnszone_name": "lego.example.com", + "dns_name": "dns.smart", + "dns_type": "vdns", + "dns_cloud": "0", + "vdns_parent_id": "0", + "dnsview_name": "#", + "dnsview_class_name": "", + "dnsview_id": "0", + "dnszone_site_name": "#", + "dnszone_is_reverse": "0", + "dnszone_masters": "", + "vdns_parent_name": "#", + "dnszone_forwarders": "", + "dns_class_name": "", + "dnszone_class_name": "", + "dns_version": "", + "dns_comment": "", + "delayed_create_time": "0", + "delayed_delete_time": "0", + "multistatus": "", + "rr_auth_gsstsig": "0", + "rr_last_update_time": "", + "rr_last_update_days": "", + "rr_name_id": "21", + "rr_value_id": "275", + "rr_type_id": "6", + "rr_glue_id": "3", + "dnsview_class_parameters": "", + "dnsview_class_parameters_properties": "", + "dnsview_class_parameters_inheritance_source": "", + "rr_class_parameters": "", + "rr_class_parameters_properties": "", + "rr_class_parameters_inheritance_source": "" + }, + { + "errno": "0", + "rr_all_value": "dns.smart, root@lego.example.com, 2023062719, 1200, 600, 1209600, 3600", + "dnszone_sort_zone": "lego.example.com", + "dnszone_is_rpz": "0", + "dnszone_type": "master", + "rr_full_name": "lego.example.com", + "rr_full_name_utf": "lego.example.com", + "rr_name_ip_addr": "", + "rr_name_ip4_addr": "", + "rr_value_ip_addr": "", + "rr_value_ip4_addr": "", + "rr_glue": ".", + "rr_type": "SOA", + "ttl": "3600", + "delayed_time": "0", + "rr_class_name": "", + "value1": "dns.smart", + "value2": "root@lego.example.com", + "value3": "2023062719", + "value4": "1200", + "value5": "600", + "value6": "1209600", + "value7": "3600", + "dnszone_id": "9", + "rr_id": "201", + "dns_id": "3", + "dnszone_name_utf": "lego.example.com", + "dnszone_name": "lego.example.com", + "dns_name": "dns.smart", + "dns_type": "vdns", + "dns_cloud": "0", + "vdns_parent_id": "0", + "dnsview_name": "#", + "dnsview_class_name": "", + "dnsview_id": "0", + "dnszone_site_name": "#", + "dnszone_is_reverse": "0", + "dnszone_masters": "", + "vdns_parent_name": "#", + "dnszone_forwarders": "", + "dns_class_name": "", + "dnszone_class_name": "", + "dns_version": "", + "dns_comment": "", + "delayed_create_time": "0", + "delayed_delete_time": "0", + "multistatus": "", + "rr_auth_gsstsig": "0", + "rr_last_update_time": "", + "rr_last_update_days": "", + "rr_name_id": "21", + "rr_value_id": "282", + "rr_type_id": "2", + "rr_glue_id": "3", + "dnsview_class_parameters": "", + "dnsview_class_parameters_properties": "", + "dnsview_class_parameters_inheritance_source": "", + "rr_class_parameters": "", + "rr_class_parameters_properties": "", + "rr_class_parameters_inheritance_source": "" + }, + { + "errno": "0", + "rr_all_value": "dns.smart", + "dnszone_sort_zone": "lego.example.com", + "dnszone_is_rpz": "0", + "dnszone_type": "master", + "rr_full_name": "lego.example.com", + "rr_full_name_utf": "lego.example.com", + "rr_name_ip_addr": "", + "rr_name_ip4_addr": "", + "rr_value_ip_addr": "", + "rr_value_ip4_addr": "", + "rr_glue": ".", + "rr_type": "NS", + "ttl": "3600", + "delayed_time": "0", + "rr_class_name": "", + "value1": "dns.smart", + "value2": "", + "value3": "", + "value4": "", + "value5": "", + "value6": "", + "value7": "", + "dnszone_id": "9", + "rr_id": "200", + "dns_id": "3", + "dnszone_name_utf": "lego.example.com", + "dnszone_name": "lego.example.com", + "dns_name": "dns.smart", + "dns_type": "vdns", + "dns_cloud": "0", + "vdns_parent_id": "0", + "dnsview_name": "#", + "dnsview_class_name": "", + "dnsview_id": "0", + "dnszone_site_name": "#", + "dnszone_is_reverse": "0", + "dnszone_masters": "", + "vdns_parent_name": "#", + "dnszone_forwarders": "", + "dns_class_name": "", + "dnszone_class_name": "", + "dns_version": "", + "dns_comment": "", + "delayed_create_time": "0", + "delayed_delete_time": "0", + "multistatus": "", + "rr_auth_gsstsig": "0", + "rr_last_update_time": "", + "rr_last_update_days": "", + "rr_name_id": "21", + "rr_value_id": "10", + "rr_type_id": "1", + "rr_glue_id": "3", + "dnsview_class_parameters": "", + "dnsview_class_parameters_properties": "", + "dnsview_class_parameters_inheritance_source": "", + "rr_class_parameters": "", + "rr_class_parameters_properties": "", + "rr_class_parameters_inheritance_source": "" + }, + { + "errno": "0", + "rr_all_value": "127.0.0.1", + "dnszone_sort_zone": "lego.example.com", + "dnszone_is_rpz": "0", + "dnszone_type": "master", + "rr_full_name": "loopback.lego.example.com", + "rr_full_name_utf": "loopback.lego.example.com", + "rr_name_ip_addr": "", + "rr_name_ip4_addr": "", + "rr_value_ip_addr": "7f000001", + "rr_value_ip4_addr": "7f000001", + "rr_glue": "loopback", + "rr_type": "A", + "ttl": "3600", + "delayed_time": "0", + "rr_class_name": "", + "value1": "127.0.0.1", + "value2": "", + "value3": "", + "value4": "", + "value5": "", + "value6": "", + "value7": "", + "dnszone_id": "9", + "rr_id": "208", + "dns_id": "3", + "dnszone_name_utf": "lego.example.com", + "dnszone_name": "lego.example.com", + "dns_name": "dns.smart", + "dns_type": "vdns", + "dns_cloud": "0", + "vdns_parent_id": "0", + "dnsview_name": "#", + "dnsview_class_name": "", + "dnsview_id": "0", + "dnszone_site_name": "#", + "dnszone_is_reverse": "0", + "dnszone_masters": "", + "vdns_parent_name": "#", + "dnszone_forwarders": "", + "dns_class_name": "", + "dnszone_class_name": "", + "dns_version": "", + "dns_comment": "", + "delayed_create_time": "0", + "delayed_delete_time": "0", + "multistatus": "", + "rr_auth_gsstsig": "0", + "rr_last_update_time": "", + "rr_last_update_days": "", + "rr_name_id": "22", + "rr_value_id": "237", + "rr_type_id": "3", + "rr_glue_id": "17", + "dnsview_class_parameters": "", + "dnsview_class_parameters_properties": "", + "dnsview_class_parameters_inheritance_source": "", + "rr_class_parameters": "", + "rr_class_parameters_properties": "", + "rr_class_parameters_inheritance_source": "" + } +] diff --git a/providers/efficientip/internal/types.go b/providers/efficientip/internal/types.go new file mode 100644 index 0000000..017089a --- /dev/null +++ b/providers/efficientip/internal/types.go @@ -0,0 +1,109 @@ +package internal + +import "fmt" + +type ResourceRecord struct { + ErrorCode string `json:"errno,omitempty"` + + DelayedCreateTime string `json:"delayed_create_time,omitempty"` + DelayedDeleteTime string `json:"delayed_delete_time,omitempty"` + DelayedTime string `json:"delayed_time,omitempty"` + DNSClassName string `json:"dns_class_name,omitempty"` + DNSCloud string `json:"dns_cloud,omitempty"` + DNSComment string `json:"dns_comment,omitempty"` + DNSID string `json:"dns_id,omitempty"` + DNSName string `json:"dns_name,omitempty"` + DNSType string `json:"dns_type,omitempty"` + DNSVersion string `json:"dns_version,omitempty"` + DNSViewClassName string `json:"dnsview_class_name,omitempty"` + DNSViewClassParameters string `json:"dnsview_class_parameters,omitempty"` + DNSViewClassParametersInheritanceSource string `json:"dnsview_class_parameters_inheritance_source,omitempty"` + DNSViewClassParametersProperties string `json:"dnsview_class_parameters_properties,omitempty"` + DNSViewID string `json:"dnsview_id,omitempty"` + DNSViewName string `json:"dnsview_name,omitempty"` + DNSZoneClassName string `json:"dnszone_class_name,omitempty"` + DNSZoneForwarders string `json:"dnszone_forwarders,omitempty"` + DNSZoneID string `json:"dnszone_id,omitempty"` + DNSZoneIsReverse string `json:"dnszone_is_reverse,omitempty"` + DNSZoneIsRpz string `json:"dnszone_is_rpz,omitempty"` + DNSZoneMasters string `json:"dnszone_masters,omitempty"` + DNSZoneName string `json:"dnszone_name,omitempty"` + DNSZoneNameUTF string `json:"dnszone_name_utf,omitempty"` + DNSZoneSiteName string `json:"dnszone_site_name,omitempty"` + DNSZoneSortZone string `json:"dnszone_sort_zone,omitempty"` + DNSZoneType string `json:"dnszone_type,omitempty"` + MultiStatus string `json:"multistatus,omitempty"` + RRAllValue string `json:"rr_all_value,omitempty"` + RRAuthGsstsig string `json:"rr_auth_gsstsig,omitempty"` + RRClassName string `json:"rr_class_name,omitempty"` + RRClassParameters string `json:"rr_class_parameters,omitempty"` + RRClassParametersInheritanceSource string `json:"rr_class_parameters_inheritance_source,omitempty"` + RRClassParametersProperties string `json:"rr_class_parameters_properties,omitempty"` + RRFullName string `json:"rr_full_name,omitempty"` + RRFullNameUTF string `json:"rr_full_name_utf,omitempty"` + RRGlue string `json:"rr_glue,omitempty"` + RRGlueID string `json:"rr_glue_id,omitempty"` + RRID string `json:"rr_id,omitempty"` + RRLastUpdateDays string `json:"rr_last_update_days,omitempty"` + RRLastUpdateTime string `json:"rr_last_update_time,omitempty"` + RRName string `json:"rr_name,omitempty"` + RRNameID string `json:"rr_name_id,omitempty"` + RRNameIP4Addr string `json:"rr_name_ip4_addr,omitempty"` + RRNameIPAddr string `json:"rr_name_ip_addr,omitempty"` + RRType string `json:"rr_type,omitempty"` + RRTypeID string `json:"rr_type_id,omitempty"` + RRValueID string `json:"rr_value_id,omitempty"` + RRValueIP4Addr string `json:"rr_value_ip4_addr,omitempty"` + RRValueIPAddr string `json:"rr_value_ip_addr,omitempty"` + TTL string `json:"ttl,omitempty"` + Value1 string `json:"value1,omitempty"` + Value2 string `json:"value2,omitempty"` + Value3 string `json:"value3,omitempty"` + Value4 string `json:"value4,omitempty"` + Value5 string `json:"value5,omitempty"` + Value6 string `json:"value6,omitempty"` + Value7 string `json:"value7,omitempty"` + VDNSParentID string `json:"vdns_parent_id,omitempty"` + VDNSParentName string `json:"vdns_parent_name,omitempty"` +} + +type DeleteInputParameters struct { + RRID string `url:"rr_id,omitempty"` + DNSName string `url:"dns_name,omitempty"` + DNSViewName string `url:"dnsview_name,omitempty"` + RRName string `url:"rr_name,omitempty"` + RRType string `url:"rr_type,omitempty"` + RRValue1 string `url:"rr_value1,omitempty"` +} + +type BaseOutput struct { + RetOID string `json:"ret_oid,omitempty"` +} + +type APIError struct { + ErrorCode string `json:"errno,omitempty"` + ErrMsg string `json:"errmsg,omitempty"` + Severity string `json:"severity,omitempty"` + Category string `json:"category,omitempty"` + Parameters string `json:"parameters,omitempty"` + ParamFormat string `json:"param_format,omitempty"` + ParamValue string `json:"param_value,omitempty"` +} + +func (a APIError) Error() string { + msg := fmt.Sprintf("%s: %s %s %s", a.Category, a.Severity, a.ErrorCode, a.ErrMsg) + + if a.Parameters != "" { + msg += fmt.Sprintf(" parameters: %s", a.Parameters) + } + + if a.ParamFormat != "" { + msg += fmt.Sprintf(" param_format: %s", a.ParamFormat) + } + + if a.ParamValue != "" { + msg += fmt.Sprintf(" param_value: %s", a.ParamValue) + } + + return msg +} diff --git a/providers/exec/exec.toml b/providers/exec/exec.toml index a274f07..52243fe 100644 --- a/providers/exec/exec.toml +++ b/providers/exec/exec.toml @@ -94,19 +94,4 @@ you can use the `--` delimiter to specify the start of positional arguments, and | default | `myprogram cleanup -- ` | | `RAW` | `myprogram cleanup -- ` | -### Timeout - -The command have to display propagation timeout and polling interval into Stdout. - -The values must be formatted as JSON, and times are in seconds. -Example: `{"timeout": 30, "interval": 5}` - -If an error occurs or if the command is not provided: -the default display propagation timeout and polling interval are used. - -| Mode | Command | -|---------|----------------------------------------------------| -| default | `myprogram timeout` | -| `RAW` | `myprogram timeout` | - ''' diff --git a/providers/gcore/gcore.go b/providers/gcore/gcore.go index e23b4de..bc8f457 100644 --- a/providers/gcore/gcore.go +++ b/providers/gcore/gcore.go @@ -130,8 +130,8 @@ func (d *DNSProvider) CleanUp(domain, _, keyAuth string) error { return nil } -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. +// 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/godaddy/godaddy.go b/providers/godaddy/godaddy.go index d96dab5..fb6c70a 100644 --- a/providers/godaddy/godaddy.go +++ b/providers/godaddy/godaddy.go @@ -95,8 +95,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return &DNSProvider{config: config, client: client}, nil } -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. +// 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/hetzner/hetzner.go b/providers/hetzner/hetzner.go index d6b977e..75b3595 100644 --- a/providers/hetzner/hetzner.go +++ b/providers/hetzner/hetzner.go @@ -91,8 +91,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return &DNSProvider{config: config, client: client}, nil } -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. +// 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/linode/linode.go b/providers/linode/linode.go index f9fc3dd..97f3b21 100644 --- a/providers/linode/linode.go +++ b/providers/linode/linode.go @@ -104,8 +104,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return &DNSProvider{config: config, client: &client}, nil } -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. +// 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() (time.Duration, time.Duration) { timeout := d.config.PropagationTimeout if d.config.PropagationTimeout <= 0 { diff --git a/providers/metaname/metaname.go b/providers/metaname/metaname.go new file mode 100644 index 0000000..e5d5e20 --- /dev/null +++ b/providers/metaname/metaname.go @@ -0,0 +1,159 @@ +// Package metaname implements a DNS provider for solving the DNS-01 challenge using Metaname. +package metaname + +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + "github.com/entrustcorporation/dv/dns01" + "github.com/go-acme/lego/v4/platform/config/env" + "github.com/nzdjb/go-metaname" +) + +// Environment variables names. +const ( + envNamespace = "METANAME_" + + EnvAccountReference = envNamespace + "ACCOUNT_REFERENCE" + EnvAPIKey = envNamespace + "API_KEY" + + EnvTTL = envNamespace + "TTL" + EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" + EnvPollingInterval = envNamespace + "POLLING_INTERVAL" +) + +// Config is used to configure the creation of the DNSProvider. +type Config struct { + AccountReference string + APIKey string + PropagationTimeout time.Duration + PollingInterval time.Duration + TTL int +} + +// NewDefaultConfig returns a default configuration for the DNSProvider. +func NewDefaultConfig() *Config { + return &Config{ + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, dns01.DefaultPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), + TTL: env.GetOrDefaultInt(EnvTTL, dns01.DefaultTTL), + } +} + +// DNSProvider implements the challenge.Provider interface. +type DNSProvider struct { + config *Config + client *metaname.MetanameClient + + records map[string]string + recordsMu sync.Mutex +} + +// NewDNSProvider returns a new DNS provider +// using environment variable METANAME_API_KEY for adding and removing the DNS record. +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvAccountReference, EnvAPIKey) + if err != nil { + return nil, fmt.Errorf("metaname: %w", err) + } + + config := NewDefaultConfig() + config.AccountReference = values[EnvAccountReference] + config.APIKey = values[EnvAPIKey] + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for Metaname. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("metaname: the configuration of the DNS provider is nil") + } + + if config.AccountReference == "" { + return nil, errors.New("metaname: missing account reference") + } + if config.APIKey == "" { + return nil, errors.New("metaname: missing api key") + } + + return &DNSProvider{ + config: config, + client: metaname.NewMetanameClient(config.AccountReference, config.APIKey), + records: make(map[string]string), + }, nil +} + +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("metaname: could not find zone for domain %q (%s): %w", domain, info.EffectiveFQDN, err) + } + + authZone = dns01.UnFqdn(authZone) + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) + if err != nil { + return fmt.Errorf("metaname: could not extract subDomain: %w", err) + } + + ctx := context.Background() + + r := metaname.ResourceRecord{ + Name: subDomain, + Type: "TXT", + Aux: nil, + Ttl: d.config.TTL, + Data: info.Value, + } + + ref, err := d.client.CreateDnsRecord(ctx, authZone, r) + if err != nil { + return fmt.Errorf("metaname: add record: %w", err) + } + + d.recordsMu.Lock() + d.records[token] = ref + d.recordsMu.Unlock() + + return nil +} + +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("metaname: could not find zone for domain %q (%s): %w", domain, info.EffectiveFQDN, err) + } + + authZone = dns01.UnFqdn(authZone) + + ctx := context.Background() + + d.recordsMu.Lock() + ref, ok := d.records[token] + d.recordsMu.Unlock() + + if !ok { + return fmt.Errorf("metaname: unknown ref for %s", info.EffectiveFQDN) + } + + err = d.client.DeleteDnsRecord(ctx, authZone, ref) + if err != nil { + return fmt.Errorf("metaname: delete 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/metaname/metaname.toml b/providers/metaname/metaname.toml new file mode 100644 index 0000000..bacdf9b --- /dev/null +++ b/providers/metaname/metaname.toml @@ -0,0 +1,24 @@ +Name = "Metaname" +Description = '''''' +URL = "https://metaname.net" +Code = "metaname" +Since = "v4.13.0" + +Example = ''' +METANAME_ACCOUNT_REFERENCE=xxxx \ +METANAME_API_KEY=yyyyyyy \ +lego --email you@example.com --dns metaname --domains my.example.org run +''' + +[Configuration] + [Configuration.Credentials] + METANAME_ACCOUNT_REFERENCE = "The four-digit reference of a Metaname account" + METANAME_API_KEY = "API Key" + [Configuration.Additional] + METANAME_POLLING_INTERVAL = "Time between DNS propagation check" + METANAME_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + METANAME_TTL = "The TTL of the TXT record used for the DNS challenge" + +[Links] + API = "https://metaname.net/api/1.1/doc" + GoClient = "https://github.com/nzdjb/go-metaname" diff --git a/providers/metaname/metaname_test.go b/providers/metaname/metaname_test.go new file mode 100644 index 0000000..174af40 --- /dev/null +++ b/providers/metaname/metaname_test.go @@ -0,0 +1,145 @@ +package metaname + +import ( + "testing" + "time" + + "github.com/go-acme/lego/v4/platform/tester" + "github.com/stretchr/testify/require" +) + +const envDomain = envNamespace + "DOMAIN" + +var envTest = tester.NewEnvTest(EnvAccountReference, EnvAPIKey).WithDomain(envDomain) + +func TestNewDNSProvider(t *testing.T) { + testCases := []struct { + desc string + envVars map[string]string + expected string + }{ + { + desc: "success", + envVars: map[string]string{ + EnvAccountReference: "user", + EnvAPIKey: "secret", + }, + }, + { + desc: "missing username", + envVars: map[string]string{ + EnvAccountReference: "", + EnvAPIKey: "secret", + }, + expected: "metaname: some credentials information are missing: METANAME_ACCOUNT_REFERENCE", + }, + { + desc: "missing password", + envVars: map[string]string{ + EnvAccountReference: "user", + EnvAPIKey: "", + }, + expected: "metaname: some credentials information are missing: METANAME_API_KEY", + }, + { + desc: "missing credentials", + envVars: map[string]string{}, + expected: "metaname: some credentials information are missing: METANAME_ACCOUNT_REFERENCE,METANAME_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) + } else { + require.EqualError(t, err, test.expected) + } + }) + } +} + +func TestNewDNSProviderConfig(t *testing.T) { + testCases := []struct { + desc string + accountReference string + apiKey string + expected string + }{ + { + desc: "success", + accountReference: "user", + apiKey: "secret", + }, + { + desc: "missing username", + apiKey: "secret", + expected: "metaname: missing account reference", + }, + { + desc: "missing password", + accountReference: "user", + expected: "metaname: missing api key", + }, + { + desc: "missing all", + expected: "metaname: missing account reference", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + config := NewDefaultConfig() + + config.AccountReference = test.accountReference + config.APIKey = test.apiKey + + 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) + + time.Sleep(1 * time.Second) + + err = provider.CleanUp(envTest.GetDomain(), "", "123d==") + require.NoError(t, err) +} diff --git a/providers/pdns/pdns.go b/providers/pdns/pdns.go index de3f2d5..40cd932 100644 --- a/providers/pdns/pdns.go +++ b/providers/pdns/pdns.go @@ -104,8 +104,8 @@ func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { return &DNSProvider{config: config, client: client}, nil } -// Timeout returns the timeout and interval to use when checking for DNS -// propagation. Adjusting here to cope with spikes in propagation times. +// 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 } @@ -142,7 +142,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { } rec := internal.Record{ - Content: "\"" + info.EffectiveFQDN + "\"", + Content: "\"" + info.Value + "\"", Disabled: false, // pre-v1 API diff --git a/providers/rcodezero/internal/client.go b/providers/rcodezero/internal/client.go new file mode 100644 index 0000000..4893f4d --- /dev/null +++ b/providers/rcodezero/internal/client.go @@ -0,0 +1,114 @@ +package internal + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "github.com/entrustcorporation/dv/providers/internal/errutils" + "github.com/miekg/dns" +) + +const defaultBaseURL = "https://my.rcodezero.at/api" + +const authorizationHeader = "Authorization" + +// Client for the RcodeZero API. +type Client struct { + apiToken string + + baseURL *url.URL + HTTPClient *http.Client +} + +// NewClient creates a new Client. +func NewClient(apiToken string) *Client { + baseURL, _ := url.Parse(defaultBaseURL) + + return &Client{ + apiToken: apiToken, + baseURL: baseURL, + HTTPClient: &http.Client{Timeout: 5 * time.Second}, + } +} + +func (c *Client) UpdateRecords(ctx context.Context, authZone string, sets []UpdateRRSet) (*APIResponse, error) { + endpoint := c.baseURL.JoinPath("v1", "acme", "zones", strings.TrimSuffix(dns.Fqdn(authZone), "."), "rrsets") + + req, err := newJSONRequest(ctx, http.MethodPatch, endpoint, sets) + if err != nil { + return nil, err + } + + return c.do(req) +} + +func (c *Client) do(req *http.Request) (*APIResponse, error) { + req.Header.Set(authorizationHeader, "Bearer "+c.apiToken) + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return nil, errutils.NewHTTPDoError(req, err) + } + + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode/100 != 2 { + return nil, parseError(req, resp) + } + + result := &APIResponse{} + raw, err := io.ReadAll(resp.Body) + if err != nil { + return nil, errutils.NewReadResponseError(req, resp.StatusCode, err) + } + + err = json.Unmarshal(raw, result) + if err != nil { + return nil, errutils.NewUnmarshalError(req, resp.StatusCode, raw, 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) + + errAPI := &APIResponse{} + err := json.Unmarshal(raw, errAPI) + if err != nil { + return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw) + } + + return fmt.Errorf("[status code: %d] %w", resp.StatusCode, errAPI) +} diff --git a/providers/rcodezero/internal/client_test.go b/providers/rcodezero/internal/client_test.go new file mode 100644 index 0000000..c19e6e5 --- /dev/null +++ b/providers/rcodezero/internal/client_test.go @@ -0,0 +1,96 @@ +package internal + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func setupTest(t *testing.T, method, pattern string, status int, file string) *Client { + t.Helper() + + mux := http.NewServeMux() + server := httptest.NewServer(mux) + t.Cleanup(server.Close) + + mux.HandleFunc(pattern, func(rw http.ResponseWriter, req *http.Request) { + if req.Method != method { + http.Error(rw, fmt.Sprintf("unsupported method: %s", req.Method), http.StatusBadRequest) + return + } + + apiToken := req.Header.Get(authorizationHeader) + if apiToken != "Bearer secret" { + http.Error(rw, fmt.Sprintf("invalid credentials: %s", apiToken), http.StatusBadRequest) + return + } + + if file == "" { + rw.WriteHeader(status) + return + } + + open, err := os.Open(filepath.Join("fixtures", file)) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + defer func() { _ = open.Close() }() + + rw.WriteHeader(status) + _, err = io.Copy(rw, open) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + }) + + client := NewClient("secret") + client.HTTPClient = server.Client() + client.baseURL, _ = url.Parse(server.URL) + + return client +} + +func TestClient_UpdateRecords_error(t *testing.T) { + client := setupTest(t, http.MethodPatch, "/v1/acme/zones/example.org/rrsets", http.StatusUnprocessableEntity, "error.json") + + rrSet := []UpdateRRSet{{ + Name: "acme.example.org.", + ChangeType: "add", + Type: "TXT", + Records: []Record{{Content: `"my-acme-challenge"`}}, + }} + + resp, err := client.UpdateRecords(context.Background(), "example.org", rrSet) + require.ErrorAs(t, err, new(*APIResponse)) + assert.Nil(t, resp) +} + +func TestClient_UpdateRecords(t *testing.T) { + client := setupTest(t, http.MethodPatch, "/v1/acme/zones/example.org/rrsets", http.StatusOK, "rrsets-response.json") + + rrSet := []UpdateRRSet{{ + Name: "acme.example.org.", + ChangeType: "add", + Type: "TXT", + Records: []Record{{Content: `"my-acme-challenge"`}}, + }} + + resp, err := client.UpdateRecords(context.Background(), "example.org", rrSet) + require.NoError(t, err) + + expected := &APIResponse{Status: "ok", Message: "RRsets updated"} + + assert.Equal(t, expected, resp) +} diff --git a/providers/rcodezero/internal/fixtures/error.json b/providers/rcodezero/internal/fixtures/error.json new file mode 100644 index 0000000..6257f75 --- /dev/null +++ b/providers/rcodezero/internal/fixtures/error.json @@ -0,0 +1,4 @@ +{ + "status": "failed", + "message": "A human readable error message" +} diff --git a/providers/rcodezero/internal/fixtures/rrsets-response.json b/providers/rcodezero/internal/fixtures/rrsets-response.json new file mode 100644 index 0000000..83bdfa1 --- /dev/null +++ b/providers/rcodezero/internal/fixtures/rrsets-response.json @@ -0,0 +1,4 @@ +{ + "status": "ok", + "message": "RRsets updated" +} diff --git a/providers/rcodezero/internal/types.go b/providers/rcodezero/internal/types.go new file mode 100644 index 0000000..6fdffb8 --- /dev/null +++ b/providers/rcodezero/internal/types.go @@ -0,0 +1,25 @@ +package internal + +import "fmt" + +type UpdateRRSet struct { + Name string `json:"name"` + Type string `json:"type"` + ChangeType string `json:"changetype"` + Records []Record `json:"records"` + TTL int `json:"ttl"` +} + +type Record struct { + Content string `json:"content"` + Disabled bool `json:"disabled"` +} + +type APIResponse struct { + Status string `json:"status"` + Message string `json:"message"` +} + +func (a APIResponse) Error() string { + return fmt.Sprintf("%s: %s", a.Status, a.Message) +} diff --git a/providers/rcodezero/rcodezero.go b/providers/rcodezero/rcodezero.go new file mode 100644 index 0000000..46b3aae --- /dev/null +++ b/providers/rcodezero/rcodezero.go @@ -0,0 +1,145 @@ +// Package rcodezero implements a DNS provider for solving the DNS-01 challenge using RcodeZero Anycast network. +package rcodezero + +import ( + "context" + "errors" + "fmt" + "net/http" + "time" + + "github.com/entrustcorporation/dv/dns01" + "github.com/go-acme/lego/v4/platform/config/env" + "github.com/entrustcorporation/dv/providers/rcodezero/internal" +) + +// Environment variables names. +const ( + envNamespace = "RCODEZERO_" + + EnvAPIToken = envNamespace + "API_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 { + APIToken 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, 240*time.Second), + 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 RcodeZero. +// Credentials must be passed in the environment variable: +// RCODEZERO_API_URL and RCODEZERO_API_TOKEN. +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvAPIToken) + if err != nil { + return nil, fmt.Errorf("rcodezero: %w", err) + } + + config := NewDefaultConfig() + config.APIToken = values[EnvAPIToken] + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for RcodeZero. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("rcodezero: the configuration of the DNS provider is nil") + } + + if config.APIToken == "" { + return nil, errors.New("rcodezero: API token missing") + } + + client := internal.NewClient(config.APIToken) + + if config.HTTPClient != nil { + client.HTTPClient = config.HTTPClient + } + + return &DNSProvider{config: config, client: client}, 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 +} + +// Present creates a TXT record to fulfill the dns-01 challenge. +func (d *DNSProvider) Present(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + ctx := context.Background() + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("rcodezero: could not find zone for domain %q (%s): %w", domain, info.EffectiveFQDN, err) + } + + rrSet := []internal.UpdateRRSet{{ + Name: info.EffectiveFQDN, + ChangeType: "update", + Type: "TXT", + TTL: d.config.TTL, + Records: []internal.Record{{Content: `"` + info.Value + `"`}}, + }} + + _, err = d.client.UpdateRecords(ctx, authZone, rrSet) + if err != nil { + return fmt.Errorf("rcodezero: %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) + + ctx := context.Background() + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("rcodezero: could not find zone for domain %q (%s): %w", domain, info.EffectiveFQDN, err) + } + + rrSet := []internal.UpdateRRSet{{ + Name: info.EffectiveFQDN, + Type: "TXT", + ChangeType: "delete", + }} + + _, err = d.client.UpdateRecords(ctx, authZone, rrSet) + if err != nil { + return fmt.Errorf("rcodezero: %w", err) + } + + return nil +} diff --git a/providers/rcodezero/rcodezero.toml b/providers/rcodezero/rcodezero.toml new file mode 100644 index 0000000..ee475d2 --- /dev/null +++ b/providers/rcodezero/rcodezero.toml @@ -0,0 +1,33 @@ +Name = "RcodeZero" +Description = '''''' +URL = "https://www.rcodezero.at/" +Code = "rcodezero" +Since = "v4.13" + +Example = ''' +RCODEZERO_API_TOKEN= \ +lego --email you@example.com --dns rcodezero --domains my.example.org run +''' + +Additional = ''' +## Description + +Generate your API Token via https://my.rcodezero.at with the `ACME` permissions. +This are special tokens with limited access for ACME requests only. + +RcodeZero is an Anycast Network so the distribution of the DNS01-Challenge can take up to 2 minutes. + +''' + +[Configuration] + [Configuration.Credentials] + RCODEZERO_API_TOKEN = "API token" + [Configuration.Additional] + RCODEZERO_POLLING_INTERVAL = "Time between DNS propagation check" + RCODEZERO_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + RCODEZERO_TTL = "The TTL of the TXT record used for the DNS challenge" + RCODEZERO_HTTP_TIMEOUT = "API request timeout" + +[Links] + # Note: the API endpoint used inside the client is not documented. + API = "https://my.rcodezero.at/openapi" diff --git a/providers/rcodezero/rcodezero_test.go b/providers/rcodezero/rcodezero_test.go new file mode 100644 index 0000000..1f09460 --- /dev/null +++ b/providers/rcodezero/rcodezero_test.go @@ -0,0 +1,105 @@ +package rcodezero + +import ( + "testing" + + "github.com/go-acme/lego/v4/platform/tester" + "github.com/stretchr/testify/require" +) + +const envDomain = envNamespace + "DOMAIN" + +var envTest = tester.NewEnvTest( + EnvAPIToken). + WithDomain(envDomain) + +func TestNewDNSProvider(t *testing.T) { + testCases := []struct { + desc string + envVars map[string]string + expected string + }{ + { + desc: "success", + envVars: map[string]string{ + EnvAPIToken: "123", + }, + }, + { + desc: "missing credentials", + envVars: map[string]string{ + EnvAPIToken: "", + }, + expected: "rcodezero: some credentials information are missing: RCODEZERO_API_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) + } else { + require.EqualError(t, err, test.expected) + } + }) + } +} + +func TestNewDNSProviderConfig(t *testing.T) { + testCases := []struct { + desc string + apiToken string + expected string + }{ + { + desc: "success", + apiToken: "123", + }, + { + desc: "missing credentials", + expected: "rcodezero: API token missing", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + config := NewDefaultConfig() + config.APIToken = test.apiToken + + 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 TestLivePresentAndCleanup(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) + + err = provider.CleanUp(envTest.GetDomain(), "", "123d==") + require.NoError(t, err) +} diff --git a/providers/rfc2136/rfc2136.go b/providers/rfc2136/rfc2136.go index 656e944..b7615fa 100644 --- a/providers/rfc2136/rfc2136.go +++ b/providers/rfc2136/rfc2136.go @@ -176,7 +176,6 @@ func (d *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error { // Setup client c := &dns.Client{Timeout: d.config.DNSTimeout} - c.SingleInflight = true // TSIG authentication / msg signing if d.config.TSIGKey != "" && d.config.TSIGSecret != "" { diff --git a/providers/route53/route53.go b/providers/route53/route53.go index 75c900f..34c9b95 100644 --- a/providers/route53/route53.go +++ b/providers/route53/route53.go @@ -30,6 +30,7 @@ const ( EnvHostedZoneID = envNamespace + "HOSTED_ZONE_ID" EnvMaxRetries = envNamespace + "MAX_RETRIES" EnvAssumeRoleArn = envNamespace + "ASSUME_ROLE_ARN" + EnvExternalID = envNamespace + "EXTERNAL_ID" EnvTTL = envNamespace + "TTL" EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" @@ -48,6 +49,7 @@ type Config struct { HostedZoneID string MaxRetries int AssumeRoleArn string + ExternalID string TTL int PropagationTimeout time.Duration @@ -62,6 +64,7 @@ func NewDefaultConfig() *Config { HostedZoneID: env.GetOrFile(EnvHostedZoneID), MaxRetries: env.GetOrDefaultInt(EnvMaxRetries, 5), AssumeRoleArn: env.GetOrDefaultString(EnvAssumeRoleArn, ""), + ExternalID: env.GetOrDefaultString(EnvExternalID, ""), TTL: env.GetOrDefaultInt(EnvTTL, 10), PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute), @@ -337,8 +340,12 @@ func createSession(config *Config) (*session.Session, error) { } return session.NewSession(&aws.Config{ - Region: sess.Config.Region, - Credentials: stscreds.NewCredentials(sess, config.AssumeRoleArn), + Region: sess.Config.Region, + Credentials: stscreds.NewCredentials(sess, config.AssumeRoleArn, func(arp *stscreds.AssumeRoleProvider) { + if config.ExternalID != "" { + arp.ExternalID = &config.ExternalID + } + }), }) } diff --git a/providers/route53/route53.toml b/providers/route53/route53.toml index 3b5f2a5..07e10d9 100644 --- a/providers/route53/route53.toml +++ b/providers/route53/route53.toml @@ -129,7 +129,8 @@ Replace `Z11111112222222333333` with your hosted zone ID and `example.com` with AWS_HOSTED_ZONE_ID = "Override the hosted zone ID." AWS_PROFILE = "Managed by the AWS client (`AWS_PROFILE_FILE` is not supported)" AWS_SDK_LOAD_CONFIG = "Managed by the AWS client. Retrieve the region from the CLI config file (`AWS_SDK_LOAD_CONFIG_FILE` is not supported)" - AWS_ASSUME_ROLE_ARN = "Managed by the AWS Role ARN (`AWS_ASSUME_ROLE_ARN` is not supported)" + AWS_ASSUME_ROLE_ARN = "Managed by the AWS Role ARN (`AWS_ASSUME_ROLE_ARN_FILE` is not supported)" + AWS_EXTERNAL_ID = "Managed by STS AssumeRole API operation (`AWS_EXTERNAL_ID_FILE` is not supported)" [Configuration.Additional] AWS_SHARED_CREDENTIALS_FILE = "Managed by the AWS client. Shared credentials file." AWS_MAX_RETRIES = "The number of maximum returns the service will use to make an individual API request" diff --git a/providers/transip/fakeclient_test.go b/providers/transip/fakeclient_test.go index 4713274..7696c22 100644 --- a/providers/transip/fakeclient_test.go +++ b/providers/transip/fakeclient_test.go @@ -24,6 +24,18 @@ type fakeClient struct { domainName string } +func (f *fakeClient) PutWithResponse(_ rest.Request) (rest.Response, error) { + panic("not implemented") +} + +func (f *fakeClient) PostWithResponse(_ rest.Request) (rest.Response, error) { + panic("not implemented") +} + +func (f *fakeClient) PatchWithResponse(_ rest.Request) (rest.Response, error) { + panic("not implemented") +} + func (f *fakeClient) Get(request rest.Request, dest interface{}) error { if f.getInfoLatency != 0 { time.Sleep(f.getInfoLatency) diff --git a/providers/websupport/websupport.go b/providers/websupport/websupport.go index e7622db..a3a15b0 100644 --- a/providers/websupport/websupport.go +++ b/providers/websupport/websupport.go @@ -133,12 +133,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error { return nil } - err = internal.ParseError(resp) - if err != nil { - return fmt.Errorf("websupport: %w", err) - } - - return nil + return fmt.Errorf("websupport: %w", internal.ParseError(resp)) } // CleanUp removes the TXT record matching the specified parameters. @@ -172,12 +167,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { return nil } - err = internal.ParseError(resp) - if err != nil { - return fmt.Errorf("websupport: %w", err) - } - - return nil + return fmt.Errorf("websupport: %w", internal.ParseError(resp)) } // Timeout returns the timeout and interval to use when checking for DNS propagation.