mirror of
https://github.com/go-acme/lego.git
synced 2024-12-26 03:09:37 +02:00
Add Brandit.com as DNS provider (#1890)
This commit is contained in:
parent
3b70a85cf7
commit
9db046581b
55
README.md
55
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/) |
|
| [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/) |
|
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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](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/) |
|
| [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 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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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.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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) |
|
||||||
| [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/) |
|
| [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/) | | | |
|
||||||
|
|
||||||
<!-- END DNS PROVIDERS LIST -->
|
<!-- END DNS PROVIDERS LIST -->
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ func allDNSCodes() string {
|
|||||||
"azure",
|
"azure",
|
||||||
"bindman",
|
"bindman",
|
||||||
"bluecat",
|
"bluecat",
|
||||||
|
"brandit",
|
||||||
"bunny",
|
"bunny",
|
||||||
"checkdomain",
|
"checkdomain",
|
||||||
"civo",
|
"civo",
|
||||||
@ -332,6 +333,27 @@ func displayDNSHelp(w io.Writer, name string) error {
|
|||||||
ew.writeln()
|
ew.writeln()
|
||||||
ew.writeln(`More information: https://go-acme.github.io/lego/dns/bluecat`)
|
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":
|
case "bunny":
|
||||||
// generated from: providers/dns/bunny/bunny.toml
|
// generated from: providers/dns/bunny/bunny.toml
|
||||||
ew.writeln(`Configuration for Bunny.`)
|
ew.writeln(`Configuration for Bunny.`)
|
||||||
|
69
docs/content/dns/zz_gen_brandit.md
Normal file
69
docs/content/dns/zz_gen_brandit.md
Normal file
@ -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/"
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||||
|
<!-- providers/dns/brandit/brandit.toml -->
|
||||||
|
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||||
|
|
||||||
|
|
||||||
|
Configuration for [BRANDIT](https://www.brandit.com/).
|
||||||
|
|
||||||
|
|
||||||
|
<!--more-->
|
||||||
|
|
||||||
|
- 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)
|
||||||
|
|
||||||
|
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
||||||
|
<!-- providers/dns/brandit/brandit.toml -->
|
||||||
|
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
|
@ -125,7 +125,7 @@ To display the documentation for a specific DNS provider, run:
|
|||||||
$ lego dnshelp -c code
|
$ lego dnshelp -c code
|
||||||
|
|
||||||
Supported DNS providers:
|
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
|
More information: https://go-acme.github.io/lego/dns
|
||||||
"""
|
"""
|
||||||
|
197
providers/dns/brandit/brandit.go
Normal file
197
providers/dns/brandit/brandit.go
Normal file
@ -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
|
||||||
|
}
|
24
providers/dns/brandit/brandit.toml
Normal file
24
providers/dns/brandit/brandit.toml
Normal file
@ -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"
|
141
providers/dns/brandit/brandit_test.go
Normal file
141
providers/dns/brandit/brandit_test.go
Normal file
@ -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)
|
||||||
|
}
|
198
providers/dns/brandit/internal/client.go
Normal file
198
providers/dns/brandit/internal/client.go
Normal file
@ -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
|
||||||
|
}
|
149
providers/dns/brandit/internal/client_test.go
Normal file
149
providers/dns/brandit/internal/client_test.go
Normal file
@ -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)
|
||||||
|
}
|
14
providers/dns/brandit/internal/fixtures/add-record.json
Normal file
14
providers/dns/brandit/internal/fixtures/add-record.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"response": {
|
||||||
|
"zonetype": [
|
||||||
|
"com"
|
||||||
|
],
|
||||||
|
"signed": [
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"record": "example.com. 600 IN TXT txttxttxt",
|
||||||
|
"code": 200,
|
||||||
|
"status": "success",
|
||||||
|
"error": ""
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"status": "success",
|
||||||
|
"error": ""
|
||||||
|
}
|
28
providers/dns/brandit/internal/fixtures/list-records.json
Normal file
28
providers/dns/brandit/internal/fixtures/list-records.json
Normal file
@ -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": ""
|
||||||
|
}
|
80
providers/dns/brandit/internal/fixtures/status-domain.json
Normal file
80
providers/dns/brandit/internal/fixtures/status-domain.json
Normal file
@ -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": ""
|
||||||
|
}
|
91
providers/dns/brandit/internal/types.go
Normal file
91
providers/dns/brandit/internal/types.go
Normal file
@ -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"`
|
||||||
|
}
|
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/go-acme/lego/v4/providers/dns/azure"
|
"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/bindman"
|
||||||
"github.com/go-acme/lego/v4/providers/dns/bluecat"
|
"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/bunny"
|
||||||
"github.com/go-acme/lego/v4/providers/dns/checkdomain"
|
"github.com/go-acme/lego/v4/providers/dns/checkdomain"
|
||||||
"github.com/go-acme/lego/v4/providers/dns/civo"
|
"github.com/go-acme/lego/v4/providers/dns/civo"
|
||||||
@ -143,6 +144,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
|
|||||||
return bindman.NewDNSProvider()
|
return bindman.NewDNSProvider()
|
||||||
case "bluecat":
|
case "bluecat":
|
||||||
return bluecat.NewDNSProvider()
|
return bluecat.NewDNSProvider()
|
||||||
|
case "brandit":
|
||||||
|
return brandit.NewDNSProvider()
|
||||||
case "bunny":
|
case "bunny":
|
||||||
return bunny.NewDNSProvider()
|
return bunny.NewDNSProvider()
|
||||||
case "checkdomain":
|
case "checkdomain":
|
||||||
|
Loading…
Reference in New Issue
Block a user