Add DNS provider for Selectel v2 (#2152)
Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
This commit is contained in:
parent
a832515ad5
commit
8623f0df01
10 changed files with 631 additions and 14 deletions
14
README.md
14
README.md
|
@ -79,13 +79,13 @@ 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/) | [plesk.com](https://go-acme.github.io/lego/dns/plesk/) | [Porkbun](https://go-acme.github.io/lego/dns/porkbun/) |
|
||||
| [PowerDNS](https://go-acme.github.io/lego/dns/pdns/) | [Rackspace](https://go-acme.github.io/lego/dns/rackspace/) | [RcodeZero](https://go-acme.github.io/lego/dns/rcodezero/) | [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/) | [Shellrent](https://go-acme.github.io/lego/dns/shellrent/) | [Simply.com](https://go-acme.github.io/lego/dns/simply/) |
|
||||
| [Sonic](https://go-acme.github.io/lego/dns/sonic/) | [Stackpath](https://go-acme.github.io/lego/dns/stackpath/) | [Tencent Cloud DNS](https://go-acme.github.io/lego/dns/tencentcloud/) | [TransIP](https://go-acme.github.io/lego/dns/transip/) |
|
||||
| [UKFast SafeDNS](https://go-acme.github.io/lego/dns/safedns/) | [Ultradns](https://go-acme.github.io/lego/dns/ultradns/) | [Variomedia](https://go-acme.github.io/lego/dns/variomedia/) | [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/) |
|
||||
| [Vercel](https://go-acme.github.io/lego/dns/vercel/) | [Versio.[nl/eu/uk]](https://go-acme.github.io/lego/dns/versio/) | [VinylDNS](https://go-acme.github.io/lego/dns/vinyldns/) | [VK Cloud](https://go-acme.github.io/lego/dns/vkcloud/) |
|
||||
| [Vscale](https://go-acme.github.io/lego/dns/vscale/) | [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [Webnames](https://go-acme.github.io/lego/dns/webnames/) | [Websupport](https://go-acme.github.io/lego/dns/websupport/) |
|
||||
| [WEDOS](https://go-acme.github.io/lego/dns/wedos/) | [Yandex 360](https://go-acme.github.io/lego/dns/yandex360/) | [Yandex Cloud](https://go-acme.github.io/lego/dns/yandexcloud/) | [Yandex PDD](https://go-acme.github.io/lego/dns/yandex/) |
|
||||
| [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) | | |
|
||||
| [Selectel v2](https://go-acme.github.io/lego/dns/selectelv2/) | [Selectel](https://go-acme.github.io/lego/dns/selectel/) | [Servercow](https://go-acme.github.io/lego/dns/servercow/) | [Shellrent](https://go-acme.github.io/lego/dns/shellrent/) |
|
||||
| [Simply.com](https://go-acme.github.io/lego/dns/simply/) | [Sonic](https://go-acme.github.io/lego/dns/sonic/) | [Stackpath](https://go-acme.github.io/lego/dns/stackpath/) | [Tencent Cloud DNS](https://go-acme.github.io/lego/dns/tencentcloud/) |
|
||||
| [TransIP](https://go-acme.github.io/lego/dns/transip/) | [UKFast SafeDNS](https://go-acme.github.io/lego/dns/safedns/) | [Ultradns](https://go-acme.github.io/lego/dns/ultradns/) | [Variomedia](https://go-acme.github.io/lego/dns/variomedia/) |
|
||||
| [VegaDNS](https://go-acme.github.io/lego/dns/vegadns/) | [Vercel](https://go-acme.github.io/lego/dns/vercel/) | [Versio.[nl/eu/uk]](https://go-acme.github.io/lego/dns/versio/) | [VinylDNS](https://go-acme.github.io/lego/dns/vinyldns/) |
|
||||
| [VK Cloud](https://go-acme.github.io/lego/dns/vkcloud/) | [Vscale](https://go-acme.github.io/lego/dns/vscale/) | [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [Webnames](https://go-acme.github.io/lego/dns/webnames/) |
|
||||
| [Websupport](https://go-acme.github.io/lego/dns/websupport/) | [WEDOS](https://go-acme.github.io/lego/dns/wedos/) | [Yandex 360](https://go-acme.github.io/lego/dns/yandex360/) | [Yandex Cloud](https://go-acme.github.io/lego/dns/yandexcloud/) |
|
||||
| [Yandex PDD](https://go-acme.github.io/lego/dns/yandex/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) | |
|
||||
|
||||
<!-- END DNS PROVIDERS LIST -->
|
||||
|
||||
|
|
|
@ -119,6 +119,7 @@ func allDNSCodes() string {
|
|||
"sakuracloud",
|
||||
"scaleway",
|
||||
"selectel",
|
||||
"selectelv2",
|
||||
"servercow",
|
||||
"shellrent",
|
||||
"simply",
|
||||
|
@ -2398,6 +2399,30 @@ func displayDNSHelp(w io.Writer, name string) error {
|
|||
ew.writeln()
|
||||
ew.writeln(`More information: https://go-acme.github.io/lego/dns/selectel`)
|
||||
|
||||
case "selectelv2":
|
||||
// generated from: providers/dns/selectelv2/selectelv2.toml
|
||||
ew.writeln(`Configuration for Selectel v2.`)
|
||||
ew.writeln(`Code: 'selectelv2'`)
|
||||
ew.writeln(`Since: 'v4.17.0'`)
|
||||
ew.writeln()
|
||||
|
||||
ew.writeln(`Credentials:`)
|
||||
ew.writeln(` - "SELECTELV2_ACCOUNT_ID": Selectel account ID (INT)`)
|
||||
ew.writeln(` - "SELECTELV2_PASSWORD": Openstack username's password`)
|
||||
ew.writeln(` - "SELECTELV2_PROJECT_ID": Cloud project ID (UUID)`)
|
||||
ew.writeln(` - "SELECTELV2_USERNAME": Openstack username`)
|
||||
ew.writeln()
|
||||
|
||||
ew.writeln(`Additional Configuration:`)
|
||||
ew.writeln(` - "SELECTELV2_BASE_URL": API endpoint URL`)
|
||||
ew.writeln(` - "SELECTELV2_HTTP_TIMEOUT": API request timeout`)
|
||||
ew.writeln(` - "SELECTELV2_POLLING_INTERVAL": Time between DNS propagation check`)
|
||||
ew.writeln(` - "SELECTELV2_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
|
||||
ew.writeln(` - "SELECTELV2_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/selectelv2`)
|
||||
|
||||
case "servercow":
|
||||
// generated from: providers/dns/servercow/servercow.toml
|
||||
ew.writeln(`Configuration for Servercow.`)
|
||||
|
|
75
docs/content/dns/zz_gen_selectelv2.md
Normal file
75
docs/content/dns/zz_gen_selectelv2.md
Normal file
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
title: "Selectel v2"
|
||||
date: 2019-03-03T16:39:46+01:00
|
||||
draft: false
|
||||
slug: selectelv2
|
||||
dnsprovider:
|
||||
since: "v4.17.0"
|
||||
code: "selectelv2"
|
||||
url: "https://selectel.ru"
|
||||
---
|
||||
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||
<!-- providers/dns/selectelv2/selectelv2.toml -->
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||
|
||||
|
||||
Configuration for [Selectel v2](https://selectel.ru).
|
||||
|
||||
|
||||
<!--more-->
|
||||
|
||||
- Code: `selectelv2`
|
||||
- Since: v4.17.0
|
||||
|
||||
|
||||
Here is an example bash command using the Selectel v2 provider:
|
||||
|
||||
```bash
|
||||
SELECTEL_USERNAME=trex \
|
||||
SELECTEL_PASSWORD=xxxxx \
|
||||
SELECTEL_ACCOUNT_ID=1234567 \
|
||||
SELECTEL_PROJECT_ID=111a11111aaa11aa1a11aaa11111aa1a \
|
||||
lego --email you@example.com --dns selectelv2 --domains my.example.org run
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
## Credentials
|
||||
|
||||
| Environment Variable Name | Description |
|
||||
|-----------------------|-------------|
|
||||
| `SELECTELV2_ACCOUNT_ID` | Selectel account ID (INT) |
|
||||
| `SELECTELV2_PASSWORD` | Openstack username's password |
|
||||
| `SELECTELV2_PROJECT_ID` | Cloud project ID (UUID) |
|
||||
| `SELECTELV2_USERNAME` | Openstack username |
|
||||
|
||||
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
|
||||
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
|
||||
|
||||
|
||||
## Additional Configuration
|
||||
|
||||
| Environment Variable Name | Description |
|
||||
|--------------------------------|-------------|
|
||||
| `SELECTELV2_BASE_URL` | API endpoint URL |
|
||||
| `SELECTELV2_HTTP_TIMEOUT` | API request timeout |
|
||||
| `SELECTELV2_POLLING_INTERVAL` | Time between DNS propagation check |
|
||||
| `SELECTELV2_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
|
||||
| `SELECTELV2_TTL` | The TTL of the TXT record used for the DNS challenge |
|
||||
|
||||
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
|
||||
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
|
||||
|
||||
|
||||
|
||||
|
||||
## More information
|
||||
|
||||
- [API documentation](https://developers.selectel.ru/docs/cloud-services/dns_api/dns_api_actual/)
|
||||
- [Go client](https://github.com/selectel/domains-go)
|
||||
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||
<!-- providers/dns/selectelv2/selectelv2.toml -->
|
||||
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
|
@ -137,7 +137,7 @@ To display the documentation for a specific DNS provider, run:
|
|||
$ lego dnshelp -c code
|
||||
|
||||
Supported DNS providers:
|
||||
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, cpanel, derak, desec, designate, digitalocean, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, mailinabox, manual, metaname, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rcodezero, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, servercow, shellrent, simply, sonic, stackpath, tencentcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, vscale, vultr, webnames, websupport, wedos, yandex, yandex360, yandexcloud, zoneee, zonomi
|
||||
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, cpanel, derak, desec, designate, digitalocean, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, mailinabox, manual, metaname, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rcodezero, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, servercow, shellrent, simply, sonic, stackpath, tencentcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, vscale, vultr, webnames, websupport, wedos, yandex, yandex360, yandexcloud, zoneee, zonomi
|
||||
|
||||
More information: https://go-acme.github.io/lego/dns
|
||||
"""
|
||||
|
|
4
go.mod
4
go.mod
|
@ -35,7 +35,7 @@ require (
|
|||
github.com/go-jose/go-jose/v4 v4.0.1
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1
|
||||
github.com/google/go-querystring v1.1.0
|
||||
github.com/gophercloud/gophercloud v1.0.0
|
||||
github.com/gophercloud/gophercloud v1.5.0
|
||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df
|
||||
|
@ -65,6 +65,8 @@ require (
|
|||
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.22
|
||||
github.com/selectel/domains-go v1.0.2
|
||||
github.com/selectel/go-selvpcclient/v3 v3.1.1
|
||||
github.com/softlayer/softlayer-go v1.1.3
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490
|
||||
|
|
14
go.sum
14
go.sum
|
@ -304,8 +304,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
|||
github.com/googleapis/gax-go/v2 v2.11.0 h1:9V9PWXEsWnPpQhu/PeQIkS4eGzMlTLGgt80cUUI8Ki4=
|
||||
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
|
||||
github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
|
||||
github.com/gophercloud/gophercloud v1.0.0 h1:9nTGx0jizmHxDobe4mck89FyQHVyA3CaXLIUSGJjP9k=
|
||||
github.com/gophercloud/gophercloud v1.0.0/go.mod h1:Q8fZtyi5zZxPS/j9aj3sSxtvj41AdQMDwyo1myduD5c=
|
||||
github.com/gophercloud/gophercloud v1.5.0 h1:cDN6XFCLKiiqvYpjQLq9AiM7RDRbIC9450WpPH+yvXo=
|
||||
github.com/gophercloud/gophercloud v1.5.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
|
||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae h1:Hi3IgB9RQDE15Kfovd8MTZrcana+UlQqNbOif8dLpA0=
|
||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae/go.mod h1:wx8HMD8oQD0Ryhz6+6ykq75PJ79iPyEqYHfwZ4l7OsA=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
|
@ -357,8 +357,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
|
|||
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.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik=
|
||||
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
|
||||
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||
github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
|
||||
github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||
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=
|
||||
|
@ -572,6 +572,10 @@ github.com/sacloud/packages-go v0.0.9/go.mod h1:k+EEUMF2LlncjbNIJNOqLyZ9wjTESPIW
|
|||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.22 h1:wJrcTdddKOI8TFxs8cemnhKP2EmKy3yfUKHj3ZdfzYo=
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.22/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/selectel/domains-go v1.0.2 h1:Si6iGaMnTFJxwiJVI50DOdZnwcxc87kqaWrVQYW0a4U=
|
||||
github.com/selectel/domains-go v1.0.2/go.mod h1:SugRKfq4sTpnOHquslCpzda72wV8u0cMBHx0C0l+bzA=
|
||||
github.com/selectel/go-selvpcclient/v3 v3.1.1 h1:C1q2LqqosiapoLpnGITGmysg0YCSQYDo2Gh69CioevM=
|
||||
github.com/selectel/go-selvpcclient/v3 v3.1.1/go.mod h1:NM7IXhh1IzqZ88DOw1Qc5Ez3tULLViXo95l5+rKPuyQ=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
|
@ -683,10 +687,10 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
|
|||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
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.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
|
|
|
@ -110,6 +110,7 @@ import (
|
|||
"github.com/go-acme/lego/v4/providers/dns/sakuracloud"
|
||||
"github.com/go-acme/lego/v4/providers/dns/scaleway"
|
||||
"github.com/go-acme/lego/v4/providers/dns/selectel"
|
||||
"github.com/go-acme/lego/v4/providers/dns/selectelv2"
|
||||
"github.com/go-acme/lego/v4/providers/dns/servercow"
|
||||
"github.com/go-acme/lego/v4/providers/dns/shellrent"
|
||||
"github.com/go-acme/lego/v4/providers/dns/simply"
|
||||
|
@ -351,6 +352,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
|
|||
return scaleway.NewDNSProvider()
|
||||
case "selectel":
|
||||
return selectel.NewDNSProvider()
|
||||
case "selectelv2":
|
||||
return selectelv2.NewDNSProvider()
|
||||
case "servercow":
|
||||
return servercow.NewDNSProvider()
|
||||
case "shellrent":
|
||||
|
|
293
providers/dns/selectelv2/selectelv2.go
Normal file
293
providers/dns/selectelv2/selectelv2.go
Normal file
|
@ -0,0 +1,293 @@
|
|||
// Package selectelv2 implements a DNS provider for solving the DNS-01 challenge using Selectel Domains APIv2.
|
||||
package selectelv2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"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/providers/dns/internal/selectel"
|
||||
selectelapi "github.com/selectel/domains-go/pkg/v2"
|
||||
"github.com/selectel/go-selvpcclient/v3/selvpcclient"
|
||||
)
|
||||
|
||||
const tokenHeader = "X-Auth-Token"
|
||||
|
||||
const (
|
||||
defaultBaseURL = "https://api.selectel.ru/domains/v2"
|
||||
defaultTTL = 60
|
||||
defaultPropagationTimeout = 120 * time.Second
|
||||
defaultPollingInterval = 5 * time.Second
|
||||
defaultHTTPTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "SELECTELV2_"
|
||||
|
||||
EnvBaseURL = envNamespace + "BASE_URL"
|
||||
EnvUsernameOS = envNamespace + "USERNAME"
|
||||
EnvPasswordOS = envNamespace + "PASSWORD"
|
||||
EnvAccount = envNamespace + "ACCOUNT_ID"
|
||||
EnvProjectID = envNamespace + "PROJECT_ID"
|
||||
|
||||
EnvTTL = envNamespace + "TTL"
|
||||
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
|
||||
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
var errNotFound = errors.New("rrset not found")
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider.
|
||||
type Config struct {
|
||||
BaseURL string
|
||||
Username string
|
||||
Password string
|
||||
Account string
|
||||
ProjectID string
|
||||
TTL int
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider.
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
BaseURL: env.GetOrDefaultString(EnvBaseURL, selectel.DefaultSelectelBaseURL),
|
||||
TTL: env.GetOrDefaultInt(EnvTTL, defaultTTL),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, defaultPropagationTimeout),
|
||||
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, defaultPollingInterval),
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, defaultHTTPTimeout),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type DNSProvider struct {
|
||||
baseClient selectelapi.DNSClient[selectelapi.Zone, selectelapi.RRSet]
|
||||
config *Config
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for Selectel Domains APIv2.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(EnvUsernameOS, EnvPasswordOS, EnvAccount, EnvProjectID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("selectelv2: %w", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.Username = values[EnvUsernameOS]
|
||||
config.Password = values[EnvPasswordOS]
|
||||
config.Account = values[EnvAccount]
|
||||
config.ProjectID = values[EnvProjectID]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for selectel.
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("selectelv2: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.Username == "" {
|
||||
return nil, errors.New("selectelv2: missing username")
|
||||
}
|
||||
|
||||
if config.Password == "" {
|
||||
return nil, errors.New("selectelv2: missing password")
|
||||
}
|
||||
|
||||
if config.Account == "" {
|
||||
return nil, errors.New("selectelv2: missing account")
|
||||
}
|
||||
|
||||
if config.ProjectID == "" {
|
||||
return nil, errors.New("selectelv2: missing project ID")
|
||||
}
|
||||
|
||||
headers := http.Header{}
|
||||
headers.Set("User-Agent", "lego/selectelv2")
|
||||
|
||||
return &DNSProvider{
|
||||
baseClient: selectelapi.NewClient(defaultBaseURL, config.HTTPClient, headers),
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Timeout returns the Timeout and interval to use when checking for DNS propagation.
|
||||
// Adjusting here to cope with spikes in propagation times.
|
||||
func (p *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return p.config.PropagationTimeout, p.config.PollingInterval
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfill DNS-01 challenge.
|
||||
func (p *DNSProvider) Present(domain, _, keyAuth string) error {
|
||||
ctx := context.Background()
|
||||
|
||||
client, err := p.authorize()
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectelv2: authorize: %w", err)
|
||||
}
|
||||
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
zone, err := client.getZone(ctx, domain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectelv2: get zone: %w", err)
|
||||
}
|
||||
|
||||
rrset, err := client.getRRset(ctx, dns01.UnFqdn(info.EffectiveFQDN), zone.ID)
|
||||
if err != nil {
|
||||
if !errors.Is(err, errNotFound) {
|
||||
return fmt.Errorf("selectelv2: get RRSet: %w", err)
|
||||
}
|
||||
|
||||
newRRSet := &selectelapi.RRSet{
|
||||
Name: info.EffectiveFQDN,
|
||||
Type: selectelapi.TXT,
|
||||
TTL: p.config.TTL,
|
||||
Records: []selectelapi.RecordItem{{Content: fmt.Sprintf("%q", info.Value)}},
|
||||
}
|
||||
|
||||
_, err = client.CreateRRSet(ctx, zone.ID, newRRSet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectelv2: create RRSet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
rrset.Records = append(rrset.Records, selectelapi.RecordItem{Content: fmt.Sprintf("%q", info.Value)})
|
||||
|
||||
err = client.UpdateRRSet(ctx, zone.ID, rrset.ID, rrset)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectelv2: update RRSet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes a TXT record used for DNS-01 challenge.
|
||||
func (p *DNSProvider) CleanUp(domain, _, keyAuth string) error {
|
||||
ctx := context.Background()
|
||||
|
||||
client, err := p.authorize()
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectelv2: authorize: %w", err)
|
||||
}
|
||||
|
||||
info := dns01.GetChallengeInfo(domain, keyAuth)
|
||||
|
||||
zone, err := client.getZone(ctx, domain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectelv2: get zone: %w", err)
|
||||
}
|
||||
|
||||
rrset, err := client.getRRset(ctx, dns01.UnFqdn(info.EffectiveFQDN), zone.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectelv2: get RRSet: %w", err)
|
||||
}
|
||||
|
||||
if len(rrset.Records) <= 1 {
|
||||
err = client.DeleteRRSet(ctx, zone.ID, rrset.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectelv2: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, item := range rrset.Records {
|
||||
if strings.Trim(item.Content, `"`) == info.Value {
|
||||
rrset.Records = append(rrset.Records[:i], rrset.Records[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
err = client.UpdateRRSet(ctx, zone.ID, rrset.ID, rrset)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectelv2: update RRSet: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *DNSProvider) authorize() (*clientWrapper, error) {
|
||||
token, err := obtainOpenstackToken(p.config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extraHeaders := http.Header{}
|
||||
extraHeaders.Set(tokenHeader, token)
|
||||
|
||||
return &clientWrapper{
|
||||
DNSClient: p.baseClient.WithHeaders(extraHeaders),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func obtainOpenstackToken(config *Config) (string, error) {
|
||||
vpcClient, err := selvpcclient.NewClient(&selvpcclient.ClientOptions{
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
UserDomainName: config.Account,
|
||||
ProjectID: config.ProjectID,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("new VPC client: %w", err)
|
||||
}
|
||||
|
||||
return vpcClient.GetXAuthToken(), nil
|
||||
}
|
||||
|
||||
type clientWrapper struct {
|
||||
selectelapi.DNSClient[selectelapi.Zone, selectelapi.RRSet]
|
||||
}
|
||||
|
||||
func (w *clientWrapper) getZone(ctx context.Context, name string) (*selectelapi.Zone, error) {
|
||||
params := &map[string]string{"filter": name}
|
||||
|
||||
zones, err := w.ListZones(ctx, params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list zone: %w", err)
|
||||
}
|
||||
|
||||
for _, zone := range zones.GetItems() {
|
||||
if zone.Name == dns01.ToFqdn(name) {
|
||||
return zone, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(strings.Split(dns01.UnFqdn(name), ".")) == 1 {
|
||||
return nil, errors.New("zone for challenge has not been found")
|
||||
}
|
||||
|
||||
// -1 can not be returned since if no dots present we exit above
|
||||
i := strings.Index(name, ".")
|
||||
|
||||
return w.getZone(ctx, name[i+1:])
|
||||
}
|
||||
|
||||
func (w *clientWrapper) getRRset(ctx context.Context, name, zoneID string) (*selectelapi.RRSet, error) {
|
||||
params := &map[string]string{"name": name, "rrset_types": string(selectelapi.TXT)}
|
||||
|
||||
resp, err := w.ListRRSets(ctx, zoneID, params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list rrset: %w", err)
|
||||
}
|
||||
|
||||
for _, rrset := range resp.GetItems() {
|
||||
if rrset.Name == dns01.ToFqdn(name) {
|
||||
return rrset, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errNotFound
|
||||
}
|
30
providers/dns/selectelv2/selectelv2.toml
Normal file
30
providers/dns/selectelv2/selectelv2.toml
Normal file
|
@ -0,0 +1,30 @@
|
|||
Name = "Selectel v2"
|
||||
Description = ''''''
|
||||
URL = "https://selectel.ru"
|
||||
Code = "selectelv2"
|
||||
Since = "v4.17.0"
|
||||
|
||||
Example = '''
|
||||
SELECTEL_USERNAME=trex \
|
||||
SELECTEL_PASSWORD=xxxxx \
|
||||
SELECTEL_ACCOUNT_ID=1234567 \
|
||||
SELECTEL_PROJECT_ID=111a11111aaa11aa1a11aaa11111aa1a \
|
||||
lego --email you@example.com --dns selectelv2 --domains my.example.org run
|
||||
'''
|
||||
|
||||
[Configuration]
|
||||
[Configuration.Credentials]
|
||||
SELECTELV2_USERNAME = "Openstack username"
|
||||
SELECTELV2_PASSWORD = "Openstack username's password"
|
||||
SELECTELV2_ACCOUNT_ID = "Selectel account ID (INT)"
|
||||
SELECTELV2_PROJECT_ID = "Cloud project ID (UUID)"
|
||||
[Configuration.Additional]
|
||||
SELECTELV2_BASE_URL = "API endpoint URL"
|
||||
SELECTELV2_POLLING_INTERVAL = "Time between DNS propagation check"
|
||||
SELECTELV2_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
|
||||
SELECTELV2_TTL = "The TTL of the TXT record used for the DNS challenge"
|
||||
SELECTELV2_HTTP_TIMEOUT = "API request timeout"
|
||||
|
||||
[Links]
|
||||
API = "https://developers.selectel.ru/docs/cloud-services/dns_api/dns_api_actual/"
|
||||
GoClient = "https://github.com/selectel/domains-go"
|
185
providers/dns/selectelv2/selectelv2_test.go
Normal file
185
providers/dns/selectelv2/selectelv2_test.go
Normal file
|
@ -0,0 +1,185 @@
|
|||
package selectelv2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-acme/lego/v4/platform/tester"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const envDomain = envNamespace + "DOMAIN"
|
||||
|
||||
var envTest = tester.NewEnvTest(EnvUsernameOS, EnvPasswordOS, EnvAccount, EnvProjectID).
|
||||
WithDomain(envDomain)
|
||||
|
||||
func TestNewDNSProvider(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
envVars map[string]string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
envVars: map[string]string{
|
||||
EnvUsernameOS: "someName",
|
||||
EnvPasswordOS: "qwerty",
|
||||
EnvAccount: "1",
|
||||
EnvProjectID: "111a11111aaa11aa1a11aaa11111aa1a",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "missing username",
|
||||
envVars: map[string]string{
|
||||
EnvPasswordOS: "qwerty",
|
||||
EnvAccount: "1",
|
||||
EnvProjectID: "111a11111aaa11aa1a11aaa11111aa1a",
|
||||
},
|
||||
expected: "selectelv2: some credentials information are missing: SELECTELV2_USERNAME",
|
||||
},
|
||||
{
|
||||
desc: "missing password",
|
||||
envVars: map[string]string{
|
||||
EnvUsernameOS: "someName",
|
||||
EnvAccount: "1",
|
||||
EnvProjectID: "111a11111aaa11aa1a11aaa11111aa1a",
|
||||
},
|
||||
expected: "selectelv2: some credentials information are missing: SELECTELV2_PASSWORD",
|
||||
},
|
||||
{
|
||||
desc: "missing account",
|
||||
envVars: map[string]string{
|
||||
EnvUsernameOS: "someName",
|
||||
EnvPasswordOS: "qwerty",
|
||||
EnvProjectID: "111a11111aaa11aa1a11aaa11111aa1a",
|
||||
},
|
||||
expected: "selectelv2: some credentials information are missing: SELECTELV2_ACCOUNT_ID",
|
||||
},
|
||||
{
|
||||
desc: "missing project",
|
||||
envVars: map[string]string{
|
||||
EnvUsernameOS: "someName",
|
||||
EnvPasswordOS: "qwerty",
|
||||
EnvAccount: "1",
|
||||
},
|
||||
expected: "selectelv2: some credentials information are missing: SELECTELV2_PROJECT_ID",
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
assert.NotNil(t, p.config)
|
||||
assert.NotNil(t, p.baseClient)
|
||||
} else {
|
||||
require.EqualError(t, err, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewDNSProviderConfig(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
username string
|
||||
password string
|
||||
account string
|
||||
projectID string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "success",
|
||||
username: "user",
|
||||
password: "secret",
|
||||
account: "1",
|
||||
projectID: "111a11111aaa11aa1a11aaa11111aa1a",
|
||||
},
|
||||
{
|
||||
desc: "missing username",
|
||||
password: "secret",
|
||||
account: "1",
|
||||
projectID: "111a11111aaa11aa1a11aaa11111aa1a",
|
||||
expected: "selectelv2: missing username",
|
||||
},
|
||||
{
|
||||
desc: "missing password",
|
||||
username: "user",
|
||||
account: "1",
|
||||
projectID: "111a11111aaa11aa1a11aaa11111aa1a",
|
||||
expected: "selectelv2: missing password",
|
||||
},
|
||||
{
|
||||
desc: "missing account",
|
||||
username: "user",
|
||||
password: "secret",
|
||||
projectID: "111a11111aaa11aa1a11aaa11111aa1a",
|
||||
expected: "selectelv2: missing account",
|
||||
},
|
||||
{
|
||||
desc: "missing projectID",
|
||||
username: "user",
|
||||
password: "secret",
|
||||
account: "1",
|
||||
expected: "selectelv2: missing project ID",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
config := NewDefaultConfig()
|
||||
config.Username = test.username
|
||||
config.Password = test.password
|
||||
config.Account = test.account
|
||||
config.ProjectID = test.projectID
|
||||
|
||||
p, err := NewDNSProviderConfig(config)
|
||||
|
||||
if test.expected == "" {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, p)
|
||||
assert.NotNil(t, p.config)
|
||||
assert.NotNil(t, p.baseClient)
|
||||
} 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(2 * time.Second)
|
||||
|
||||
err = provider.CleanUp(envTest.GetDomain(), "", "123d==")
|
||||
require.NoError(t, err)
|
||||
}
|
Loading…
Reference in a new issue