Update lego
This commit is contained in:
parent
c8ae97fd38
commit
aabebb2185
13 changed files with 696 additions and 112 deletions
9
Gopkg.lock
generated
9
Gopkg.lock
generated
|
@ -316,6 +316,12 @@
|
||||||
revision = "48702e0da86bd25e76cfef347e2adeb434a0d0a6"
|
revision = "48702e0da86bd25e76cfef347e2adeb434a0d0a6"
|
||||||
version = "v14"
|
version = "v14"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/cpu/goacmedns"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "565ecf2a84df654865cc102705ac160a3b04fc01"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/davecgh/go-spew"
|
name = "github.com/davecgh/go-spew"
|
||||||
packages = ["spew"]
|
packages = ["spew"]
|
||||||
|
@ -1299,6 +1305,7 @@
|
||||||
"log",
|
"log",
|
||||||
"platform/config/env",
|
"platform/config/env",
|
||||||
"providers/dns",
|
"providers/dns",
|
||||||
|
"providers/dns/acmedns",
|
||||||
"providers/dns/auroradns",
|
"providers/dns/auroradns",
|
||||||
"providers/dns/azure",
|
"providers/dns/azure",
|
||||||
"providers/dns/bluecat",
|
"providers/dns/bluecat",
|
||||||
|
@ -1334,7 +1341,7 @@
|
||||||
"providers/dns/vegadns",
|
"providers/dns/vegadns",
|
||||||
"providers/dns/vultr"
|
"providers/dns/vultr"
|
||||||
]
|
]
|
||||||
revision = "e0d512138c43e3f056a41cd7a5beff662ec130d3"
|
revision = "8b6701514cc0a6285a327908f3f9ce05bcacbffd"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
|
21
vendor/github.com/cpu/goacmedns/LICENSE
generated
vendored
Normal file
21
vendor/github.com/cpu/goacmedns/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Daniel McCarney
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
11
vendor/github.com/cpu/goacmedns/account.go
generated
vendored
Normal file
11
vendor/github.com/cpu/goacmedns/account.go
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package goacmedns
|
||||||
|
|
||||||
|
// Account is a struct that holds the registration response from an ACME-DNS
|
||||||
|
// server. It represents an API username/key that can be used to update TXT
|
||||||
|
// records for the account's subdomain.
|
||||||
|
type Account struct {
|
||||||
|
FullDomain string
|
||||||
|
SubDomain string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
}
|
191
vendor/github.com/cpu/goacmedns/client.go
generated
vendored
Normal file
191
vendor/github.com/cpu/goacmedns/client.go
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
package goacmedns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ua is a custom user-agent identifier
|
||||||
|
ua = "goacmedns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// userAgent returns a string that can be used as a HTTP request `User-Agent`
|
||||||
|
// header. It includes the `ua` string alongside the OS and architecture of the
|
||||||
|
// system.
|
||||||
|
func userAgent() string {
|
||||||
|
return fmt.Sprintf("%s (%s; %s)", ua, runtime.GOOS, runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// defaultTimeout is used for the httpClient Timeout settings
|
||||||
|
defaultTimeout = 30 * time.Second
|
||||||
|
// httpClient is a `http.Client` that is customized with the `defaultTimeout`
|
||||||
|
httpClient = http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
Dial: (&net.Dialer{
|
||||||
|
Timeout: defaultTimeout,
|
||||||
|
KeepAlive: defaultTimeout,
|
||||||
|
}).Dial,
|
||||||
|
TLSHandshakeTimeout: defaultTimeout,
|
||||||
|
ResponseHeaderTimeout: defaultTimeout,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// postAPI makes an HTTP POST request to the given URL, sending the given body
|
||||||
|
// and attaching the requested custom headers to the request. If there is no
|
||||||
|
// error the HTTP response body and HTTP response object are returned, otherwise
|
||||||
|
// an error is returned.. All POST requests include a `User-Agent` header
|
||||||
|
// populated with the `userAgent` function and a `Content-Type` header of
|
||||||
|
// `application/json`.
|
||||||
|
func postAPI(url string, body []byte, headers map[string]string) ([]byte, *http.Response, error) {
|
||||||
|
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to make req: %s\n", err.Error())
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("User-Agent", userAgent())
|
||||||
|
for h, v := range headers {
|
||||||
|
req.Header.Set(h, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to do req: %s\n", err.Error())
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
respBody, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to read body: %s\n", err.Error())
|
||||||
|
return nil, resp, err
|
||||||
|
}
|
||||||
|
return respBody, resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientError represents an error from the ACME-DNS server. It holds
|
||||||
|
// a `Message` describing the operation the client was doing, a `HTTPStatus`
|
||||||
|
// code returned by the server, and the `Body` of the HTTP Response from the
|
||||||
|
// server.
|
||||||
|
type ClientError struct {
|
||||||
|
// Message is a string describing the client operation that failed
|
||||||
|
Message string
|
||||||
|
// HTTPStatus is the HTTP status code the ACME DNS server returned
|
||||||
|
HTTPStatus int
|
||||||
|
// Body is the response body the ACME DNS server returned
|
||||||
|
Body []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error collects all of the ClientError fields into a single string
|
||||||
|
func (e ClientError) Error() string {
|
||||||
|
return fmt.Sprintf("%s : status code %d response: %s",
|
||||||
|
e.Message, e.HTTPStatus, string(e.Body))
|
||||||
|
}
|
||||||
|
|
||||||
|
// newClientError creates a ClientError instance populated with the given
|
||||||
|
// arguments
|
||||||
|
func newClientError(msg string, respCode int, respBody []byte) ClientError {
|
||||||
|
return ClientError{
|
||||||
|
Message: msg,
|
||||||
|
HTTPStatus: respCode,
|
||||||
|
Body: respBody,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client is a struct that can be used to interact with an ACME DNS server to
|
||||||
|
// register accounts and update TXT records.
|
||||||
|
type Client struct {
|
||||||
|
// baseURL is the address of the ACME DNS server
|
||||||
|
baseURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient returns a Client configured to interact with the ACME DNS server at
|
||||||
|
// the given URL.
|
||||||
|
func NewClient(url string) Client {
|
||||||
|
return Client{
|
||||||
|
baseURL: url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterAccount creates an Account with the ACME DNS server. The optional
|
||||||
|
// `allowFrom` argument is used to constrain which CIDR ranges can use the
|
||||||
|
// created Account.
|
||||||
|
func (c Client) RegisterAccount(allowFrom []string) (Account, error) {
|
||||||
|
var body []byte
|
||||||
|
if len(allowFrom) > 0 {
|
||||||
|
req := struct {
|
||||||
|
AllowFrom []string
|
||||||
|
}{
|
||||||
|
AllowFrom: allowFrom,
|
||||||
|
}
|
||||||
|
reqBody, err := json.Marshal(req)
|
||||||
|
if err != nil {
|
||||||
|
return Account{}, err
|
||||||
|
}
|
||||||
|
body = reqBody
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("%s/register", c.baseURL)
|
||||||
|
respBody, resp, err := postAPI(url, body, nil)
|
||||||
|
if err != nil {
|
||||||
|
return Account{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusCreated {
|
||||||
|
return Account{}, newClientError(
|
||||||
|
"failed to register account", resp.StatusCode, respBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
var acct Account
|
||||||
|
err = json.Unmarshal(respBody, &acct)
|
||||||
|
if err != nil {
|
||||||
|
return Account{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return acct, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateTXTRecord updates a TXT record with the ACME DNS server to the `value`
|
||||||
|
// provided using the `account` specified.
|
||||||
|
func (c Client) UpdateTXTRecord(account Account, value string) error {
|
||||||
|
update := struct {
|
||||||
|
SubDomain string
|
||||||
|
Txt string
|
||||||
|
}{
|
||||||
|
SubDomain: account.SubDomain,
|
||||||
|
Txt: value,
|
||||||
|
}
|
||||||
|
updateBody, err := json.Marshal(update)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to marshal update: %s\n", update)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
headers := map[string]string{
|
||||||
|
"X-Api-User": account.Username,
|
||||||
|
"X-Api-Key": account.Password,
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf("%s/update", c.baseURL)
|
||||||
|
respBody, resp, err := postAPI(url, updateBody, headers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return newClientError(
|
||||||
|
"failed to update txt record", resp.StatusCode, respBody)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
89
vendor/github.com/cpu/goacmedns/storage.go
generated
vendored
Normal file
89
vendor/github.com/cpu/goacmedns/storage.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package goacmedns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Storage is an interface describing the required functions for an ACME DNS
|
||||||
|
// Account storage mechanism.
|
||||||
|
type Storage interface {
|
||||||
|
// Save will persist the `Account` data that has been `Put` so far
|
||||||
|
Save() error
|
||||||
|
// Put will add an `Account` for the given domain to the storage. It may not
|
||||||
|
// be persisted until `Save` is called.
|
||||||
|
Put(string, Account) error
|
||||||
|
// Fetch will retrieve an `Account` for the given domain from the storage. If
|
||||||
|
// the provided domain does not have an `Account` saved in the storage
|
||||||
|
// `ErrDomainNotFound` will be returned
|
||||||
|
Fetch(string) (Account, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrDomainNotFound is returned from `Fetch` when the provided domain is not
|
||||||
|
// present in the storage.
|
||||||
|
ErrDomainNotFound = errors.New("requested domain is not present in storage")
|
||||||
|
)
|
||||||
|
|
||||||
|
// fileStorage implements the `Storage` interface and persists `Accounts` to
|
||||||
|
// a JSON file on disk.
|
||||||
|
type fileStorage struct {
|
||||||
|
// path is the filepath that the `accounts` are persisted to when the `Save`
|
||||||
|
// function is called.
|
||||||
|
path string
|
||||||
|
// mode is the file mode used when the `path` JSON file must be created
|
||||||
|
mode os.FileMode
|
||||||
|
// accounts holds the `Account` data that has been `Put` into the storage
|
||||||
|
accounts map[string]Account
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFileStorage returns a `Storage` implementation backed by JSON content
|
||||||
|
// saved into the provided `path` on disk. The file at `path` will be created if
|
||||||
|
// required. When creating a new file the provided `mode` is used to set the
|
||||||
|
// permissions.
|
||||||
|
func NewFileStorage(path string, mode os.FileMode) Storage {
|
||||||
|
fs := fileStorage{
|
||||||
|
path: path,
|
||||||
|
mode: mode,
|
||||||
|
accounts: make(map[string]Account),
|
||||||
|
}
|
||||||
|
// Opportunistically try to load the account data. Return an empty account if
|
||||||
|
// any errors occur.
|
||||||
|
if jsonData, err := ioutil.ReadFile(path); err == nil {
|
||||||
|
if err := json.Unmarshal(jsonData, &fs.accounts); err != nil {
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save persists the `Account` data to the fileStorage's configured path. The
|
||||||
|
// file at that path will be created with the fileStorage's mode if required.
|
||||||
|
func (f fileStorage) Save() error {
|
||||||
|
if serialized, err := json.Marshal(f.accounts); err != nil {
|
||||||
|
return err
|
||||||
|
} else if err = ioutil.WriteFile(f.path, serialized, f.mode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put saves an `Account` for the given `Domain` into the in-memory accounts of
|
||||||
|
// the fileStorage instance. The `Account` data will not be written to disk
|
||||||
|
// until the `Save` function is called
|
||||||
|
func (f fileStorage) Put(domain string, acct Account) error {
|
||||||
|
f.accounts[domain] = acct
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch retrieves the `Account` object for the given `domain` from the
|
||||||
|
// fileStorage in-memory accounts. If the `domain` provided does not have an
|
||||||
|
// `Account` in the storage an `ErrDomainNotFound` error is returned.
|
||||||
|
func (f fileStorage) Fetch(domain string) (Account, error) {
|
||||||
|
if acct, exists := f.accounts[domain]; exists {
|
||||||
|
return acct, nil
|
||||||
|
}
|
||||||
|
return Account{}, ErrDomainNotFound
|
||||||
|
}
|
170
vendor/github.com/xenolf/lego/providers/dns/acmedns/acmedns.go
generated
vendored
Normal file
170
vendor/github.com/xenolf/lego/providers/dns/acmedns/acmedns.go
generated
vendored
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
// Package acmedns implements a DNS provider for solving DNS-01 challenges using
|
||||||
|
// Joohoi's acme-dns project. For more information see the ACME-DNS homepage:
|
||||||
|
// https://github.com/joohoi/acme-dns
|
||||||
|
package acmedns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/cpu/goacmedns"
|
||||||
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// envNamespace is the prefix for ACME-DNS environment variables.
|
||||||
|
envNamespace = "ACME_DNS_"
|
||||||
|
// apiBaseEnvVar is the environment variable name for the ACME-DNS API address
|
||||||
|
// (e.g. https://acmedns.your-domain.com).
|
||||||
|
apiBaseEnvVar = envNamespace + "API_BASE"
|
||||||
|
// storagePathEnvVar is the environment variable name for the ACME-DNS JSON
|
||||||
|
// account data file. A per-domain account will be registered/persisted to
|
||||||
|
// this file and used for TXT updates.
|
||||||
|
storagePathEnvVar = envNamespace + "STORAGE_PATH"
|
||||||
|
)
|
||||||
|
|
||||||
|
// acmeDNSClient is an interface describing the goacmedns.Client functions
|
||||||
|
// the DNSProvider uses. It makes it easier for tests to shim a mock Client into
|
||||||
|
// the DNSProvider.
|
||||||
|
type acmeDNSClient interface {
|
||||||
|
// UpdateTXTRecord updates the provided account's TXT record to the given
|
||||||
|
// value or returns an error.
|
||||||
|
UpdateTXTRecord(goacmedns.Account, string) error
|
||||||
|
// RegisterAccount registers and returns a new account with the given
|
||||||
|
// allowFrom restriction or returns an error.
|
||||||
|
RegisterAccount([]string) (goacmedns.Account, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DNSProvider is an implementation of the acme.ChallengeProvider interface for
|
||||||
|
// an ACME-DNS server.
|
||||||
|
type DNSProvider struct {
|
||||||
|
client acmeDNSClient
|
||||||
|
storage goacmedns.Storage
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProvider creates an ACME-DNS provider using file based account storage.
|
||||||
|
// Its configuration is loaded from the environment by reading apiBaseEnvVar and
|
||||||
|
// storagePathEnvVar.
|
||||||
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
values, err := env.Get(apiBaseEnvVar, storagePathEnvVar)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("acme-dns: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
client := goacmedns.NewClient(values[apiBaseEnvVar])
|
||||||
|
storage := goacmedns.NewFileStorage(values[storagePathEnvVar], 0600)
|
||||||
|
return NewDNSProviderClient(client, storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderClient creates an ACME-DNS DNSProvider with the given
|
||||||
|
// acmeDNSClient and goacmedns.Storage.
|
||||||
|
func NewDNSProviderClient(client acmeDNSClient, storage goacmedns.Storage) (*DNSProvider, error) {
|
||||||
|
if client == nil {
|
||||||
|
return nil, errors.New("ACME-DNS Client must be not nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if storage == nil {
|
||||||
|
return nil, errors.New("ACME-DNS Storage must be not nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DNSProvider{
|
||||||
|
client: client,
|
||||||
|
storage: storage,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrCNAMERequired is returned by Present when the Domain indicated had no
|
||||||
|
// existing ACME-DNS account in the Storage and additional setup is required.
|
||||||
|
// The user must create a CNAME in the DNS zone for Domain that aliases FQDN
|
||||||
|
// to Target in order to complete setup for the ACME-DNS account that was
|
||||||
|
// created.
|
||||||
|
type ErrCNAMERequired struct {
|
||||||
|
// The Domain that is being issued for.
|
||||||
|
Domain string
|
||||||
|
// The alias of the CNAME (left hand DNS label).
|
||||||
|
FQDN string
|
||||||
|
// The RDATA of the CNAME (right hand side, canonical name).
|
||||||
|
Target string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns a descriptive message for the ErrCNAMERequired instance telling
|
||||||
|
// the user that a CNAME needs to be added to the DNS zone of c.Domain before
|
||||||
|
// the ACME-DNS hook will work. The CNAME to be created should be of the form:
|
||||||
|
// {{ c.FQDN }} CNAME {{ c.Target }}
|
||||||
|
func (e ErrCNAMERequired) Error() string {
|
||||||
|
return fmt.Sprintf("acme-dns: new account created for %q. "+
|
||||||
|
"To complete setup for %q you must provision the following "+
|
||||||
|
"CNAME in your DNS zone and re-run this provider when it is "+
|
||||||
|
"in place:\n"+
|
||||||
|
"%s CNAME %s.",
|
||||||
|
e.Domain, e.Domain, e.FQDN, e.Target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present creates a TXT record to fulfil the DNS-01 challenge. If there is an
|
||||||
|
// existing account for the domain in the provider's storage then it will be
|
||||||
|
// used to set the challenge response TXT record with the ACME-DNS server and
|
||||||
|
// issuance will continue. If there is not an account for the given domain
|
||||||
|
// present in the DNSProvider storage one will be created and registered with
|
||||||
|
// the ACME DNS server and an ErrCNAMERequired error is returned. This will halt
|
||||||
|
// issuance and indicate to the user that a one-time manual setup is required
|
||||||
|
// for the domain.
|
||||||
|
func (d *DNSProvider) Present(domain, _, keyAuth string) error {
|
||||||
|
// Compute the challenge response FQDN and TXT value for the domain based
|
||||||
|
// on the keyAuth.
|
||||||
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
|
// Check if credentials were previously saved for this domain.
|
||||||
|
account, err := d.storage.Fetch(domain)
|
||||||
|
// Errors other than goacmeDNS.ErrDomainNotFound are unexpected.
|
||||||
|
if err != nil && err != goacmedns.ErrDomainNotFound {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err == goacmedns.ErrDomainNotFound {
|
||||||
|
// The account did not exist. Create a new one and return an error
|
||||||
|
// indicating the required one-time manual CNAME setup.
|
||||||
|
return d.register(domain, fqdn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the acme-dns TXT record.
|
||||||
|
return d.client.UpdateTXTRecord(account, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CleanUp removes the record matching the specified parameters. It is not
|
||||||
|
// implemented for the ACME-DNS provider.
|
||||||
|
func (d *DNSProvider) CleanUp(_, _, _ string) error {
|
||||||
|
// ACME-DNS doesn't support the notion of removing a record. For users of
|
||||||
|
// ACME-DNS it is expected the stale records remain in-place.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// register creates a new ACME-DNS account for the given domain. If account
|
||||||
|
// creation works as expected a ErrCNAMERequired error is returned describing
|
||||||
|
// the one-time manual CNAME setup required to complete setup of the ACME-DNS
|
||||||
|
// hook for the domain. If any other error occurs it is returned as-is.
|
||||||
|
func (d *DNSProvider) register(domain, fqdn string) error {
|
||||||
|
// TODO(@cpu): Read CIDR whitelists from the environment
|
||||||
|
newAcct, err := d.client.RegisterAccount(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the new account in the storage and call save to persist the data.
|
||||||
|
err = d.storage.Put(domain, newAcct)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.storage.Save()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop issuance by returning an error. The user needs to perform a manual
|
||||||
|
// one-time CNAME setup in their DNS zone to complete the setup of the new
|
||||||
|
// account we created.
|
||||||
|
return ErrCNAMERequired{
|
||||||
|
Domain: domain,
|
||||||
|
FQDN: fqdn,
|
||||||
|
Target: newAcct.FullDomain,
|
||||||
|
}
|
||||||
|
}
|
3
vendor/github.com/xenolf/lego/providers/dns/dns_providers.go
generated
vendored
3
vendor/github.com/xenolf/lego/providers/dns/dns_providers.go
generated
vendored
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/providers/dns/acmedns"
|
||||||
"github.com/xenolf/lego/providers/dns/auroradns"
|
"github.com/xenolf/lego/providers/dns/auroradns"
|
||||||
"github.com/xenolf/lego/providers/dns/azure"
|
"github.com/xenolf/lego/providers/dns/azure"
|
||||||
"github.com/xenolf/lego/providers/dns/bluecat"
|
"github.com/xenolf/lego/providers/dns/bluecat"
|
||||||
|
@ -43,6 +44,8 @@ import (
|
||||||
// NewDNSChallengeProviderByName Factory for DNS providers
|
// NewDNSChallengeProviderByName Factory for DNS providers
|
||||||
func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) {
|
func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) {
|
||||||
switch name {
|
switch name {
|
||||||
|
case "acme-dns":
|
||||||
|
return acmedns.NewDNSProvider()
|
||||||
case "azure":
|
case "azure":
|
||||||
return azure.NewDNSProvider()
|
return azure.NewDNSProvider()
|
||||||
case "auroradns":
|
case "auroradns":
|
||||||
|
|
52
vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go
generated
vendored
52
vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go
generated
vendored
|
@ -30,26 +30,32 @@ func NewDNSProvider() (*DNSProvider, error) {
|
||||||
|
|
||||||
// NewDNSProviderCredentials uses the supplied credentials to return a
|
// NewDNSProviderCredentials uses the supplied credentials to return a
|
||||||
// DNSProvider instance configured for http://duckdns.org .
|
// DNSProvider instance configured for http://duckdns.org .
|
||||||
func NewDNSProviderCredentials(duckdnsToken string) (*DNSProvider, error) {
|
func NewDNSProviderCredentials(token string) (*DNSProvider, error) {
|
||||||
if duckdnsToken == "" {
|
if token == "" {
|
||||||
return nil, errors.New("DuckDNS: credentials missing")
|
return nil, errors.New("DuckDNS: credentials missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DNSProvider{token: duckdnsToken}, nil
|
return &DNSProvider{token: token}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeDuckdnsURL creates a url to clear the set or unset the TXT record.
|
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||||
// txt == "" will clear the TXT record.
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
func makeDuckdnsURL(domain, token, txt string) string {
|
_, txtRecord, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
requestBase := fmt.Sprintf("https://www.duckdns.org/update?domains=%s&token=%s", domain, token)
|
return updateTxtRecord(domain, d.token, txtRecord, false)
|
||||||
if txt == "" {
|
|
||||||
return requestBase + "&clear=true"
|
|
||||||
}
|
|
||||||
return requestBase + "&txt=" + txt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func issueDuckdnsRequest(url string) error {
|
// CleanUp clears DuckDNS TXT record
|
||||||
response, err := acme.HTTPClient.Get(url)
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
return updateTxtRecord(domain, d.token, "", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateTxtRecord Update the domains TXT record
|
||||||
|
// To update the TXT record we just need to make one simple get request.
|
||||||
|
// In DuckDNS you only have one TXT record shared with the domain and all sub domains.
|
||||||
|
func updateTxtRecord(domain, token, txt string, clear bool) error {
|
||||||
|
u := fmt.Sprintf("https://www.duckdns.org/update?domains=%s&token=%s&clear=%t&txt=%s", domain, token, clear, txt)
|
||||||
|
|
||||||
|
response, err := acme.HTTPClient.Get(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -59,26 +65,10 @@ func issueDuckdnsRequest(url string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
body := string(bodyBytes)
|
body := string(bodyBytes)
|
||||||
if body != "OK" {
|
if body != "OK" {
|
||||||
return fmt.Errorf("Request to change TXT record for duckdns returned the following result (%s) this does not match expectation (OK) used url [%s]", body, url)
|
return fmt.Errorf("request to change TXT record for DuckDNS returned the following result (%s) this does not match expectation (OK) used url [%s]", body, u)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
|
||||||
// In duckdns you only have one TXT record shared with
|
|
||||||
// the domain and all sub domains.
|
|
||||||
//
|
|
||||||
// To update the TXT record we just need to make one simple get request.
|
|
||||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|
||||||
_, txtRecord, _ := acme.DNS01Record(domain, keyAuth)
|
|
||||||
url := makeDuckdnsURL(domain, d.token, txtRecord)
|
|
||||||
return issueDuckdnsRequest(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CleanUp clears duckdns TXT record
|
|
||||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|
||||||
url := makeDuckdnsURL(domain, d.token, "")
|
|
||||||
return issueDuckdnsRequest(url)
|
|
||||||
}
|
|
||||||
|
|
42
vendor/github.com/xenolf/lego/providers/dns/exec/doc.go
generated
vendored
Normal file
42
vendor/github.com/xenolf/lego/providers/dns/exec/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
Package exec implements a manual DNS provider which runs a program for adding/removing the DNS record.
|
||||||
|
|
||||||
|
The file name of the external program is specified in the environment variable `EXEC_PATH`.
|
||||||
|
When it is run by lego, three command-line parameters are passed to it:
|
||||||
|
The action ("present" or "cleanup"), the fully-qualified domain name, the value for the record and the TTL.
|
||||||
|
|
||||||
|
For example, requesting a certificate for the domain 'foo.example.com' can be achieved by calling lego as follows:
|
||||||
|
|
||||||
|
EXEC_PATH=./update-dns.sh \
|
||||||
|
lego --dns exec \
|
||||||
|
--domains foo.example.com \
|
||||||
|
--email invalid@example.com run
|
||||||
|
|
||||||
|
It will then call the program './update-dns.sh' with like this:
|
||||||
|
|
||||||
|
./update-dns.sh "present" "_acme-challenge.foo.example.com." "MsijOYZxqyjGnFGwhjrhfg-Xgbl5r68WPda0J9EgqqI" "120"
|
||||||
|
|
||||||
|
The program then needs to make sure the record is inserted.
|
||||||
|
When it returns an error via a non-zero exit code, lego aborts.
|
||||||
|
|
||||||
|
When the record is to be removed again,
|
||||||
|
the program is called with the first command-line parameter set to "cleanup" instead of "present".
|
||||||
|
|
||||||
|
If you want to use the raw domain, token, and keyAuth values with your program, you can set `EXEC_MODE=RAW`:
|
||||||
|
|
||||||
|
EXEC_MODE=RAW \
|
||||||
|
EXEC_PATH=./update-dns.sh \
|
||||||
|
lego --dns exec \
|
||||||
|
--domains foo.example.com \
|
||||||
|
--email invalid@example.com run
|
||||||
|
|
||||||
|
It will then call the program './update-dns.sh' like this:
|
||||||
|
|
||||||
|
./update-dns.sh "present" "foo.example.com." "--" "some-token" "KxAy-J3NwUmg9ZQuM-gP_Mq1nStaYSaP9tYQs5_-YsE.ksT-qywTd8058G-SHHWA3RAN72Pr0yWtPYmmY5UBpQ8"
|
||||||
|
|
||||||
|
NOTE:
|
||||||
|
The `--` is because the token MAY start with a `-`, and the called program may try and interpret a - as indicating a flag.
|
||||||
|
In the case of urfave, which is commonly used,
|
||||||
|
you can use the `--` delimiter to specify the start of positional arguments, and handle such a string safely.
|
||||||
|
*/
|
||||||
|
package exec
|
104
vendor/github.com/xenolf/lego/providers/dns/exec/exec.go
generated
vendored
104
vendor/github.com/xenolf/lego/providers/dns/exec/exec.go
generated
vendored
|
@ -1,78 +1,100 @@
|
||||||
// Package exec implements a manual DNS provider which runs a program for
|
|
||||||
// adding/removing the DNS record.
|
|
||||||
//
|
|
||||||
// The file name of the external program is specified in the environment
|
|
||||||
// variable EXEC_PATH. When it is run by lego, three command-line parameters
|
|
||||||
// are passed to it: The action ("present" or "cleanup"), the fully-qualified domain
|
|
||||||
// name, the value for the record and the TTL.
|
|
||||||
//
|
|
||||||
// For example, requesting a certificate for the domain 'foo.example.com' can
|
|
||||||
// be achieved by calling lego as follows:
|
|
||||||
//
|
|
||||||
// EXEC_PATH=./update-dns.sh \
|
|
||||||
// lego --dns exec \
|
|
||||||
// --domains foo.example.com \
|
|
||||||
// --email invalid@example.com run
|
|
||||||
//
|
|
||||||
// It will then call the program './update-dns.sh' with like this:
|
|
||||||
//
|
|
||||||
// ./update-dns.sh "present" "_acme-challenge.foo.example.com." "MsijOYZxqyjGnFGwhjrhfg-Xgbl5r68WPda0J9EgqqI" "120"
|
|
||||||
//
|
|
||||||
// The program then needs to make sure the record is inserted. When it returns
|
|
||||||
// an error via a non-zero exit code, lego aborts.
|
|
||||||
//
|
|
||||||
// When the record is to be removed again, the program is called with the first
|
|
||||||
// command-line parameter set to "cleanup" instead of "present".
|
|
||||||
package exec
|
package exec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/log"
|
||||||
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Config Provider configuration.
|
||||||
|
type Config struct {
|
||||||
|
Program string
|
||||||
|
Mode string
|
||||||
|
}
|
||||||
|
|
||||||
// DNSProvider adds and removes the record for the DNS challenge by calling a
|
// DNSProvider adds and removes the record for the DNS challenge by calling a
|
||||||
// program with command-line parameters.
|
// program with command-line parameters.
|
||||||
type DNSProvider struct {
|
type DNSProvider struct {
|
||||||
program string
|
config *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a new DNS provider which runs the program in the
|
// NewDNSProvider returns a new DNS provider which runs the program in the
|
||||||
// environment variable EXEC_PATH for adding and removing the DNS record.
|
// environment variable EXEC_PATH for adding and removing the DNS record.
|
||||||
func NewDNSProvider() (*DNSProvider, error) {
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
s := os.Getenv("EXEC_PATH")
|
values, err := env.Get("EXEC_PATH")
|
||||||
if s == "" {
|
if err != nil {
|
||||||
return nil, errors.New("environment variable EXEC_PATH not set")
|
return nil, fmt.Errorf("exec: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewDNSProviderProgram(s)
|
return NewDNSProviderConfig(&Config{
|
||||||
|
Program: values["EXEC_PATH"],
|
||||||
|
Mode: os.Getenv("EXEC_MODE"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderConfig returns a new DNS provider which runs the given configuration
|
||||||
|
// for adding and removing the DNS record.
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("the configuration is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DNSProvider{config: config}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProviderProgram returns a new DNS provider which runs the given program
|
// NewDNSProviderProgram returns a new DNS provider which runs the given program
|
||||||
// for adding and removing the DNS record.
|
// for adding and removing the DNS record.
|
||||||
|
// Deprecated: use NewDNSProviderConfig instead
|
||||||
func NewDNSProviderProgram(program string) (*DNSProvider, error) {
|
func NewDNSProviderProgram(program string) (*DNSProvider, error) {
|
||||||
return &DNSProvider{program: program}, nil
|
if len(program) == 0 {
|
||||||
|
return nil, errors.New("the program is undefined")
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewDNSProviderConfig(&Config{Program: program})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
var args []string
|
||||||
cmd := exec.Command(d.program, "present", fqdn, value, strconv.Itoa(ttl))
|
if d.config.Mode == "RAW" {
|
||||||
cmd.Stdout = os.Stdout
|
args = []string{"present", "--", domain, token, keyAuth}
|
||||||
cmd.Stderr = os.Stderr
|
} else {
|
||||||
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
|
args = []string{"present", fqdn, value, strconv.Itoa(ttl)}
|
||||||
|
}
|
||||||
|
|
||||||
return cmd.Run()
|
cmd := exec.Command(d.config.Program, args...)
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if len(output) > 0 {
|
||||||
|
log.Println(string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
var args []string
|
||||||
cmd := exec.Command(d.program, "cleanup", fqdn, value, strconv.Itoa(ttl))
|
if d.config.Mode == "RAW" {
|
||||||
cmd.Stdout = os.Stdout
|
args = []string{"cleanup", "--", domain, token, keyAuth}
|
||||||
cmd.Stderr = os.Stderr
|
} else {
|
||||||
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
|
args = []string{"cleanup", fqdn, value, strconv.Itoa(ttl)}
|
||||||
|
}
|
||||||
|
|
||||||
return cmd.Run()
|
cmd := exec.Command(d.config.Program, args...)
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if len(output) > 0 {
|
||||||
|
log.Println(string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
31
vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go
generated
vendored
31
vendor/github.com/xenolf/lego/providers/dns/gcloud/googlecloud.go
generated
vendored
|
@ -115,13 +115,13 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for existing records.
|
// Look for existing records.
|
||||||
list, err := d.client.ResourceRecordSets.List(d.project, zone).Name(fqdn).Type("TXT").Do()
|
existing, err := d.findTxtRecords(zone, fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(list.Rrsets) > 0 {
|
if len(existing) > 0 {
|
||||||
// Attempt to delete the existing records when adding our new one.
|
// Attempt to delete the existing records when adding our new one.
|
||||||
change.Deletions = list.Rrsets
|
change.Deletions = existing
|
||||||
}
|
}
|
||||||
|
|
||||||
chg, err := d.client.Changes.Create(d.project, zone, change).Do()
|
chg, err := d.client.Changes.Create(d.project, zone, change).Do()
|
||||||
|
@ -156,16 +156,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rec := range records {
|
if len(records) == 0 {
|
||||||
change := &dns.Change{
|
return nil
|
||||||
Deletions: []*dns.ResourceRecordSet{rec},
|
|
||||||
}
|
|
||||||
_, err = d.client.Changes.Create(d.project, zone, change).Do()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
_, err = d.client.Changes.Create(d.project, zone, &dns.Change{Deletions: records}).Do()
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timeout customizes the timeout values used by the ACME package for checking
|
// Timeout customizes the timeout values used by the ACME package for checking
|
||||||
|
@ -198,17 +194,10 @@ func (d *DNSProvider) getHostedZone(domain string) (string, error) {
|
||||||
|
|
||||||
func (d *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) {
|
func (d *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) {
|
||||||
|
|
||||||
recs, err := d.client.ResourceRecordSets.List(d.project, zone).Do()
|
recs, err := d.client.ResourceRecordSets.List(d.project, zone).Name(fqdn).Type("TXT").Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var found []*dns.ResourceRecordSet
|
return recs.Rrsets, nil
|
||||||
for _, r := range recs.Rrsets {
|
|
||||||
if r.Type == "TXT" && r.Name == fqdn {
|
|
||||||
found = append(found, r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return found, nil
|
|
||||||
}
|
}
|
||||||
|
|
21
vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go
generated
vendored
21
vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go
generated
vendored
|
@ -5,6 +5,7 @@ package ns1
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
|
@ -75,7 +76,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) {
|
func (d *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) {
|
||||||
zone, _, err := d.client.Zones.Get(domain)
|
authZone, err := getAuthZone(domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
zone, _, err := d.client.Zones.Get(authZone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -83,6 +89,19 @@ func (d *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) {
|
||||||
return zone, nil
|
return zone, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getAuthZone(fqdn string) (string, error) {
|
||||||
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(authZone, ".") {
|
||||||
|
authZone = authZone[:len(authZone)-len(".")]
|
||||||
|
}
|
||||||
|
|
||||||
|
return authZone, err
|
||||||
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record {
|
func (d *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record {
|
||||||
name := acme.UnFqdn(fqdn)
|
name := acme.UnFqdn(fqdn)
|
||||||
|
|
||||||
|
|
64
vendor/github.com/xenolf/lego/providers/dns/route53/route53.go
generated
vendored
64
vendor/github.com/xenolf/lego/providers/dns/route53/route53.go
generated
vendored
|
@ -3,6 +3,7 @@
|
||||||
package route53
|
package route53
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
@ -17,15 +18,30 @@ import (
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// Config is used to configure the creation of the DNSProvider
|
||||||
maxRetries = 5
|
type Config struct {
|
||||||
route53TTL = 10
|
MaxRetries int
|
||||||
)
|
TTL int
|
||||||
|
PropagationTimeout time.Duration
|
||||||
|
PollingInterval time.Duration
|
||||||
|
HostedZoneID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultConfig returns a default configuration for the DNSProvider
|
||||||
|
func NewDefaultConfig() *Config {
|
||||||
|
return &Config{
|
||||||
|
MaxRetries: 5,
|
||||||
|
TTL: 10,
|
||||||
|
PropagationTimeout: time.Minute * 2,
|
||||||
|
PollingInterval: time.Second * 4,
|
||||||
|
HostedZoneID: os.Getenv("AWS_HOSTED_ZONE_ID"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DNSProvider implements the acme.ChallengeProvider interface
|
// DNSProvider implements the acme.ChallengeProvider interface
|
||||||
type DNSProvider struct {
|
type DNSProvider struct {
|
||||||
client *route53.Route53
|
client *route53.Route53
|
||||||
hostedZoneID string
|
config *Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// customRetryer implements the client.Retryer interface by composing the
|
// customRetryer implements the client.Retryer interface by composing the
|
||||||
|
@ -65,35 +81,49 @@ func (d customRetryer) RetryRules(r *request.Request) time.Duration {
|
||||||
//
|
//
|
||||||
// See also: https://github.com/aws/aws-sdk-go/wiki/configuring-sdk
|
// See also: https://github.com/aws/aws-sdk-go/wiki/configuring-sdk
|
||||||
func NewDNSProvider() (*DNSProvider, error) {
|
func NewDNSProvider() (*DNSProvider, error) {
|
||||||
hostedZoneID := os.Getenv("AWS_HOSTED_ZONE_ID")
|
return NewDNSProviderConfig(NewDefaultConfig())
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDNSProviderConfig takes a given config ans returns a custom configured
|
||||||
|
// DNSProvider instance
|
||||||
|
func NewDNSProviderConfig(config *Config) (*DNSProvider, error) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, errors.New("the configuration of the Route53 DNS provider is nil")
|
||||||
|
}
|
||||||
|
|
||||||
r := customRetryer{}
|
r := customRetryer{}
|
||||||
r.NumMaxRetries = maxRetries
|
r.NumMaxRetries = config.MaxRetries
|
||||||
config := request.WithRetryer(aws.NewConfig(), r)
|
sessionCfg := request.WithRetryer(aws.NewConfig(), r)
|
||||||
session, err := session.NewSessionWithOptions(session.Options{Config: *config})
|
session, err := session.NewSessionWithOptions(session.Options{Config: *sessionCfg})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
client := route53.New(session)
|
client := route53.New(session)
|
||||||
|
|
||||||
return &DNSProvider{
|
return &DNSProvider{
|
||||||
client: client,
|
client: client,
|
||||||
hostedZoneID: hostedZoneID,
|
config: config,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Timeout returns the timeout and interval to use when checking for DNS
|
||||||
|
// propagation.
|
||||||
|
func (r *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
|
return r.config.PropagationTimeout, r.config.PollingInterval
|
||||||
|
}
|
||||||
|
|
||||||
// Present creates a TXT record using the specified parameters
|
// Present creates a TXT record using the specified parameters
|
||||||
func (r *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (r *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
value = `"` + value + `"`
|
value = `"` + value + `"`
|
||||||
return r.changeRecord("UPSERT", fqdn, value, route53TTL)
|
return r.changeRecord("UPSERT", fqdn, value, r.config.TTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
value = `"` + value + `"`
|
value = `"` + value + `"`
|
||||||
return r.changeRecord("DELETE", fqdn, value, route53TTL)
|
return r.changeRecord("DELETE", fqdn, value, r.config.TTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
|
func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
|
||||||
|
@ -123,7 +153,7 @@ func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
|
||||||
|
|
||||||
statusID := resp.ChangeInfo.Id
|
statusID := resp.ChangeInfo.Id
|
||||||
|
|
||||||
return acme.WaitFor(120*time.Second, 4*time.Second, func() (bool, error) {
|
return acme.WaitFor(r.config.PropagationTimeout, r.config.PollingInterval, func() (bool, error) {
|
||||||
reqParams := &route53.GetChangeInput{
|
reqParams := &route53.GetChangeInput{
|
||||||
Id: statusID,
|
Id: statusID,
|
||||||
}
|
}
|
||||||
|
@ -139,8 +169,8 @@ func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
func (r *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
||||||
if r.hostedZoneID != "" {
|
if r.config.HostedZoneID != "" {
|
||||||
return r.hostedZoneID, nil
|
return r.config.HostedZoneID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
|
|
Loading…
Reference in a new issue