Update lego
This commit is contained in:
parent
c8ae97fd38
commit
aabebb2185
13 changed files with 696 additions and 112 deletions
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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue