Update lego

This commit is contained in:
Michael 2018-07-23 17:30:03 +02:00 committed by Traefiker Bot
parent c8ae97fd38
commit aabebb2185
13 changed files with 696 additions and 112 deletions

21
vendor/github.com/cpu/goacmedns/LICENSE generated vendored Normal file
View 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
View 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
View 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
View 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
}