Cherry pick v1.7 into master
This commit is contained in:
parent
a09dfa3ce1
commit
b6498cdcbc
73 changed files with 6573 additions and 186 deletions
19
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
19
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
|
@ -665,7 +665,7 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error)
|
|||
|
||||
go func(authzURL string) {
|
||||
var authz authorization
|
||||
_, err := getJSON(authzURL, &authz)
|
||||
_, err := postAsGet(c.jws, authzURL, &authz)
|
||||
if err != nil {
|
||||
errc <- domainError{Domain: authz.Identifier.Value, Error: err}
|
||||
return
|
||||
|
@ -789,7 +789,7 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
|
|||
case <-stopTimer.C:
|
||||
return nil, errors.New("certificate polling timed out")
|
||||
case <-retryTick.C:
|
||||
_, err := getJSON(order.URL, &retOrder)
|
||||
_, err := postAsGet(c.jws, order.URL, &retOrder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -813,7 +813,7 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
|
|||
func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResource, bundle bool) (bool, error) {
|
||||
switch order.Status {
|
||||
case statusValid:
|
||||
resp, err := httpGet(order.Certificate)
|
||||
resp, err := postAsGet(c.jws, order.Certificate, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -871,7 +871,7 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
|
|||
// getIssuerCertificate requests the issuer certificate
|
||||
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
|
||||
log.Infof("acme: Requesting issuer cert from %s", url)
|
||||
resp, err := httpGet(url)
|
||||
resp, err := postAsGet(c.jws, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -914,7 +914,10 @@ func parseLinks(links []string) map[string]string {
|
|||
func validate(j *jws, domain, uri string, c challenge) error {
|
||||
var chlng challenge
|
||||
|
||||
hdr, err := postJSON(j, uri, c, &chlng)
|
||||
// Challenge initiation is done by sending a JWS payload containing the
|
||||
// trivial JSON object `{}`. We use an empty struct instance as the postJSON
|
||||
// payload here to achieve this result.
|
||||
hdr, err := postJSON(j, uri, struct{}{}, &chlng)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -940,11 +943,15 @@ func validate(j *jws, domain, uri string, c challenge) error {
|
|||
// If it doesn't, we'll just poll hard.
|
||||
ra = 5
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(ra) * time.Second)
|
||||
|
||||
hdr, err = getJSON(uri, &chlng)
|
||||
resp, err := postAsGet(j, uri, &chlng)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp != nil {
|
||||
hdr = resp.Header
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
52
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
52
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
|
@ -42,12 +42,14 @@ var (
|
|||
)
|
||||
|
||||
const (
|
||||
// defaultGoUserAgent is the Go HTTP package user agent string. Too
|
||||
// bad it isn't exported. If it changes, we should update it here, too.
|
||||
defaultGoUserAgent = "Go-http-client/1.1"
|
||||
|
||||
// ourUserAgent is the User-Agent of this underlying library package.
|
||||
ourUserAgent = "xenolf-acme"
|
||||
// NOTE: Update this with each tagged release.
|
||||
ourUserAgent = "xenolf-acme/1.2.1"
|
||||
|
||||
// ourUserAgentComment is part of the UA comment linked to the version status of this underlying library package.
|
||||
// values: detach|release
|
||||
// NOTE: Update this with each tagged release.
|
||||
ourUserAgentComment = "detach"
|
||||
|
||||
// caCertificatesEnvVar is the environment variable name that can be used to
|
||||
// specify the path to PEM encoded CA Certificates that can be used to
|
||||
|
@ -151,52 +153,60 @@ func postJSON(j *jws, uri string, reqBody, respBody interface{}) (http.Header, e
|
|||
return nil, errors.New("failed to marshal network message")
|
||||
}
|
||||
|
||||
resp, err := j.post(uri, jsonBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to post JWS message. -> %v", err)
|
||||
resp, err := post(j, uri, jsonBytes, respBody)
|
||||
if resp == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
return resp.Header, err
|
||||
}
|
||||
|
||||
func postAsGet(j *jws, uri string, respBody interface{}) (*http.Response, error) {
|
||||
return post(j, uri, []byte{}, respBody)
|
||||
}
|
||||
|
||||
func post(j *jws, uri string, reqBody []byte, respBody interface{}) (*http.Response, error) {
|
||||
resp, err := j.post(uri, reqBody)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to post JWS message. -> %v", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
err = handleHTTPError(resp)
|
||||
switch err.(type) {
|
||||
case NonceError:
|
||||
// Retry once if the nonce was invalidated
|
||||
|
||||
retryResp, errP := j.post(uri, jsonBytes)
|
||||
retryResp, errP := j.post(uri, reqBody)
|
||||
if errP != nil {
|
||||
return nil, fmt.Errorf("failed to post JWS message. -> %v", errP)
|
||||
}
|
||||
|
||||
defer retryResp.Body.Close()
|
||||
|
||||
if retryResp.StatusCode >= http.StatusBadRequest {
|
||||
return retryResp.Header, handleHTTPError(retryResp)
|
||||
return retryResp, handleHTTPError(retryResp)
|
||||
}
|
||||
|
||||
if respBody == nil {
|
||||
return retryResp.Header, nil
|
||||
return retryResp, nil
|
||||
}
|
||||
|
||||
return retryResp.Header, json.NewDecoder(retryResp.Body).Decode(respBody)
|
||||
|
||||
return retryResp, json.NewDecoder(retryResp.Body).Decode(respBody)
|
||||
default:
|
||||
return resp.Header, err
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if respBody == nil {
|
||||
return resp.Header, nil
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
return resp.Header, json.NewDecoder(resp.Body).Decode(respBody)
|
||||
return resp, json.NewDecoder(resp.Body).Decode(respBody)
|
||||
}
|
||||
|
||||
// userAgent builds and returns the User-Agent string to use in requests.
|
||||
func userAgent() string {
|
||||
ua := fmt.Sprintf("%s %s (%s; %s) %s", UserAgent, ourUserAgent, runtime.GOOS, runtime.GOARCH, defaultGoUserAgent)
|
||||
ua := fmt.Sprintf("%s %s (%s; %s; %s)", UserAgent, ourUserAgent, ourUserAgentComment, runtime.GOOS, runtime.GOARCH)
|
||||
return strings.TrimSpace(ua)
|
||||
}
|
||||
|
|
6
vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go
generated
vendored
6
vendor/github.com/xenolf/lego/acme/tls_alpn_challenge.go
generated
vendored
|
@ -12,8 +12,8 @@ import (
|
|||
)
|
||||
|
||||
// idPeAcmeIdentifierV1 is the SMI Security for PKIX Certification Extension OID referencing the ACME extension.
|
||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01#section-5.1
|
||||
var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 30, 1}
|
||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-5.1
|
||||
var idPeAcmeIdentifierV1 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 31}
|
||||
|
||||
type tlsALPNChallenge struct {
|
||||
jws *jws
|
||||
|
@ -58,7 +58,7 @@ func TLSALPNChallengeBlocks(domain, keyAuth string) ([]byte, []byte, error) {
|
|||
|
||||
// Add the keyAuth digest as the acmeValidation-v1 extension
|
||||
// (marked as critical such that it won't be used by non-ACME software).
|
||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-01#section-3
|
||||
// Reference: https://tools.ietf.org/html/draft-ietf-acme-tls-alpn-05#section-3
|
||||
extensions := []pkix.Extension{
|
||||
{
|
||||
Id: idPeAcmeIdentifierV1,
|
||||
|
|
2
vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go
generated
vendored
2
vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go
generated
vendored
|
@ -126,7 +126,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
TTL: d.config.TTL,
|
||||
}
|
||||
|
||||
response, _ := d.client.CreateDNSRecord(zoneID, dnsRecord)
|
||||
response, err := d.client.CreateDNSRecord(zoneID, dnsRecord)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cloudflare: failed to create TXT record: %v", err)
|
||||
}
|
||||
|
|
205
vendor/github.com/xenolf/lego/providers/dns/conoha/client.go
generated
vendored
Normal file
205
vendor/github.com/xenolf/lego/providers/dns/conoha/client.go
generated
vendored
Normal file
|
@ -0,0 +1,205 @@
|
|||
package conoha
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
identityBaseURL = "https://identity.%s.conoha.io"
|
||||
dnsServiceBaseURL = "https://dns-service.%s.conoha.io"
|
||||
)
|
||||
|
||||
// IdentityRequest is an authentication request body.
|
||||
type IdentityRequest struct {
|
||||
Auth Auth `json:"auth"`
|
||||
}
|
||||
|
||||
// Auth is an authentication information.
|
||||
type Auth struct {
|
||||
TenantID string `json:"tenantId"`
|
||||
PasswordCredentials PasswordCredentials `json:"passwordCredentials"`
|
||||
}
|
||||
|
||||
// PasswordCredentials is API-user's credentials.
|
||||
type PasswordCredentials struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// IdentityResponse is an authentication response body.
|
||||
type IdentityResponse struct {
|
||||
Access Access `json:"access"`
|
||||
}
|
||||
|
||||
// Access is an identity information.
|
||||
type Access struct {
|
||||
Token Token `json:"token"`
|
||||
}
|
||||
|
||||
// Token is an api access token.
|
||||
type Token struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// DomainListResponse is a response of a domain listing request.
|
||||
type DomainListResponse struct {
|
||||
Domains []Domain `json:"domains"`
|
||||
}
|
||||
|
||||
// Domain is a hosted domain entry.
|
||||
type Domain struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// RecordListResponse is a response of record listing request.
|
||||
type RecordListResponse struct {
|
||||
Records []Record `json:"records"`
|
||||
}
|
||||
|
||||
// Record is a record entry.
|
||||
type Record struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Data string `json:"data"`
|
||||
TTL int `json:"ttl"`
|
||||
}
|
||||
|
||||
// Client is a ConoHa API client.
|
||||
type Client struct {
|
||||
token string
|
||||
endpoint string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// NewClient returns a client instance logged into the ConoHa service.
|
||||
func NewClient(region string, auth Auth, httpClient *http.Client) (*Client, error) {
|
||||
if httpClient == nil {
|
||||
httpClient = &http.Client{}
|
||||
}
|
||||
|
||||
c := &Client{httpClient: httpClient}
|
||||
|
||||
c.endpoint = fmt.Sprintf(identityBaseURL, region)
|
||||
|
||||
identity, err := c.getIdentity(auth)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to login: %v", err)
|
||||
}
|
||||
|
||||
c.token = identity.Access.Token.ID
|
||||
c.endpoint = fmt.Sprintf(dnsServiceBaseURL, region)
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Client) getIdentity(auth Auth) (*IdentityResponse, error) {
|
||||
req := &IdentityRequest{Auth: auth}
|
||||
|
||||
identity := &IdentityResponse{}
|
||||
|
||||
err := c.do(http.MethodPost, "/v2.0/tokens", req, identity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return identity, nil
|
||||
}
|
||||
|
||||
// GetDomainID returns an ID of specified domain.
|
||||
func (c *Client) GetDomainID(domainName string) (string, error) {
|
||||
domainList := &DomainListResponse{}
|
||||
|
||||
err := c.do(http.MethodGet, "/v1/domains", nil, domainList)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, domain := range domainList.Domains {
|
||||
if domain.Name == domainName {
|
||||
return domain.ID, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("no such domain: %s", domainName)
|
||||
}
|
||||
|
||||
// GetRecordID returns an ID of specified record.
|
||||
func (c *Client) GetRecordID(domainID, recordName, recordType, data string) (string, error) {
|
||||
recordList := &RecordListResponse{}
|
||||
|
||||
err := c.do(http.MethodGet, fmt.Sprintf("/v1/domains/%s/records", domainID), nil, recordList)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, record := range recordList.Records {
|
||||
if record.Name == recordName && record.Type == recordType && record.Data == data {
|
||||
return record.ID, nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("no such record")
|
||||
}
|
||||
|
||||
// CreateRecord adds new record.
|
||||
func (c *Client) CreateRecord(domainID string, record Record) error {
|
||||
return c.do(http.MethodPost, fmt.Sprintf("/v1/domains/%s/records", domainID), record, nil)
|
||||
}
|
||||
|
||||
// DeleteRecord removes specified record.
|
||||
func (c *Client) DeleteRecord(domainID, recordID string) error {
|
||||
return c.do(http.MethodDelete, fmt.Sprintf("/v1/domains/%s/records/%s", domainID, recordID), nil, nil)
|
||||
}
|
||||
|
||||
func (c *Client) do(method, path string, payload, result interface{}) error {
|
||||
body := bytes.NewReader(nil)
|
||||
|
||||
if payload != nil {
|
||||
bodyBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body = bytes.NewReader(bodyBytes)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, c.endpoint+path, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "application/json")
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("X-Auth-Token", c.token)
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return fmt.Errorf("HTTP request failed with status code %d: %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return json.Unmarshal(respBody, result)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
148
vendor/github.com/xenolf/lego/providers/dns/conoha/conoha.go
generated
vendored
Normal file
148
vendor/github.com/xenolf/lego/providers/dns/conoha/conoha.go
generated
vendored
Normal file
|
@ -0,0 +1,148 @@
|
|||
// Package conoha implements a DNS provider for solving the DNS-01 challenge
|
||||
// using ConoHa DNS.
|
||||
package conoha
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/platform/config/env"
|
||||
)
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider
|
||||
type Config struct {
|
||||
Region string
|
||||
TenantID string
|
||||
Username string
|
||||
Password string
|
||||
TTL int
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
Region: env.GetOrDefaultString("CONOHA_REGION", "tyo1"),
|
||||
TTL: env.GetOrDefaultInt("CONOHA_TTL", 60),
|
||||
PropagationTimeout: env.GetOrDefaultSecond("CONOHA_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout),
|
||||
PollingInterval: env.GetOrDefaultSecond("CONOHA_POLLING_INTERVAL", acme.DefaultPollingInterval),
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: env.GetOrDefaultSecond("CONOHA_HTTP_TIMEOUT", 30*time.Second),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
client *Client
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for ConoHa DNS.
|
||||
// Credentials must be passed in the environment variables: CONOHA_TENANT_ID, CONOHA_API_USERNAME, CONOHA_API_PASSWORD
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get("CONOHA_TENANT_ID", "CONOHA_API_USERNAME", "CONOHA_API_PASSWORD")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("conoha: %v", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.TenantID = values["CONOHA_TENANT_ID"]
|
||||
config.Username = values["CONOHA_API_USERNAME"]
|
||||
config.Password = values["CONOHA_API_PASSWORD"]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for ConoHa DNS.
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("conoha: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.TenantID == "" || config.Username == "" || config.Password == "" {
|
||||
return nil, errors.New("conoha: some credentials information are missing")
|
||||
}
|
||||
|
||||
auth := Auth{
|
||||
TenantID: config.TenantID,
|
||||
PasswordCredentials: PasswordCredentials{
|
||||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
},
|
||||
}
|
||||
|
||||
client, err := NewClient(config.Region, auth, config.HTTPClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("conoha: failed to create client: %v", err)
|
||||
}
|
||||
|
||||
return &DNSProvider{config: config, client: client}, nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfill the dns-01 challenge.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
id, err := d.client.GetDomainID(authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("conoha: failed to get domain ID: %v", err)
|
||||
}
|
||||
|
||||
record := Record{
|
||||
Name: fqdn,
|
||||
Type: "TXT",
|
||||
Data: value,
|
||||
TTL: d.config.TTL,
|
||||
}
|
||||
|
||||
err = d.client.CreateRecord(id, record)
|
||||
if err != nil {
|
||||
return fmt.Errorf("conoha: failed to create record: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp clears ConoHa DNS TXT record
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domID, err := d.client.GetDomainID(authZone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("conoha: failed to get domain ID: %v", err)
|
||||
}
|
||||
|
||||
recID, err := d.client.GetRecordID(domID, fqdn, "TXT", value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("conoha: failed to get record ID: %v", err)
|
||||
}
|
||||
|
||||
err = d.client.DeleteRecord(domID, recID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("conoha: failed to delete record: %v", err)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
21
vendor/github.com/xenolf/lego/providers/dns/dns_providers.go
generated
vendored
21
vendor/github.com/xenolf/lego/providers/dns/dns_providers.go
generated
vendored
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/xenolf/lego/providers/dns/bluecat"
|
||||
"github.com/xenolf/lego/providers/dns/cloudflare"
|
||||
"github.com/xenolf/lego/providers/dns/cloudxns"
|
||||
"github.com/xenolf/lego/providers/dns/conoha"
|
||||
"github.com/xenolf/lego/providers/dns/digitalocean"
|
||||
"github.com/xenolf/lego/providers/dns/dnsimple"
|
||||
"github.com/xenolf/lego/providers/dns/dnsmadeeasy"
|
||||
|
@ -27,10 +28,13 @@ import (
|
|||
"github.com/xenolf/lego/providers/dns/glesys"
|
||||
"github.com/xenolf/lego/providers/dns/godaddy"
|
||||
"github.com/xenolf/lego/providers/dns/hostingde"
|
||||
"github.com/xenolf/lego/providers/dns/httpreq"
|
||||
"github.com/xenolf/lego/providers/dns/iij"
|
||||
"github.com/xenolf/lego/providers/dns/inwx"
|
||||
"github.com/xenolf/lego/providers/dns/lightsail"
|
||||
"github.com/xenolf/lego/providers/dns/linode"
|
||||
"github.com/xenolf/lego/providers/dns/linodev4"
|
||||
"github.com/xenolf/lego/providers/dns/mydnsjp"
|
||||
"github.com/xenolf/lego/providers/dns/namecheap"
|
||||
"github.com/xenolf/lego/providers/dns/namedotcom"
|
||||
"github.com/xenolf/lego/providers/dns/netcup"
|
||||
|
@ -43,8 +47,11 @@ import (
|
|||
"github.com/xenolf/lego/providers/dns/rfc2136"
|
||||
"github.com/xenolf/lego/providers/dns/route53"
|
||||
"github.com/xenolf/lego/providers/dns/sakuracloud"
|
||||
"github.com/xenolf/lego/providers/dns/selectel"
|
||||
"github.com/xenolf/lego/providers/dns/stackpath"
|
||||
"github.com/xenolf/lego/providers/dns/transip"
|
||||
"github.com/xenolf/lego/providers/dns/vegadns"
|
||||
"github.com/xenolf/lego/providers/dns/vscale"
|
||||
"github.com/xenolf/lego/providers/dns/vultr"
|
||||
)
|
||||
|
||||
|
@ -65,6 +72,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
|||
return cloudflare.NewDNSProvider()
|
||||
case "cloudxns":
|
||||
return cloudxns.NewDNSProvider()
|
||||
case "conoha":
|
||||
return conoha.NewDNSProvider()
|
||||
case "digitalocean":
|
||||
return digitalocean.NewDNSProvider()
|
||||
case "dnsimple":
|
||||
|
@ -97,8 +106,12 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
|||
return godaddy.NewDNSProvider()
|
||||
case "hostingde":
|
||||
return hostingde.NewDNSProvider()
|
||||
case "httpreq":
|
||||
return httpreq.NewDNSProvider()
|
||||
case "iij":
|
||||
return iij.NewDNSProvider()
|
||||
case "inwx":
|
||||
return inwx.NewDNSProvider()
|
||||
case "lightsail":
|
||||
return lightsail.NewDNSProvider()
|
||||
case "linode":
|
||||
|
@ -107,6 +120,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
|||
return linodev4.NewDNSProvider()
|
||||
case "manual":
|
||||
return acme.NewDNSProviderManual()
|
||||
case "mydnsjp":
|
||||
return mydnsjp.NewDNSProvider()
|
||||
case "namecheap":
|
||||
return namecheap.NewDNSProvider()
|
||||
case "namedotcom":
|
||||
|
@ -133,10 +148,16 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
|||
return sakuracloud.NewDNSProvider()
|
||||
case "stackpath":
|
||||
return stackpath.NewDNSProvider()
|
||||
case "selectel":
|
||||
return selectel.NewDNSProvider()
|
||||
case "transip":
|
||||
return transip.NewDNSProvider()
|
||||
case "vegadns":
|
||||
return vegadns.NewDNSProvider()
|
||||
case "vultr":
|
||||
return vultr.NewDNSProvider()
|
||||
case "vscale":
|
||||
return vscale.NewDNSProvider()
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognised DNS provider: %s", name)
|
||||
}
|
||||
|
|
193
vendor/github.com/xenolf/lego/providers/dns/httpreq/httpreq.go
generated
vendored
Normal file
193
vendor/github.com/xenolf/lego/providers/dns/httpreq/httpreq.go
generated
vendored
Normal file
|
@ -0,0 +1,193 @@
|
|||
// Package httpreq implements a DNS provider for solving the DNS-01 challenge through a HTTP server.
|
||||
package httpreq
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/platform/config/env"
|
||||
)
|
||||
|
||||
type message struct {
|
||||
FQDN string `json:"fqdn"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type messageRaw struct {
|
||||
Domain string `json:"domain"`
|
||||
Token string `json:"token"`
|
||||
KeyAuth string `json:"keyAuth"`
|
||||
}
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider
|
||||
type Config struct {
|
||||
Endpoint *url.URL
|
||||
Mode string
|
||||
Username string
|
||||
Password string
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
PropagationTimeout: env.GetOrDefaultSecond("HTTPREQ_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout),
|
||||
PollingInterval: env.GetOrDefaultSecond("HTTPREQ_POLLING_INTERVAL", acme.DefaultPollingInterval),
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: env.GetOrDefaultSecond("HTTPREQ_HTTP_TIMEOUT", 30*time.Second),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider describes a provider for acme-proxy
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get("HTTPREQ_ENDPOINT")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("httpreq: %v", err)
|
||||
}
|
||||
|
||||
endpoint, err := url.Parse(values["HTTPREQ_ENDPOINT"])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("httpreq: %v", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.Mode = os.Getenv("HTTPREQ_MODE")
|
||||
config.Username = os.Getenv("HTTPREQ_USERNAME")
|
||||
config.Password = os.Getenv("HTTPREQ_PASSWORD")
|
||||
config.Endpoint = endpoint
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider .
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("httpreq: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.Endpoint == nil {
|
||||
return nil, errors.New("httpreq: the endpoint is missing")
|
||||
}
|
||||
|
||||
return &DNSProvider{config: config}, 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
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfill the dns-01 challenge
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
if d.config.Mode == "RAW" {
|
||||
msg := &messageRaw{
|
||||
Domain: domain,
|
||||
Token: token,
|
||||
KeyAuth: keyAuth,
|
||||
}
|
||||
|
||||
err := d.doPost("/present", msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("httpreq: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
msg := &message{
|
||||
FQDN: fqdn,
|
||||
Value: value,
|
||||
}
|
||||
|
||||
err := d.doPost("/present", msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("httpreq: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
if d.config.Mode == "RAW" {
|
||||
msg := &messageRaw{
|
||||
Domain: domain,
|
||||
Token: token,
|
||||
KeyAuth: keyAuth,
|
||||
}
|
||||
|
||||
err := d.doPost("/cleanup", msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("httpreq: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
msg := &message{
|
||||
FQDN: fqdn,
|
||||
Value: value,
|
||||
}
|
||||
|
||||
err := d.doPost("/cleanup", msg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("httpreq: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) doPost(uri string, msg interface{}) error {
|
||||
reqBody := &bytes.Buffer{}
|
||||
err := json.NewEncoder(reqBody).Encode(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpoint, err := d.config.Endpoint.Parse(uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, endpoint.String(), reqBody)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
if len(d.config.Username) > 0 && len(d.config.Password) > 0 {
|
||||
req.SetBasicAuth(d.config.Username, d.config.Password)
|
||||
}
|
||||
|
||||
resp, err := d.config.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%d: failed to read response body: %v", resp.StatusCode, err)
|
||||
}
|
||||
|
||||
return fmt.Errorf("%d: request failed: %v", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
166
vendor/github.com/xenolf/lego/providers/dns/inwx/inwx.go
generated
vendored
Normal file
166
vendor/github.com/xenolf/lego/providers/dns/inwx/inwx.go
generated
vendored
Normal file
|
@ -0,0 +1,166 @@
|
|||
// Package inwx implements a DNS provider for solving the DNS-01 challenge using inwx dom robot
|
||||
package inwx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/smueller18/goinwx"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/log"
|
||||
"github.com/xenolf/lego/platform/config/env"
|
||||
)
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider
|
||||
type Config struct {
|
||||
Username string
|
||||
Password string
|
||||
Sandbox bool
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
PropagationTimeout: env.GetOrDefaultSecond("INWX_PROPAGATION_TIMEOUT", acme.DefaultPropagationTimeout),
|
||||
PollingInterval: env.GetOrDefaultSecond("INWX_POLLING_INTERVAL", acme.DefaultPollingInterval),
|
||||
TTL: env.GetOrDefaultInt("INWX_TTL", 300),
|
||||
Sandbox: env.GetOrDefaultBool("INWX_SANDBOX", false),
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
client *goinwx.Client
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for Dyn DNS.
|
||||
// Credentials must be passed in the environment variables:
|
||||
// INWX_USERNAME and INWX_PASSWORD.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get("INWX_USERNAME", "INWX_PASSWORD")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.Username = values["INWX_USERNAME"]
|
||||
config.Password = values["INWX_PASSWORD"]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for Dyn DNS
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("inwx: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.Username == "" || config.Password == "" {
|
||||
return nil, fmt.Errorf("inwx: credentials missing")
|
||||
}
|
||||
|
||||
if config.Sandbox {
|
||||
log.Infof("inwx: sandbox mode is enabled")
|
||||
}
|
||||
|
||||
client := goinwx.NewClient(config.Username, config.Password, &goinwx.ClientOptions{Sandbox: config.Sandbox})
|
||||
|
||||
return &DNSProvider{config: config, client: client}, nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record using the specified parameters
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
err = d.client.Account.Login()
|
||||
if err != nil {
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
errL := d.client.Account.Logout()
|
||||
if errL != nil {
|
||||
log.Infof("inwx: failed to logout: %v", errL)
|
||||
}
|
||||
}()
|
||||
|
||||
var request = &goinwx.NameserverRecordRequest{
|
||||
Domain: acme.UnFqdn(authZone),
|
||||
Name: acme.UnFqdn(fqdn),
|
||||
Type: "TXT",
|
||||
Content: value,
|
||||
Ttl: d.config.TTL,
|
||||
}
|
||||
|
||||
_, err = d.client.Nameservers.CreateRecord(request)
|
||||
if err != nil {
|
||||
switch err.(type) {
|
||||
case *goinwx.ErrorResponse:
|
||||
if err.(*goinwx.ErrorResponse).Message == "Object exists" {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
default:
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
err = d.client.Account.Login()
|
||||
if err != nil {
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
errL := d.client.Account.Logout()
|
||||
if errL != nil {
|
||||
log.Infof("inwx: failed to logout: %v", errL)
|
||||
}
|
||||
}()
|
||||
|
||||
response, err := d.client.Nameservers.Info(&goinwx.NameserverInfoRequest{
|
||||
Domain: acme.UnFqdn(authZone),
|
||||
Name: acme.UnFqdn(fqdn),
|
||||
Type: "TXT",
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
for _, record := range response.Records {
|
||||
err = d.client.Nameservers.DeleteRecord(record.Id)
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("inwx: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return lastErr
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
140
vendor/github.com/xenolf/lego/providers/dns/mydnsjp/mydnsjp.go
generated
vendored
Normal file
140
vendor/github.com/xenolf/lego/providers/dns/mydnsjp/mydnsjp.go
generated
vendored
Normal file
|
@ -0,0 +1,140 @@
|
|||
// Package mydnsjp implements a DNS provider for solving the DNS-01
|
||||
// challenge using MyDNS.jp.
|
||||
package mydnsjp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/platform/config/env"
|
||||
)
|
||||
|
||||
const defaultBaseURL = "https://www.mydns.jp/directedit.html"
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider
|
||||
type Config struct {
|
||||
MasterID string
|
||||
Password string
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
PropagationTimeout: env.GetOrDefaultSecond("MYDNSJP_PROPAGATION_TIMEOUT", 2*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond("MYDNSJP_POLLING_INTERVAL", 2*time.Second),
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: env.GetOrDefaultSecond("MYDNSJP_HTTP_TIMEOUT", 30*time.Second),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for MyDNS.jp.
|
||||
// Credentials must be passed in the environment variables: MYDNSJP_MASTER_ID and MYDNSJP_PASSWORD.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get("MYDNSJP_MASTER_ID", "MYDNSJP_PASSWORD")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mydnsjp: %v", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.MasterID = values["MYDNSJP_MASTER_ID"]
|
||||
config.Password = values["MYDNSJP_PASSWORD"]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for MyDNS.jp.
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("mydnsjp: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.MasterID == "" || config.Password == "" {
|
||||
return nil, errors.New("mydnsjp: some credentials information are missing")
|
||||
}
|
||||
|
||||
return &DNSProvider{config: config}, 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
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfill the dns-01 challenge
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
_, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
err := d.doRequest(domain, value, "REGIST")
|
||||
if err != nil {
|
||||
return fmt.Errorf("mydnsjp: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
_, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
err := d.doRequest(domain, value, "DELETE")
|
||||
if err != nil {
|
||||
return fmt.Errorf("mydnsjp: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) doRequest(domain, value string, cmd string) error {
|
||||
req, err := d.buildRequest(domain, value, cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := d.config.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error querying API: %v", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
var content []byte
|
||||
content, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("request %s failed [status code %d]: %s", req.URL, resp.StatusCode, string(content))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DNSProvider) buildRequest(domain, value string, cmd string) (*http.Request, error) {
|
||||
params := url.Values{}
|
||||
params.Set("CERTBOT_DOMAIN", domain)
|
||||
params.Set("CERTBOT_VALIDATION", value)
|
||||
params.Set("EDIT_CMD", cmd)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, defaultBaseURL, strings.NewReader(params.Encode()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid request: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.SetBasicAuth(d.config.MasterID, d.config.Password)
|
||||
|
||||
return req, nil
|
||||
}
|
211
vendor/github.com/xenolf/lego/providers/dns/selectel/client.go
generated
vendored
Normal file
211
vendor/github.com/xenolf/lego/providers/dns/selectel/client.go
generated
vendored
Normal file
|
@ -0,0 +1,211 @@
|
|||
package selectel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Domain represents domain name.
|
||||
type Domain struct {
|
||||
ID int `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// Record represents DNS record.
|
||||
type Record struct {
|
||||
ID int `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"` // Record type (SOA, NS, A/AAAA, CNAME, SRV, MX, TXT, SPF)
|
||||
TTL int `json:"ttl,omitempty"`
|
||||
Email string `json:"email,omitempty"` // Email of domain's admin (only for SOA records)
|
||||
Content string `json:"content,omitempty"` // Record content (not for SRV)
|
||||
}
|
||||
|
||||
// APIError API error message
|
||||
type APIError struct {
|
||||
Description string `json:"error"`
|
||||
Code int `json:"code"`
|
||||
Field string `json:"field"`
|
||||
}
|
||||
|
||||
func (a *APIError) Error() string {
|
||||
return fmt.Sprintf("API error: %d - %s - %s", a.Code, a.Description, a.Field)
|
||||
}
|
||||
|
||||
// ClientOpts represents options to init client.
|
||||
type ClientOpts struct {
|
||||
BaseURL string
|
||||
Token string
|
||||
UserAgent string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// Client represents DNS client.
|
||||
type Client struct {
|
||||
baseURL string
|
||||
token string
|
||||
userAgent string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// NewClient returns a client instance.
|
||||
func NewClient(opts ClientOpts) *Client {
|
||||
if opts.HTTPClient == nil {
|
||||
opts.HTTPClient = &http.Client{}
|
||||
}
|
||||
|
||||
return &Client{
|
||||
token: opts.Token,
|
||||
baseURL: opts.BaseURL,
|
||||
httpClient: opts.HTTPClient,
|
||||
userAgent: opts.UserAgent,
|
||||
}
|
||||
}
|
||||
|
||||
// GetDomainByName gets Domain object by its name.
|
||||
func (c *Client) GetDomainByName(domainName string) (*Domain, error) {
|
||||
uri := fmt.Sprintf("/%s", domainName)
|
||||
req, err := c.newRequest(http.MethodGet, uri, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domain := &Domain{}
|
||||
_, err = c.do(req, domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
// AddRecord adds Record for given domain.
|
||||
func (c *Client) AddRecord(domainID int, body Record) (*Record, error) {
|
||||
uri := fmt.Sprintf("/%d/records/", domainID)
|
||||
req, err := c.newRequest(http.MethodPost, uri, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record := &Record{}
|
||||
_, err = c.do(req, record)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// ListRecords returns list records for specific domain.
|
||||
func (c *Client) ListRecords(domainID int) ([]*Record, error) {
|
||||
uri := fmt.Sprintf("/%d/records/", domainID)
|
||||
req, err := c.newRequest(http.MethodGet, uri, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var records []*Record
|
||||
_, err = c.do(req, &records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// DeleteRecord deletes specific record.
|
||||
func (c *Client) DeleteRecord(domainID, recordID int) error {
|
||||
uri := fmt.Sprintf("/%d/records/%d", domainID, recordID)
|
||||
req, err := c.newRequest(http.MethodDelete, uri, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.do(req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) newRequest(method, uri string, body interface{}) (*http.Request, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if body != nil {
|
||||
err := json.NewEncoder(buf).Encode(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode request body with error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, c.baseURL+uri, buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create new http request with error: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Add("X-Token", c.token)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Accept", "application/json")
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (c *Client) do(req *http.Request, to interface{}) (*http.Response, error) {
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed with error: %v", err)
|
||||
}
|
||||
|
||||
err = checkResponse(resp)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if to != nil {
|
||||
if err = unmarshalBody(resp, to); err != nil {
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func checkResponse(resp *http.Response) error {
|
||||
if resp.StatusCode >= http.StatusBadRequest &&
|
||||
resp.StatusCode <= http.StatusNetworkAuthenticationRequired {
|
||||
|
||||
if resp.Body == nil {
|
||||
return fmt.Errorf("request failed with status code %d and empty body", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
apiError := APIError{}
|
||||
err = json.Unmarshal(body, &apiError)
|
||||
if err != nil {
|
||||
return fmt.Errorf("request failed with status code %d, response body: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
return fmt.Errorf("request failed with status code %d: %v", resp.StatusCode, apiError)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalBody(resp *http.Response, to interface{}) error {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = json.Unmarshal(body, to)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshaling error: %v: %s", err, string(body))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
153
vendor/github.com/xenolf/lego/providers/dns/selectel/selectel.go
generated
vendored
Normal file
153
vendor/github.com/xenolf/lego/providers/dns/selectel/selectel.go
generated
vendored
Normal file
|
@ -0,0 +1,153 @@
|
|||
// Package selectel implements a DNS provider for solving the DNS-01 challenge using Selectel Domains API.
|
||||
// Selectel Domain API reference: https://kb.selectel.com/23136054.html
|
||||
// Token: https://my.selectel.ru/profile/apikeys
|
||||
package selectel
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/platform/config/env"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBaseURL = "https://api.selectel.ru/domains/v1"
|
||||
minTTL = 60
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "SELECTEL_"
|
||||
baseURLEnvVar = envNamespace + "BASE_URL"
|
||||
apiTokenEnvVar = envNamespace + "API_TOKEN"
|
||||
ttlEnvVar = envNamespace + "TTL"
|
||||
propagationTimeoutEnvVar = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
pollingIntervalEnvVar = envNamespace + "POLLING_INTERVAL"
|
||||
httpTimeoutEnvVar = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider.
|
||||
type Config struct {
|
||||
BaseURL string
|
||||
Token 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{
|
||||
BaseURL: env.GetOrDefaultString(baseURLEnvVar, defaultBaseURL),
|
||||
TTL: env.GetOrDefaultInt(ttlEnvVar, minTTL),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(propagationTimeoutEnvVar, 120*time.Second),
|
||||
PollingInterval: env.GetOrDefaultSecond(pollingIntervalEnvVar, 2*time.Second),
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: env.GetOrDefaultSecond(httpTimeoutEnvVar, 30*time.Second),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
client *Client
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for Selectel Domains API.
|
||||
// API token must be passed in the environment variable SELECTEL_API_TOKEN.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(apiTokenEnvVar)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("selectel: %v", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.Token = values[apiTokenEnvVar]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for selectel.
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("selectel: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.Token == "" {
|
||||
return nil, errors.New("selectel: credentials missing")
|
||||
}
|
||||
|
||||
if config.TTL < minTTL {
|
||||
return nil, fmt.Errorf("selectel: invalid TTL, TTL (%d) must be greater than %d", config.TTL, minTTL)
|
||||
}
|
||||
|
||||
client := NewClient(ClientOpts{
|
||||
BaseURL: config.BaseURL,
|
||||
Token: config.Token,
|
||||
UserAgent: acme.UserAgent,
|
||||
HTTPClient: config.HTTPClient,
|
||||
})
|
||||
|
||||
return &DNSProvider{config: config, client: client}, 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
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfill DNS-01 challenge.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
domainObj, err := d.client.GetDomainByName(domain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectel: %v", err)
|
||||
}
|
||||
|
||||
txtRecord := Record{
|
||||
Type: "TXT",
|
||||
TTL: d.config.TTL,
|
||||
Name: fqdn,
|
||||
Content: value,
|
||||
}
|
||||
_, err = d.client.AddRecord(domainObj.ID, txtRecord)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectel: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes a TXT record used for DNS-01 challenge.
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
domainObj, err := d.client.GetDomainByName(domain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectel: %v", err)
|
||||
}
|
||||
|
||||
records, err := d.client.ListRecords(domainObj.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("selectel: %v", err)
|
||||
}
|
||||
|
||||
// Delete records with specific FQDN
|
||||
var lastErr error
|
||||
for _, record := range records {
|
||||
if record.Name == fqdn {
|
||||
err = d.client.DeleteRecord(domainObj.ID, record.ID)
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("selectel: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lastErr
|
||||
}
|
150
vendor/github.com/xenolf/lego/providers/dns/transip/transip.go
generated
vendored
Normal file
150
vendor/github.com/xenolf/lego/providers/dns/transip/transip.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
// Package transip implements a DNS provider for solving the DNS-01 challenge using TransIP.
|
||||
package transip
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/transip/gotransip"
|
||||
transipdomain "github.com/transip/gotransip/domain"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/platform/config/env"
|
||||
)
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider
|
||||
type Config struct {
|
||||
AccountName string
|
||||
PrivateKeyPath string
|
||||
PropagationTimeout time.Duration
|
||||
PollingInterval time.Duration
|
||||
TTL int64
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||
func NewDefaultConfig() *Config {
|
||||
return &Config{
|
||||
TTL: int64(env.GetOrDefaultInt("TRANSIP_TTL", 10)),
|
||||
PropagationTimeout: env.GetOrDefaultSecond("TRANSIP_PROPAGATION_TIMEOUT", 10*time.Minute),
|
||||
PollingInterval: env.GetOrDefaultSecond("TRANSIP_POLLING_INTERVAL", 10*time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider describes a provider for TransIP
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
client gotransip.SOAPClient
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for TransIP.
|
||||
// Credentials must be passed in the environment variables:
|
||||
// TRANSIP_ACCOUNTNAME, TRANSIP_PRIVATEKEYPATH.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get("TRANSIP_ACCOUNT_NAME", "TRANSIP_PRIVATE_KEY_PATH")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("transip: %v", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.AccountName = values["TRANSIP_ACCOUNT_NAME"]
|
||||
config.PrivateKeyPath = values["TRANSIP_PRIVATE_KEY_PATH"]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for TransIP.
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("transip: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
client, err := gotransip.NewSOAPClient(gotransip.ClientConfig{
|
||||
AccountName: config.AccountName,
|
||||
PrivateKeyPath: config.PrivateKeyPath,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("transip: %v", err)
|
||||
}
|
||||
|
||||
return &DNSProvider{client: client, config: config}, 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
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfill the dns-01 challenge
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainName := acme.UnFqdn(authZone)
|
||||
|
||||
// get the subDomain
|
||||
subDomain := strings.TrimSuffix(acme.UnFqdn(fqdn), "."+domainName)
|
||||
|
||||
// get all DNS entries
|
||||
info, err := transipdomain.GetInfo(d.client, domainName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("transip: error for %s in Present: %v", domain, err)
|
||||
}
|
||||
|
||||
// include the new DNS entry
|
||||
dnsEntries := append(info.DNSEntries, transipdomain.DNSEntry{
|
||||
Name: subDomain,
|
||||
TTL: d.config.TTL,
|
||||
Type: transipdomain.DNSEntryTypeTXT,
|
||||
Content: value,
|
||||
})
|
||||
|
||||
// set the updated DNS entries
|
||||
err = transipdomain.SetDNSEntries(d.client, domainName, dnsEntries)
|
||||
if err != nil {
|
||||
return fmt.Errorf("transip: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainName := acme.UnFqdn(authZone)
|
||||
|
||||
// get the subDomain
|
||||
subDomain := strings.TrimSuffix(acme.UnFqdn(fqdn), "."+domainName)
|
||||
|
||||
// get all DNS entries
|
||||
info, err := transipdomain.GetInfo(d.client, domainName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("transip: error for %s in CleanUp: %v", fqdn, err)
|
||||
}
|
||||
|
||||
// loop through the existing entries and remove the specific record
|
||||
updatedEntries := info.DNSEntries[:0]
|
||||
for _, e := range info.DNSEntries {
|
||||
if e.Name != subDomain {
|
||||
updatedEntries = append(updatedEntries, e)
|
||||
}
|
||||
}
|
||||
|
||||
// set the updated DNS entries
|
||||
err = transipdomain.SetDNSEntries(d.client, domainName, updatedEntries)
|
||||
if err != nil {
|
||||
return fmt.Errorf("transip: couldn't get Record ID in CleanUp: %sv", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
211
vendor/github.com/xenolf/lego/providers/dns/vscale/client.go
generated
vendored
Normal file
211
vendor/github.com/xenolf/lego/providers/dns/vscale/client.go
generated
vendored
Normal file
|
@ -0,0 +1,211 @@
|
|||
package vscale
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Domain represents domain name.
|
||||
type Domain struct {
|
||||
ID int `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// Record represents DNS record.
|
||||
type Record struct {
|
||||
ID int `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"` // Record type (SOA, NS, A/AAAA, CNAME, SRV, MX, TXT, SPF)
|
||||
TTL int `json:"ttl,omitempty"`
|
||||
Email string `json:"email,omitempty"` // Email of domain's admin (only for SOA records)
|
||||
Content string `json:"content,omitempty"` // Record content (not for SRV)
|
||||
}
|
||||
|
||||
// APIError API error message
|
||||
type APIError struct {
|
||||
Description string `json:"error"`
|
||||
Code int `json:"code"`
|
||||
Field string `json:"field"`
|
||||
}
|
||||
|
||||
func (a *APIError) Error() string {
|
||||
return fmt.Sprintf("API error: %d - %s - %s", a.Code, a.Description, a.Field)
|
||||
}
|
||||
|
||||
// ClientOpts represents options to init client.
|
||||
type ClientOpts struct {
|
||||
BaseURL string
|
||||
Token string
|
||||
UserAgent string
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// Client represents DNS client.
|
||||
type Client struct {
|
||||
baseURL string
|
||||
token string
|
||||
userAgent string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// NewClient returns a client instance.
|
||||
func NewClient(opts ClientOpts) *Client {
|
||||
if opts.HTTPClient == nil {
|
||||
opts.HTTPClient = &http.Client{}
|
||||
}
|
||||
|
||||
return &Client{
|
||||
token: opts.Token,
|
||||
baseURL: opts.BaseURL,
|
||||
httpClient: opts.HTTPClient,
|
||||
userAgent: opts.UserAgent,
|
||||
}
|
||||
}
|
||||
|
||||
// GetDomainByName gets Domain object by its name.
|
||||
func (c *Client) GetDomainByName(domainName string) (*Domain, error) {
|
||||
uri := fmt.Sprintf("/%s", domainName)
|
||||
req, err := c.newRequest(http.MethodGet, uri, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domain := &Domain{}
|
||||
_, err = c.do(req, domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
// AddRecord adds Record for given domain.
|
||||
func (c *Client) AddRecord(domainID int, body Record) (*Record, error) {
|
||||
uri := fmt.Sprintf("/%d/records/", domainID)
|
||||
req, err := c.newRequest(http.MethodPost, uri, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record := &Record{}
|
||||
_, err = c.do(req, record)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return record, nil
|
||||
}
|
||||
|
||||
// ListRecords returns list records for specific domain.
|
||||
func (c *Client) ListRecords(domainID int) ([]*Record, error) {
|
||||
uri := fmt.Sprintf("/%d/records/", domainID)
|
||||
req, err := c.newRequest(http.MethodGet, uri, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var records []*Record
|
||||
_, err = c.do(req, &records)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// DeleteRecord deletes specific record.
|
||||
func (c *Client) DeleteRecord(domainID, recordID int) error {
|
||||
uri := fmt.Sprintf("/%d/records/%d", domainID, recordID)
|
||||
req, err := c.newRequest(http.MethodDelete, uri, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.do(req, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) newRequest(method, uri string, body interface{}) (*http.Request, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
if body != nil {
|
||||
err := json.NewEncoder(buf).Encode(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode request body with error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, c.baseURL+uri, buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create new http request with error: %v", err)
|
||||
}
|
||||
|
||||
req.Header.Add("X-Token", c.token)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Accept", "application/json")
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (c *Client) do(req *http.Request, to interface{}) (*http.Response, error) {
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("request failed with error: %v", err)
|
||||
}
|
||||
|
||||
err = checkResponse(resp)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if to != nil {
|
||||
if err = unmarshalBody(resp, to); err != nil {
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func checkResponse(resp *http.Response) error {
|
||||
if resp.StatusCode >= http.StatusBadRequest &&
|
||||
resp.StatusCode <= http.StatusNetworkAuthenticationRequired {
|
||||
|
||||
if resp.Body == nil {
|
||||
return fmt.Errorf("request failed with status code %d and empty body", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
apiError := APIError{}
|
||||
err = json.Unmarshal(body, &apiError)
|
||||
if err != nil {
|
||||
return fmt.Errorf("request failed with status code %d, response body: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
return fmt.Errorf("request failed with status code %d: %v", resp.StatusCode, apiError)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unmarshalBody(resp *http.Response, to interface{}) error {
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = json.Unmarshal(body, to)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshaling error: %v: %s", err, string(body))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
153
vendor/github.com/xenolf/lego/providers/dns/vscale/vscale.go
generated
vendored
Normal file
153
vendor/github.com/xenolf/lego/providers/dns/vscale/vscale.go
generated
vendored
Normal file
|
@ -0,0 +1,153 @@
|
|||
// Package selectel implements a DNS provider for solving the DNS-01 challenge using Vscale Domains API.
|
||||
// Vscale Domain API reference: https://developers.vscale.io/documentation/api/v1/#api-Domains
|
||||
// Token: https://vscale.io/panel/settings/tokens/
|
||||
package vscale
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/platform/config/env"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBaseURL = "https://api.vscale.io/v1/domains"
|
||||
minTTL = 60
|
||||
)
|
||||
|
||||
const (
|
||||
envNamespace = "VSCALE_"
|
||||
baseURLEnvVar = envNamespace + "BASE_URL"
|
||||
apiTokenEnvVar = envNamespace + "API_TOKEN"
|
||||
ttlEnvVar = envNamespace + "TTL"
|
||||
propagationTimeoutEnvVar = envNamespace + "PROPAGATION_TIMEOUT"
|
||||
pollingIntervalEnvVar = envNamespace + "POLLING_INTERVAL"
|
||||
httpTimeoutEnvVar = envNamespace + "HTTP_TIMEOUT"
|
||||
)
|
||||
|
||||
// Config is used to configure the creation of the DNSProvider.
|
||||
type Config struct {
|
||||
BaseURL string
|
||||
Token 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{
|
||||
BaseURL: env.GetOrDefaultString(baseURLEnvVar, defaultBaseURL),
|
||||
TTL: env.GetOrDefaultInt(ttlEnvVar, minTTL),
|
||||
PropagationTimeout: env.GetOrDefaultSecond(propagationTimeoutEnvVar, 120*time.Second),
|
||||
PollingInterval: env.GetOrDefaultSecond(pollingIntervalEnvVar, 2*time.Second),
|
||||
HTTPClient: &http.Client{
|
||||
Timeout: env.GetOrDefaultSecond(httpTimeoutEnvVar, 30*time.Second),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
|
||||
type DNSProvider struct {
|
||||
config *Config
|
||||
client *Client
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for Vscale Domains API.
|
||||
// API token must be passed in the environment variable VSCALE_API_TOKEN.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
values, err := env.Get(apiTokenEnvVar)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vscale: %v", err)
|
||||
}
|
||||
|
||||
config := NewDefaultConfig()
|
||||
config.Token = values[apiTokenEnvVar]
|
||||
|
||||
return NewDNSProviderConfig(config)
|
||||
}
|
||||
|
||||
// NewDNSProviderConfig return a DNSProvider instance configured for Vscale.
|
||||
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("vscale: the configuration of the DNS provider is nil")
|
||||
}
|
||||
|
||||
if config.Token == "" {
|
||||
return nil, errors.New("vscale: credentials missing")
|
||||
}
|
||||
|
||||
if config.TTL < minTTL {
|
||||
return nil, fmt.Errorf("vscale: invalid TTL, TTL (%d) must be greater than %d", config.TTL, minTTL)
|
||||
}
|
||||
|
||||
client := NewClient(ClientOpts{
|
||||
BaseURL: config.BaseURL,
|
||||
Token: config.Token,
|
||||
UserAgent: acme.UserAgent,
|
||||
HTTPClient: config.HTTPClient,
|
||||
})
|
||||
|
||||
return &DNSProvider{config: config, client: client}, 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
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfill DNS-01 challenge.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
domainObj, err := d.client.GetDomainByName(domain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vscale: %v", err)
|
||||
}
|
||||
|
||||
txtRecord := Record{
|
||||
Type: "TXT",
|
||||
TTL: d.config.TTL,
|
||||
Name: fqdn,
|
||||
Content: value,
|
||||
}
|
||||
_, err = d.client.AddRecord(domainObj.ID, txtRecord)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vscale: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes a TXT record used for DNS-01 challenge.
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
|
||||
domainObj, err := d.client.GetDomainByName(domain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vscale: %v", err)
|
||||
}
|
||||
|
||||
records, err := d.client.ListRecords(domainObj.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("vscale: %v", err)
|
||||
}
|
||||
|
||||
// Delete records with specific FQDN
|
||||
var lastErr error
|
||||
for _, record := range records {
|
||||
if record.Name == fqdn {
|
||||
err = d.client.DeleteRecord(domainObj.ID, record.ID)
|
||||
if err != nil {
|
||||
lastErr = fmt.Errorf("vscale: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lastErr
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue