Skip to content

Commit c759f56

Browse files
jwklijnsmaldez
andauthored
Add DNS provider for DirectAdmin (#2225)
Co-authored-by: Fernandez Ludovic <[email protected]>
1 parent 9873b9b commit c759f56

File tree

11 files changed

+750
-27
lines changed

11 files changed

+750
-27
lines changed

README.md

Lines changed: 26 additions & 26 deletions
Large diffs are not rendered by default.

cmd/zz_gen_cmd_dnshelp.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func allDNSCodes() string {
4040
"desec",
4141
"designate",
4242
"digitalocean",
43+
"directadmin",
4344
"dnshomede",
4445
"dnsimple",
4546
"dnsmadeeasy",
@@ -733,6 +734,29 @@ func displayDNSHelp(w io.Writer, name string) error {
733734
ew.writeln()
734735
ew.writeln(`More information: https://go-acme.github.io/lego/dns/digitalocean`)
735736

737+
case "directadmin":
738+
// generated from: providers/dns/directadmin/directadmin.toml
739+
ew.writeln(`Configuration for directadmin.`)
740+
ew.writeln(`Code: 'directadmin'`)
741+
ew.writeln(`Since: ''`)
742+
ew.writeln()
743+
744+
ew.writeln(`Credentials:`)
745+
ew.writeln(` - "DIRECTADMIN_API_URL": URL of the API`)
746+
ew.writeln(` - "DIRECTADMIN_PASSWORD": API password`)
747+
ew.writeln(` - "DIRECTADMIN_USERNAME": API username`)
748+
ew.writeln()
749+
750+
ew.writeln(`Additional Configuration:`)
751+
ew.writeln(` - "DIRECTADMIN_HTTP_TIMEOUT": API request timeout`)
752+
ew.writeln(` - "DIRECTADMIN_POLLING_INTERVAL": Time between DNS propagation check`)
753+
ew.writeln(` - "DIRECTADMIN_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
754+
ew.writeln(` - "DIRECTADMIN_TTL": The TTL of the TXT record used for the DNS challenge`)
755+
ew.writeln(` - "DIRECTADMIN_ZONE_NAME": API password`)
756+
757+
ew.writeln()
758+
ew.writeln(`More information: https://go-acme.github.io/lego/dns/directadmin`)
759+
736760
case "dnshomede":
737761
// generated from: providers/dns/dnshomede/dnshomede.toml
738762
ew.writeln(`Configuration for dnsHome.de.`)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
title: "directadmin"
3+
date: 2019-03-03T16:39:46+01:00
4+
draft: false
5+
slug: directadmin
6+
dnsprovider:
7+
since: ""
8+
code: "directadmin"
9+
url: "directadmin"
10+
---
11+
12+
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
13+
<!-- providers/dns/directadmin/directadmin.toml -->
14+
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
15+
16+
directadmin api
17+
18+
19+
<!--more-->
20+
21+
- Code: `directadmin`
22+
- Since:
23+
24+
25+
Here is an example bash command using the directadmin provider:
26+
27+
```bash
28+
DIRECTADMIN_API_URL="http://example.com:2222" \
29+
DIRECTADMIN_USERNAME=xxxx \
30+
DIRECTADMIN_PASSWORD=yyy \
31+
lego --email [email protected] --dns directadmin -d "my.example.org" -d "*.example.org" run
32+
```
33+
34+
35+
36+
37+
## Credentials
38+
39+
| Environment Variable Name | Description |
40+
|-----------------------|-------------|
41+
| `DIRECTADMIN_API_URL` | URL of the API |
42+
| `DIRECTADMIN_PASSWORD` | API password |
43+
| `DIRECTADMIN_USERNAME` | API username |
44+
45+
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
46+
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
47+
48+
49+
## Additional Configuration
50+
51+
| Environment Variable Name | Description |
52+
|--------------------------------|-------------|
53+
| `DIRECTADMIN_HTTP_TIMEOUT` | API request timeout |
54+
| `DIRECTADMIN_POLLING_INTERVAL` | Time between DNS propagation check |
55+
| `DIRECTADMIN_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
56+
| `DIRECTADMIN_TTL` | The TTL of the TXT record used for the DNS challenge |
57+
| `DIRECTADMIN_ZONE_NAME` | API password |
58+
59+
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
60+
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
61+
62+
63+
64+
65+
## More information
66+
67+
- [API documentation](https://www.directadmin.com/api.php)
68+
69+
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
70+
<!-- providers/dns/directadmin/directadmin.toml -->
71+
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->

docs/data/zz_cli_help.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ To display the documentation for a specific DNS provider, run:
138138
$ lego dnshelp -c code
139139
140140
Supported DNS providers:
141-
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, cpanel, derak, desec, designate, digitalocean, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, mailinabox, manual, metaname, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rcodezero, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, servercow, shellrent, simply, sonic, stackpath, tencentcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, vscale, vultr, webnames, websupport, wedos, yandex, yandex360, yandexcloud, zoneee, zonomi
141+
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, cpanel, derak, desec, designate, digitalocean, directadmin, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, mailinabox, manual, metaname, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rcodezero, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, selectelv2, servercow, shellrent, simply, sonic, stackpath, tencentcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, vscale, vultr, webnames, websupport, wedos, yandex, yandex360, yandexcloud, zoneee, zonomi
142142
143143
More information: https://go-acme.github.io/lego/dns
144144
"""
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package directadmin
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"net/http"
8+
"time"
9+
10+
"github.com/go-acme/lego/v4/challenge/dns01"
11+
"github.com/go-acme/lego/v4/platform/config/env"
12+
"github.com/go-acme/lego/v4/providers/dns/directadmin/internal"
13+
)
14+
15+
// Environment variables names.
16+
const (
17+
envNamespace = "DIRECTADMIN_"
18+
19+
EnvAPIURL = envNamespace + "API_URL"
20+
EnvUsername = envNamespace + "USERNAME"
21+
EnvPassword = envNamespace + "PASSWORD"
22+
EnvZoneName = envNamespace + "ZONE_NAME"
23+
24+
EnvTTL = envNamespace + "TTL"
25+
EnvPropagationTimeout = envNamespace + "PROPAGATION_TIMEOUT"
26+
EnvPollingInterval = envNamespace + "POLLING_INTERVAL"
27+
EnvHTTPTimeout = envNamespace + "HTTP_TIMEOUT"
28+
)
29+
30+
// Config is used to configure the creation of the DNSProvider.
31+
type Config struct {
32+
BaseURL string
33+
Username string
34+
Password string
35+
TTL int
36+
PropagationTimeout time.Duration
37+
PollingInterval time.Duration
38+
HTTPClient *http.Client
39+
}
40+
41+
// NewDefaultConfig returns a default configuration for the DNSProvider.
42+
func NewDefaultConfig() *Config {
43+
return &Config{
44+
TTL: env.GetOrDefaultInt(EnvTTL, 30),
45+
PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 60*time.Second),
46+
PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, 5*time.Second),
47+
HTTPClient: &http.Client{
48+
Timeout: env.GetOrDefaultSecond(EnvHTTPTimeout, 30*time.Second),
49+
},
50+
}
51+
}
52+
53+
// DNSProvider implements the challenge.Provider interface.
54+
type DNSProvider struct {
55+
client *internal.Client
56+
config *Config
57+
}
58+
59+
// NewDNSProvider returns a DNSProvider instance configured for DirectAdmin.
60+
// Credentials must be passed in the environment variables:
61+
// DIRECTADMIN_API_URL, DIRECTADMIN_USERNAME, DIRECTADMIN_PASSWORD.
62+
func NewDNSProvider() (*DNSProvider, error) {
63+
values, err := env.Get(EnvAPIURL, EnvUsername, EnvPassword)
64+
if err != nil {
65+
return nil, fmt.Errorf("directadmin: %w", err)
66+
}
67+
68+
config := NewDefaultConfig()
69+
config.BaseURL = values[EnvAPIURL]
70+
config.Username = values[EnvUsername]
71+
config.Password = values[EnvPassword]
72+
73+
return NewDNSProviderConfig(config)
74+
}
75+
76+
// NewDNSProviderConfig return a DNSProvider instance configured for DirectAdmin.
77+
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
78+
if config.BaseURL == "" {
79+
return nil, errors.New("directadmin: missing API URL")
80+
}
81+
82+
if config.Username == "" || config.Password == "" {
83+
return nil, errors.New("directadmin: some credentials information are missing")
84+
}
85+
86+
client, err := internal.NewClient(config.BaseURL, config.Username, config.Password)
87+
if err != nil {
88+
return nil, fmt.Errorf("directadmin: %w", err)
89+
}
90+
91+
return &DNSProvider{client: client, config: config}, nil
92+
}
93+
94+
// Present creates a TXT record using the specified parameters.
95+
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
96+
info := dns01.GetChallengeInfo(domain, keyAuth)
97+
98+
authZone, err := getAuthZone(info.EffectiveFQDN)
99+
if err != nil {
100+
return fmt.Errorf("directadmin: [domain: %q] %w", domain, err)
101+
}
102+
103+
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
104+
if err != nil {
105+
return fmt.Errorf("directadmin: %w", err)
106+
}
107+
108+
record := internal.Record{
109+
Name: subDomain,
110+
Type: "TXT",
111+
Value: info.Value,
112+
TTL: d.config.TTL,
113+
}
114+
115+
err = d.client.SetRecord(context.Background(), dns01.UnFqdn(authZone), record)
116+
if err != nil {
117+
return fmt.Errorf("directadmin: set record for zone %s and subdomain %s: %w", authZone, subDomain, err)
118+
}
119+
120+
return nil
121+
}
122+
123+
// CleanUp removes the TXT record matching the specified parameters.
124+
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
125+
info := dns01.GetChallengeInfo(domain, keyAuth)
126+
127+
authZone, err := getAuthZone(info.EffectiveFQDN)
128+
if err != nil {
129+
return fmt.Errorf("directadmin: [domain: %q] %w", domain, err)
130+
}
131+
132+
subDomain, err := dns01.ExtractSubDomain(info.EffectiveFQDN, authZone)
133+
if err != nil {
134+
return fmt.Errorf("directadmin: %w", err)
135+
}
136+
137+
record := internal.Record{
138+
Name: subDomain,
139+
Type: "TXT",
140+
Value: info.Value,
141+
}
142+
143+
err = d.client.DeleteRecord(context.Background(), dns01.UnFqdn(authZone), record)
144+
if err != nil {
145+
return fmt.Errorf("directadmin: delete record for zone %s and subdomain %s: %w", authZone, subDomain, err)
146+
}
147+
148+
return nil
149+
}
150+
151+
func getAuthZone(fqdn string) (string, error) {
152+
authZone := env.GetOrFile(EnvZoneName)
153+
if authZone != "" {
154+
return authZone, nil
155+
}
156+
157+
authZone, err := dns01.FindZoneByFqdn(fqdn)
158+
if err != nil {
159+
return "", fmt.Errorf("could not find zone for %s: %w", fqdn, err)
160+
}
161+
162+
return authZone, nil
163+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Name = "directadmin"
2+
Description = '''directadmin api'''
3+
URL = "directadmin"
4+
Code = "directadmin"
5+
6+
7+
Example = '''
8+
DIRECTADMIN_API_URL="http://example.com:2222" \
9+
DIRECTADMIN_USERNAME=xxxx \
10+
DIRECTADMIN_PASSWORD=yyy \
11+
lego --email [email protected] --dns directadmin -d "my.example.org" -d "*.example.org" run
12+
'''
13+
14+
[Configuration]
15+
[Configuration.Credentials]
16+
DIRECTADMIN_API_URL = "URL of the API"
17+
DIRECTADMIN_USERNAME = "API username"
18+
DIRECTADMIN_PASSWORD = "API password"
19+
[Configuration.Additional]
20+
DIRECTADMIN_ZONE_NAME = "API password"
21+
DIRECTADMIN_POLLING_INTERVAL = "Time between DNS propagation check"
22+
DIRECTADMIN_PROPAGATION_TIMEOUT = "Maximum waiting time for DNS propagation"
23+
DIRECTADMIN_TTL = "The TTL of the TXT record used for the DNS challenge"
24+
DIRECTADMIN_HTTP_TIMEOUT = "API request timeout"
25+
26+
[Links]
27+
API = "https://www.directadmin.com/api.php"

0 commit comments

Comments
 (0)