Skip to content

Commit

Permalink
Add DNS provider for Yandex 360 (#1975)
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez authored Aug 4, 2023
1 parent 01747e3 commit 07c4dae
Show file tree
Hide file tree
Showing 16 changed files with 739 additions and 4 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ Detailed documentation is available [here](https://go-acme.github.io/lego/dns).
| [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/) |
| [Yandex 360](https://go-acme.github.io/lego/dns/yandex360/) | [Yandex Cloud](https://go-acme.github.io/lego/dns/yandexcloud/) | [Yandex PDD](https://go-acme.github.io/lego/dns/yandex/) | [Zone.ee](https://go-acme.github.io/lego/dns/zoneee/) |
| [Zonomi](https://go-acme.github.io/lego/dns/zonomi/) | | | |

<!-- END DNS PROVIDERS LIST -->

Expand Down
22 changes: 22 additions & 0 deletions cmd/zz_gen_cmd_dnshelp.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ func allDNSCodes() string {
"websupport",
"wedos",
"yandex",
"yandex360",
"yandexcloud",
"zoneee",
"zonomi",
Expand Down Expand Up @@ -2700,6 +2701,27 @@ func displayDNSHelp(w io.Writer, name string) error {
ew.writeln()
ew.writeln(`More information: https://go-acme.github.io/lego/dns/yandex`)

case "yandex360":
// generated from: providers/dns/yandex360/yandex360.toml
ew.writeln(`Configuration for Yandex 360.`)
ew.writeln(`Code: 'yandex360'`)
ew.writeln(`Since: 'v4.14.0'`)
ew.writeln()

ew.writeln(`Credentials:`)
ew.writeln(` - "YANDEX360_OAUTH_TOKEN": The OAuth Token`)
ew.writeln(` - "YANDEX360_ORG_ID": The organization ID`)
ew.writeln()

ew.writeln(`Additional Configuration:`)
ew.writeln(` - "YANDEX360_HTTP_TIMEOUT": API request timeout`)
ew.writeln(` - "YANDEX360_POLLING_INTERVAL": Time between DNS propagation check`)
ew.writeln(` - "YANDEX360_PROPAGATION_TIMEOUT": Maximum waiting time for DNS propagation`)
ew.writeln(` - "YANDEX360_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/yandex360`)

case "yandexcloud":
// generated from: providers/dns/yandexcloud/yandexcloud.toml
ew.writeln(`Configuration for Yandex Cloud.`)
Expand Down
69 changes: 69 additions & 0 deletions docs/content/dns/zz_gen_yandex360.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
title: "Yandex 360"
date: 2019-03-03T16:39:46+01:00
draft: false
slug: yandex360
dnsprovider:
since: "v4.14.0"
code: "yandex360"
url: "https://360.yandex.ru"
---

<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
<!-- providers/dns/yandex360/yandex360.toml -->
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->


Configuration for [Yandex 360](https://360.yandex.ru).


<!--more-->

- Code: `yandex360`
- Since: v4.14.0


Here is an example bash command using the Yandex 360 provider:

```bash
YANDEX360_OAUTH_TOKEN=<your OAuth Token> \
YANDEX360_ORG_ID=<your organization ID> \
lego --email you@example.com --dns yandex360 --domains my.example.org run
```




## Credentials

| Environment Variable Name | Description |
|-----------------------|-------------|
| `YANDEX360_OAUTH_TOKEN` | The OAuth Token |
| `YANDEX360_ORG_ID` | The organization ID |

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 |
|--------------------------------|-------------|
| `YANDEX360_HTTP_TIMEOUT` | API request timeout |
| `YANDEX360_POLLING_INTERVAL` | Time between DNS propagation check |
| `YANDEX360_PROPAGATION_TIMEOUT` | Maximum waiting time for DNS propagation |
| `YANDEX360_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://yandex.ru/dev/api360/doc/ref/DomainDNSService.html)

<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
<!-- providers/dns/yandex360/yandex360.toml -->
<!-- THIS DOCUMENTATION IS AUTO-GENERATED. PLEASE DO NOT EDIT. -->
2 changes: 1 addition & 1 deletion docs/data/zz_cli_help.toml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ To display the documentation for a specific DNS provider, run:
$ lego dnshelp -c code
Supported DNS providers:
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, 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, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, 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, websupport, wedos, yandex, yandexcloud, zoneee, zonomi
acme-dns, alidns, allinkl, arvancloud, auroradns, autodns, azure, azuredns, bindman, bluecat, brandit, bunny, checkdomain, civo, clouddns, cloudflare, cloudns, cloudru, cloudxns, conoha, constellix, 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, httpreq, hurricane, hyperone, ibmcloud, iij, iijdpf, infoblox, infomaniak, internetbs, inwx, ionos, ipv64, iwantmyname, joker, liara, lightsail, linode, liquidweb, loopia, luadns, 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, websupport, wedos, yandex, yandex360, yandexcloud, zoneee, zonomi
More information: https://go-acme.github.io/lego/dns
"""
3 changes: 3 additions & 0 deletions providers/dns/dns_providers.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ import (
"github.com/go-acme/lego/v4/providers/dns/websupport"
"github.com/go-acme/lego/v4/providers/dns/wedos"
"github.com/go-acme/lego/v4/providers/dns/yandex"
"github.com/go-acme/lego/v4/providers/dns/yandex360"
"github.com/go-acme/lego/v4/providers/dns/yandexcloud"
"github.com/go-acme/lego/v4/providers/dns/zoneee"
"github.com/go-acme/lego/v4/providers/dns/zonomi"
Expand Down Expand Up @@ -375,6 +376,8 @@ func NewDNSChallengeProviderByName(name string) (challenge.Provider, error) {
return wedos.NewDNSProvider()
case "yandex":
return yandex.NewDNSProvider()
case "yandex360":
return yandex360.NewDNSProvider()
case "yandexcloud":
return yandexcloud.NewDNSProvider()
case "zoneee":
Expand Down
2 changes: 1 addition & 1 deletion providers/dns/safedns/internal/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (c *Client) AddRecord(ctx context.Context, zone string, record Record) (*Ad
respData := &AddRecordResponse{}
err = c.do(req, respData)
if err != nil {
return nil, fmt.Errorf("remove record: %w", err)
return nil, fmt.Errorf("add record: %w", err)
}

return respData, nil
Expand Down
2 changes: 1 addition & 1 deletion providers/dns/yandex/yandex.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Package yandex implements a DNS provider for solving the DNS-01 challenge using Yandex.
// Package yandex implements a DNS provider for solving the DNS-01 challenge using Yandex PDD.
package yandex

import (
Expand Down
147 changes: 147 additions & 0 deletions providers/dns/yandex360/internal/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package internal

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"time"

"github.com/go-acme/lego/v4/providers/dns/internal/errutils"
)

const defaultBaseURL = "https://api360.yandex.net/"

type Client struct {
oauthToken string
orgID int64

baseURL *url.URL
HTTPClient *http.Client
}

func NewClient(oauthToken string, orgID int64) (*Client, error) {
if oauthToken == "" {
return nil, errors.New("OAuth token is required")
}

if orgID == 0 {
return nil, errors.New("orgID is required")
}

baseURL, _ := url.Parse(defaultBaseURL)

return &Client{
oauthToken: oauthToken,
orgID: orgID,
baseURL: baseURL,
HTTPClient: &http.Client{Timeout: 10 * time.Second},
}, nil
}

// AddRecord Adds a DNS record.
// POST https://api30.yandex.net/directory/v1/org/{orgId}/domains/{domain}/dns
// https://yandex.ru/dev/api360/doc/ref/DomainDNSService/DomainDNSService_Create.html
func (c Client) AddRecord(ctx context.Context, domain string, record Record) (*Record, error) {
endpoint := c.baseURL.JoinPath("directory", "v1", "org", strconv.FormatInt(c.orgID, 10), "domains", domain, "dns")

req, err := newJSONRequest(ctx, http.MethodPost, endpoint, record)
if err != nil {
return nil, err
}

var newRecord Record

err = c.do(req, &newRecord)
if err != nil {
return nil, err
}

return &newRecord, nil
}

// DeleteRecord Deletes a DNS record.
// DELETE https://api360.yandex.net/directory/v1/org/{orgId}/domains/{domain}/dns/{recordId}
// https://yandex.ru/dev/api360/doc/ref/DomainDNSService/DomainDNSService_Delete.html
func (c Client) DeleteRecord(ctx context.Context, domain string, recordID int64) error {
endpoint := c.baseURL.JoinPath("directory", "v1", "org", strconv.FormatInt(c.orgID, 10), "domains", domain, "dns", strconv.FormatInt(recordID, 10))

req, err := newJSONRequest(ctx, http.MethodDelete, endpoint, nil)
if err != nil {
return err
}

return c.do(req, nil)
}

func (c Client) do(req *http.Request, result any) error {
req.Header.Set("Authorization", "OAuth "+c.oauthToken)

resp, err := c.HTTPClient.Do(req)
if err != nil {
return errutils.NewHTTPDoError(req, err)
}

defer func() { _ = resp.Body.Close() }()

if resp.StatusCode/100 != 2 {
return parseError(req, resp)
}

if result == nil {
return nil
}

raw, err := io.ReadAll(resp.Body)
if err != nil {
return errutils.NewReadResponseError(req, resp.StatusCode, err)
}

err = json.Unmarshal(raw, result)
if err != nil {
return errutils.NewUnmarshalError(req, resp.StatusCode, raw, err)
}

return nil
}

func newJSONRequest(ctx context.Context, method string, endpoint *url.URL, payload any) (*http.Request, error) {
buf := new(bytes.Buffer)

if payload != nil {
err := json.NewEncoder(buf).Encode(payload)
if err != nil {
return nil, fmt.Errorf("failed to create request JSON body: %w", err)
}
}

req, err := http.NewRequestWithContext(ctx, method, endpoint.String(), buf)
if err != nil {
return nil, fmt.Errorf("unable to create request: %w", err)
}

req.Header.Set("Accept", "application/json")

if payload != nil {
req.Header.Set("Content-Type", "application/json")
}

return req, nil
}

func parseError(req *http.Request, resp *http.Response) error {
raw, _ := io.ReadAll(resp.Body)

var apiErr APIError
err := json.Unmarshal(raw, &apiErr)
if err != nil {
return errutils.NewUnexpectedStatusCodeError(req, resp.StatusCode, raw)
}

return fmt.Errorf("[status code: %d] %w", resp.StatusCode, apiErr)
}
Loading

0 comments on commit 07c4dae

Please sign in to comment.