From 9db046581b7706126d21eb693ce249fe197fa688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Gr=C3=B8ntved?= Date: Fri, 14 Apr 2023 09:44:20 +0200 Subject: [PATCH] Add Brandit.com as DNS provider (#1890) --- README.md | 55 ++--- cmd/zz_gen_cmd_dnshelp.go | 22 ++ docs/content/dns/zz_gen_brandit.md | 69 ++++++ docs/data/zz_cli_help.toml | 2 +- providers/dns/brandit/brandit.go | 197 +++++++++++++++++ providers/dns/brandit/brandit.toml | 24 +++ providers/dns/brandit/brandit_test.go | 141 +++++++++++++ providers/dns/brandit/internal/client.go | 198 ++++++++++++++++++ providers/dns/brandit/internal/client_test.go | 149 +++++++++++++ .../brandit/internal/fixtures/add-record.json | 14 ++ .../internal/fixtures/delete-record.json | 5 + .../internal/fixtures/list-records.json | 28 +++ .../internal/fixtures/status-domain.json | 80 +++++++ providers/dns/brandit/internal/types.go | 91 ++++++++ providers/dns/dns_providers.go | 3 + 15 files changed, 1050 insertions(+), 28 deletions(-) create mode 100644 docs/content/dns/zz_gen_brandit.md create mode 100644 providers/dns/brandit/brandit.go create mode 100644 providers/dns/brandit/brandit.toml create mode 100644 providers/dns/brandit/brandit_test.go create mode 100644 providers/dns/brandit/internal/client.go create mode 100644 providers/dns/brandit/internal/client_test.go create mode 100644 providers/dns/brandit/internal/fixtures/add-record.json create mode 100644 providers/dns/brandit/internal/fixtures/delete-record.json create mode 100644 providers/dns/brandit/internal/fixtures/list-records.json create mode 100644 providers/dns/brandit/internal/fixtures/status-domain.json create mode 100644 providers/dns/brandit/internal/types.go diff --git a/README.md b/README.md index dd8ae198..b35ba09e 100644 --- a/README.md +++ b/README.md @@ -51,33 +51,34 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns). |---------------------------------------------------------------------------------|---------------------------------------------------------------------------------|---------------------------------------------------------------------------------|---------------------------------------------------------------------------------| | [Akamai EdgeDNS](https://go-acme.github.io/lego/dns/edgedns/) | [Alibaba Cloud DNS](https://go-acme.github.io/lego/dns/alidns/) | [all-inkl](https://go-acme.github.io/lego/dns/allinkl/) | [Amazon Lightsail](https://go-acme.github.io/lego/dns/lightsail/) | | [Amazon Route 53](https://go-acme.github.io/lego/dns/route53/) | [ArvanCloud](https://go-acme.github.io/lego/dns/arvancloud/) | [Aurora DNS](https://go-acme.github.io/lego/dns/auroradns/) | [Autodns](https://go-acme.github.io/lego/dns/autodns/) | -| [Azure](https://go-acme.github.io/lego/dns/azure/) | [Bindman](https://go-acme.github.io/lego/dns/bindman/) | [Bluecat](https://go-acme.github.io/lego/dns/bluecat/) | [Bunny](https://go-acme.github.io/lego/dns/bunny/) | -| [Checkdomain](https://go-acme.github.io/lego/dns/checkdomain/) | [Civo](https://go-acme.github.io/lego/dns/civo/) | [CloudDNS](https://go-acme.github.io/lego/dns/clouddns/) | [Cloudflare](https://go-acme.github.io/lego/dns/cloudflare/) | -| [ClouDNS](https://go-acme.github.io/lego/dns/cloudns/) | [CloudXNS](https://go-acme.github.io/lego/dns/cloudxns/) | [ConoHa](https://go-acme.github.io/lego/dns/conoha/) | [Constellix](https://go-acme.github.io/lego/dns/constellix/) | -| [deSEC.io](https://go-acme.github.io/lego/dns/desec/) | [Designate DNSaaS for Openstack](https://go-acme.github.io/lego/dns/designate/) | [Digital Ocean](https://go-acme.github.io/lego/dns/digitalocean/) | [DNS Made Easy](https://go-acme.github.io/lego/dns/dnsmadeeasy/) | -| [dnsHome.de](https://go-acme.github.io/lego/dns/dnshomede/) | [DNSimple](https://go-acme.github.io/lego/dns/dnsimple/) | [DNSPod (deprecated)](https://go-acme.github.io/lego/dns/dnspod/) | [Domain Offensive (do.de)](https://go-acme.github.io/lego/dns/dode/) | -| [Domeneshop](https://go-acme.github.io/lego/dns/domeneshop/) | [DreamHost](https://go-acme.github.io/lego/dns/dreamhost/) | [Duck DNS](https://go-acme.github.io/lego/dns/duckdns/) | [Dyn](https://go-acme.github.io/lego/dns/dyn/) | -| [Dynu](https://go-acme.github.io/lego/dns/dynu/) | [EasyDNS](https://go-acme.github.io/lego/dns/easydns/) | [Epik](https://go-acme.github.io/lego/dns/epik/) | [Exoscale](https://go-acme.github.io/lego/dns/exoscale/) | -| [External program](https://go-acme.github.io/lego/dns/exec/) | [freemyip.com](https://go-acme.github.io/lego/dns/freemyip/) | [G-Core Labs](https://go-acme.github.io/lego/dns/gcore/) | [Gandi Live DNS (v5)](https://go-acme.github.io/lego/dns/gandiv5/) | -| [Gandi](https://go-acme.github.io/lego/dns/gandi/) | [Glesys](https://go-acme.github.io/lego/dns/glesys/) | [Go Daddy](https://go-acme.github.io/lego/dns/godaddy/) | [Google Cloud](https://go-acme.github.io/lego/dns/gcloud/) | -| [Google Domains](https://go-acme.github.io/lego/dns/googledomains/) | [Hetzner](https://go-acme.github.io/lego/dns/hetzner/) | [Hosting.de](https://go-acme.github.io/lego/dns/hostingde/) | [Hosttech](https://go-acme.github.io/lego/dns/hosttech/) | -| [HTTP request](https://go-acme.github.io/lego/dns/httpreq/) | [Hurricane Electric DNS](https://go-acme.github.io/lego/dns/hurricane/) | [HyperOne](https://go-acme.github.io/lego/dns/hyperone/) | [IBM Cloud (SoftLayer)](https://go-acme.github.io/lego/dns/ibmcloud/) | -| [IIJ DNS Platform Service](https://go-acme.github.io/lego/dns/iijdpf/) | [Infoblox](https://go-acme.github.io/lego/dns/infoblox/) | [Infomaniak](https://go-acme.github.io/lego/dns/infomaniak/) | [Internet Initiative Japan](https://go-acme.github.io/lego/dns/iij/) | -| [Internet.bs](https://go-acme.github.io/lego/dns/internetbs/) | [INWX](https://go-acme.github.io/lego/dns/inwx/) | [Ionos](https://go-acme.github.io/lego/dns/ionos/) | [iwantmyname](https://go-acme.github.io/lego/dns/iwantmyname/) | -| [Joker](https://go-acme.github.io/lego/dns/joker/) | [Joohoi's ACME-DNS](https://go-acme.github.io/lego/dns/acme-dns/) | [Liara](https://go-acme.github.io/lego/dns/liara/) | [Linode (v4)](https://go-acme.github.io/lego/dns/linode/) | -| [Liquid Web](https://go-acme.github.io/lego/dns/liquidweb/) | [Loopia](https://go-acme.github.io/lego/dns/loopia/) | [LuaDNS](https://go-acme.github.io/lego/dns/luadns/) | [Manual](https://go-acme.github.io/lego/dns/manual/) | -| [MyDNS.jp](https://go-acme.github.io/lego/dns/mydnsjp/) | [MythicBeasts](https://go-acme.github.io/lego/dns/mythicbeasts/) | [Name.com](https://go-acme.github.io/lego/dns/namedotcom/) | [Namecheap](https://go-acme.github.io/lego/dns/namecheap/) | -| [Namesilo](https://go-acme.github.io/lego/dns/namesilo/) | [NearlyFreeSpeech.NET](https://go-acme.github.io/lego/dns/nearlyfreespeech/) | [Netcup](https://go-acme.github.io/lego/dns/netcup/) | [Netlify](https://go-acme.github.io/lego/dns/netlify/) | -| [Nicmanager](https://go-acme.github.io/lego/dns/nicmanager/) | [NIFCloud](https://go-acme.github.io/lego/dns/nifcloud/) | [Njalla](https://go-acme.github.io/lego/dns/njalla/) | [Nodion](https://go-acme.github.io/lego/dns/nodion/) | -| [NS1](https://go-acme.github.io/lego/dns/ns1/) | [Open Telekom Cloud](https://go-acme.github.io/lego/dns/otc/) | [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/) | -| [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/) | [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/) | [Websupport](https://go-acme.github.io/lego/dns/websupport/) | [WEDOS](https://go-acme.github.io/lego/dns/wedos/) | -| [Yandex Cloud](https://go-acme.github.io/lego/dns/yandexcloud/) | [Yandex PDD](https://go-acme.github.io/lego/dns/yandex/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) | +| [Azure](https://go-acme.github.io/lego/dns/azure/) | [Bindman](https://go-acme.github.io/lego/dns/bindman/) | [Bluecat](https://go-acme.github.io/lego/dns/bluecat/) | [BRANDIT](https://go-acme.github.io/lego/dns/brandit/) | +| [Bunny](https://go-acme.github.io/lego/dns/bunny/) | [Checkdomain](https://go-acme.github.io/lego/dns/checkdomain/) | [Civo](https://go-acme.github.io/lego/dns/civo/) | [CloudDNS](https://go-acme.github.io/lego/dns/clouddns/) | +| [Cloudflare](https://go-acme.github.io/lego/dns/cloudflare/) | [ClouDNS](https://go-acme.github.io/lego/dns/cloudns/) | [CloudXNS](https://go-acme.github.io/lego/dns/cloudxns/) | [ConoHa](https://go-acme.github.io/lego/dns/conoha/) | +| [Constellix](https://go-acme.github.io/lego/dns/constellix/) | [deSEC.io](https://go-acme.github.io/lego/dns/desec/) | [Designate DNSaaS for Openstack](https://go-acme.github.io/lego/dns/designate/) | [Digital Ocean](https://go-acme.github.io/lego/dns/digitalocean/) | +| [DNS Made Easy](https://go-acme.github.io/lego/dns/dnsmadeeasy/) | [dnsHome.de](https://go-acme.github.io/lego/dns/dnshomede/) | [DNSimple](https://go-acme.github.io/lego/dns/dnsimple/) | [DNSPod (deprecated)](https://go-acme.github.io/lego/dns/dnspod/) | +| [Domain Offensive (do.de)](https://go-acme.github.io/lego/dns/dode/) | [Domeneshop](https://go-acme.github.io/lego/dns/domeneshop/) | [DreamHost](https://go-acme.github.io/lego/dns/dreamhost/) | [Duck DNS](https://go-acme.github.io/lego/dns/duckdns/) | +| [Dyn](https://go-acme.github.io/lego/dns/dyn/) | [Dynu](https://go-acme.github.io/lego/dns/dynu/) | [EasyDNS](https://go-acme.github.io/lego/dns/easydns/) | [Epik](https://go-acme.github.io/lego/dns/epik/) | +| [Exoscale](https://go-acme.github.io/lego/dns/exoscale/) | [External program](https://go-acme.github.io/lego/dns/exec/) | [freemyip.com](https://go-acme.github.io/lego/dns/freemyip/) | [G-Core Labs](https://go-acme.github.io/lego/dns/gcore/) | +| [Gandi Live DNS (v5)](https://go-acme.github.io/lego/dns/gandiv5/) | [Gandi](https://go-acme.github.io/lego/dns/gandi/) | [Glesys](https://go-acme.github.io/lego/dns/glesys/) | [Go Daddy](https://go-acme.github.io/lego/dns/godaddy/) | +| [Google Cloud](https://go-acme.github.io/lego/dns/gcloud/) | [Google Domains](https://go-acme.github.io/lego/dns/googledomains/) | [Hetzner](https://go-acme.github.io/lego/dns/hetzner/) | [Hosting.de](https://go-acme.github.io/lego/dns/hostingde/) | +| [Hosttech](https://go-acme.github.io/lego/dns/hosttech/) | [HTTP request](https://go-acme.github.io/lego/dns/httpreq/) | [Hurricane Electric DNS](https://go-acme.github.io/lego/dns/hurricane/) | [HyperOne](https://go-acme.github.io/lego/dns/hyperone/) | +| [IBM Cloud (SoftLayer)](https://go-acme.github.io/lego/dns/ibmcloud/) | [IIJ DNS Platform Service](https://go-acme.github.io/lego/dns/iijdpf/) | [Infoblox](https://go-acme.github.io/lego/dns/infoblox/) | [Infomaniak](https://go-acme.github.io/lego/dns/infomaniak/) | +| [Internet Initiative Japan](https://go-acme.github.io/lego/dns/iij/) | [Internet.bs](https://go-acme.github.io/lego/dns/internetbs/) | [INWX](https://go-acme.github.io/lego/dns/inwx/) | [Ionos](https://go-acme.github.io/lego/dns/ionos/) | +| [iwantmyname](https://go-acme.github.io/lego/dns/iwantmyname/) | [Joker](https://go-acme.github.io/lego/dns/joker/) | [Joohoi's ACME-DNS](https://go-acme.github.io/lego/dns/acme-dns/) | [Liara](https://go-acme.github.io/lego/dns/liara/) | +| [Linode (v4)](https://go-acme.github.io/lego/dns/linode/) | [Liquid Web](https://go-acme.github.io/lego/dns/liquidweb/) | [Loopia](https://go-acme.github.io/lego/dns/loopia/) | [LuaDNS](https://go-acme.github.io/lego/dns/luadns/) | +| [Manual](https://go-acme.github.io/lego/dns/manual/) | [MyDNS.jp](https://go-acme.github.io/lego/dns/mydnsjp/) | [MythicBeasts](https://go-acme.github.io/lego/dns/mythicbeasts/) | [Name.com](https://go-acme.github.io/lego/dns/namedotcom/) | +| [Namecheap](https://go-acme.github.io/lego/dns/namecheap/) | [Namesilo](https://go-acme.github.io/lego/dns/namesilo/) | [NearlyFreeSpeech.NET](https://go-acme.github.io/lego/dns/nearlyfreespeech/) | [Netcup](https://go-acme.github.io/lego/dns/netcup/) | +| [Netlify](https://go-acme.github.io/lego/dns/netlify/) | [Nicmanager](https://go-acme.github.io/lego/dns/nicmanager/) | [NIFCloud](https://go-acme.github.io/lego/dns/nifcloud/) | [Njalla](https://go-acme.github.io/lego/dns/njalla/) | +| [Nodion](https://go-acme.github.io/lego/dns/nodion/) | [NS1](https://go-acme.github.io/lego/dns/ns1/) | [Open Telekom Cloud](https://go-acme.github.io/lego/dns/otc/) | [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/) | [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/) | +| [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/) | [Websupport](https://go-acme.github.io/lego/dns/websupport/) | +| [WEDOS](https://go-acme.github.io/lego/dns/wedos/) | [Yandex Cloud](https://go-acme.github.io/lego/dns/yandexcloud/) | [Yandex PDD](https://go-acme.github.io/lego/dns/yandex/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | +| [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) | | | | diff --git a/cmd/zz_gen_cmd_dnshelp.go b/cmd/zz_gen_cmd_dnshelp.go index edaff3e0..f839e949 100644 --- a/cmd/zz_gen_cmd_dnshelp.go +++ b/cmd/zz_gen_cmd_dnshelp.go @@ -23,6 +23,7 @@ func allDNSCodes() string { "azure", "bindman", "bluecat", + "brandit", "bunny", "checkdomain", "civo", @@ -332,6 +333,27 @@ func displayDNSHelp(w io.Writer, name string) error { ew.writeln() ew.writeln(`More information: https://go-acme.github.io/lego/dns/bluecat`) + case "brandit": + // generated from: providers/dns/brandit/brandit.toml + ew.writeln(`Configuration for BRANDIT.`) + ew.writeln(`Code: 'brandit'`) + ew.writeln(`Since: 'v4.11.0'`) + ew.writeln() + + ew.writeln(`Credentials:`) + ew.writeln(` - "BRANDIT_API_KEY": The API key`) + ew.writeln(` - "BRANDIT_API_USERNAME": The API username`) + ew.writeln() + + ew.writeln(`Additional Configuration:`) + ew.writeln(` - "BRANDIT_HTTP_TIMEOUT": API request timeout`) + ew.writeln(` - "BRANDIT_POLLING_INTERVAL": Time between DNS propagation check`) + ew.writeln(` - "BRANDIT_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`) + ew.writeln(` - "BRANDIT_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/brandit`) + case "bunny": // generated from: providers/dns/bunny/bunny.toml ew.writeln(`Configuration for Bunny.`) diff --git a/docs/content/dns/zz_gen_brandit.md b/docs/content/dns/zz_gen_brandit.md new file mode 100644 index 00000000..307dfa57 --- /dev/null +++ b/docs/content/dns/zz_gen_brandit.md @@ -0,0 +1,69 @@ +--- +title: "BRANDIT" +date: 2019-03-03T16:39:46+01:00 +draft: false +slug: brandit +dnsprovider: + since: "v4.11.0" + code: "brandit" + url: "https://www.brandit.com/" +--- + + + + + + +Configuration for [BRANDIT](https://www.brandit.com/). + + + + +- Code: `brandit` +- Since: v4.11.0 + + +Here is an example bash command using the BRANDIT provider: + +```bash +BRANDIT_API_KEY=xxxxxxxxxxxxxxxxxxxxx \ +BRANDIT_API_USERNAME=yyyyyyyyyyyyyyyyyyyy \ +lego --email myemail@example.com --dns brandit --domains my.example.org run +``` + + + + +## Credentials + +| Environment Variable Name | Description | +|-----------------------|-------------| +| `BRANDIT_API_KEY` | The API key | +| `BRANDIT_API_USERNAME` | The API username | + +The environment variable names can be suffixed by `_FILE` to reference a file instead of a value. +More information [here]({{< ref "dns#configuration-and-credentials" >}}). + + +## Additional Configuration + +| Environment Variable Name | Description | +|--------------------------------|-------------| +| `BRANDIT_HTTP_TIMEOUT` | API request timeout | +| `BRANDIT_POLLING_INTERVAL` | Time between DNS propagation check | +| `BRANDIT_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation | +| `BRANDIT_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://portal.brandit.com/apidocv3) + + + + diff --git a/docs/data/zz_cli_help.toml b/docs/data/zz_cli_help.toml index cf08d7e0..f3a65e5f 100644 --- a/docs/data/zz_cli_help.toml +++ b/docs/data/zz_cli_help.toml @@ -125,7 +125,7 @@ To display the documentation for a specific DNS provider, run: $ lego dnshelp -c code Supported DNS providers: - acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, bindman, bluecat, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudxns, conoha, constellix, desec, designate, digitalocean, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, manual, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, servercow, simply, sonic, stackpath, tencentcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, vscale, vultr, websupport, wedos, yandex, yandexcloud, zoneee, zonomi + acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudxns, conoha, constellix, desec, designate, digitalocean, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, manual, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, servercow, simply, sonic, stackpath, tencentcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, vscale, vultr, websupport, wedos, yandex, yandexcloud, zoneee, zonomi More information: https://go-acme.github.io/lego/dns """ diff --git a/providers/dns/brandit/brandit.go b/providers/dns/brandit/brandit.go new file mode 100644 index 00000000..0b80f490 --- /dev/null +++ b/providers/dns/brandit/brandit.go @@ -0,0 +1,197 @@ +package brandit + +import ( + "errors" + "fmt" + "net/http" + "sync" + "time" + + "github.com/go-acme/lego/v4/challenge/dns01" + "github.com/go-acme/lego/v4/platform/config/env" + "github.com/go-acme/lego/v4/providers/dns/brandit/internal" +) + +const defaultTTL = 600 + +// Environment variables names. +const ( + envNamespace = "BRANDIT_" + + EnvAPIKey = envNamespace + "API_KEY" + EnvAPIUsername = envNamespace + "API_USERNAME" + + EnvTTL = envNamespace + "TTL" + EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT" + EnvPollingInterval = envNamespace + "POLLING_INTERVAL" + EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT" + DefaultBrandItPropagationTimeout = 600 * time.Second +) + +// Config is used to configure the creation of the DNSProvider. +type Config struct { + APIKey string + APIUsername 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, defaultTTL), + PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, DefaultBrandItPropagationTimeout), + PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), + HTTPClient: &http.Client{ + Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second), + }, + } +} + +// DNSProvider implements the challenge.Provider interface. +type DNSProvider struct { + config *Config + client *internal.Client + + records map[string]string + recordsMu sync.Mutex +} + +// NewDNSProvider returns a DNSProvider instance configured for BrandIT. +// Credentials must be passed in the environment variables: BRANDIT_API_KEY, BRANDIT_API_USERNAME. +func NewDNSProvider() (*DNSProvider, error) { + values, err := env.Get(EnvAPIKey, EnvAPIUsername) + if err != nil { + return nil, fmt.Errorf("brandit: %w", err) + } + + config := NewDefaultConfig() + config.APIKey = values[EnvAPIKey] + config.APIUsername = values[EnvAPIUsername] + + return NewDNSProviderConfig(config) +} + +// NewDNSProviderConfig return a DNSProvider instance configured for BrandIT. +func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { + if config == nil { + return nil, errors.New("brandit: the configuration of the DNS provider is nil") + } + + client, err := internal.NewClient(config.APIUsername, config.APIKey) + if err != nil { + return nil, fmt.Errorf("brandit: %w", err) + } + + if config.HTTPClient != nil { + client.HTTPClient = config.HTTPClient + } + + return &DNSProvider{ + config: config, + client: client, + records: make(map[string]string), + }, nil +} + +// Present creates a TXT record using the specified parameters. +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("brandit: %w", err) + } + + subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone) + if err != nil { + return fmt.Errorf("brandit: %w", err) + } + + record := internal.Record{ + Type: "TXT", + Name: subDomain, + Content: info.Value, + TTL: d.config.TTL, + } + + // find the account associated with the domain + account, err := d.client.StatusDomain(dns01.UnFqdn(authZone)) + if err != nil { + return fmt.Errorf("brandit: status domain: %w", err) + } + + // Find the next record id + recordID, err := d.client.ListRecords(account.Response.Registrar[0], dns01.UnFqdn(authZone)) + if err != nil { + return fmt.Errorf("brandit: list records: %w", err) + } + + result, err := d.client.AddRecord(dns01.UnFqdn(authZone), account.Response.Registrar[0], fmt.Sprint(recordID.Response.Total[0]), record) + if err != nil { + return fmt.Errorf("brandit: add record: %w", err) + } + + d.recordsMu.Lock() + d.records[token] = result.Record + d.recordsMu.Unlock() + + return nil +} + +// CleanUp removes the TXT record matching the specified parameters. +func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error { + info := dns01.GetChallengeInfo(domain, keyAuth) + + authZone, err := dns01.FindZoneByFqdn(info.EffectiveFQDN) + if err != nil { + return fmt.Errorf("brandit: %w", err) + } + + // gets the record's unique ID + d.recordsMu.Lock() + dnsRecord, ok := d.records[token] + d.recordsMu.Unlock() + if !ok { + return fmt.Errorf("brandit: unknown record ID for '%s' '%s'", info.EffectiveFQDN, token) + } + + // find the account associated with the domain + account, err := d.client.StatusDomain(dns01.UnFqdn(authZone)) + if err != nil { + return fmt.Errorf("brandit: status domain: %w", err) + } + + records, err := d.client.ListRecords(account.Response.Registrar[0], dns01.UnFqdn(authZone)) + if err != nil { + return fmt.Errorf("brandit: list records: %w", err) + } + + var recordID int + for i, r := range records.Response.RR { + if r == dnsRecord { + recordID = i + } + } + + _, err = d.client.DeleteRecord(dns01.UnFqdn(authZone), account.Response.Registrar[0], dnsRecord, fmt.Sprint(recordID)) + if err != nil { + return fmt.Errorf("brandit: delete record: %w", err) + } + + // deletes record ID from map + d.recordsMu.Lock() + delete(d.records, token) + d.recordsMu.Unlock() + + return nil +} + +// Timeout returns the timeout and interval to use when checking for DNS propagation. +// Adjusting here to cope with spikes in propagation times. +func (d *DNSProvider) Timeout() (timeout, interval time.Duration) { + return d.config.PropagationTimeout, d.config.PollingInterval +} diff --git a/providers/dns/brandit/brandit.toml b/providers/dns/brandit/brandit.toml new file mode 100644 index 00000000..07346c2b --- /dev/null +++ b/providers/dns/brandit/brandit.toml @@ -0,0 +1,24 @@ +Name = "BRANDIT" +Description = '''''' +URL = "https://www.brandit.com/" +Code = "brandit" +Since = "v4.11.0" + +Example = ''' +BRANDIT_API_KEY=xxxxxxxxxxxxxxxxxxxxx \ +BRANDIT_API_USERNAME=yyyyyyyyyyyyyyyyyyyy \ +lego --email myemail@example.com --dns brandit --domains my.example.org run +''' + +[Configuration] + [Configuration.Credentials] + BRANDIT_API_KEY = "The API key" + BRANDIT_API_USERNAME = "The API username" + [Configuration.Additional] + BRANDIT_POLLING_INTERVAL = "Time between DNS propagation check" + BRANDIT_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation" + BRANDIT_TTL = "The TTL of the TXT record used for the DNS challenge" + BRANDIT_HTTP_TIMEOUT = "API request timeout" + +[Links] + API = "https://portal.brandit.com/apidocv3" diff --git a/providers/dns/brandit/brandit_test.go b/providers/dns/brandit/brandit_test.go new file mode 100644 index 00000000..156e7c3f --- /dev/null +++ b/providers/dns/brandit/brandit_test.go @@ -0,0 +1,141 @@ +package brandit + +import ( + "testing" + + "github.com/go-acme/lego/v4/platform/tester" + "github.com/stretchr/testify/require" +) + +const envDomain = envNamespace + "DOMAIN" + +var envTest = tester.NewEnvTest(EnvAPIKey, EnvAPIUsername).WithDomain(envDomain) + +func TestNewDNSProvider(t *testing.T) { + testCases := []struct { + desc string + envVars map[string]string + expected string + }{ + { + desc: "success", + envVars: map[string]string{ + EnvAPIKey: "key", + EnvAPIUsername: "test_user", + }, + }, + { + desc: "missing API key", + envVars: map[string]string{ + EnvAPIUsername: "test_user", + }, + expected: "brandit: some credentials information are missing: BRANDIT_API_KEY", + }, + { + desc: "missing secret", + envVars: map[string]string{ + EnvAPIKey: "key", + }, + expected: "brandit: some credentials information are missing: BRANDIT_API_USERNAME", + }, + { + desc: "missing credentials", + envVars: map[string]string{}, + expected: "brandit: some credentials information are missing: BRANDIT_API_KEY,BRANDIT_API_USERNAME", + }, + } + + 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 + apiKey string + user string + expected string + }{ + { + desc: "success", + apiKey: "key", + user: "test_user", + }, + { + desc: "missing API key", + user: "test_user", + expected: "brandit: credentials missing", + }, + { + desc: "missing secret", + apiKey: "key", + expected: "brandit: credentials missing", + }, + { + desc: "missing credentials", + expected: "brandit: credentials missing", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + config := NewDefaultConfig() + config.APIKey = test.apiKey + config.APIUsername = test.user + + p, err := NewDNSProviderConfig(config) + + if test.expected == "" { + require.NoError(t, err) + require.NotNil(t, p) + require.NotNil(t, p.config) + require.NotNil(t, p.client) + } else { + require.EqualError(t, err, test.expected) + } + }) + } +} + +func TestLivePresent(t *testing.T) { + if !envTest.IsLiveTest() { + t.Skip("skipping live test") + } + + envTest.RestoreEnv() + provider, err := NewDNSProvider() + require.NoError(t, err) + + err = provider.Present(envTest.GetDomain(), "", "123d==") + require.NoError(t, err) +} + +func TestLiveCleanUp(t *testing.T) { + if !envTest.IsLiveTest() { + t.Skip("skipping live test") + } + + envTest.RestoreEnv() + provider, err := NewDNSProvider() + require.NoError(t, err) + + err = provider.CleanUp(envTest.GetDomain(), "", "123d==") + require.NoError(t, err) +} diff --git a/providers/dns/brandit/internal/client.go b/providers/dns/brandit/internal/client.go new file mode 100644 index 00000000..d145315d --- /dev/null +++ b/providers/dns/brandit/internal/client.go @@ -0,0 +1,198 @@ +package internal + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" +) + +const defaultBaseURL = "https://portal.brandit.com/api/v3/" + +// Client a BrandIT DNS API client. +type Client struct { + apiUsername string + apiKey string + BaseURL string + HTTPClient *http.Client +} + +// NewClient creates a new Client. +func NewClient(apiUsername, apiKey string) (*Client, error) { + if apiKey == "" || apiUsername == "" { + return nil, errors.New("credentials missing") + } + + return &Client{ + apiUsername: apiUsername, + apiKey: apiKey, + BaseURL: defaultBaseURL, + HTTPClient: &http.Client{Timeout: 10 * time.Second}, + }, nil +} + +// ListRecords lists all records. +// https://portal.brandit.com/apidocv3#listDNSRR +func (c *Client) ListRecords(account, dnsZone string) (*ListRecords, error) { + // Create a new query + query := url.Values{} + query.Add("command", "listDNSRR") + query.Add("account", account) + query.Add("dnszone", dnsZone) + + result := &ListRecords{} + + err := c.do(query, result) + if err != nil { + return nil, fmt.Errorf("do: %w", err) + } + + for len(result.Response.RR) < result.Response.Total[0] { + query.Add("first", fmt.Sprint(result.Response.Last[0]+1)) + + tmp := &ListRecords{} + err := c.do(query, tmp) + if err != nil { + return nil, fmt.Errorf("do: %w", err) + } + + result.Response.RR = append(result.Response.RR, tmp.Response.RR...) + result.Response.Last = tmp.Response.Last + } + + return result, nil +} + +// AddRecord adds a DNS record. +// https://portal.brandit.com/apidocv3#addDNSRR +func (c *Client) AddRecord(domainName, account, newRecordID string, record Record) (*AddRecord, error) { + // Create a new query + + query := url.Values{} + query.Add("command", "addDNSRR") + query.Add("account", account) + query.Add("dnszone", domainName) + query.Add("rrdata", strings.Join([]string{record.Name, fmt.Sprint(record.TTL), "IN", record.Type, record.Content}, " ")) + query.Add("key", newRecordID) + + result := &AddRecord{} + + err := c.do(query, result) + if err != nil { + return nil, fmt.Errorf("do: %w", err) + } + result.Record = strings.Join([]string{record.Name, fmt.Sprint(record.TTL), "IN", record.Type, record.Content}, " ") + + return result, nil +} + +// DeleteRecord deletes a DNS record. +// https://portal.brandit.com/apidocv3#deleteDNSRR +func (c *Client) DeleteRecord(domainName, account, dnsRecord, recordID string) (*DeleteRecord, error) { + // Create a new query + query := url.Values{} + query.Add("command", "deleteDNSRR") + query.Add("account", account) + query.Add("dnszone", domainName) + query.Add("rrdata", dnsRecord) + query.Add("key", recordID) + + result := &DeleteRecord{} + + err := c.do(query, result) + if err != nil { + return nil, fmt.Errorf("do: %w", err) + } + + return result, nil +} + +// StatusDomain returns the status of a domain and account associated with it. +// https://portal.brandit.com/apidocv3#statusDomain +func (c *Client) StatusDomain(domain string) (*StatusDomain, error) { + // Create a new query + query := url.Values{} + + query.Add("command", "statusDomain") + query.Add("domain", domain) + + result := &StatusDomain{} + + err := c.do(query, result) + if err != nil { + return nil, fmt.Errorf("do: %w", err) + } + + return result, nil +} + +func (c *Client) do(query url.Values, result any) error { + // Add signature + v, err := sign(c.apiUsername, c.apiKey, query) + if err != nil { + return fmt.Errorf("signature: %w", err) + } + + resp, err := c.HTTPClient.PostForm(c.BaseURL, v) + if err != nil { + return err + } + + defer func() { _ = resp.Body.Close() }() + + raw, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("read response body: %w", err) + } + + // Unmarshal the error response, because the API returns a 200 OK even if there is an error. + var apiError APIError + err = json.Unmarshal(raw, &apiError) + if err != nil { + return fmt.Errorf("unmarshal error response: %w %s", err, string(raw)) + } + + if apiError.Code > 299 || apiError.Status != "success" { + return apiError + } + + err = json.Unmarshal(raw, result) + if err != nil { + return fmt.Errorf("unmarshal response body: %w %s", err, string(raw)) + } + + return nil +} + +func sign(apiUsername, apiKey string, query url.Values) (url.Values, error) { + location, err := time.LoadLocation("GMT") + if err != nil { + return nil, fmt.Errorf("time location: %w", err) + } + + timestamp := time.Now().In(location).Format("2006-01-02T15:04:05Z") + + canonicalRequest := fmt.Sprintf("%s%s%s", apiUsername, timestamp, defaultBaseURL) + + mac := hmac.New(sha256.New, []byte(apiKey)) + _, err = mac.Write([]byte(canonicalRequest)) + if err != nil { + return nil, err + } + + hashed := mac.Sum(nil) + signature := hex.EncodeToString(hashed) + + query.Add("user", apiUsername) + query.Add("timestamp", timestamp) + query.Add("signature", signature) + + return query, nil +} diff --git a/providers/dns/brandit/internal/client_test.go b/providers/dns/brandit/internal/client_test.go new file mode 100644 index 00000000..6e75294d --- /dev/null +++ b/providers/dns/brandit/internal/client_test.go @@ -0,0 +1,149 @@ +package internal + +import ( + "io" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func setupTest(t *testing.T, file string) *Client { + t.Helper() + + server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + open, err := os.Open(file) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + defer func() { _ = open.Close() }() + + rw.WriteHeader(http.StatusOK) + _, err = io.Copy(rw, open) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + })) + t.Cleanup(server.Close) + + client, err := NewClient("test_user", "apiKey") + require.NoError(t, err) + + client.HTTPClient = server.Client() + client.BaseURL = server.URL + + return client +} + +func TestClient_StatusDomain(t *testing.T) { + client := setupTest(t, "./fixtures/status-domain.json") + + domain, err := client.StatusDomain("example.com") + require.NoError(t, err) + + expected := &StatusDomain{ + Response: StatusResponse{ + RenewalMode: []string{"DEFAULT"}, + Status: []string{"clientTransferProhibited"}, + TransferLock: []int{1}, + Registrar: []string{"brandit"}, + PaidUntilDate: []string{"2021-12-15 05:00:00.0"}, + Nameserver: []string{"NS1.RRPPROXY.NET", "NS2.RRPPROXY.NET"}, + RegistrationExpirationDate: []string{"2021-12-15 05:00:00.0"}, + Domain: []string{"example.com"}, + RenewalDate: []string{"2024-01-19 05:00:00.0"}, + UpdatedDate: []string{"2022-12-16 08:01:27.0"}, + BillingContact: []string{"example"}, + XDomainRoID: []string{"example"}, + AdminContact: []string{"example"}, + TechContact: []string{"example"}, + DomainIDN: []string{"example.com"}, + CreatedDate: []string{"2016-12-16 05:00:00.0"}, + RegistrarTransferDate: []string{"2021-12-09 05:17:42.0"}, + Zone: []string{"com"}, + Auth: []string{"example"}, + UpdatedBy: []string{"example"}, + RoID: []string{"example"}, + OwnerContact: []string{"example"}, + CreatedBy: []string{"example"}, + TransferMode: []string{"auto"}, + }, + Code: 200, + Status: "success", + Error: "", + } + + assert.Equal(t, expected, domain) +} + +func TestClient_ListRecords(t *testing.T) { + client := setupTest(t, "./fixtures/list-records.json") + + resp, err := client.ListRecords("example", "example.com") + require.NoError(t, err) + + expected := &ListRecords{ + Response: ListRecordsResponse{ + Limit: []int{100}, + Column: []string{"rr"}, + Count: []int{1}, + First: []int{0}, + Total: []int{1}, + RR: []string{"example.com. 600 IN TXT txttxttxt"}, + Last: []int{0}, + }, + Code: 200, + Status: "success", + Error: "", + } + + assert.Equal(t, expected, resp) +} + +func TestClient_AddRecord(t *testing.T) { + client := setupTest(t, "./fixtures/add-record.json") + + testRecord := Record{ + ID: 2565, + Type: "TXT", + Name: "example.com", + Content: "txttxttxt", + TTL: 600, + } + resp, err := client.AddRecord("example.com", "test", "2565", testRecord) + require.NoError(t, err) + + expected := &AddRecord{ + Response: AddRecordResponse{ + ZoneType: []string{"com"}, + Signed: []int{1}, + }, + Record: "example.com 600 IN TXT txttxttxt", + Code: 200, + Status: "success", + Error: "", + } + + assert.Equal(t, expected, resp) +} + +func TestClient_DeleteRecord(t *testing.T) { + client := setupTest(t, "./fixtures/delete-record.json") + + resp, err := client.DeleteRecord("example.com", "test", "example.com 600 IN TXT txttxttxt", "2374") + require.NoError(t, err) + + expected := &DeleteRecord{ + Code: 200, + Status: "success", + Error: "", + } + + assert.Equal(t, expected, resp) +} diff --git a/providers/dns/brandit/internal/fixtures/add-record.json b/providers/dns/brandit/internal/fixtures/add-record.json new file mode 100644 index 00000000..8dbbbdd0 --- /dev/null +++ b/providers/dns/brandit/internal/fixtures/add-record.json @@ -0,0 +1,14 @@ +{ + "response": { + "zonetype": [ + "com" + ], + "signed": [ + 1 + ] + }, + "record": "example.com. 600 IN TXT txttxttxt", + "code": 200, + "status": "success", + "error": "" +} \ No newline at end of file diff --git a/providers/dns/brandit/internal/fixtures/delete-record.json b/providers/dns/brandit/internal/fixtures/delete-record.json new file mode 100644 index 00000000..7c203eda --- /dev/null +++ b/providers/dns/brandit/internal/fixtures/delete-record.json @@ -0,0 +1,5 @@ +{ + "code": 200, + "status": "success", + "error": "" +} \ No newline at end of file diff --git a/providers/dns/brandit/internal/fixtures/list-records.json b/providers/dns/brandit/internal/fixtures/list-records.json new file mode 100644 index 00000000..d2cf7ae6 --- /dev/null +++ b/providers/dns/brandit/internal/fixtures/list-records.json @@ -0,0 +1,28 @@ +{ + "response": { + "limit": [ + 100 + ], + "column": [ + "rr" + ], + "count": [ + 1 + ], + "first": [ + 0 + ], + "total": [ + 1 + ], + "rr": [ + "example.com. 600 IN TXT txttxttxt" + ], + "last": [ + 0 + ] + }, + "code": 200, + "status": "success", + "error": "" +} \ No newline at end of file diff --git a/providers/dns/brandit/internal/fixtures/status-domain.json b/providers/dns/brandit/internal/fixtures/status-domain.json new file mode 100644 index 00000000..fc89713a --- /dev/null +++ b/providers/dns/brandit/internal/fixtures/status-domain.json @@ -0,0 +1,80 @@ +{ + "response": { + "renewalmode": [ + "DEFAULT" + ], + "status": [ + "clientTransferProhibited" + ], + "transferlock": [ + 1 + ], + "registrar": [ + "brandit" + ], + "paiduntildate": [ + "2021-12-15 05:00:00.0" + ], + "nameserver": [ + "NS1.RRPPROXY.NET", + "NS2.RRPPROXY.NET" + ], + "registrationexpirationdate": [ + "2021-12-15 05:00:00.0" + ], + "domain": [ + "example.com" + ], + "renewaldate": [ + "2024-01-19 05:00:00.0" + ], + "updateddate": [ + "2022-12-16 08:01:27.0" + ], + "billingcontact": [ + "example" + ], + "x-domain-roid": [ + "example" + ], + "admincontact": [ + "example" + ], + "techcontact": [ + "example" + ], + "domainidn": [ + "example.com" + ], + "createddate": [ + "2016-12-16 05:00:00.0" + ], + "registrartransferdate": [ + "2021-12-09 05:17:42.0" + ], + "zone": [ + "com" + ], + "auth": [ + "example" + ], + "updatedby": [ + "example" + ], + "roid": [ + "example" + ], + "ownercontact": [ + "example" + ], + "createdby": [ + "example" + ], + "transfermode": [ + "auto" + ] + }, + "code": 200, + "status": "success", + "error": "" +} \ No newline at end of file diff --git a/providers/dns/brandit/internal/types.go b/providers/dns/brandit/internal/types.go new file mode 100644 index 00000000..099c5fe1 --- /dev/null +++ b/providers/dns/brandit/internal/types.go @@ -0,0 +1,91 @@ +package internal + +import "fmt" + +type StatusDomain struct { + Response StatusResponse `json:"response,omitempty"` + Code int `json:"code"` + Status string `json:"status"` + Error string `json:"error"` +} + +type StatusResponse struct { + RenewalMode []string `json:"renewalmode"` + Status []string `json:"status"` + TransferLock []int `json:"transferlock"` + Registrar []string `json:"registrar"` + PaidUntilDate []string `json:"paiduntildate"` + Nameserver []string `json:"nameserver"` + RegistrationExpirationDate []string `json:"registrationexpirationdate"` + Domain []string `json:"domain"` + RenewalDate []string `json:"renewaldate"` + UpdatedDate []string `json:"updateddate"` + BillingContact []string `json:"billingcontact"` + XDomainRoID []string `json:"x-domain-roid"` + AdminContact []string `json:"admincontact"` + TechContact []string `json:"techcontact"` + DomainIDN []string `json:"domainidn"` + CreatedDate []string `json:"createddate"` + RegistrarTransferDate []string `json:"registrartransferdate"` + Zone []string `json:"zone"` + Auth []string `json:"auth"` + UpdatedBy []string `json:"updatedby"` + RoID []string `json:"roid"` + OwnerContact []string `json:"ownercontact"` + CreatedBy []string `json:"createdby"` + TransferMode []string `json:"transfermode"` +} + +type ListRecords struct { + Response ListRecordsResponse `json:"response,omitempty"` + Code int `json:"code"` + Status string `json:"status"` + Error string `json:"error"` +} + +type ListRecordsResponse struct { + Limit []int `json:"limit,omitempty"` + Column []string `json:"column,omitempty"` + Count []int `json:"count,omitempty"` + First []int `json:"first,omitempty"` + Total []int `json:"total,omitempty"` + RR []string `json:"rr,omitempty"` + Last []int `json:"last,omitempty"` +} + +type APIError struct { + Code int `json:"code"` + Status string `json:"status"` + Message string `json:"error"` +} + +func (a APIError) Error() string { + return fmt.Sprintf("code: %d, status: %s, message: %s", a.Code, a.Status, a.Message) +} + +type AddRecord struct { + Response AddRecordResponse `json:"response"` + Record string `json:"record"` + Code int `json:"code"` + Status string `json:"status"` + Error string `json:"error"` +} + +type AddRecordResponse struct { + ZoneType []string `json:"zonetype"` + Signed []int `json:"signed"` +} + +type Record struct { + ID int `json:"id,omitempty"` + Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` // subdomain name or @ if you don't want subdomain + Content string `json:"content,omitempty"` + TTL int `json:"ttl,omitempty"` // default 600 +} + +type DeleteRecord struct { + Code int `json:"code"` + Status string `json:"status"` + Error string `json:"error"` +} diff --git a/providers/dns/dns_providers.go b/providers/dns/dns_providers.go index d27da5d3..685b6f80 100644 --- a/providers/dns/dns_providers.go +++ b/providers/dns/dns_providers.go @@ -14,6 +14,7 @@ import ( "github.com/go-acme/lego/v4/providers/dns/azure" "github.com/go-acme/lego/v4/providers/dns/bindman" "github.com/go-acme/lego/v4/providers/dns/bluecat" + "github.com/go-acme/lego/v4/providers/dns/brandit" "github.com/go-acme/lego/v4/providers/dns/bunny" "github.com/go-acme/lego/v4/providers/dns/checkdomain" "github.com/go-acme/lego/v4/providers/dns/civo" @@ -143,6 +144,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) { return bindman.NewDNSProvider() case "bluecat": return bluecat.NewDNSProvider() + case "brandit": + return brandit.NewDNSProvider() case "bunny": return bunny.NewDNSProvider() case "checkdomain":