Add DNS provider for VinylDNS (#1384)
This commit is contained in:
parent
2334340d7a
commit
7f53f88555
17 changed files with 1006 additions and 14 deletions
|
@ -65,7 +65,8 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns).
|
|||
| [Oracle Cloud](https://go-acme.github.io/lego/dns/oraclecloud/) | [OVH](https://go-acme.github.io/lego/dns/ovh/) | [PowerDNS](https://go-acme.github.io/lego/dns/pdns/) | [Rackspace](https://go-acme.github.io/lego/dns/rackspace/) |
|
||||
| [reg.ru](https://go-acme.github.io/lego/dns/regru/) | [RFC2136](https://go-acme.github.io/lego/dns/rfc2136/) | [RimuHosting](https://go-acme.github.io/lego/dns/rimuhosting/) | [Sakura Cloud](https://go-acme.github.io/lego/dns/sakuracloud/) |
|
||||
| [Scaleway](https://go-acme.github.io/lego/dns/scaleway/) | [Selectel](https://go-acme.github.io/lego/dns/selectel/) | [Servercow](https://go-acme.github.io/lego/dns/servercow/) | [Stackpath](https://go-acme.github.io/lego/dns/stackpath/) |
|
||||
| [TransIP](https://go-acme.github.io/lego/dns/transip/) | [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/) | [Versio.[nl/eu/uk]](https://go-acme.github.io/lego/dns/versio/) | [Vscale](https://go-acme.github.io/lego/dns/vscale/) |
|
||||
| [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [Yandex](https://go-acme.github.io/lego/dns/yandex/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) |
|
||||
| [TransIP](https://go-acme.github.io/lego/dns/transip/) | [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/) | [Versio.[nl/eu/uk]](https://go-acme.github.io/lego/dns/versio/) | [VinylDNS](https://go-acme.github.io/lego/dns/vinyldns/) |
|
||||
| [Vscale](https://go-acme.github.io/lego/dns/vscale/) | [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [Yandex](https://go-acme.github.io/lego/dns/yandex/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) |
|
||||
| [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) | | | |
|
||||
|
||||
<!-- END DNS PROVIDERS LIST -->
|
||||
|
|
|
@ -92,6 +92,7 @@ func allDNSCodes() string {
|
|||
"transip",
|
||||
"vegadns",
|
||||
"versio",
|
||||
"vinyldns",
|
||||
"vscale",
|
||||
"vultr",
|
||||
"yandex",
|
||||
|
@ -1767,6 +1768,27 @@ func displayDNSHelp(name string) error {
|
|||
ew.writeln()
|
||||
ew.writeln(`More information: https://go-acme.github.io/lego/dns/versio`)
|
||||
|
||||
case "vinyldns":
|
||||
// generated from: providers/dns/vinyldns/vinyldns.toml
|
||||
ew.writeln(`Configuration for VinylDNS.`)
|
||||
ew.writeln(`Code: 'vinyldns'`)
|
||||
ew.writeln(`Since: 'v4.4.0'`)
|
||||
ew.writeln()
|
||||
|
||||
ew.writeln(`Credentials:`)
|
||||
ew.writeln(` - "VINYLDNS_ACCESS_KEY": The VinylDNS API key`)
|
||||
ew.writeln(` - "VINYLDNS_HOST": The VinylDNS API URL`)
|
||||
ew.writeln(` - "VINYLDNS_SECRET_KEY": The VinylDNS API Secret key`)
|
||||
ew.writeln()
|
||||
|
||||
ew.writeln(`Additional Configuration:`)
|
||||
ew.writeln(` - "VINYLDNS_POLLING_INTERVAL": Time between DNS propagation check`)
|
||||
ew.writeln(` - "VINYLDNS_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
|
||||
ew.writeln(` - "VINYLDNS_TTL": The TTL of the TXT record used for the DNS challenge`)
|
||||
|
||||
ew.writeln()
|
||||
ew.writeln(`More information: https://go-acme.github.io/lego/dns/vinyldns`)
|
||||
|
||||
case "vscale":
|
||||
// generated from: providers/dns/vscale/vscale.toml
|
||||
ew.writeln(`Configuration for Vscale.`)
|
||||
|
|
68
docs/content/dns/zz_gen_vinyldns.md
Normal file
68
docs/content/dns/zz_gen_vinyldns.md
Normal file
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
title: "VinylDNS"
|
||||
date: 2019-03-03T16:39:46+01:00
|
||||
draft: false
|
||||
slug: vinyldns
|
||||
---
|
||||
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||
<!-- providers/dns/vinyldns/vinyldns.toml -->
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||
|
||||
Since: v4.4.0
|
||||
|
||||
Configuration for [VinylDNS](https://www.vinyldns.io).
|
||||
|
||||
|
||||
<!--more-->
|
||||
|
||||
- Code: `vinyldns`
|
||||
|
||||
Here is an example bash command using the VinylDNS provider:
|
||||
|
||||
```bash
|
||||
VINYLDNS_ACCESS_KEY=xxxxxx \
|
||||
VINYLDNS_SECRET_KEY=yyyyy \
|
||||
VINYLDNS_HOST=https://api.vinyldns.example.org:9443 \
|
||||
lego --email myemail@example.com --dns vinyldns --domains my.example.org run
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## Credentials
|
||||
|
||||
| Environment Variable Name | Description |
|
||||
|-----------------------|-------------|
|
||||
| `VINYLDNS_ACCESS_KEY` | The VinylDNS API key |
|
||||
| `VINYLDNS_HOST` | The VinylDNS API URL |
|
||||
| `VINYLDNS_SECRET_KEY` | The VinylDNS API Secret key |
|
||||
|
||||
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
|
||||
More information [here](/lego/dns/#configuration-and-credentials).
|
||||
|
||||
|
||||
## Additional Configuration
|
||||
|
||||
| Environment Variable Name | Description |
|
||||
|--------------------------------|-------------|
|
||||
| `VINYLDNS_POLLING_INTERVAL` | Time between DNS propagation check |
|
||||
| `VINYLDNS_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
|
||||
| `VINYLDNS_TTL` | The TTL of the TXT record used for the DNS challenge |
|
||||
|
||||
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
|
||||
More information [here](/lego/dns/#configuration-and-credentials).
|
||||
|
||||
The vinyldns integration makes use of dotted hostnames to ease permission management.
|
||||
Users are required to have DELETE ACL level or zone admin permissions on the VinylDNS zone containing the target host.
|
||||
|
||||
|
||||
|
||||
## More information
|
||||
|
||||
- [API documentation](https://www.vinyldns.io/api/)
|
||||
- [Go client](https://github.com/vinyldns/go-vinyldns)
|
||||
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||
<!-- providers/dns/vinyldns/vinyldns.toml -->
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
7
go.mod
7
go.mod
|
@ -42,9 +42,10 @@ require (
|
|||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2
|
||||
github.com/sacloud/libsacloud v1.36.2
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/transip/gotransip/v6 v6.6.0
|
||||
github.com/urfave/cli v1.22.5
|
||||
github.com/vultr/govultr/v2 v2.4.0
|
||||
github.com/transip/gotransip/v6 v6.2.0
|
||||
github.com/urfave/cli v1.22.4
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14
|
||||
github.com/vultr/govultr/v2 v2.0.0
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
|
|
27
go.sum
27
go.sum
|
@ -139,6 +139,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
|
|||
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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5FzwuL2wwWBgBbtj/YLCi4LuA=
|
||||
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
|
@ -215,8 +217,8 @@ github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrj
|
|||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8 h1:92lWxgpa+fF3FozM4B3UZtHZMJX8T5XT+TFdCxsPyWs=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
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=
|
||||
|
@ -404,11 +406,16 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
|||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
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/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
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 v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
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/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
|
@ -436,18 +443,20 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
|
|||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/transip/gotransip/v6 v6.6.0 h1:dAHCTZzX98H6QE2kA4R9acAXu5RPPTwMSUFtpKZF3Nk=
|
||||
github.com/transip/gotransip/v6 v6.6.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
|
||||
github.com/transip/gotransip/v6 v6.2.0 h1:0Z+qVsyeiQdWfcAUeJyF0IEKAPvhJwwpwPi2WGtBIiE=
|
||||
github.com/transip/gotransip/v6 v6.2.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
|
||||
github.com/uber-go/atomic v1.3.2 h1:Azu9lPBWRNKzYXSIwRfgRuDuS0YKsK4NFhiQv98gkxo=
|
||||
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
|
||||
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
|
||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
|
||||
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
github.com/vultr/govultr/v2 v2.4.0 h1:6ySGGAsoOann0lmVNkS8grLvbAT2iYWnO4R1RVYFg0A=
|
||||
github.com/vultr/govultr/v2 v2.4.0/go.mod h1:U+dZLAmyGD62IGykgC9JYU/zQIOkIhf93nw6dJL/47M=
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14 h1:TFXGGMHmml4rs29PdPisC/aaCzOxUu1Vsh9on/IpUfE=
|
||||
github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg=
|
||||
github.com/vultr/govultr/v2 v2.0.0 h1:+lAtqfWy3g9VwL7tT2Fpyad8Vv4MxOhT/NU8O5dk+EQ=
|
||||
github.com/vultr/govultr/v2 v2.0.0/go.mod h1:2PsEeg+gs3p/Fo5Pw8F9mv+DUBEOlrNZ8GmCTGmhOhs=
|
||||
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=
|
||||
|
|
|
@ -83,6 +83,7 @@ import (
|
|||
"github.com/go-acme/lego/v4/providers/dns/transip"
|
||||
"github.com/go-acme/lego/v4/providers/dns/vegadns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/versio"
|
||||
"github.com/go-acme/lego/v4/providers/dns/vinyldns"
|
||||
"github.com/go-acme/lego/v4/providers/dns/vscale"
|
||||
"github.com/go-acme/lego/v4/providers/dns/vultr"
|
||||
"github.com/go-acme/lego/v4/providers/dns/yandex"
|
||||
|
@ -253,6 +254,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
|
|||
return versio.NewDNSProvider()
|
||||
case "vultr":
|
||||
return vultr.NewDNSProvider()
|
||||
case "vinyldns":
|
||||
return vinyldns.NewDNSProvider()
|
||||
case "vscale":
|
||||
return vscale.NewDNSProvider()
|
||||
case "yandex":
|
||||
|
|
40
providers/dns/vinyldns/fixtures/recordSetChange-create.json
Normal file
40
providers/dns/vinyldns/fixtures/recordSetChange-create.json
Normal file
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"changeType": "Create",
|
||||
"created": "2021-03-04T00:49:00Z",
|
||||
"id": "27ba5c17-a217-4e8d-b662-b1dc8bee588f",
|
||||
"recordSet": {
|
||||
"account": "",
|
||||
"created": "2021-03-04T00:49:00Z",
|
||||
"id": "10000000-0000-0000-0000-000000000000",
|
||||
"name": "_acme-challenge.host",
|
||||
"records": [
|
||||
{
|
||||
"text": "O2UTPYgIzRNt5N27EVcNKDxv6goSF7ru3zi3chZXKUw"
|
||||
}
|
||||
],
|
||||
"status": "Active",
|
||||
"ttl": 30,
|
||||
"type": "TXT",
|
||||
"updated": "2021-03-04T00:49:00Z",
|
||||
"zoneId": "00000000-0000-0000-0000-000000000000"
|
||||
},
|
||||
"singleBatchChangeIds": [],
|
||||
"status": "Complete",
|
||||
"userId": "50000000-0000-0000-0000-000000000000",
|
||||
"zone": {
|
||||
"account": "system",
|
||||
"acl": {
|
||||
"rules": []
|
||||
},
|
||||
"adminGroupId": "40000000-0000-0000-0000-000000000000",
|
||||
"created": "2020-07-15T21:15:36Z",
|
||||
"email": "Ops@company.invalid",
|
||||
"id": "00000000-0000-0000-0000-000000000000",
|
||||
"isTest": false,
|
||||
"latestSync": "2020-07-15T21:15:36Z",
|
||||
"name": "example.com.",
|
||||
"shared": false,
|
||||
"status": "Active",
|
||||
"updated": "2021-03-03T18:02:47Z"
|
||||
}
|
||||
}
|
40
providers/dns/vinyldns/fixtures/recordSetChange-delete.json
Normal file
40
providers/dns/vinyldns/fixtures/recordSetChange-delete.json
Normal file
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"changeType": "Delete",
|
||||
"created": "2021-03-04T00:49:00Z",
|
||||
"id": "27ba5c17-a217-4e8d-b662-b1dc8bee588f",
|
||||
"recordSet": {
|
||||
"account": "",
|
||||
"created": "2021-03-04T00:49:00Z",
|
||||
"id": "10000000-0000-0000-0000-000000000000",
|
||||
"name": "_acme-challenge.host",
|
||||
"records": [
|
||||
{
|
||||
"text": "O2UTPYgIzRNt5N27EVcNKDxv6goSF7ru3zi3chZXKUw"
|
||||
}
|
||||
],
|
||||
"status": "Active",
|
||||
"ttl": 30,
|
||||
"type": "TXT",
|
||||
"updated": "2021-03-04T00:49:00Z",
|
||||
"zoneId": "00000000-0000-0000-0000-000000000000"
|
||||
},
|
||||
"singleBatchChangeIds": [],
|
||||
"status": "Complete",
|
||||
"userId": "50000000-0000-0000-0000-000000000000",
|
||||
"zone": {
|
||||
"account": "system",
|
||||
"acl": {
|
||||
"rules": []
|
||||
},
|
||||
"adminGroupId": "40000000-0000-0000-0000-000000000000",
|
||||
"created": "2020-07-15T21:15:36Z",
|
||||
"email": "Ops@company.invalid",
|
||||
"id": "00000000-0000-0000-0000-000000000000",
|
||||
"isTest": false,
|
||||
"latestSync": "2020-07-15T21:15:36Z",
|
||||
"name": "example.com.",
|
||||
"shared": false,
|
||||
"status": "Active",
|
||||
"updated": "2021-03-03T18:02:47Z"
|
||||
}
|
||||
}
|
39
providers/dns/vinyldns/fixtures/recordSetDelete.json
Normal file
39
providers/dns/vinyldns/fixtures/recordSetDelete.json
Normal file
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"changeType": "Delete",
|
||||
"created": "2021-03-04T16:21:54Z",
|
||||
"id": "20000000-0000-0000-0000-000000000000",
|
||||
"recordSet": {
|
||||
"account": "",
|
||||
"created": "2021-03-04T16:21:54Z",
|
||||
"id": "11000000-0000-0000-0000-000000000000",
|
||||
"name": "_acme-challenge.host",
|
||||
"records": [
|
||||
{
|
||||
"text": "O2UTPYgIzRNt5N27EVcNKDxv6goSF7ru3zi3chZXKUw"
|
||||
}
|
||||
],
|
||||
"status": "Pending",
|
||||
"ttl": 30,
|
||||
"type": "TXT",
|
||||
"zoneId": "00000000-0000-0000-0000-000000000000"
|
||||
},
|
||||
"singleBatchChangeIds": [],
|
||||
"status": "Pending",
|
||||
"userId": "50000000-0000-0000-0000-000000000000",
|
||||
"zone": {
|
||||
"account": "system",
|
||||
"acl": {
|
||||
"rules": []
|
||||
},
|
||||
"adminGroupId": "40000000-0000-0000-0000-000000000000",
|
||||
"created": "2020-07-15T21:15:36Z",
|
||||
"email": "Ops@company.invalid",
|
||||
"id": "00000000-0000-0000-0000-000000000000",
|
||||
"isTest": false,
|
||||
"latestSync": "2020-07-15T21:15:36Z",
|
||||
"name": "example.com.",
|
||||
"shared": false,
|
||||
"status": "Active",
|
||||
"updated": "2021-03-03T18:02:47Z"
|
||||
}
|
||||
}
|
39
providers/dns/vinyldns/fixtures/recordSetUpdate-create.json
Normal file
39
providers/dns/vinyldns/fixtures/recordSetUpdate-create.json
Normal file
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"changeType": "Create",
|
||||
"created": "2021-03-04T16:21:54Z",
|
||||
"id": "20000000-0000-0000-0000-000000000000",
|
||||
"recordSet": {
|
||||
"account": "",
|
||||
"created": "2021-03-04T16:21:54Z",
|
||||
"id": "11000000-0000-0000-0000-000000000000",
|
||||
"name": "_acme-challenge.host",
|
||||
"records": [
|
||||
{
|
||||
"text": "O2UTPYgIzRNt5N27EVcNKDxv6goSF7ru3zi3chZXKUw"
|
||||
}
|
||||
],
|
||||
"status": "Pending",
|
||||
"ttl": 30,
|
||||
"type": "TXT",
|
||||
"zoneId": "00000000-0000-0000-0000-000000000000"
|
||||
},
|
||||
"singleBatchChangeIds": [],
|
||||
"status": "Pending",
|
||||
"userId": "50000000-0000-0000-0000-000000000000",
|
||||
"zone": {
|
||||
"account": "system",
|
||||
"acl": {
|
||||
"rules": []
|
||||
},
|
||||
"adminGroupId": "40000000-0000-0000-0000-000000000000",
|
||||
"created": "2020-07-15T21:15:36Z",
|
||||
"email": "Ops@company.invalid",
|
||||
"id": "00000000-0000-0000-0000-000000000000",
|
||||
"isTest": false,
|
||||
"latestSync": "2020-07-15T21:15:36Z",
|
||||
"name": "example.com.",
|
||||
"shared": false,
|
||||
"status": "Active",
|
||||
"updated": "2021-03-03T18:02:47Z"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"maxItems": 100,
|
||||
"nameSort": "ASC",
|
||||
"recordNameFilter": "_acme-challenge.host",
|
||||
"recordSets": []
|
||||
}
|
25
providers/dns/vinyldns/fixtures/recordSetsListAll.json
Normal file
25
providers/dns/vinyldns/fixtures/recordSetsListAll.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"maxItems": 100,
|
||||
"nameSort": "ASC",
|
||||
"recordNameFilter": "_acme-challenge.host",
|
||||
"recordSets": [
|
||||
{
|
||||
"accessLevel": "Delete",
|
||||
"account": "",
|
||||
"created": "2021-03-04T00:51:43Z",
|
||||
"fqdn": "_acme-challenge.host.example.com.",
|
||||
"id": "30000000-0000-0000-0000-000000000000",
|
||||
"name": "_acme-challenge.host",
|
||||
"records": [
|
||||
{
|
||||
"text": "O2UTPYgIzRNt5N27EVcNKDxv6goSF7ru3zi3chZXKUw"
|
||||
}
|
||||
],
|
||||
"status": "Active",
|
||||
"ttl": 30,
|
||||
"type": "TXT",
|
||||
"updated": "2021-03-04T00:51:43Z",
|
||||
"zoneId": "00000000-0000-0000-0000-000000000000"
|
||||
}
|
||||
]
|
||||
}
|
19
providers/dns/vinyldns/fixtures/zoneByName.json
Normal file
19
providers/dns/vinyldns/fixtures/zoneByName.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"zone": {
|
||||
"accessLevel": "Delete",
|
||||
"account": "system",
|
||||
"acl": {
|
||||
"rules": []
|
||||
},
|
||||
"adminGroupId": "40000000-0000-0000-0000-000000000000",
|
||||
"adminGroupName": "OpsTeam",
|
||||
"created": "2020-07-15T21:15:36Z",
|
||||
"email": "Ops@company.invalid",
|
||||
"id": "00000000-0000-0000-0000-000000000000",
|
||||
"latestSync": "2020-07-15T21:15:36Z",
|
||||
"name": "example.com.",
|
||||
"shared": false,
|
||||
"status": "Active",
|
||||
"updated": "2021-03-03T18:02:47Z"
|
||||
}
|
||||
}
|
114
providers/dns/vinyldns/mock_test.go
Normal file
114
providers/dns/vinyldns/mock_test.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
package vinyldns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func setup(t *testing.T) (*http.ServeMux, *DNSProvider) {
|
||||
t.Helper()
|
||||
|
||||
mux := http.NewServeMux()
|
||||
server := httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AccessKey = "foo"
|
||||
config.SecretKey = "bar"
|
||||
config.Host = server.URL
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
require.NoError(t, err)
|
||||
|
||||
return mux, p
|
||||
}
|
||||
|
||||
type mockRouter struct {
|
||||
debug bool
|
||||
|
||||
mu sync.Mutex
|
||||
routes map[string]map[string]http.HandlerFunc
|
||||
}
|
||||
|
||||
func newMockRouter() *mockRouter {
|
||||
routes := map[string]map[string]http.HandlerFunc{
|
||||
http.MethodGet: {},
|
||||
http.MethodPost: {},
|
||||
http.MethodPut: {},
|
||||
http.MethodDelete: {},
|
||||
}
|
||||
|
||||
return &mockRouter{
|
||||
routes: routes,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *mockRouter) Debug() *mockRouter {
|
||||
h.debug = true
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *mockRouter) Get(path string, statusCode int, filename string) *mockRouter {
|
||||
h.add(http.MethodGet, path, statusCode, filename)
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *mockRouter) Post(path string, statusCode int, filename string) *mockRouter {
|
||||
h.add(http.MethodPost, path, statusCode, filename)
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *mockRouter) Put(path string, statusCode int, filename string) *mockRouter {
|
||||
h.add(http.MethodPut, path, statusCode, filename)
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *mockRouter) Delete(path string, statusCode int, filename string) *mockRouter {
|
||||
h.add(http.MethodDelete, path, statusCode, filename)
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *mockRouter) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
if h.debug {
|
||||
fmt.Println(req)
|
||||
}
|
||||
|
||||
rt := h.routes[req.Method]
|
||||
if rt == nil {
|
||||
http.NotFound(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
hdl := rt[req.URL.Path]
|
||||
if hdl == nil {
|
||||
http.NotFound(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
hdl(rw, req)
|
||||
}
|
||||
|
||||
func (h *mockRouter) add(method, path string, statusCode int, filename string) {
|
||||
h.routes[method][path] = func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(statusCode)
|
||||
|
||||
data, err := ioutil.ReadFile(fmt.Sprintf("./fixtures/%s.json", filename))
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
rw.Header().Set("Content-Type", "application/json")
|
||||
_, _ = rw.Write(data)
|
||||
}
|
||||
}
|
291
providers/dns/vinyldns/vinyldns.go
Normal file
291
providers/dns/vinyldns/vinyldns.go
Normal file
|
@ -0,0 +1,291 @@
|
|||
// Package vinyldns implements a DNS provider for solving the DNS-01 challenge using VinylDNS.
|
||||
package vinyldns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/challenge/dns01"
|
||||
"github.com/go-acme/lego/v4/platform/config/env"
|
||||
"github.com/go-acme/lego/v4/platform/wait"
|
||||
"github.com/vinyldns/go-vinyldns/vinyldns"
|
||||
)
|
||||
|
||||
// Environment variables names.
|
||||
const (
|
||||
envNamespace = "VINYLDNS_"
|
||||
|
||||
EnvAccessKey = envNamespace + "ACCESS_KEY"
|
||||
EnvSecretKey = envNamespace + "SECRET_KEY"
|
||||
EnvHost = envNamespace + "HOST"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
)
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider.
|
||||
type Config struct {
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
Host string
|
||||
TTL int
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider.
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, 30),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 4*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider implements the challenge.Provider interface.
|
||||
type DNSProvider struct {
|
||||
client *vinyldns.Client
|
||||
config *Config
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for VinylDNS.
|
||||
// Credentials must be passed in the environment variables:
|
||||
// VINYLDNS_ACCESS_KEY, VINYLDNS_SECRET_KEY, VINYLDNS_HOST.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvAccessKey, EnvSecretKey, EnvHost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vinyldns: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AccessKey = values[EnvAccessKey]
|
||||
config.SecretKey = values[EnvSecretKey]
|
||||
config.Host = values[EnvHost]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for VinylDNS.
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("vinyldns: the configuration of the VinylDNS DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.AccessKey == "" || config.SecretKey == "" {
|
||||
return nil, errors.New("vinyldns: credentials are missing")
|
||||
}
|
||||
|
||||
if config.Host == "" {
|
||||
return nil, errors.New("vinyldns: host is missing")
|
||||
}
|
||||
|
||||
client := vinyldns.NewClient(vinyldns.ClientConfiguration{
|
||||
AccessKey: config.AccessKey,
|
||||
SecretKey: config.SecretKey,
|
||||
Host: config.Host,
|
||||
UserAgent: "go-acme/lego",
|
||||
})
|
||||
|
||||
client.HTTPClient.Timeout = 30 * time.Second
|
||||
|
||||
return &DNSProvider{client: client, config: config}, nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfill the dns-01 challenge.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value := dns01.GetRecord(domain, keyAuth)
|
||||
|
||||
existingRecord, err := d.getRecordSet(fqdn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vinyldns: %w", err)
|
||||
}
|
||||
|
||||
record := vinyldns.Record{Text: value}
|
||||
|
||||
if existingRecord == nil || existingRecord.ID == "" {
|
||||
err = d.createRecordSet(fqdn, []vinyldns.Record{record})
|
||||
if err != nil {
|
||||
return fmt.Errorf("vinyldns: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, i := range existingRecord.Records {
|
||||
if i.Text == value {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
records := existingRecord.Records
|
||||
records = append(records, record)
|
||||
|
||||
err = d.updateRecordSet(existingRecord, records)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vinyldns: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, value := dns01.GetRecord(domain, keyAuth)
|
||||
|
||||
existingRecord, err := d.getRecordSet(fqdn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vinyldns: %w", err)
|
||||
}
|
||||
|
||||
if existingRecord == nil || existingRecord.ID == "" || len(existingRecord.Records) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var records []vinyldns.Record
|
||||
for _, i := range existingRecord.Records {
|
||||
if i.Text != value {
|
||||
records = append(records, i)
|
||||
}
|
||||
}
|
||||
|
||||
if len(records) == 0 {
|
||||
err = d.deleteRecordSet(existingRecord)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vinyldns: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err = d.updateRecordSet(existingRecord, records)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vinyldns: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Timeout returns the timeout and interval to use when checking for DNS propagation.
|
||||
// Adjusting here to cope with spikes in propagation times.
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return d.config.PropagationTimeout, d.config.PollingInterval
|
||||
}
|
||||
|
||||
func (d *DNSProvider) getRecordSet(fqdn string) (*vinyldns.RecordSet, error) {
|
||||
zoneName, hostName, err := splitDomain(fqdn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
zone, err := d.client.ZoneByName(zoneName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allRecordSets, err := d.client.RecordSetsListAll(zone.ID, vinyldns.ListFilter{NameFilter: hostName})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var recordSets []vinyldns.RecordSet
|
||||
for _, i := range allRecordSets {
|
||||
if i.Type == "TXT" {
|
||||
recordSets = append(recordSets, i)
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(recordSets) > 1:
|
||||
return nil, fmt.Errorf("ambiguous recordset definition of %s", fqdn)
|
||||
case len(recordSets) == 1:
|
||||
return &recordSets[0], nil
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSProvider) createRecordSet(fqdn string, records []vinyldns.Record) error {
|
||||
zoneName, hostName, err := splitDomain(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
zone, err := d.client.ZoneByName(zoneName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
recordSet := vinyldns.RecordSet{
|
||||
Name: hostName,
|
||||
ZoneID: zone.ID,
|
||||
Type: "TXT",
|
||||
TTL: d.config.TTL,
|
||||
Records: records,
|
||||
}
|
||||
|
||||
resp, err := d.client.RecordSetCreate(&recordSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.waitForChanges("CreateRS", resp)
|
||||
}
|
||||
|
||||
func (d *DNSProvider) updateRecordSet(recordSet *vinyldns.RecordSet, newRecords []vinyldns.Record) error {
|
||||
operation := "delete"
|
||||
if len(recordSet.Records) < len(newRecords) {
|
||||
operation = "add"
|
||||
}
|
||||
|
||||
recordSet.Records = newRecords
|
||||
recordSet.TTL = d.config.TTL
|
||||
|
||||
resp, err := d.client.RecordSetUpdate(recordSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.waitForChanges("UpdateRS - "+operation, resp)
|
||||
}
|
||||
|
||||
func (d *DNSProvider) deleteRecordSet(existingRecord *vinyldns.RecordSet) error {
|
||||
resp, err := d.client.RecordSetDelete(existingRecord.ZoneID, existingRecord.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return d.waitForChanges("DeleteRS", resp)
|
||||
}
|
||||
|
||||
func (d *DNSProvider) waitForChanges(operation string, resp *vinyldns.RecordSetUpdateResponse) error {
|
||||
return wait.For("vinyldns", d.config.PropagationTimeout, d.config.PollingInterval,
|
||||
func() (bool, error) {
|
||||
change, err := d.client.RecordSetChange(resp.Zone.ID, resp.RecordSet.ID, resp.ChangeID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to query change status: %w", err)
|
||||
}
|
||||
|
||||
if change.Status == "Complete" {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("waiting operation: %s, zoneID: %s, recordsetID: %s, changeID: %s",
|
||||
operation, resp.Zone.ID, resp.RecordSet.ID, resp.ChangeID)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// splitDomain splits the hostname from the authoritative zone, and returns both parts.
|
||||
func splitDomain(fqdn string) (string, string, error) {
|
||||
zone, err := dns01.FindZoneByFqdn(fqdn)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
host := dns01.UnFqdn(strings.TrimSuffix(fqdn, zone))
|
||||
|
||||
return zone, host, nil
|
||||
}
|
31
providers/dns/vinyldns/vinyldns.toml
Normal file
31
providers/dns/vinyldns/vinyldns.toml
Normal file
|
@ -0,0 +1,31 @@
|
|||
Name = "VinylDNS"
|
||||
Description = ''''''
|
||||
URL = "https://www.vinyldns.io"
|
||||
Code = "vinyldns"
|
||||
Since = "v4.4.0"
|
||||
|
||||
Example = '''
|
||||
VINYLDNS_ACCESS_KEY=xxxxxx \
|
||||
VINYLDNS_SECRET_KEY=yyyyy \
|
||||
VINYLDNS_HOST=https://api.vinyldns.example.org:9443 \
|
||||
lego --email myemail@example.com --dns vinyldns --domains my.example.org run
|
||||
'''
|
||||
|
||||
Additional = '''
|
||||
The vinyldns integration makes use of dotted hostnames to ease permission management.
|
||||
Users are required to have DELETE ACL level or zone admin permissions on the VinylDNS zone containing the target host.
|
||||
'''
|
||||
|
||||
[Configuration]
|
||||
[Configuration.Credentials]
|
||||
VINYLDNS_ACCESS_KEY = "The VinylDNS API key"
|
||||
VINYLDNS_SECRET_KEY = "The VinylDNS API Secret key"
|
||||
VINYLDNS_HOST = "The VinylDNS API URL"
|
||||
[Configuration.Additional]
|
||||
VINYLDNS_POLLING_INTERVAL = "Time between DNS propagation check"
|
||||
VINYLDNS_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
|
||||
VINYLDNS_TTL = "The TTL of the TXT record used for the DNS challenge"
|
||||
|
||||
[Links]
|
||||
API = "https://www.vinyldns.io/api/"
|
||||
GoClient = "https://github.com/vinyldns/go-vinyldns"
|
244
providers/dns/vinyldns/vinyldns_test.go
Normal file
244
providers/dns/vinyldns/vinyldns_test.go
Normal file
|
@ -0,0 +1,244 @@
|
|||
package vinyldns
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const envDomain = envNamespace + "DOMAIN"
|
||||
|
||||
const (
|
||||
targetRootDomain = "example.com"
|
||||
targetDomain = "host." + targetRootDomain
|
||||
zoneID = "00000000-0000-0000-0000-000000000000"
|
||||
newRecordSetID = "11000000-0000-0000-0000-000000000000"
|
||||
newCreateChangeID = "20000000-0000-0000-0000-000000000000"
|
||||
recordID = "30000000-0000-0000-0000-000000000000"
|
||||
)
|
||||
|
||||
var envTest = tester.NewEnvTest(
|
||||
EnvAccessKey,
|
||||
EnvSecretKey,
|
||||
EnvHost).
|
||||
WithDomain(envDomain)
|
||||
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
envVars map[string]string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
envVars: map[string]string{
|
||||
EnvAccessKey: "123",
|
||||
EnvSecretKey: "456",
|
||||
EnvHost: "https://example.org",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "missing all credentials",
|
||||
envVars: map[string]string{
|
||||
EnvHost: "https://example.org",
|
||||
},
|
||||
expected: "vinyldns: some credentials information are missing: VINYLDNS_ACCESS_KEY,VINYLDNS_SECRET_KEY",
|
||||
},
|
||||
{
|
||||
desc: "missing access key",
|
||||
envVars: map[string]string{
|
||||
EnvSecretKey: "456",
|
||||
EnvHost: "https://example.org",
|
||||
},
|
||||
expected: "vinyldns: some credentials information are missing: VINYLDNS_ACCESS_KEY",
|
||||
},
|
||||
{
|
||||
desc: "missing secret key",
|
||||
envVars: map[string]string{
|
||||
EnvAccessKey: "123",
|
||||
EnvHost: "https://example.org",
|
||||
},
|
||||
expected: "vinyldns: some credentials information are missing: VINYLDNS_SECRET_KEY",
|
||||
},
|
||||
{
|
||||
desc: "missing host",
|
||||
envVars: map[string]string{
|
||||
EnvAccessKey: "123",
|
||||
EnvSecretKey: "456",
|
||||
},
|
||||
expected: "vinyldns: some credentials information are missing: VINYLDNS_HOST",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
defer envTest.RestoreEnv()
|
||||
envTest.ClearEnv()
|
||||
|
||||
envTest.Apply(test.envVars)
|
||||
|
||||
p, err := NewDNSProvider()
|
||||
|
||||
if test.expected == "" {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
require.NotNil(t, p.config)
|
||||
require.NotNil(t, p.client)
|
||||
} else {
|
||||
require.EqualError(t, err, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDNSProviderConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
accessKey string
|
||||
secretKey string
|
||||
host string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
accessKey: "123",
|
||||
secretKey: "456",
|
||||
host: "https://example.org",
|
||||
},
|
||||
{
|
||||
desc: "missing all credentials",
|
||||
host: "https://example.org",
|
||||
expected: "vinyldns: credentials are missing",
|
||||
},
|
||||
{
|
||||
desc: "missing access key",
|
||||
secretKey: "456",
|
||||
host: "https://example.org",
|
||||
expected: "vinyldns: credentials are missing",
|
||||
},
|
||||
{
|
||||
desc: "missing secret key",
|
||||
accessKey: "123",
|
||||
host: "https://example.org",
|
||||
expected: "vinyldns: credentials are missing",
|
||||
},
|
||||
{
|
||||
desc: "missing host",
|
||||
accessKey: "123",
|
||||
secretKey: "456",
|
||||
expected: "vinyldns: host is missing",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
config := NewDefaultConfig()
|
||||
config.AccessKey = test.accessKey
|
||||
config.SecretKey = test.secretKey
|
||||
config.Host = test.host
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
|
||||
if test.expected == "" {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
require.NotNil(t, p.config)
|
||||
require.NotNil(t, p.client)
|
||||
} else {
|
||||
require.EqualError(t, err, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSProvider_Present(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
keyAuth string
|
||||
handler http.Handler
|
||||
}{
|
||||
{
|
||||
desc: "new record",
|
||||
keyAuth: "123456d==",
|
||||
handler: newMockRouter().
|
||||
Get("/zones/name/"+targetRootDomain+".", http.StatusOK, "zoneByName").
|
||||
Get("/zones/"+zoneID+"/recordsets", http.StatusOK, "recordSetsListAll-empty").
|
||||
Post("/zones/"+zoneID+"/recordsets", http.StatusAccepted, "recordSetUpdate-create").
|
||||
Get("/zones/"+zoneID+"/recordsets/"+newRecordSetID+"/changes/"+newCreateChangeID, http.StatusOK, "recordSetChange-create"),
|
||||
},
|
||||
{
|
||||
desc: "existing record",
|
||||
keyAuth: "123456d==",
|
||||
handler: newMockRouter().
|
||||
Get("/zones/name/"+targetRootDomain+".", http.StatusOK, "zoneByName").
|
||||
Get("/zones/"+zoneID+"/recordsets", http.StatusOK, "recordSetsListAll"),
|
||||
},
|
||||
{
|
||||
desc: "duplicate key",
|
||||
keyAuth: "abc123!!",
|
||||
handler: newMockRouter().
|
||||
Get("/zones/name/"+targetRootDomain+".", http.StatusOK, "zoneByName").
|
||||
Get("/zones/"+zoneID+"/recordsets", http.StatusOK, "recordSetsListAll").
|
||||
Put("/zones/"+zoneID+"/recordsets/"+recordID, http.StatusAccepted, "recordSetUpdate-create").
|
||||
Get("/zones/"+zoneID+"/recordsets/"+newRecordSetID+"/changes/"+newCreateChangeID, http.StatusOK, "recordSetChange-create"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mux, p := setup(t)
|
||||
mux.Handle("/", test.handler)
|
||||
|
||||
err := p.Present(targetDomain, "token"+test.keyAuth, test.keyAuth)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDNSProvider_CleanUp(t *testing.T) {
|
||||
mux, p := setup(t)
|
||||
|
||||
mux.Handle("/", newMockRouter().
|
||||
Get("/zones/name/"+targetRootDomain+".", http.StatusOK, "zoneByName").
|
||||
Get("/zones/"+zoneID+"/recordsets", http.StatusOK, "recordSetsListAll").
|
||||
Delete("/zones/"+zoneID+"/recordsets/"+recordID, http.StatusAccepted, "recordSetDelete").
|
||||
Get("/zones/"+zoneID+"/recordsets/"+newRecordSetID+"/changes/"+newCreateChangeID, http.StatusOK, "recordSetChange-delete"),
|
||||
)
|
||||
|
||||
err := p.CleanUp(targetDomain, "123456d==", "123456d==")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
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(2 * time.Second)
|
||||
|
||||
err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
|
||||
require.NoError(t, err)
|
||||
}
|
Loading…
Reference in a new issue