Skip to content

Commit 008adfd

Browse files
authored
Add DNS provider for Shellrent (#2126)
1 parent 6dd8d03 commit 008adfd

File tree

17 files changed

+1071
-8
lines changed

17 files changed

+1071
-8
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,13 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns).
7979
| [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/) |
8080
| [PowerDNS](https://go-acme.github.io/lego/dns/pdns/) | [Rackspace](https://go-acme.github.io/lego/dns/rackspace/) | [RcodeZero](https://go-acme.github.io/lego/dns/rcodezero/) | [reg.ru](https://go-acme.github.io/lego/dns/regru/) |
8181
| [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/) |
82-
| [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/) |
83-
| [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/) |
84-
| [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/) |
85-
| [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/) |
86-
| [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [Webnames](https://go-acme.github.io/lego/dns/webnames/) | [Websupport](https://go-acme.github.io/lego/dns/websupport/) | [WEDOS](https://go-acme.github.io/lego/dns/wedos/) |
87-
| [Yandex 360](https://go-acme.github.io/lego/dns/yandex360/) | [Yandex Cloud](https://go-acme.github.io/lego/dns/yandexcloud/) | [Yandex PDD](https://go-acme.github.io/lego/dns/yandex/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) |
88-
| [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) | | | |
82+
| [Selectel](https://go-acme.github.io/lego/dns/selectel/) | [Servercow](https://go-acme.github.io/lego/dns/servercow/) | [Shellrent](https://go-acme.github.io/lego/dns/shellrent/) | [Simply.com](https://go-acme.github.io/lego/dns/simply/) |
83+
| [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/) |
84+
| [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/) |
85+
| [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/) |
86+
| [Vscale](https://go-acme.github.io/lego/dns/vscale/) | [Vultr](https://go-acme.github.io/lego/dns/vultr/) | [Webnames](https://go-acme.github.io/lego/dns/webnames/) | [Websupport](https://go-acme.github.io/lego/dns/websupport/) |
87+
| [WEDOS](https://go-acme.github.io/lego/dns/wedos/) | [Yandex 360](https://go-acme.github.io/lego/dns/yandex360/) | [Yandex Cloud](https://go-acme.github.io/lego/dns/yandexcloud/) | [Yandex PDD](https://go-acme.github.io/lego/dns/yandex/) |
88+
| [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) | [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) | | |
8989

9090
<!-- END DNS PROVIDERS LIST -->
9191

cmd/zz_gen_cmd_dnshelp.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ func allDNSCodes() string {
120120
"scaleway",
121121
"selectel",
122122
"servercow",
123+
"shellrent",
123124
"simply",
124125
"sonic",
125126
"stackpath",
@@ -2416,6 +2417,27 @@ func displayDNSHelp(w io.Writer, name string) error {
24162417
ew.writeln()
24172418
ew.writeln(`More information: https://go-acme.github.io/lego/dns/servercow`)
24182419

2420+
case "shellrent":
2421+
// generated from: providers/dns/shellrent/shellrent.toml
2422+
ew.writeln(`Configuration for Shellrent.`)
2423+
ew.writeln(`Code: 'shellrent'`)
2424+
ew.writeln(`Since: 'v4.16.0'`)
2425+
ew.writeln()
2426+
2427+
ew.writeln(`Credentials:`)
2428+
ew.writeln(` - "SHELLRENT_TOKEN": Token`)
2429+
ew.writeln(` - "SHELLRENT_USERNAME": Username`)
2430+
ew.writeln()
2431+
2432+
ew.writeln(`Additional Configuration:`)
2433+
ew.writeln(` - "SHELLRENT_HTTP_TIMEOUT": API request timeout`)
2434+
ew.writeln(` - "SHELLRENT_POLLING_INTERVAL": Time between DNS propagation check`)
2435+
ew.writeln(` - "SHELLRENT_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
2436+
ew.writeln(` - "SHELLRENT_TTL": The TTL of the TXT record used for the DNS challenge`)
2437+
2438+
ew.writeln()
2439+
ew.writeln(`More information: https://go-acme.github.io/lego/dns/shellrent`)
2440+
24192441
case "simply":
24202442
// generated from: providers/dns/simply/simply.toml
24212443
ew.writeln(`Configuration for Simply.com.`)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
title: "Shellrent"
3+
date: 2019-03-03T16:39:46+01:00
4+
draft: false
5+
slug: shellrent
6+
dnsprovider:
7+
since: "v4.16.0"
8+
code: "shellrent"
9+
url: "https://www.shellrent.com/"
10+
---
11+
12+
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
13+
<!-- providers/dns/shellrent/shellrent.toml -->
14+
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
15+
16+
17+
Configuration for [Shellrent](https://www.shellrent.com/).
18+
19+
20+
<!--more-->
21+
22+
- Code: `shellrent`
23+
- Since: v4.16.0
24+
25+
26+
Here is an example bash command using the Shellrent provider:
27+
28+
```bash
29+
SHELLRENT_USERNAME=xxxx \
30+
SHELLRENT_TOKEN=yyyy \
31+
lego --email [email protected] --dns shellrent --domains my.example.org run
32+
```
33+
34+
35+
36+
37+
## Credentials
38+
39+
| Environment Variable Name | Description |
40+
|-----------------------|-------------|
41+
| `SHELLRENT_TOKEN` | Token |
42+
| `SHELLRENT_USERNAME` | Username |
43+
44+
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
45+
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
46+
47+
48+
## Additional Configuration
49+
50+
| Environment Variable Name | Description |
51+
|--------------------------------|-------------|
52+
| `SHELLRENT_HTTP_TIMEOUT` | API request timeout |
53+
| `SHELLRENT_POLLING_INTERVAL` | Time between DNS propagation check |
54+
| `SHELLRENT_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
55+
| `SHELLRENT_TTL` | The TTL of the TXT record used for the DNS challenge |
56+
57+
The environment variable names can be suffixed by `_FILE` to reference a file instead of a value.
58+
More information [here]({{< ref "dns#configuration-and-credentials" >}}).
59+
60+
61+
62+
63+
## More information
64+
65+
- [API documentation](https://api.shellrent.com/section/api2)
66+
67+
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
68+
<!-- providers/dns/shellrent/shellrent.toml -->
69+
<!-- 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
@@ -137,7 +137,7 @@ To display the documentation for a specific DNS provider, run:
137137
$ lego dnshelp -c code
138138
139139
Supported DNS providers:
140-
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, cpanel, derak, desec, designate, digitalocean, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, mailinabox, manual, metaname, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rcodezero, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, servercow, simply, sonic, stackpath, tencentcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, vscale, vultr, webnames, websupport, wedos, yandex, yandex360, yandexcloud, zoneee, zonomi
140+
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, cpanel, derak, desec, designate, digitalocean, dnshomede, dnsimple, dnsmadeeasy, dnspod, dode, domeneshop, dreamhost, duckdns, dyn, dynu, easydns, edgedns, efficientip, epik, exec, exoscale, freemyip, gandi, gandiv5, gcloud, gcore, glesys, godaddy, googledomains, hetzner, hostingde, hosttech, httpnet, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, mailinabox, manual, metaname, mydnsjp, mythicbeasts, namecheap, namedotcom, namesilo, nearlyfreespeech, netcup, netlify, nicmanager, nifcloud, njalla, nodion, ns1, oraclecloud, otc, ovh, pdns, plesk, porkbun, rackspace, rcodezero, regru, rfc2136, rimuhosting, route53, safedns, sakuracloud, scaleway, selectel, servercow, shellrent, simply, sonic, stackpath, tencentcloud, transip, ultradns, variomedia, vegadns, vercel, versio, vinyldns, vkcloud, vscale, vultr, webnames, websupport, wedos, yandex, yandex360, yandexcloud, zoneee, zonomi
141141
142142
More information: https://go-acme.github.io/lego/dns
143143
"""

providers/dns/dns_providers.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ import (
110110
"github.com/go-acme/lego/v4/providers/dns/scaleway"
111111
"github.com/go-acme/lego/v4/providers/dns/selectel"
112112
"github.com/go-acme/lego/v4/providers/dns/servercow"
113+
"github.com/go-acme/lego/v4/providers/dns/shellrent"
113114
"github.com/go-acme/lego/v4/providers/dns/simply"
114115
"github.com/go-acme/lego/v4/providers/dns/sonic"
115116
"github.com/go-acme/lego/v4/providers/dns/stackpath"
@@ -349,6 +350,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
349350
return selectel.NewDNSProvider()
350351
case "servercow":
351352
return servercow.NewDNSProvider()
353+
case "shellrent":
354+
return shellrent.NewDNSProvider()
352355
case "simply":
353356
return simply.NewDNSProvider()
354357
case "sonic":
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
package internal
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"io"
9+
"net/http"
10+
"net/url"
11+
"strconv"
12+
"time"
13+
14+
"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
15+
)
16+
17+
// DefaultBaseURL the default API endpoint.
18+
const defaultBaseURL = "https://manager.shellrent.com/api2"
19+
20+
const authorizationHeader = "Authorization"
21+
22+
// Client the Shellrent API client.
23+
type Client struct {
24+
username string
25+
token string
26+
27+
baseURL *url.URL
28+
HTTPClient *http.Client
29+
}
30+
31+
// NewClient Creates a new Client.
32+
func NewClient(username string, token string) *Client {
33+
baseURL, _ := url.Parse(defaultBaseURL)
34+
35+
return &Client{
36+
token: token,
37+
username: username,
38+
baseURL: baseURL,
39+
HTTPClient: &http.Client{Timeout: 10 * time.Second},
40+
}
41+
}
42+
43+
// ListServices lists service IDs.
44+
// https://api.shellrent.com/elenco-dei-servizi-acquistati
45+
func (c Client) ListServices(ctx context.Context) ([]int, error) {
46+
endpoint := c.baseURL.JoinPath("purchase")
47+
48+
req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
49+
if err != nil {
50+
return nil, err
51+
}
52+
53+
result := Response[[]IntOrString]{}
54+
55+
err = c.do(req, &result)
56+
if err != nil {
57+
return nil, err
58+
}
59+
60+
if result.Code != 0 {
61+
return nil, result.Base
62+
}
63+
64+
var ids []int
65+
66+
for _, datum := range result.Data {
67+
ids = append(ids, datum.Value())
68+
}
69+
70+
return ids, nil
71+
}
72+
73+
// GetServiceDetails gets service details.
74+
// https://api.shellrent.com/dettagli-servizio-acquistato
75+
func (c Client) GetServiceDetails(ctx context.Context, serviceID int) (*ServiceDetails, error) {
76+
endpoint := c.baseURL.JoinPath("purchase", "details", strconv.Itoa(serviceID))
77+
78+
req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
79+
if err != nil {
80+
return nil, err
81+
}
82+
83+
result := Response[*ServiceDetails]{}
84+
85+
err = c.do(req, &result)
86+
if err != nil {
87+
return nil, err
88+
}
89+
90+
if result.Code != 0 {
91+
return nil, result.Base
92+
}
93+
94+
return result.Data, nil
95+
}
96+
97+
// GetDomainDetails gets domain details.
98+
// https://api.shellrent.com/dettagli-dominio
99+
func (c Client) GetDomainDetails(ctx context.Context, domainID int) (*DomainDetails, error) {
100+
endpoint := c.baseURL.JoinPath("domain", "details", strconv.Itoa(domainID))
101+
102+
req, err := newJSONRequest(ctx, http.MethodGet, endpoint, nil)
103+
if err != nil {
104+
return nil, err
105+
}
106+
107+
result := Response[*DomainDetails]{}
108+
109+
err = c.do(req, &result)
110+
if err != nil {
111+
return nil, err
112+
}
113+
114+
if result.Code != 0 {
115+
return nil, result.Base
116+
}
117+
return result.Data, nil
118+
}
119+
120+
// CreateRecord created a record.
121+
// https://api.shellrent.com/creazione-record-dns-di-un-dominio
122+
func (c Client) CreateRecord(ctx context.Context, domainID int, record Record) (int, error) {
123+
endpoint := c.baseURL.JoinPath("dns_record", "store", strconv.Itoa(domainID))
124+
125+
req, err := newJSONRequest(ctx, http.MethodPost, endpoint, record)
126+
if err != nil {
127+
return 0, err
128+
}
129+
130+
result := Response[*Record]{}
131+
132+
err = c.do(req, &result)
133+
if err != nil {
134+
return 0, err
135+
}
136+
137+
if result.Code != 0 {
138+
return 0, result.Base
139+
}
140+
return result.Data.ID.Value(), nil
141+
}
142+
143+
// DeleteRecord deletes a record.
144+
// https://api.shellrent.com/eliminazione-record-dns-di-un-dominio
145+
func (c Client) DeleteRecord(ctx context.Context, domainID int, recordID int) error {
146+
endpoint := c.baseURL.JoinPath("dns_record", "remove", strconv.Itoa(domainID), strconv.Itoa(recordID))
147+
148+
req, err := newJSONRequest(ctx, http.MethodDelete, endpoint, nil)
149+
if err != nil {
150+
return err
151+
}
152+
153+
result := Response[any]{}
154+
155+
err = c.do(req, &result)
156+
if err != nil {
157+
return err
158+
}
159+
160+
if result.Code != 0 {
161+
return result.Base
162+
}
163+
164+
return nil
165+
}
166+
167+
func (c Client) do(req *http.Request, result any) error {
168+
req.Header.Set(authorizationHeader, c.username+"."+c.token)
169+
170+
resp, err := c.HTTPClient.Do(req)
171+
if err != nil {
172+
return errutils.NewHTTPDoError(req, err)
173+
}
174+
175+
defer func() { _ = resp.Body.Close() }()
176+
177+
if resp.StatusCode/100 != 2 {
178+
return parseError(req, resp)
179+
}
180+
181+
raw, err := io.ReadAll(resp.Body)
182+
if err != nil {
183+
return errutils.NewReadResponseError(req, resp.StatusCode, err)
184+
}
185+
186+
err = json.Unmarshal(raw, result)
187+
if err != nil {
188+
return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
189+
}
190+
191+
return nil
192+
}
193+
194+
func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
195+
buf := new(bytes.Buffer)
196+
197+
if payload != nil {
198+
err := json.NewEncoder(buf).Encode(payload)
199+
if err != nil {
200+
return nil, fmt.Errorf("failed to create request JSON body: %w", err)
201+
}
202+
}
203+
204+
req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), buf)
205+
if err != nil {
206+
return nil, fmt.Errorf("unable to create request: %w", err)
207+
}
208+
209+
req.Header.Set("Accept", "application/json")
210+
211+
if payload != nil {
212+
req.Header.Set("Content-Type", "application/json")
213+
}
214+
215+
return req, nil
216+
}
217+
218+
func parseError(req *http.Request, resp *http.Response) error {
219+
raw, _ := io.ReadAll(resp.Body)
220+
221+
var response Base
222+
err := json.Unmarshal(raw, &response)
223+
if err != nil {
224+
return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
225+
}
226+
227+
return response
228+
}
229+
230+
// TTLRounder rounds the given TTL in seconds to the next accepted value.
231+
// Accepted TTL values are:
232+
// - 3600
233+
// - 14400
234+
// - 28800
235+
// - 57600
236+
// - 86400
237+
func TTLRounder(ttl int) int {
238+
for _, validTTL := range []int{3600, 14400, 28800, 57600, 86400} {
239+
if ttl <= validTTL {
240+
return validTTL
241+
}
242+
}
243+
244+
return 3600
245+
}

0 commit comments

Comments
 (0)