Fix: Add TTL and custom Timeout in DigitalOcean DNS provider
This commit is contained in:
parent
66485e81b4
commit
0ef1b7b683
120 changed files with 23764 additions and 9782 deletions
3
.gitattributes
vendored
3
.gitattributes
vendored
|
@ -1,2 +1 @@
|
|||
vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go eol=crlf
|
||||
|
||||
# vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go eol=crlf
|
||||
|
|
56
Gopkg.lock
generated
56
Gopkg.lock
generated
|
@ -140,6 +140,17 @@
|
|||
]
|
||||
revision = "063d875e3c5fd734fa2aa12fac83829f62acfc70"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/akamai/AkamaiOPEN-edgegrid-golang"
|
||||
packages = [
|
||||
"client-v1",
|
||||
"configdns-v1",
|
||||
"edgegrid",
|
||||
"jsonhooks-v1"
|
||||
]
|
||||
revision = "a494eba1efa1f38338393727dff63389a6a66534"
|
||||
version = "v0.6.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/aokoli/goutils"
|
||||
packages = ["."]
|
||||
|
@ -193,6 +204,7 @@
|
|||
"service/dynamodb/dynamodbiface",
|
||||
"service/ec2",
|
||||
"service/ecs",
|
||||
"service/lightsail",
|
||||
"service/route53",
|
||||
"service/sts"
|
||||
]
|
||||
|
@ -745,7 +757,6 @@
|
|||
version = "v1.3.7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/jjcollinge/servicefabric"
|
||||
packages = ["."]
|
||||
revision = "8eebe170fa1ba25d3dfb928b3f86a7313b13b9fe"
|
||||
|
@ -914,6 +925,12 @@
|
|||
packages = ["."]
|
||||
revision = "db96455566f05ffe42bd6ac671f05eeb1152b45d"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/namedotcom/go"
|
||||
packages = ["namecom"]
|
||||
revision = "08470befbe04613bd4b44cb6978b05d50294c4d4"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/ogier/pflag"
|
||||
|
@ -1120,6 +1137,12 @@
|
|||
]
|
||||
revision = "37e84520dcf74488f67654f9c775b9752c232dc1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/tuvistavie/securerandom"
|
||||
packages = ["."]
|
||||
revision = "15512123a948d62f6361bd84818e11f2ad84059a"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/tv42/zbase32"
|
||||
packages = ["."]
|
||||
|
@ -1214,28 +1237,34 @@
|
|||
revision = "0c8571ac0ce161a5feb57375a9cdf148c98c0f70"
|
||||
|
||||
[[projects]]
|
||||
branch = "acmev2"
|
||||
branch = "containous-fork"
|
||||
name = "github.com/xenolf/lego"
|
||||
packages = [
|
||||
"acme",
|
||||
"acmev2",
|
||||
"providers/dns",
|
||||
"providers/dns/auroradns",
|
||||
"providers/dns/azure",
|
||||
"providers/dns/bluecat",
|
||||
"providers/dns/cloudflare",
|
||||
"providers/dns/cloudxns",
|
||||
"providers/dns/digitalocean",
|
||||
"providers/dns/dnsimple",
|
||||
"providers/dns/dnsmadeeasy",
|
||||
"providers/dns/dnspod",
|
||||
"providers/dns/duckdns",
|
||||
"providers/dns/dyn",
|
||||
"providers/dns/exec",
|
||||
"providers/dns/exoscale",
|
||||
"providers/dns/fastdns",
|
||||
"providers/dns/gandi",
|
||||
"providers/dns/gandiv5",
|
||||
"providers/dns/glesys",
|
||||
"providers/dns/godaddy",
|
||||
"providers/dns/googlecloud",
|
||||
"providers/dns/lightsail",
|
||||
"providers/dns/linode",
|
||||
"providers/dns/namecheap",
|
||||
"providers/dns/namedotcom",
|
||||
"providers/dns/ns1",
|
||||
"providers/dns/otc",
|
||||
"providers/dns/ovh",
|
||||
|
@ -1245,7 +1274,8 @@
|
|||
"providers/dns/route53",
|
||||
"providers/dns/vultr"
|
||||
]
|
||||
revision = "a149e7d6506feb4003da7093cbf818c6b75ed4a4"
|
||||
revision = "2357607e4ddeb93d5364c9ae6571ded32b7d01f1"
|
||||
source = "github.com/containous/lego"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
@ -1403,6 +1433,12 @@
|
|||
revision = "5b3e00af70a9484542169a976dcab8d03e601a17"
|
||||
version = "v1.30.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "v1"
|
||||
name = "gopkg.in/mattes/go-expand-tilde.v1"
|
||||
packages = ["."]
|
||||
revision = "cb884138e64c9a8bf5c7d6106d74b0fca082df0c"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/ns1/ns1-go.v2"
|
||||
packages = [
|
||||
|
@ -1415,16 +1451,6 @@
|
|||
]
|
||||
revision = "c563826f4cbef9c11bebeb9f20a3f7afe9c1e2f4"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/square/go-jose.v1"
|
||||
packages = [
|
||||
".",
|
||||
"cipher",
|
||||
"json"
|
||||
]
|
||||
revision = "aa2e30fdd1fe9dd3394119af66451ae790d50e0d"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "gopkg.in/square/go-jose.v2"
|
||||
packages = [
|
||||
|
@ -1644,6 +1670,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "593d67272ac35ca0fa59df7f2ac077a81ea842b3181b00acffa20553bfe6f2e0"
|
||||
inputs-digest = "37f92cc9416b1449e9fe4fd1b02d999af8d9887617424db7cb334060cd362e5c"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -181,8 +181,9 @@
|
|||
name = "github.com/vulcand/oxy"
|
||||
|
||||
[[constraint]]
|
||||
branch = "acmev2"
|
||||
branch = "containous-fork"
|
||||
name = "github.com/xenolf/lego"
|
||||
source = "github.com/containous/lego"
|
||||
|
||||
[[constraint]]
|
||||
name = "google.golang.org/grpc"
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
"github.com/containous/traefik/tls/generate"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/eapache/channels"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
acme "github.com/xenolf/lego/acmev2"
|
||||
"github.com/xenolf/lego/providers/dns"
|
||||
)
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/containous/traefik/tls/generate"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
acme "github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
func TestDomainsSet(t *testing.T) {
|
||||
|
|
|
@ -286,21 +286,28 @@ Select the provider that matches the DNS domain that will host the challenge TXT
|
|||
|--------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------|
|
||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` - The Cloudflare `Global API Key` needs to be used and not the `Origin CA Key` |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` |
|
||||
| [DNSPod](http://www.dnspod.net/) | `dnspod` | `DNSPOD_API_KEY` |
|
||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` |
|
||||
| External Program | `exec` | `EXEC_PATH` |
|
||||
| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` |
|
||||
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` |
|
||||
| [Gandi V5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` |
|
||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` |
|
||||
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` |
|
||||
| manual | - | none, but run Træfik interactively & turn on `acmeLogging` to see instructions & press <kbd>Enter</kbd>. |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` |
|
||||
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de/en/) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` |
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
traefikTLS "github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
acme "github.com/xenolf/lego/acmev2"
|
||||
"github.com/xenolf/lego/providers/dns"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
@ -179,7 +178,7 @@
|
|||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
|
@ -187,7 +186,7 @@
|
|||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
36
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1/api.go
generated
vendored
Normal file
36
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1/api.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Resource is the "base" type for all API resources
|
||||
type Resource struct {
|
||||
Complete chan bool `json:"-"`
|
||||
}
|
||||
|
||||
// Init initializes the Complete channel, if it is necessary
|
||||
// need to create a resource specific Init(), make sure to
|
||||
// initialize the channel.
|
||||
func (resource *Resource) Init() {
|
||||
resource.Complete = make(chan bool, 1)
|
||||
}
|
||||
|
||||
// PostUnmarshalJSON is a default implementation of the
|
||||
// PostUnmarshalJSON hook that simply calls Init() and
|
||||
// sends true to the Complete channel. This is overridden
|
||||
// in many resources, in particular those that represent
|
||||
// collections, and have to initialize sub-resources also.
|
||||
func (resource *Resource) PostUnmarshalJSON() error {
|
||||
resource.Init()
|
||||
resource.Complete <- true
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetJSON returns the raw (indented) JSON (as []bytes)
|
||||
func (resource *Resource) GetJSON() ([]byte, error) {
|
||||
return json.MarshalIndent(resource, "", " ")
|
||||
}
|
||||
|
||||
// JSONBody is a generic struct for temporary JSON unmarshalling.
|
||||
type JSONBody map[string]interface{}
|
111
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1/client.go
generated
vendored
Normal file
111
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1/client.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Package client is a simple library for http.Client to sign Akamai OPEN Edgegrid API requests
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
|
||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/jsonhooks-v1"
|
||||
)
|
||||
|
||||
var (
|
||||
libraryVersion = "0.6.0"
|
||||
// UserAgent is the User-Agent value sent for all requests
|
||||
UserAgent = "Akamai-Open-Edgegrid-golang/" + libraryVersion + " golang/" + strings.TrimPrefix(runtime.Version(), "go")
|
||||
// Client is the *http.Client to use
|
||||
Client = http.DefaultClient
|
||||
)
|
||||
|
||||
// NewRequest creates an HTTP request that can be sent to Akamai APIs. A relative URL can be provided in path, which will be resolved to the
|
||||
// Host specified in Config. If body is specified, it will be sent as the request body.
|
||||
func NewRequest(config edgegrid.Config, method, path string, body io.Reader) (*http.Request, error) {
|
||||
var (
|
||||
baseURL *url.URL
|
||||
err error
|
||||
)
|
||||
|
||||
if strings.HasPrefix(config.Host, "https://") {
|
||||
baseURL, err = url.Parse(config.Host)
|
||||
} else {
|
||||
baseURL, err = url.Parse("https://" + config.Host)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rel, err := url.Parse(strings.TrimPrefix(path, "/"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u := baseURL.ResolveReference(rel)
|
||||
|
||||
req, err := http.NewRequest(method, u.String(), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Add("User-Agent", UserAgent)
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// NewJSONRequest creates an HTTP request that can be sent to the Akamai APIs with a JSON body
|
||||
// The JSON body is encoded and the Content-Type/Accept headers are set automatically.
|
||||
func NewJSONRequest(config edgegrid.Config, method, path string, body interface{}) (*http.Request, error) {
|
||||
jsonBody, err := jsonhooks.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := bytes.NewReader(jsonBody)
|
||||
req, err := NewRequest(config, method, path, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Accept", "application/json,*/*")
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// Do performs a given HTTP Request, signed with the Akamai OPEN Edgegrid
|
||||
// Authorization header. An edgegrid.Response or an error is returned.
|
||||
func Do(config edgegrid.Config, req *http.Request) (*http.Response, error) {
|
||||
Client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||
req = edgegrid.AddRequestHeader(config, req)
|
||||
return nil
|
||||
}
|
||||
|
||||
req = edgegrid.AddRequestHeader(config, req)
|
||||
res, err := Client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// BodyJSON unmarshals the Response.Body into a given data structure
|
||||
func BodyJSON(r *http.Response, data interface{}) error {
|
||||
if data == nil {
|
||||
return errors.New("You must pass in an interface{}")
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = jsonhooks.Unmarshal(body, data)
|
||||
|
||||
return err
|
||||
}
|
88
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1/errors.go
generated
vendored
Normal file
88
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/jsonhooks-v1"
|
||||
)
|
||||
|
||||
// APIError exposes an Akamai OPEN Edgegrid Error
|
||||
type APIError struct {
|
||||
error
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Status int `json:"status"`
|
||||
Detail string `json:"detail"`
|
||||
Instance string `json:"instance"`
|
||||
Method string `json:"method"`
|
||||
ServerIP string `json:"serverIp"`
|
||||
ClientIP string `json:"clientIp"`
|
||||
RequestID string `json:"requestId"`
|
||||
RequestTime string `json:"requestTime"`
|
||||
Response *http.Response `json:"-"`
|
||||
RawBody string `json:"-"`
|
||||
}
|
||||
|
||||
func (error APIError) Error() string {
|
||||
return strings.TrimSpace(fmt.Sprintf("API Error: %d %s %s More Info %s", error.Status, error.Title, error.Detail, error.Type))
|
||||
}
|
||||
|
||||
// NewAPIError creates a new API error based on a Response,
|
||||
// or http.Response-like.
|
||||
func NewAPIError(response *http.Response) APIError {
|
||||
// TODO: handle this error
|
||||
body, _ := ioutil.ReadAll(response.Body)
|
||||
|
||||
return NewAPIErrorFromBody(response, body)
|
||||
}
|
||||
|
||||
// NewAPIErrorFromBody creates a new API error, allowing you to pass in a body
|
||||
//
|
||||
// This function is intended to be used after the body has already been read for
|
||||
// other purposes.
|
||||
func NewAPIErrorFromBody(response *http.Response, body []byte) APIError {
|
||||
error := APIError{}
|
||||
|
||||
if err := jsonhooks.Unmarshal(body, &error); err == nil {
|
||||
error.Status = response.StatusCode
|
||||
error.Title = response.Status
|
||||
}
|
||||
|
||||
error.Response = response
|
||||
error.RawBody = string(body)
|
||||
|
||||
return error
|
||||
}
|
||||
|
||||
// IsInformational determines if a response was informational (1XX status)
|
||||
func IsInformational(r *http.Response) bool {
|
||||
return r.StatusCode > 99 && r.StatusCode < 200
|
||||
}
|
||||
|
||||
// IsSuccess determines if a response was successful (2XX status)
|
||||
func IsSuccess(r *http.Response) bool {
|
||||
return r.StatusCode > 199 && r.StatusCode < 300
|
||||
}
|
||||
|
||||
// IsRedirection determines if a response was a redirect (3XX status)
|
||||
func IsRedirection(r *http.Response) bool {
|
||||
return r.StatusCode > 299 && r.StatusCode < 400
|
||||
}
|
||||
|
||||
// IsClientError determines if a response was a client error (4XX status)
|
||||
func IsClientError(r *http.Response) bool {
|
||||
return r.StatusCode > 399 && r.StatusCode < 500
|
||||
}
|
||||
|
||||
// IsServerError determines if a response was a server error (5XX status)
|
||||
func IsServerError(r *http.Response) bool {
|
||||
return r.StatusCode > 499 && r.StatusCode < 600
|
||||
}
|
||||
|
||||
// IsError determines if the response was a client or server error (4XX or 5XX status)
|
||||
func IsError(r *http.Response) bool {
|
||||
return r.StatusCode > 399 && r.StatusCode < 600
|
||||
}
|
125
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/errors.go
generated
vendored
Normal file
125
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,125 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ConfigDNSError interface {
|
||||
error
|
||||
Network() bool
|
||||
NotFound() bool
|
||||
FailedToSave() bool
|
||||
ValidationFailed() bool
|
||||
}
|
||||
|
||||
func IsConfigDNSError(e error) bool {
|
||||
_, ok := e.(ConfigDNSError)
|
||||
return ok
|
||||
}
|
||||
|
||||
type ZoneError struct {
|
||||
zoneName string
|
||||
httpErrorMessage string
|
||||
apiErrorMessage string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *ZoneError) Network() bool {
|
||||
if e.httpErrorMessage != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *ZoneError) NotFound() bool {
|
||||
if e.err == nil && e.httpErrorMessage == "" && e.apiErrorMessage == "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *ZoneError) FailedToSave() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *ZoneError) ValidationFailed() bool {
|
||||
if e.apiErrorMessage != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *ZoneError) Error() string {
|
||||
if e.Network() {
|
||||
return fmt.Sprintf("Zone \"%s\" network error: [%s]", e.zoneName, e.httpErrorMessage)
|
||||
}
|
||||
|
||||
if e.NotFound() {
|
||||
return fmt.Sprintf("Zone \"%s\" not found.", e.zoneName)
|
||||
}
|
||||
|
||||
if e.FailedToSave() {
|
||||
return fmt.Sprintf("Zone \"%s\" failed to save: [%s]", e.zoneName, e.err.Error())
|
||||
}
|
||||
|
||||
if e.ValidationFailed() {
|
||||
return fmt.Sprintf("Zone \"%s\" validation failed: [%s]", e.zoneName, e.apiErrorMessage)
|
||||
}
|
||||
|
||||
if e.err != nil {
|
||||
return e.err.Error()
|
||||
}
|
||||
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
type RecordError struct {
|
||||
fieldName string
|
||||
httpErrorMessage string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *RecordError) Network() bool {
|
||||
if e.httpErrorMessage != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *RecordError) NotFound() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *RecordError) FailedToSave() bool {
|
||||
if e.fieldName == "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *RecordError) ValidationFailed() bool {
|
||||
if e.fieldName != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e *RecordError) Error() string {
|
||||
if e.Network() {
|
||||
return fmt.Sprintf("Record network error: [%s]", e.httpErrorMessage)
|
||||
}
|
||||
|
||||
if e.NotFound() {
|
||||
return fmt.Sprintf("Record not found.")
|
||||
}
|
||||
|
||||
if e.FailedToSave() {
|
||||
return fmt.Sprintf("Record failed to save: [%s]", e.err.Error())
|
||||
}
|
||||
|
||||
if e.ValidationFailed() {
|
||||
return fmt.Sprintf("Record validation failed for field [%s]", e.fieldName)
|
||||
}
|
||||
|
||||
return "<nil>"
|
||||
}
|
1738
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/record.go
generated
vendored
Normal file
1738
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/record.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
16
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/service.go
generated
vendored
Normal file
16
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/service.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
|
||||
)
|
||||
|
||||
var (
|
||||
// Config contains the Akamai OPEN Edgegrid API credentials
|
||||
// for automatic signing of requests
|
||||
Config edgegrid.Config
|
||||
)
|
||||
|
||||
// Init sets the FastDNS edgegrid Config
|
||||
func Init(config edgegrid.Config) {
|
||||
Config = config
|
||||
}
|
1557
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/zone.go
generated
vendored
Normal file
1557
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1/zone.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
181
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid/config.go
generated
vendored
Normal file
181
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid/config.go
generated
vendored
Normal file
|
@ -0,0 +1,181 @@
|
|||
package edgegrid
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-ini/ini"
|
||||
"gopkg.in/mattes/go-expand-tilde.v1"
|
||||
)
|
||||
|
||||
// Config struct provides all the necessary fields to
|
||||
// create authorization header, debug is optional
|
||||
type Config struct {
|
||||
Host string `ini:"host"`
|
||||
ClientToken string `ini:"client_token"`
|
||||
ClientSecret string `ini:"client_secret"`
|
||||
AccessToken string `ini:"access_token"`
|
||||
HeaderToSign []string `ini:"headers_to_sign"`
|
||||
MaxBody int `ini:"max_body"`
|
||||
Debug bool `ini:"debug"`
|
||||
}
|
||||
|
||||
// Init initializes by first attempting to use ENV vars, with .edgerc as a fallback
|
||||
//
|
||||
// See: InitEnv()
|
||||
// See: InitEdgeRc()
|
||||
func Init(filepath string, section string) (Config, error) {
|
||||
if section == "" {
|
||||
section = defaultSection
|
||||
} else {
|
||||
section = strings.ToUpper(section)
|
||||
}
|
||||
|
||||
_, exists := os.LookupEnv("AKAMAI_" + section + "_HOST")
|
||||
if !exists && section == defaultSection {
|
||||
_, exists := os.LookupEnv("AKAMAI_HOST")
|
||||
|
||||
if exists {
|
||||
return InitEnv("")
|
||||
}
|
||||
}
|
||||
|
||||
if exists {
|
||||
return InitEnv(section)
|
||||
}
|
||||
|
||||
c, err := InitEdgeRc(filepath, strings.ToLower(section))
|
||||
|
||||
if err == nil {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
if section != defaultSection {
|
||||
_, ok := os.LookupEnv("AKAMAI_HOST")
|
||||
if ok {
|
||||
return InitEnv("")
|
||||
}
|
||||
}
|
||||
|
||||
return c, fmt.Errorf("Unable to create instance using environment or .edgerc file")
|
||||
}
|
||||
|
||||
// InitEdgeRc initializes using a configuration file in standard INI format
|
||||
//
|
||||
// By default, it uses the .edgerc found in the users home directory, and the
|
||||
// "default" section.
|
||||
func InitEdgeRc(filepath string, section string) (Config, error) {
|
||||
var (
|
||||
c Config
|
||||
requiredOptions = []string{"host", "client_token", "client_secret", "access_token"}
|
||||
missing []string
|
||||
)
|
||||
|
||||
// Check if filepath is empty
|
||||
if filepath == "" {
|
||||
filepath = "~/.edgerc"
|
||||
}
|
||||
|
||||
// Check if section is empty
|
||||
if section == "" {
|
||||
section = "default"
|
||||
}
|
||||
|
||||
// Tilde seems to be not working when passing ~/.edgerc as file
|
||||
// Takes current user and use home dir instead
|
||||
|
||||
path, err := tilde.Expand(filepath)
|
||||
|
||||
if err != nil {
|
||||
return c, fmt.Errorf(errorMap[ErrHomeDirNotFound], err)
|
||||
}
|
||||
|
||||
edgerc, err := ini.Load(path)
|
||||
if err != nil {
|
||||
return c, fmt.Errorf(errorMap[ErrConfigFile], err)
|
||||
}
|
||||
err = edgerc.Section(section).MapTo(&c)
|
||||
if err != nil {
|
||||
return c, fmt.Errorf(errorMap[ErrConfigFileSection], err)
|
||||
}
|
||||
for _, opt := range requiredOptions {
|
||||
if !(edgerc.Section(section).HasKey(opt)) {
|
||||
missing = append(missing, opt)
|
||||
}
|
||||
}
|
||||
if len(missing) > 0 {
|
||||
return c, fmt.Errorf(errorMap[ErrConfigMissingOptions], missing)
|
||||
}
|
||||
if c.MaxBody == 0 {
|
||||
c.MaxBody = 131072
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// InitEnv initializes using the Environment (ENV)
|
||||
//
|
||||
// By default, it uses AKAMAI_HOST, AKAMAI_CLIENT_TOKEN, AKAMAI_CLIENT_SECRET,
|
||||
// AKAMAI_ACCESS_TOKEN, and AKAMAI_MAX_BODY variables.
|
||||
//
|
||||
// You can define multiple configurations by prefixing with the section name specified, e.g.
|
||||
// passing "ccu" will cause it to look for AKAMAI_CCU_HOST, etc.
|
||||
//
|
||||
// If AKAMAI_{SECTION} does not exist, it will fall back to just AKAMAI_.
|
||||
func InitEnv(section string) (Config, error) {
|
||||
var (
|
||||
c Config
|
||||
requiredOptions = []string{"HOST", "CLIENT_TOKEN", "CLIENT_SECRET", "ACCESS_TOKEN"}
|
||||
missing []string
|
||||
prefix string
|
||||
)
|
||||
|
||||
// Check if section is empty
|
||||
if section == "" {
|
||||
section = defaultSection
|
||||
} else {
|
||||
section = strings.ToUpper(section)
|
||||
}
|
||||
|
||||
prefix = "AKAMAI_"
|
||||
_, ok := os.LookupEnv("AKAMAI_" + section + "_HOST")
|
||||
if ok {
|
||||
prefix = "AKAMAI_" + section + "_"
|
||||
}
|
||||
|
||||
for _, opt := range requiredOptions {
|
||||
val, ok := os.LookupEnv(prefix + opt)
|
||||
if !ok {
|
||||
missing = append(missing, prefix+opt)
|
||||
} else {
|
||||
switch {
|
||||
case opt == "HOST":
|
||||
c.Host = val
|
||||
case opt == "CLIENT_TOKEN":
|
||||
c.ClientToken = val
|
||||
case opt == "CLIENT_SECRET":
|
||||
c.ClientSecret = val
|
||||
case opt == "ACCESS_TOKEN":
|
||||
c.AccessToken = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
return c, fmt.Errorf(errorMap[ErrMissingEnvVariables], missing)
|
||||
}
|
||||
|
||||
c.MaxBody = 0
|
||||
|
||||
val, ok := os.LookupEnv(prefix + "MAX_BODY")
|
||||
if i, err := strconv.Atoi(val); err == nil {
|
||||
c.MaxBody = i
|
||||
}
|
||||
|
||||
if !ok || c.MaxBody == 0 {
|
||||
c.MaxBody = 131072
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
22
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid/errors.go
generated
vendored
Normal file
22
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
package edgegrid
|
||||
|
||||
// Error constants
|
||||
const (
|
||||
ErrUUIDGenerateFailed = 500
|
||||
ErrHomeDirNotFound = 501
|
||||
ErrConfigFile = 502
|
||||
ErrConfigFileSection = 503
|
||||
ErrConfigMissingOptions = 504
|
||||
ErrMissingEnvVariables = 505
|
||||
)
|
||||
|
||||
var (
|
||||
errorMap = map[int]string{
|
||||
ErrUUIDGenerateFailed: "Generate UUID failed: %s",
|
||||
ErrHomeDirNotFound: "Fatal could not find home dir from user: %s",
|
||||
ErrConfigFile: "Fatal error edgegrid file: %s",
|
||||
ErrConfigFileSection: "Could not map section: %s",
|
||||
ErrConfigMissingOptions: "Fatal missing required options: %s",
|
||||
ErrMissingEnvVariables: "Fatal missing required environment variables: %s",
|
||||
}
|
||||
)
|
195
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid/signer.go
generated
vendored
Normal file
195
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid/signer.go
generated
vendored
Normal file
|
@ -0,0 +1,195 @@
|
|||
// Package edgegrid allows you to sign http.Request's using the Akamai OPEN Edgegrid Signing Scheme
|
||||
package edgegrid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tuvistavie/securerandom"
|
||||
)
|
||||
|
||||
const defaultSection = "DEFAULT"
|
||||
|
||||
// AddRequestHeader sets the Authorization header to use Akamai Open API
|
||||
func AddRequestHeader(config Config, req *http.Request) *http.Request {
|
||||
if config.Debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
timestamp := makeEdgeTimeStamp()
|
||||
nonce := createNonce()
|
||||
|
||||
if req.Header.Get("Content-Type") == "" {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
|
||||
req.Header.Set("Authorization", createAuthHeader(config, req, timestamp, nonce))
|
||||
return req
|
||||
}
|
||||
|
||||
// Must be assigned the UTC time when the request is signed.
|
||||
// Format of “yyyyMMddTHH:mm:ss+0000”
|
||||
func makeEdgeTimeStamp() string {
|
||||
local := time.FixedZone("GMT", 0)
|
||||
t := time.Now().In(local)
|
||||
return fmt.Sprintf("%d%02d%02dT%02d:%02d:%02d+0000",
|
||||
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second())
|
||||
}
|
||||
|
||||
// Must be assigned a nonce (number used once) for the request.
|
||||
// It is a random string used to detect replayed request messages.
|
||||
// A GUID is recommended.
|
||||
func createNonce() string {
|
||||
uuid, err := securerandom.Uuid()
|
||||
if err != nil {
|
||||
log.Errorf(errorMap[ErrUUIDGenerateFailed], err)
|
||||
return ""
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
|
||||
func stringMinifier(in string) (out string) {
|
||||
white := false
|
||||
for _, c := range in {
|
||||
if unicode.IsSpace(c) {
|
||||
if !white {
|
||||
out = out + " "
|
||||
}
|
||||
white = true
|
||||
} else {
|
||||
out = out + string(c)
|
||||
white = false
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func concatPathQuery(path, query string) string {
|
||||
if query == "" {
|
||||
return path
|
||||
}
|
||||
return fmt.Sprintf("%s?%s", path, query)
|
||||
}
|
||||
|
||||
// createSignature is the base64-encoding of the SHA–256 HMAC of the data to sign with the signing key.
|
||||
func createSignature(message string, secret string) string {
|
||||
key := []byte(secret)
|
||||
h := hmac.New(sha256.New, key)
|
||||
h.Write([]byte(message))
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func createHash(data string) string {
|
||||
h := sha256.Sum256([]byte(data))
|
||||
return base64.StdEncoding.EncodeToString(h[:])
|
||||
}
|
||||
|
||||
func canonicalizeHeaders(config Config, req *http.Request) string {
|
||||
var unsortedHeader []string
|
||||
var sortedHeader []string
|
||||
for k := range req.Header {
|
||||
unsortedHeader = append(unsortedHeader, k)
|
||||
}
|
||||
sort.Strings(unsortedHeader)
|
||||
for _, k := range unsortedHeader {
|
||||
for _, sign := range config.HeaderToSign {
|
||||
if sign == k {
|
||||
v := strings.TrimSpace(req.Header.Get(k))
|
||||
sortedHeader = append(sortedHeader, fmt.Sprintf("%s:%s", strings.ToLower(k), strings.ToLower(stringMinifier(v))))
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.Join(sortedHeader, "\t")
|
||||
|
||||
}
|
||||
|
||||
// signingKey is derived from the client secret.
|
||||
// The signing key is computed as the base64 encoding of the SHA–256 HMAC of the timestamp string
|
||||
// (the field value included in the HTTP authorization header described above) with the client secret as the key.
|
||||
func signingKey(config Config, timestamp string) string {
|
||||
key := createSignature(timestamp, config.ClientSecret)
|
||||
return key
|
||||
}
|
||||
|
||||
// The content hash is the base64-encoded SHA–256 hash of the POST body.
|
||||
// For any other request methods, this field is empty. But the tac separator (\t) must be included.
|
||||
// The size of the POST body must be less than or equal to the value specified by the service.
|
||||
// Any request that does not meet this criteria SHOULD be rejected during the signing process,
|
||||
// as the request will be rejected by EdgeGrid.
|
||||
func createContentHash(config Config, req *http.Request) string {
|
||||
var (
|
||||
contentHash string
|
||||
preparedBody string
|
||||
bodyBytes []byte
|
||||
)
|
||||
if req.Body != nil {
|
||||
bodyBytes, _ = ioutil.ReadAll(req.Body)
|
||||
req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
preparedBody = string(bodyBytes)
|
||||
}
|
||||
|
||||
log.Debugf("Body is %s", preparedBody)
|
||||
if req.Method == "POST" && len(preparedBody) > 0 {
|
||||
log.Debugf("Signing content: %s", preparedBody)
|
||||
if len(preparedBody) > config.MaxBody {
|
||||
log.Debugf("Data length %d is larger than maximum %d",
|
||||
len(preparedBody), config.MaxBody)
|
||||
|
||||
preparedBody = preparedBody[0:config.MaxBody]
|
||||
log.Debugf("Data truncated to %d for computing the hash", len(preparedBody))
|
||||
}
|
||||
contentHash = createHash(preparedBody)
|
||||
}
|
||||
log.Debugf("Content hash is '%s'", contentHash)
|
||||
return contentHash
|
||||
}
|
||||
|
||||
// The data to sign includes the information from the HTTP request that is relevant to ensuring that the request is authentic.
|
||||
// This data set comprised of the request data combined with the authorization header value (excluding the signature field,
|
||||
// but including the ; right before the signature field).
|
||||
func signingData(config Config, req *http.Request, authHeader string) string {
|
||||
|
||||
dataSign := []string{
|
||||
req.Method,
|
||||
req.URL.Scheme,
|
||||
req.URL.Host,
|
||||
concatPathQuery(req.URL.Path, req.URL.RawQuery),
|
||||
canonicalizeHeaders(config, req),
|
||||
createContentHash(config, req),
|
||||
authHeader,
|
||||
}
|
||||
log.Debugf("Data to sign %s", strings.Join(dataSign, "\t"))
|
||||
return strings.Join(dataSign, "\t")
|
||||
}
|
||||
|
||||
func signingRequest(config Config, req *http.Request, authHeader string, timestamp string) string {
|
||||
return createSignature(signingData(config, req, authHeader),
|
||||
signingKey(config, timestamp))
|
||||
}
|
||||
|
||||
// The Authorization header starts with the signing algorithm moniker (name of the algorithm) used to sign the request.
|
||||
// The moniker below identifies EdgeGrid V1, hash message authentication code, SHA–256 as the hash standard.
|
||||
// This moniker is then followed by a space and an ordered list of name value pairs with each field separated by a semicolon.
|
||||
func createAuthHeader(config Config, req *http.Request, timestamp string, nonce string) string {
|
||||
authHeader := fmt.Sprintf("EG1-HMAC-SHA256 client_token=%s;access_token=%s;timestamp=%s;nonce=%s;",
|
||||
config.ClientToken,
|
||||
config.AccessToken,
|
||||
timestamp,
|
||||
nonce,
|
||||
)
|
||||
log.Debugf("Unsigned authorization header: '%s'", authHeader)
|
||||
|
||||
signedAuthHeader := fmt.Sprintf("%ssignature=%s", authHeader, signingRequest(config, req, authHeader, timestamp))
|
||||
|
||||
log.Debugf("Signed authorization header: '%s'", signedAuthHeader)
|
||||
return signedAuthHeader
|
||||
}
|
1
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/jsonhooks-v1/errors.go
generated
vendored
Normal file
1
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/jsonhooks-v1/errors.go
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
package jsonhooks
|
69
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/jsonhooks-v1/jsonhooks.go
generated
vendored
Normal file
69
vendor/github.com/akamai/AkamaiOPEN-edgegrid-golang/jsonhooks-v1/jsonhooks.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
// Package jsonhooks adds hooks that are automatically called before JSON marshaling (PreMarshalJSON) and
|
||||
// after JSON unmarshaling (PostUnmarshalJSON). It does not do so recursively.
|
||||
package jsonhooks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Marshal wraps encoding/json.Marshal, calls v.PreMarshalJSON() if it exists
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
if ImplementsPreJSONMarshaler(v) {
|
||||
err := v.(PreJSONMarshaler).PreMarshalJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return json.Marshal(v)
|
||||
}
|
||||
|
||||
// Unmarshal wraps encoding/json.Unmarshal, calls v.PostUnmarshalJSON() if it exists
|
||||
func Unmarshal(data []byte, v interface{}) error {
|
||||
err := json.Unmarshal(data, v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ImplementsPostJSONUnmarshaler(v) {
|
||||
err := v.(PostJSONUnmarshaler).PostUnmarshalJSON()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PreJSONMarshaler infers support for the PreMarshalJSON pre-hook
|
||||
type PreJSONMarshaler interface {
|
||||
PreMarshalJSON() error
|
||||
}
|
||||
|
||||
// ImplementsPreJSONMarshaler checks for support for the PreMarshalJSON pre-hook
|
||||
func ImplementsPreJSONMarshaler(v interface{}) bool {
|
||||
value := reflect.ValueOf(v)
|
||||
if value.Kind() == reflect.Ptr && value.IsNil() {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := value.Interface().(PreJSONMarshaler)
|
||||
return ok
|
||||
}
|
||||
|
||||
// PostJSONUnmarshaler infers support for the PostUnmarshalJSON post-hook
|
||||
type PostJSONUnmarshaler interface {
|
||||
PostUnmarshalJSON() error
|
||||
}
|
||||
|
||||
// ImplementsPostJSONUnmarshaler checks for support for the PostUnmarshalJSON post-hook
|
||||
func ImplementsPostJSONUnmarshaler(v interface{}) bool {
|
||||
value := reflect.ValueOf(v)
|
||||
if value.Kind() == reflect.Ptr && value.IsNil() {
|
||||
return false
|
||||
}
|
||||
|
||||
_, ok := value.Interface().(PostJSONUnmarshaler)
|
||||
return ok
|
||||
}
|
15975
vendor/github.com/aws/aws-sdk-go/service/lightsail/api.go
generated
vendored
Normal file
15975
vendor/github.com/aws/aws-sdk-go/service/lightsail/api.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
40
vendor/github.com/aws/aws-sdk-go/service/lightsail/doc.go
generated
vendored
Normal file
40
vendor/github.com/aws/aws-sdk-go/service/lightsail/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
|
||||
|
||||
// Package lightsail provides the client and types for making API
|
||||
// requests to Amazon Lightsail.
|
||||
//
|
||||
// Amazon Lightsail is the easiest way to get started with AWS for developers
|
||||
// who just need virtual private servers. Lightsail includes everything you
|
||||
// need to launch your project quickly - a virtual machine, SSD-based storage,
|
||||
// data transfer, DNS management, and a static IP - for a low, predictable price.
|
||||
// You manage those Lightsail servers through the Lightsail console or by using
|
||||
// the API or command-line interface (CLI).
|
||||
//
|
||||
// For more information about Lightsail concepts and tasks, see the Lightsail
|
||||
// Dev Guide (https://lightsail.aws.amazon.com/ls/docs/all).
|
||||
//
|
||||
// To use the Lightsail API or the CLI, you will need to use AWS Identity and
|
||||
// Access Management (IAM) to generate access keys. For details about how to
|
||||
// set this up, see the Lightsail Dev Guide (http://lightsail.aws.amazon.com/ls/docs/how-to/article/lightsail-how-to-set-up-access-keys-to-use-sdk-api-cli).
|
||||
//
|
||||
// See https://docs.aws.amazon.com/goto/WebAPI/lightsail-2016-11-28 for more information on this service.
|
||||
//
|
||||
// See lightsail package documentation for more information.
|
||||
// https://docs.aws.amazon.com/sdk-for-go/api/service/lightsail/
|
||||
//
|
||||
// Using the Client
|
||||
//
|
||||
// To contact Amazon Lightsail with the SDK use the New function to create
|
||||
// a new service client. With that client you can make API requests to the service.
|
||||
// These clients are safe to use concurrently.
|
||||
//
|
||||
// See the SDK's documentation for more information on how to use the SDK.
|
||||
// https://docs.aws.amazon.com/sdk-for-go/api/
|
||||
//
|
||||
// See aws.Config documentation for more information on configuring SDK clients.
|
||||
// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
|
||||
//
|
||||
// See the Amazon Lightsail client Lightsail for more
|
||||
// information on creating client for this service.
|
||||
// https://docs.aws.amazon.com/sdk-for-go/api/service/lightsail/#New
|
||||
package lightsail
|
55
vendor/github.com/aws/aws-sdk-go/service/lightsail/errors.go
generated
vendored
Normal file
55
vendor/github.com/aws/aws-sdk-go/service/lightsail/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
|
||||
|
||||
package lightsail
|
||||
|
||||
const (
|
||||
|
||||
// ErrCodeAccessDeniedException for service response error code
|
||||
// "AccessDeniedException".
|
||||
//
|
||||
// Lightsail throws this exception when the user cannot be authenticated or
|
||||
// uses invalid credentials to access a resource.
|
||||
ErrCodeAccessDeniedException = "AccessDeniedException"
|
||||
|
||||
// ErrCodeAccountSetupInProgressException for service response error code
|
||||
// "AccountSetupInProgressException".
|
||||
//
|
||||
// Lightsail throws this exception when an account is still in the setup in
|
||||
// progress state.
|
||||
ErrCodeAccountSetupInProgressException = "AccountSetupInProgressException"
|
||||
|
||||
// ErrCodeInvalidInputException for service response error code
|
||||
// "InvalidInputException".
|
||||
//
|
||||
// Lightsail throws this exception when user input does not conform to the validation
|
||||
// rules of an input field.
|
||||
//
|
||||
// Domain-related APIs are only available in the N. Virginia (us-east-1) Region.
|
||||
// Please set your Region configuration to us-east-1 to create, view, or edit
|
||||
// these resources.
|
||||
ErrCodeInvalidInputException = "InvalidInputException"
|
||||
|
||||
// ErrCodeNotFoundException for service response error code
|
||||
// "NotFoundException".
|
||||
//
|
||||
// Lightsail throws this exception when it cannot find a resource.
|
||||
ErrCodeNotFoundException = "NotFoundException"
|
||||
|
||||
// ErrCodeOperationFailureException for service response error code
|
||||
// "OperationFailureException".
|
||||
//
|
||||
// Lightsail throws this exception when an operation fails to execute.
|
||||
ErrCodeOperationFailureException = "OperationFailureException"
|
||||
|
||||
// ErrCodeServiceException for service response error code
|
||||
// "ServiceException".
|
||||
//
|
||||
// A general service exception.
|
||||
ErrCodeServiceException = "ServiceException"
|
||||
|
||||
// ErrCodeUnauthenticatedException for service response error code
|
||||
// "UnauthenticatedException".
|
||||
//
|
||||
// Lightsail throws this exception when the user has not been authenticated.
|
||||
ErrCodeUnauthenticatedException = "UnauthenticatedException"
|
||||
)
|
95
vendor/github.com/aws/aws-sdk-go/service/lightsail/service.go
generated
vendored
Normal file
95
vendor/github.com/aws/aws-sdk-go/service/lightsail/service.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
|
||||
|
||||
package lightsail
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/aws/signer/v4"
|
||||
"github.com/aws/aws-sdk-go/private/protocol/jsonrpc"
|
||||
)
|
||||
|
||||
// Lightsail provides the API operation methods for making requests to
|
||||
// Amazon Lightsail. See this package's package overview docs
|
||||
// for details on the service.
|
||||
//
|
||||
// Lightsail methods are safe to use concurrently. It is not safe to
|
||||
// modify mutate any of the struct's properties though.
|
||||
type Lightsail struct {
|
||||
*client.Client
|
||||
}
|
||||
|
||||
// Used for custom client initialization logic
|
||||
var initClient func(*client.Client)
|
||||
|
||||
// Used for custom request initialization logic
|
||||
var initRequest func(*request.Request)
|
||||
|
||||
// Service information constants
|
||||
const (
|
||||
ServiceName = "lightsail" // Service endpoint prefix API calls made to.
|
||||
EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata.
|
||||
)
|
||||
|
||||
// New creates a new instance of the Lightsail client with a session.
|
||||
// If additional configuration is needed for the client instance use the optional
|
||||
// aws.Config parameter to add your extra config.
|
||||
//
|
||||
// Example:
|
||||
// // Create a Lightsail client from just a session.
|
||||
// svc := lightsail.New(mySession)
|
||||
//
|
||||
// // Create a Lightsail client with additional configuration
|
||||
// svc := lightsail.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
|
||||
func New(p client.ConfigProvider, cfgs ...*aws.Config) *Lightsail {
|
||||
c := p.ClientConfig(EndpointsID, cfgs...)
|
||||
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion, c.SigningName)
|
||||
}
|
||||
|
||||
// newClient creates, initializes and returns a new service client instance.
|
||||
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion, signingName string) *Lightsail {
|
||||
svc := &Lightsail{
|
||||
Client: client.New(
|
||||
cfg,
|
||||
metadata.ClientInfo{
|
||||
ServiceName: ServiceName,
|
||||
SigningName: signingName,
|
||||
SigningRegion: signingRegion,
|
||||
Endpoint: endpoint,
|
||||
APIVersion: "2016-11-28",
|
||||
JSONVersion: "1.1",
|
||||
TargetPrefix: "Lightsail_20161128",
|
||||
},
|
||||
handlers,
|
||||
),
|
||||
}
|
||||
|
||||
// Handlers
|
||||
svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler)
|
||||
svc.Handlers.Build.PushBackNamed(jsonrpc.BuildHandler)
|
||||
svc.Handlers.Unmarshal.PushBackNamed(jsonrpc.UnmarshalHandler)
|
||||
svc.Handlers.UnmarshalMeta.PushBackNamed(jsonrpc.UnmarshalMetaHandler)
|
||||
svc.Handlers.UnmarshalError.PushBackNamed(jsonrpc.UnmarshalErrorHandler)
|
||||
|
||||
// Run custom client initialization if present
|
||||
if initClient != nil {
|
||||
initClient(svc.Client)
|
||||
}
|
||||
|
||||
return svc
|
||||
}
|
||||
|
||||
// newRequest creates a new request for a Lightsail operation and runs any
|
||||
// custom request initialization.
|
||||
func (c *Lightsail) newRequest(op *request.Operation, params, data interface{}) *request.Request {
|
||||
req := c.NewRequest(op, params, data)
|
||||
|
||||
// Run custom request initialization if present
|
||||
if initRequest != nil {
|
||||
initRequest(req)
|
||||
}
|
||||
|
||||
return req
|
||||
}
|
21
vendor/github.com/namedotcom/go/LICENSE
generated
vendored
Normal file
21
vendor/github.com/namedotcom/go/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Name.com
|
||||
|
||||
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.
|
133
vendor/github.com/namedotcom/go/namecom/dns.go
generated
vendored
Normal file
133
vendor/github.com/namedotcom/go/namecom/dns.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
package namecom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var _ = bytes.MinRead
|
||||
|
||||
// ListRecords returns all records for a zone.
|
||||
func (n *NameCom) ListRecords(request *ListRecordsRequest) (*ListRecordsResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/records", request.DomainName)
|
||||
|
||||
values := url.Values{}
|
||||
if request.PerPage != 0 {
|
||||
values.Set("perPage", fmt.Sprintf("%d", request.PerPage))
|
||||
}
|
||||
if request.Page != 0 {
|
||||
values.Set("page", fmt.Sprintf("%d", request.Page))
|
||||
}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &ListRecordsResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetRecord returns details about an individual record.
|
||||
func (n *NameCom) GetRecord(request *GetRecordRequest) (*Record, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/records/%d", request.DomainName, request.ID)
|
||||
|
||||
values := url.Values{}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Record{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CreateRecord creates a new record in the zone.
|
||||
func (n *NameCom) CreateRecord(request *Record) (*Record, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/records", request.DomainName)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Record{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UpdateRecord replaces the record with the new record that is passed.
|
||||
func (n *NameCom) UpdateRecord(request *Record) (*Record, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/records/%d", request.DomainName, request.ID)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.put(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Record{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DeleteRecord deletes a record from the zone.
|
||||
func (n *NameCom) DeleteRecord(request *DeleteRecordRequest) (*EmptyResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/records/%d", request.DomainName, request.ID)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.delete(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &EmptyResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
102
vendor/github.com/namedotcom/go/namecom/dnssecs.go
generated
vendored
Normal file
102
vendor/github.com/namedotcom/go/namecom/dnssecs.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
package namecom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var _ = bytes.MinRead
|
||||
|
||||
// ListDNSSECs lists all of the DNSSEC keys registered with the registry.
|
||||
func (n *NameCom) ListDNSSECs(request *ListDNSSECsRequest) (*ListDNSSECsResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/dnssec", request.DomainName)
|
||||
|
||||
values := url.Values{}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &ListDNSSECsResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetDNSSEC retrieves the details for a key registered with the registry.
|
||||
func (n *NameCom) GetDNSSEC(request *GetDNSSECRequest) (*DNSSEC, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/dnssec/%s", request.DomainName, request.Digest)
|
||||
|
||||
values := url.Values{}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &DNSSEC{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CreateDNSSEC registers a DNSSEC key with the registry.
|
||||
func (n *NameCom) CreateDNSSEC(request *DNSSEC) (*DNSSEC, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/dnssec", request.DomainName)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &DNSSEC{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DeleteDNSSEC removes a DNSSEC key from the registry.
|
||||
func (n *NameCom) DeleteDNSSEC(request *DeleteDNSSECRequest) (*EmptyResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/dnssec/%s", request.DomainName, request.Digest)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.delete(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &EmptyResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
382
vendor/github.com/namedotcom/go/namecom/domains.go
generated
vendored
Normal file
382
vendor/github.com/namedotcom/go/namecom/domains.go
generated
vendored
Normal file
|
@ -0,0 +1,382 @@
|
|||
package namecom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var _ = bytes.MinRead
|
||||
|
||||
// ListDomains returns all domains in the account. It omits some information that can be retrieved from GetDomain.
|
||||
func (n *NameCom) ListDomains(request *ListDomainsRequest) (*ListDomainsResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains")
|
||||
|
||||
values := url.Values{}
|
||||
if request.PerPage != 0 {
|
||||
values.Set("perPage", fmt.Sprintf("%d", request.PerPage))
|
||||
}
|
||||
if request.Page != 0 {
|
||||
values.Set("page", fmt.Sprintf("%d", request.Page))
|
||||
}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &ListDomainsResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetDomain returns details about a specific domain
|
||||
func (n *NameCom) GetDomain(request *GetDomainRequest) (*Domain, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s", request.DomainName)
|
||||
|
||||
values := url.Values{}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Domain{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CreateDomain purchases a new domain. Domains that are not regularly priced require the purchase_price field to be specified.
|
||||
func (n *NameCom) CreateDomain(request *CreateDomainRequest) (*CreateDomainResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &CreateDomainResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// EnableAutorenew enables the domain to be automatically renewed when it gets close to expiring.
|
||||
func (n *NameCom) EnableAutorenew(request *EnableAutorenewForDomainRequest) (*Domain, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Domain{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DisableAutorenew disables automatic renewals, thus requiring the domain to be renewed manually.
|
||||
func (n *NameCom) DisableAutorenew(request *DisableAutorenewForDomainRequest) (*Domain, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Domain{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// RenewDomain will renew a domain. Purchase_price is required if the renewal is not regularly priced.
|
||||
func (n *NameCom) RenewDomain(request *RenewDomainRequest) (*RenewDomainResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &RenewDomainResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetAuthCodeForDomain returns the Transfer Authorization Code for the domain.
|
||||
func (n *NameCom) GetAuthCodeForDomain(request *AuthCodeRequest) (*AuthCodeResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains")
|
||||
|
||||
values := url.Values{}
|
||||
if request.DomainName != "" {
|
||||
values.Set("domainName", request.DomainName)
|
||||
}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &AuthCodeResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// PurchasePrivacy will add Whois Privacy protection to a domain or will an renew existing subscription.
|
||||
func (n *NameCom) PurchasePrivacy(request *PrivacyRequest) (*PrivacyResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &PrivacyResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SetNameservers will set the nameservers for the Domain.
|
||||
func (n *NameCom) SetNameservers(request *SetNameserversRequest) (*Domain, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s:setNameservers", request.DomainName)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Domain{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SetContacts will set the contacts for the Domain.
|
||||
func (n *NameCom) SetContacts(request *SetContactsRequest) (*Domain, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Domain{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// LockDomain will lock a domain so that it cannot be transfered to another registrar.
|
||||
func (n *NameCom) LockDomain(request *LockDomainRequest) (*Domain, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Domain{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UnlockDomain will unlock a domain so that it can be transfered to another registrar.
|
||||
func (n *NameCom) UnlockDomain(request *UnlockDomainRequest) (*Domain, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Domain{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CheckAvailability will check a list of domains to see if they are purchaseable. A Maximum of 50 domains can be specified.
|
||||
func (n *NameCom) CheckAvailability(request *AvailabilityRequest) (*SearchResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains:checkAvailability")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &SearchResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Search will perform a search for specified keywords.
|
||||
func (n *NameCom) Search(request *SearchRequest) (*SearchResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains:search")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &SearchResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SearchStream will return JSON encoded SearchResults as they are recieved from the registry. The SearchResults are separated by newlines. This can allow clients to react to results before the search is fully completed.
|
||||
func (n *NameCom) SearchStream(request *SearchRequest) (*SearchResult, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains:searchStream")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &SearchResult{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
133
vendor/github.com/namedotcom/go/namecom/emailforwardings.go
generated
vendored
Normal file
133
vendor/github.com/namedotcom/go/namecom/emailforwardings.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
package namecom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var _ = bytes.MinRead
|
||||
|
||||
// ListEmailForwardings returns a pagenated list of email forwarding entries for a domain.
|
||||
func (n *NameCom) ListEmailForwardings(request *ListEmailForwardingsRequest) (*ListEmailForwardingsResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/email/forwarding", request.DomainName)
|
||||
|
||||
values := url.Values{}
|
||||
if request.PerPage != 0 {
|
||||
values.Set("perPage", fmt.Sprintf("%d", request.PerPage))
|
||||
}
|
||||
if request.Page != 0 {
|
||||
values.Set("page", fmt.Sprintf("%d", request.Page))
|
||||
}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &ListEmailForwardingsResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetEmailForwarding returns an email forwarding entry.
|
||||
func (n *NameCom) GetEmailForwarding(request *GetEmailForwardingRequest) (*EmailForwarding, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/email/forwarding/%s", request.DomainName, request.EmailBox)
|
||||
|
||||
values := url.Values{}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &EmailForwarding{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CreateEmailForwarding creates an email forwarding entry. If this is the first email forwarding entry, it may modify the MX records for the domain accordingly.
|
||||
func (n *NameCom) CreateEmailForwarding(request *EmailForwarding) (*EmailForwarding, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/email/forwarding", request.DomainName)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &EmailForwarding{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UpdateEmailForwarding updates which email address the email is being forwarded to.
|
||||
func (n *NameCom) UpdateEmailForwarding(request *EmailForwarding) (*EmailForwarding, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/email/forwarding/%s", request.DomainName, request.EmailBox)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.put(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &EmailForwarding{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DeleteEmailForwarding deletes the email forwarding entry.
|
||||
func (n *NameCom) DeleteEmailForwarding(request *DeleteEmailForwardingRequest) (*EmptyResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/email/forwarding/%s", request.DomainName, request.EmailBox)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.delete(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &EmptyResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
31
vendor/github.com/namedotcom/go/namecom/hello.go
generated
vendored
Normal file
31
vendor/github.com/namedotcom/go/namecom/hello.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
package namecom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var _ = bytes.MinRead
|
||||
|
||||
// HelloFunc returns some information about the API server.
|
||||
func (n *NameCom) HelloFunc(request *HelloRequest) (*HelloResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/hello")
|
||||
|
||||
values := url.Values{}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &HelloResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
729
vendor/github.com/namedotcom/go/namecom/namecom.go
generated
vendored
Normal file
729
vendor/github.com/namedotcom/go/namecom/namecom.go
generated
vendored
Normal file
|
@ -0,0 +1,729 @@
|
|||
/*
|
||||
Package namecom provides a client for accessing the Name.com v4 API.
|
||||
|
||||
See https://www.name.com/api-docs for an introduction
|
||||
to the Name.com v4 API.
|
||||
|
||||
Creating a Client
|
||||
|
||||
To start working with this package, create a client with user credentials
|
||||
|
||||
nc := namecom.New("username","apitoken")
|
||||
|
||||
To check if the username and token are correct using the HelloFunc endpoint
|
||||
|
||||
response, err := nc.HelloFunc(&namecom.HelloRequest{})
|
||||
if err != nil {
|
||||
// TODO: handle error
|
||||
}
|
||||
|
||||
*/
|
||||
package namecom
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// NameCom is a client for connecting to the Name.com API.
|
||||
type NameCom struct {
|
||||
Server string
|
||||
User string
|
||||
Token string
|
||||
Client *http.Client
|
||||
}
|
||||
|
||||
// New creates a new NameCom client using the production environment server endpoint.
|
||||
func New(user, token string) *NameCom {
|
||||
return &NameCom{
|
||||
Server: "api.name.com",
|
||||
User: user,
|
||||
Token: token,
|
||||
Client: &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Test creates a new NameCom client using the test environment server enpoint.
|
||||
func Test(user, token string) *NameCom {
|
||||
return &NameCom{
|
||||
Server: "api.dev.name.com",
|
||||
User: user,
|
||||
Token: token,
|
||||
Client: &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Error allows an ErrorResponse object to implement the error interface.
|
||||
func (er ErrorResponse) Error() string {
|
||||
return er.Message + ": " + er.Details
|
||||
}
|
||||
|
||||
func (n *NameCom) errorResponse(resp *http.Response) error {
|
||||
er := &ErrorResponse{}
|
||||
err := json.NewDecoder(resp.Body).Decode(er)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "api returned unexpected response")
|
||||
}
|
||||
|
||||
return errors.WithStack(er)
|
||||
}
|
||||
|
||||
func (n *NameCom) get(endpoint string, values url.Values) (io.Reader, error) {
|
||||
if len(values) == 0 {
|
||||
endpoint = endpoint + "?" + values.Encode()
|
||||
}
|
||||
return n.doRequest("GET", endpoint, nil)
|
||||
}
|
||||
|
||||
func (n *NameCom) post(endpoint string, post io.Reader) (io.Reader, error) {
|
||||
return n.doRequest("POST", endpoint, post)
|
||||
}
|
||||
|
||||
func (n *NameCom) put(endpoint string, post io.Reader) (io.Reader, error) {
|
||||
return n.doRequest("PUT", endpoint, post)
|
||||
}
|
||||
|
||||
func (n *NameCom) delete(endpoint string, post io.Reader) (io.Reader, error) {
|
||||
return n.doRequest("DELETE", endpoint, post)
|
||||
}
|
||||
|
||||
func (n *NameCom) doRequest(method, endpoint string, post io.Reader) (io.Reader, error) {
|
||||
if n.User == "" || n.Token == "" {
|
||||
return nil, errors.New("both User and Token must be specified")
|
||||
}
|
||||
if n.Server == "" {
|
||||
n.Server = "api.name.com"
|
||||
}
|
||||
if n.Client == nil {
|
||||
n.Client = &http.Client{Timeout: 10 * time.Second}
|
||||
}
|
||||
|
||||
url := "https://" + n.Server + endpoint
|
||||
|
||||
req, err := http.NewRequest(method, url, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.SetBasicAuth(n.User, n.Token)
|
||||
|
||||
resp, err := n.Client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, n.errorResponse(resp)
|
||||
}
|
||||
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
// EmptyResponse is an empty response used for DELETE endpoints.
|
||||
type EmptyResponse struct {
|
||||
}
|
||||
|
||||
// ErrorResponse is what is returned if the HTTP status code is not 200.
|
||||
type ErrorResponse struct {
|
||||
// Message is the error message.
|
||||
Message string `json:"message,omitempty"`
|
||||
// Details may have some additional details about the error.
|
||||
Details string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
// Record is an individual DNS resource record.
|
||||
type Record struct {
|
||||
// Unique record id. Value is ignored on Create, and must match the URI on Update.
|
||||
ID int32 `json:"id,omitempty"`
|
||||
// DomainName is the zone that the record belongs to.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Host is the hostname relative to the zone: e.g. for a record for blog.example.org, domain would be "example.org" and host would be "blog".
|
||||
// An apex record would be specified by either an empty host "" or "@".
|
||||
// A SRV record would be specified by "_{service}._{protocal}.{host}": e.g. "_sip._tcp.phone" for _sip._tcp.phone.example.org.
|
||||
Host string `json:"host,omitempty"`
|
||||
// FQDN is the Fully Qualified Domain Name. It is the combination of the host and the domain name. It always ends in a ".". FQDN is ignored in CreateRecord, specify via the Host field instead.
|
||||
Fqdn string `json:"fqdn,omitempty"`
|
||||
// Type is one of the following: A, AAAA, ANAME, CNAME, MX, NS, SRV, or TXT.
|
||||
Type string `json:"type,omitempty"`
|
||||
// Answer is either the IP address for A or AAAA records; the target for ANAME, CNAME, MX, or NS records; the text for TXT records.
|
||||
// For SRV records, answer has the following format: "{weight} {port} {target}" e.g. "1 5061 sip.example.org".
|
||||
Answer string `json:"answer,omitempty"`
|
||||
// TTL is the time this record can be cached for in seconds. Name.com allows a minimum TTL of 300, or 5 minutes.
|
||||
TTL uint32 `json:"ttl,omitempty"`
|
||||
// Priority is only required for MX and SRV records, it is ignored for all others.
|
||||
Priority uint32 `json:"priority,omitempty"`
|
||||
}
|
||||
|
||||
// ListRecordsRequest requests a list of records that exist for the domain
|
||||
type ListRecordsRequest struct {
|
||||
// DomainName is the zone to list the records for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Per Page is the number of records to return per request. Per Page defaults to 1,000.
|
||||
PerPage int32 `json:"perPage,omitempty"`
|
||||
// Page is which page to return
|
||||
Page int32 `json:"page,omitempty"`
|
||||
}
|
||||
|
||||
// ListRecordsResponse is the response for the ListRecords function.
|
||||
type ListRecordsResponse struct {
|
||||
// Records contains the records in the zone
|
||||
Records []*Record `json:"records,omitempty"`
|
||||
// NextPage is the identifier for the next page of results. It is only populated if there is another page of results after the current page.
|
||||
NextPage int32 `json:"nextPage,omitempty"`
|
||||
// LastPage is the identifier for the final page of results. It is only populated if there is another page of results after the current page.
|
||||
LastPage int32 `json:"lastPage,omitempty"`
|
||||
}
|
||||
|
||||
// GetRecordRequest requests the record identified by id and domain.
|
||||
type GetRecordRequest struct {
|
||||
// DomainName is the zone the record exists in
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// ID is the server-assigned unique identifier for this record
|
||||
ID int32 `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// DeleteRecordRequest deletes a specific record
|
||||
type DeleteRecordRequest struct {
|
||||
// DomainName is the zone that the record to be deleted exists in.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// ID is the server-assigned unique identifier for the Record to be deleted. If the Record with that ID does not exist in the specified Domain, an error is returned.
|
||||
ID int32 `json:"id,omitempty"`
|
||||
}
|
||||
|
||||
// DNSSEC contains all the data required to create a DS record at the registry.
|
||||
type DNSSEC struct {
|
||||
// DomainName is the domain name.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// KeyTag contains the key tag value of the DNSKEY RR that validates this signature. The algorithm to generate it is here: https://tools.ietf.org/html/rfc4034#appendix-B
|
||||
KeyTag int32 `json:"keyTag,omitempty"`
|
||||
// Algorithm is an integer identifying the algorithm used for signing. Valid values can be found here: https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
|
||||
Algorithm int32 `json:"algorithm,omitempty"`
|
||||
// DigestType is an integer identifying the algorithm used to create the digest. Valid values can be found here: https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
|
||||
DigestType int32 `json:"digestType,omitempty"`
|
||||
// Digest is a digest of the DNSKEY RR that is registered with the registry.
|
||||
Digest string `json:"digest,omitempty"`
|
||||
}
|
||||
|
||||
// ListDNSSECsRequest contains the domain name to list DS records for.
|
||||
type ListDNSSECsRequest struct {
|
||||
// DomainName is the domain name to list keys for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
}
|
||||
|
||||
// ListDNSSECsResponse contains the list of DS records at the registry.
|
||||
type ListDNSSECsResponse struct {
|
||||
// Dnssec is the list of registered DNSSEC keys.
|
||||
Dnssec []*DNSSEC `json:"dnssec,omitempty"`
|
||||
// NextPage is the identifier for the next page of results. It is only populated if there is another page of results after the current page.
|
||||
NextPage int32 `json:"nextPage,omitempty"`
|
||||
// LastPage is the identifier for the final page of results. It is only populated if there is another page of results after the current page.
|
||||
LastPage int32 `json:"lastPage,omitempty"`
|
||||
}
|
||||
|
||||
// GetDNSSECRequest gets the information for a specific DS record at the registry.
|
||||
type GetDNSSECRequest struct {
|
||||
// DomainName is the domain name.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Digest is the digest for the DNSKEY RR to retrieve.
|
||||
Digest string `json:"digest,omitempty"`
|
||||
}
|
||||
|
||||
// DeleteDNSSECRequest specifies the domain name and digest to remove from the registry.
|
||||
type DeleteDNSSECRequest struct {
|
||||
// DomainName is the domain name the key is registered for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Digest is the digest for the DNSKEY RR to remove from the registry.
|
||||
Digest string `json:"digest,omitempty"`
|
||||
}
|
||||
|
||||
// Contact contains all the contact data.
|
||||
type Contact struct {
|
||||
// First name of the contact.
|
||||
FirstName string `json:"firstName,omitempty"`
|
||||
// Last name of the contact.
|
||||
LastName string `json:"lastName,omitempty"`
|
||||
// Company name of the contact. Leave blank if the contact is an individual as some registries will assume it is a corporate entity otherwise.
|
||||
CompanyName string `json:"companyName,omitempty"`
|
||||
// Address1 is the first line of the contact's address.
|
||||
Address1 string `json:"address1,omitempty"`
|
||||
// Address2 is the second line of the contact's address.
|
||||
Address2 string `json:"address2,omitempty"`
|
||||
// City of the contact's address.
|
||||
City string `json:"city,omitempty"`
|
||||
// State or Province for the contact's address.
|
||||
State string `json:"state,omitempty"`
|
||||
// Zip or Postal Code for the contact's address.
|
||||
Zip string `json:"zip,omitempty"`
|
||||
// Country code for the contact's address. Required to be a ISO 3166-1 alpha-2 code.
|
||||
Country string `json:"country,omitempty"`
|
||||
// Phone number of the contact. Should be specified in the following format: "+cc.llllllll" where cc is the country code and llllllll is the local number.
|
||||
Phone string `json:"phone,omitempty"`
|
||||
// Fax number of the contact. Should be specified in the following format: "+cc.llllllll" where cc is the country code and llllllll is the local number.
|
||||
Fax string `json:"fax,omitempty"`
|
||||
// Email of the contact. Should be a complete and valid email address.
|
||||
Email string `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
// Contacts stores the contact information for the roles related to domains.
|
||||
type Contacts struct {
|
||||
// Registrant is the rightful owner of the account and has the right to use and/or sell the domain name. They are able to make changes to all account, domain, and product settings. This information should be reviewed and updated regularly to ensure accuracy.
|
||||
Registrant *Contact `json:"registrant,omitempty"`
|
||||
// Registrants often designate an administrative contact to manage their domain name(s). They primarily deal with business information such as the name on record, postal address, and contact information for the official registrant.
|
||||
Admin *Contact `json:"admin,omitempty"`
|
||||
// The technical contact manages and maintains a domain’s nameservers. If you’re working with a web designer or someone in a similar role, you many want to assign them as a technical contact.
|
||||
Tech *Contact `json:"tech,omitempty"`
|
||||
// The billing contact is the party responsible for paying bills for the account and taking care of renewals.
|
||||
Billing *Contact `json:"billing,omitempty"`
|
||||
}
|
||||
|
||||
// Domain lists all the data for a domain.
|
||||
type Domain struct {
|
||||
// DomainName is the punycode encoded value of the domain name.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Nameservers is the list of nameservers for this domain. If unspecified it defaults to your account default nameservers.
|
||||
Nameservers []string `json:"nameservers,omitempty"`
|
||||
// Contacts for the domain.
|
||||
Contacts *Contacts `json:"contacts,omitempty"`
|
||||
// PrivacyEnabled reflects if Whois Privacy is enabled for this domain.
|
||||
PrivacyEnabled bool `json:"privacyEnabled,omitempty"`
|
||||
// Locked indicates that the domain cannot be transfered to another registrar.
|
||||
Locked bool `json:"locked,omitempty"`
|
||||
// AutorenewEnabled indicates if the domain will attempt to renew automatically before expiration.
|
||||
AutorenewEnabled bool `json:"autorenewEnabled,omitempty"`
|
||||
// ExpireDate is the date the domain will expire.
|
||||
ExpireDate string `json:"expireDate,omitempty"`
|
||||
// CreateDate is the date the domain was created at the registry.
|
||||
CreateDate string `json:"createDate,omitempty"`
|
||||
// RenewalPrice is the price to renew the domain. It may be required for the RenewDomain command.
|
||||
RenewalPrice float64 `json:"renewalPrice,omitempty"`
|
||||
}
|
||||
|
||||
// SearchRequest is used to specify the search parameters.
|
||||
type SearchRequest struct {
|
||||
// Timeout is a value in milliseconds on how long to perform the search for. Valid timeouts are between 500ms to 5,000ms. If not specified, timeout defaults to 1,000ms.
|
||||
// Since some additional processing is performed on the results, a response may take longer then the timeout.
|
||||
Timeout int32 `json:"timeout,omitempty"`
|
||||
// Keyword is the search term to search for. It can be just a word, or a whole domain name.
|
||||
Keyword string `json:"keyword,omitempty"`
|
||||
// TLDFilter will limit results to only contain the specified TLDs.
|
||||
TldFilter []string `json:"tldFilter,omitempty"`
|
||||
// PromoCode is not implemented yet.
|
||||
PromoCode string `json:"promoCode,omitempty"`
|
||||
}
|
||||
|
||||
// AvailabilityRequest is used to list the domain names to check availability for.
|
||||
type AvailabilityRequest struct {
|
||||
// DomainNames is the list of domains to check if they are available.
|
||||
DomainNames []string `json:"domainNames,omitempty"`
|
||||
// PromoCode is not implemented yet.
|
||||
PromoCode string `json:"promoCode,omitempty"`
|
||||
}
|
||||
|
||||
// SearchResult is returned by the CheckAvailability, Search, and SearchStream functions.
|
||||
type SearchResult struct {
|
||||
// DomainName is the punycode encoding of the result domain name.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// SLD is first portion of the domain_name.
|
||||
Sld string `json:"sld,omitempty"`
|
||||
// TLD is the rest of the domain_name after the SLD.
|
||||
Tld string `json:"tld,omitempty"`
|
||||
// Purchaseable indicates whether the search result is available for purchase.
|
||||
Purchasable bool `json:"purchasable,omitempty"`
|
||||
// Premium indicates that this search result is a premium result and the purchase_price needs to be passed to the DomainCreate command.
|
||||
Premium bool `json:"premium,omitempty"`
|
||||
// PurchasePrice is the price for purchasing this domain for 1 year. Purchase_price is always in USD.
|
||||
PurchasePrice float64 `json:"purchasePrice,omitempty"`
|
||||
// PurchaseType indicates what kind of purchase this result is for. It should be passed to the DomainCreate command.
|
||||
PurchaseType string `json:"purchaseType,omitempty"`
|
||||
// RenewalPrice is the annual renewal price for this domain as it may be different then the purchase_price.
|
||||
RenewalPrice float64 `json:"renewalPrice,omitempty"`
|
||||
}
|
||||
|
||||
// SearchResponse returns a list of search results.
|
||||
type SearchResponse struct {
|
||||
// Results of the search are returned here, the order should not be relied upon.
|
||||
Results []*SearchResult `json:"results,omitempty"`
|
||||
}
|
||||
|
||||
// ListDomainsRequest is used to pass the pagination parameters to the ListDomains function.
|
||||
type ListDomainsRequest struct {
|
||||
// Per Page is the number of records to return per request. Per Page defaults to 1,000.
|
||||
PerPage int32 `json:"perPage,omitempty"`
|
||||
// Page is which page to return
|
||||
Page int32 `json:"page,omitempty"`
|
||||
}
|
||||
|
||||
// ListDomainsResponse is the response from a list request, it contains the paginated list of Domains.
|
||||
type ListDomainsResponse struct {
|
||||
// Domains is the list of domains in your account.
|
||||
Domains []*Domain `json:"domains,omitempty"`
|
||||
// NextPage is the identifier for the next page of results. It is only populated if there is another page of results after the current page.
|
||||
NextPage int32 `json:"nextPage,omitempty"`
|
||||
// LastPage is the identifier for the final page of results. It is only populated if there is another page of results after the current page.
|
||||
LastPage int32 `json:"lastPage,omitempty"`
|
||||
}
|
||||
|
||||
// GetDomainRequest specifies the domain name to request data for in the GetDomain function.
|
||||
type GetDomainRequest struct {
|
||||
// DomainName is the domain to retrieve.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
}
|
||||
|
||||
// CreateDomainRequest has the information that is needed to create a domain with the CreateDomain function.
|
||||
type CreateDomainRequest struct {
|
||||
// Domain is the domain object to create. If privacy_enabled is set, Whois Privacy will also be purchased for an additional amount.
|
||||
Domain *Domain `json:"domain,omitempty"`
|
||||
// PurchasePrice is the amount to pay for the domain. If privacy_enabled is set, the regular price for whois protection will be added automatically. If VAT tax applies, it will also be added automatically.
|
||||
// PurchasePrice is required if purchase_type is not "registration" or if it is a premium domain.
|
||||
PurchasePrice float64 `json:"purchasePrice,omitempty"`
|
||||
// PurchaseType defaults to "registration" but should be copied from the result of a search command otherwise.
|
||||
PurchaseType string `json:"purchaseType,omitempty"`
|
||||
// Years is for how many years to register the domain for. Years defaults to 1 if not passed and cannot be more than 10.
|
||||
// If passing purchase_price make sure to adjust it accordingly.
|
||||
Years int32 `json:"years,omitempty"`
|
||||
// TLDRequirements is a way to pass additional data that is required by some registries.
|
||||
TldRequirements map[string]string `json:"tldRequirements,omitempty"`
|
||||
// PromoCode is not yet implemented.
|
||||
PromoCode string `json:"promoCode,omitempty"`
|
||||
}
|
||||
|
||||
// CreateDomainResponse contains the domain info as well as the order info for the created domain.
|
||||
type CreateDomainResponse struct {
|
||||
// Domain is the newly created domain.
|
||||
Domain *Domain `json:"domain,omitempty"`
|
||||
// Order is an identifier for this purchase.
|
||||
Order int32 `json:"order,omitempty"`
|
||||
// TotalPaid is the total amount paid, including VAT and whois protection.
|
||||
TotalPaid float64 `json:"totalPaid,omitempty"`
|
||||
}
|
||||
|
||||
// RenewDomainRequest passes the domain name and purchase parameters to the RenewDomain function.
|
||||
type RenewDomainRequest struct {
|
||||
// DomainName is the domain to renew.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// PurchasePrice is the amount to pay for the domain renewal. If VAT tax applies, it will also be added automatically.
|
||||
// PurchasePrice is required if this is a premium domain.
|
||||
PurchasePrice float64 `json:"purchasePrice,omitempty"`
|
||||
// Years is for how many years to renew the domain for. Years defaults to 1 if not passed and cannot be more than 10.
|
||||
Years int32 `json:"years,omitempty"`
|
||||
// PromoCode is not yet implemented.
|
||||
PromoCode string `json:"promoCode,omitempty"`
|
||||
}
|
||||
|
||||
// RenewDomainResponse contains the updated domain info as well as the order info for the renewed domain.
|
||||
type RenewDomainResponse struct {
|
||||
// Domain reflects the status of the domain after renewing.
|
||||
Domain *Domain `json:"domain,omitempty"`
|
||||
// Order is an identifier for this purchase
|
||||
Order int32 `json:"order,omitempty"`
|
||||
// TotalPaid is the total amount paid, including VAT.
|
||||
TotalPaid float64 `json:"totalPaid,omitempty"`
|
||||
}
|
||||
|
||||
// AuthCodeRequest passes the domain name to the GetAuthCodeForDomain funtion.
|
||||
type AuthCodeRequest struct {
|
||||
// DomainName is the domain name to retrieve the authorization code for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
}
|
||||
|
||||
// AuthCodeResponse returns the auth code from the GetAuthCodeForDomain funtion.
|
||||
type AuthCodeResponse struct {
|
||||
// AuthCode is the authorization code needed to transfer a domain to another registrar. If you are storing auth codes, be sure to store them in a secure manner.
|
||||
AuthCode string `json:"authCode,omitempty"`
|
||||
}
|
||||
|
||||
// PrivacyRequest passes the domain name as well as the purchase parameters to the PurchasePrivacy function.
|
||||
type PrivacyRequest struct {
|
||||
// DomainName is the domain to purchase Whois Privacy for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// PurchasePrice is the amount you expect to pay.
|
||||
PurchasePrice float64 `json:"purchasePrice,omitempty"`
|
||||
// Years is the number of years you wish to purchase Whois Privacy for. Years defaults to 1 and cannot be more then the domain expiration date.
|
||||
Years int32 `json:"years,omitempty"`
|
||||
// PromoCode is not yet implemented
|
||||
PromoCode string `json:"promoCode,omitempty"`
|
||||
}
|
||||
|
||||
// PrivacyResponse contains the updated domain info as well as the order info for the newly purchased Whois Privacy.
|
||||
type PrivacyResponse struct {
|
||||
// Domain is the status of the domain after the purchase of Whois Privacy.
|
||||
Domain *Domain `json:"domain,omitempty"`
|
||||
// Order is an identifier for this purchase.
|
||||
Order int32 `json:"order,omitempty"`
|
||||
// TotalPaid is the total amount paid, including VAT.
|
||||
TotalPaid float64 `json:"totalPaid,omitempty"`
|
||||
}
|
||||
|
||||
// SetNameserversRequest passes the list of nameservers to set for the SetNameserver function.
|
||||
type SetNameserversRequest struct {
|
||||
// DomainName is the domain name to set the nameservers for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Namesevers is a list of the nameservers to set. Nameservers should already be set up and hosting the zone properly as some registries will verify before allowing the change.
|
||||
Nameservers []string `json:"nameservers,omitempty"`
|
||||
}
|
||||
|
||||
// SetContactsRequest passes the contact info for each role to the SetContacts function.
|
||||
type SetContactsRequest struct {
|
||||
// DomainName is the domain name to set the contacts for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Contacts is the list of contacts to set.
|
||||
Contacts *Contacts `json:"contacts,omitempty"`
|
||||
}
|
||||
|
||||
// EnableAutorenewForDomainRequest is used to pass the domain name to the EnableAutorenewForDomain function.
|
||||
type EnableAutorenewForDomainRequest struct {
|
||||
// DomainName is the domain name to enable autorenew for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
}
|
||||
|
||||
// DisableAutorenewForDomainRequest is used to pass the domain name to the DisableAutorenewForDomain function.
|
||||
type DisableAutorenewForDomainRequest struct {
|
||||
// DomainName is the domain name to disable autorenew for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
}
|
||||
|
||||
// LockDomainRequest is used to pass the domain name to the LockDomain function.
|
||||
type LockDomainRequest struct {
|
||||
// DomainName is the domain name to lock.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
}
|
||||
|
||||
// UnlockDomainRequest is used to pass the domain name to the UnlockDomain function.
|
||||
type UnlockDomainRequest struct {
|
||||
// DomainName is the domain name to unlock.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
}
|
||||
|
||||
// EmailForwarding contains all the information for an email forwarding entry.
|
||||
type EmailForwarding struct {
|
||||
// DomainName is the domain part of the email address to forward.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// EmailBox is the user portion of the email address to forward.
|
||||
EmailBox string `json:"emailBox,omitempty"`
|
||||
// EmailTo is the entire email address to forward email to.
|
||||
EmailTo string `json:"emailTo,omitempty"`
|
||||
}
|
||||
|
||||
// ListEmailForwardingsRequest passes the domain name and pagination information to the ListEmailForwardings function.
|
||||
type ListEmailForwardingsRequest struct {
|
||||
// DomainName is the domain to list email forwarded boxes for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Per Page is the number of records to return per request. Per Page defaults to 1,000.
|
||||
PerPage int32 `json:"perPage,omitempty"`
|
||||
// Page is which page to return.
|
||||
Page int32 `json:"page,omitempty"`
|
||||
}
|
||||
|
||||
// ListEmailForwardingsResponse returns the list of email forwarding entries as well as the pagination information.
|
||||
type ListEmailForwardingsResponse struct {
|
||||
// EmailForwarding is the list of forwarded email boxes.
|
||||
EmailForwarding []*EmailForwarding `json:"emailForwarding,omitempty"`
|
||||
// NextPage is the identifier for the next page of results. It is only populated if there is another page of results after the current page.
|
||||
NextPage int32 `json:"nextPage,omitempty"`
|
||||
// LastPage is the identifier for the final page of results. It is only populated if there is another page of results after the current page.
|
||||
LastPage int32 `json:"lastPage,omitempty"`
|
||||
}
|
||||
|
||||
// GetEmailForwardingRequest passes the domain name and email box to request the email forwarding information for.
|
||||
type GetEmailForwardingRequest struct {
|
||||
// DomainName is the domain to list email forwarded box for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// EmailBox is which email box to retrieve.
|
||||
EmailBox string `json:"emailBox,omitempty"`
|
||||
}
|
||||
|
||||
// DeleteEmailForwardingRequest passes the domain name and email box to the DeleteEmailForwarding function.
|
||||
type DeleteEmailForwardingRequest struct {
|
||||
// DomainName is the domain to delete the email forwarded box from.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// EmailBox is which email box to delete.
|
||||
EmailBox string `json:"emailBox,omitempty"`
|
||||
}
|
||||
|
||||
// HelloRequest doesn't take any parameters.
|
||||
type HelloRequest struct {
|
||||
}
|
||||
|
||||
// HelloResponse is the response from the HelloFunc command
|
||||
type HelloResponse struct {
|
||||
// ServerName is an identfier for which server is being accessed.
|
||||
ServerName string `json:"serverName,omitempty"`
|
||||
// Motd is a message of the day. It might provide some useful information.
|
||||
Motd string `json:"motd,omitempty"`
|
||||
// Username is the account name you are currently logged into.
|
||||
Username string `json:"username,omitempty"`
|
||||
// ServerTime is the current date/time at the server.
|
||||
ServerTime string `json:"serverTime,omitempty"`
|
||||
}
|
||||
|
||||
// Transfer contains the information related to a transfer of a domain name to Name.com.
|
||||
type Transfer struct {
|
||||
// DomainName is the domain to be transfered to Name.com.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Email is the email address that the approval email was sent to. Not every TLD requries an approval email. This is usaully pulled from Whois.
|
||||
Email string `json:"email,omitempty"`
|
||||
// Status is the current status of the transfer. Details about statuses can be found in the following Knowledge Base article: <https://www.name.com/support/articles/115012519688-Transfer-status-FAQ>.
|
||||
Status string `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// ListTransfersRequest passes the pagination information to the ListTransfers function.
|
||||
type ListTransfersRequest struct {
|
||||
// Per Page is the number of records to return per request. Per Page defaults to 1,000.
|
||||
PerPage int32 `json:"perPage,omitempty"`
|
||||
// Page is which page to return
|
||||
Page int32 `json:"page,omitempty"`
|
||||
}
|
||||
|
||||
// ListTransfersResponse returns the list of pending transfers as well as the paginiation information if relevent.
|
||||
type ListTransfersResponse struct {
|
||||
// Transfers is a list of pending transfers
|
||||
Transfers []*Transfer `json:"transfers,omitempty"`
|
||||
// NextPage is the identifier for the next page of results. It is only populated if there is another page of results after the current page.
|
||||
NextPage int32 `json:"nextPage,omitempty"`
|
||||
// LastPage is the identifier for the final page of results. It is only populated if there is another page of results after the current page.
|
||||
LastPage int32 `json:"lastPage,omitempty"`
|
||||
}
|
||||
|
||||
// GetTransferRequest passes the domain name to the GetTransfer function.
|
||||
type GetTransferRequest struct {
|
||||
// DomainName is the domain you want to get the transfer information for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
}
|
||||
|
||||
// CreateTransferRequest passes the required transfer info to the CreateTransfer function.
|
||||
type CreateTransferRequest struct {
|
||||
// DomainName is the domain you want to transfer to Name.com.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// AuthCode is the authorization code for the transfer. Not all TLDs require authorization codes, but most do.
|
||||
AuthCode string `json:"authCode,omitempty"`
|
||||
// PrivacyEnabled is a flag on whether to purchase Whois Privacy with the transfer.
|
||||
PrivacyEnabled bool `json:"privacyEnabled,omitempty"`
|
||||
// PurchasePrice is the amount to pay for the transfer of the domain. If privacy_enabled is set, the regular price for Whois Privacy will be added automatically. If VAT tax applies, it will also be added automatically.
|
||||
// PurchasePrice is required if the domain to transfer is a premium domain.
|
||||
PurchasePrice float64 `json:"purchasePrice,omitempty"`
|
||||
// PromoCode is not implemented yet
|
||||
PromoCode string `json:"promoCode,omitempty"`
|
||||
}
|
||||
|
||||
// CreateTransferResponse returns the newly created transfer resource as well as the order information.
|
||||
type CreateTransferResponse struct {
|
||||
// Transfer is the transfer resource created.
|
||||
Transfer *Transfer `json:"transfer,omitempty"`
|
||||
// Order is an identifier for this purchase.
|
||||
Order int32 `json:"order,omitempty"`
|
||||
// TotalPaid is the total amount paid, including VAT and Whois Privacy.
|
||||
TotalPaid float64 `json:"totalPaid,omitempty"`
|
||||
}
|
||||
|
||||
// CancelTransferRequest passes the domain name to be canceled to the CancelTransfer function.
|
||||
type CancelTransferRequest struct {
|
||||
// DomainName is the domain to cancel the transfer for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
}
|
||||
|
||||
// URLForwarding is the model for URL forwarding entries.
|
||||
type URLForwarding struct {
|
||||
// DomainName is the domain part of the hostname to forward.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Host is the entirety of the hostname. i.e. www.example.org
|
||||
Host string `json:"host,omitempty"`
|
||||
// ForwardsTo is the URL this host will be forwarded to.
|
||||
ForwardsTo string `json:"forwardsTo,omitempty"`
|
||||
// Type is the type of forwarding. Valid types are: Masked - This retains the original domain in the address bar and will not reveal or display the actual destination URL. If you are forwarding knowledgebase.ninja to Name.com, the address bar will say knowledgebase.ninja. This is sometimes called iframe forwarding. And: Redirect - This does not retain the original domain in the address bar, so the user will see it change and realize they were forwarded from the URL they originally entered. If you are forwarding knowledgebase.ninja to Name.com, the address bar will say Name.com. This is also called 301 forwarding.
|
||||
Type string `json:"type,omitempty"`
|
||||
// Title is the title for the html page to use if the type is masked. Values are ignored for types other then "masked".
|
||||
Title string `json:"title,omitempty"`
|
||||
// Meta is the meta tags to add to the html page if the type is masked. ex: "<meta name='keywords' content='fish, denver, platte'>". Values are ignored for types other then "masked".
|
||||
Meta string `json:"meta,omitempty"`
|
||||
}
|
||||
|
||||
// ListURLForwardingsRequest is the request for the ListURLForwardings function.
|
||||
type ListURLForwardingsRequest struct {
|
||||
// DomainName is the domain to list URL forwarding entries for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Per Page is the number of records to return per request. Per Page defaults to 1,000.
|
||||
PerPage int32 `json:"perPage,omitempty"`
|
||||
// Page is which page to return.
|
||||
Page int32 `json:"page,omitempty"`
|
||||
}
|
||||
|
||||
// ListURLForwardingsResponse is the response for the ListURLForwardings function.
|
||||
type ListURLForwardingsResponse struct {
|
||||
// URLForwarding is the list of URL forwarding entries.
|
||||
URLForwarding []*URLForwarding `json:"urlForwarding,omitempty"`
|
||||
// NextPage is the identifier for the next page of results. It is only populated if there is another page of results after the current page.
|
||||
NextPage int32 `json:"nextPage,omitempty"`
|
||||
// LastPage is the identifier for the final page of results. It is only populated if there is another page of results after the current page.
|
||||
LastPage int32 `json:"lastPage,omitempty"`
|
||||
}
|
||||
|
||||
// GetURLForwardingRequest is the request for the GetURLForwarding function.
|
||||
type GetURLForwardingRequest struct {
|
||||
// DomainName is the domain to list URL forwarding entry for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Host is the part of the domain name before the domain. i.e. www is the host for www.example.org.
|
||||
Host string `json:"host,omitempty"`
|
||||
}
|
||||
|
||||
// DeleteURLForwardingRequest is the request for the DeleteURLForwarding function.
|
||||
type DeleteURLForwardingRequest struct {
|
||||
// DomainName is the domain to delete the URL forwardind entry from.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Host is the part of the domain name before the domain. i.e. www is the host for www.example.org.
|
||||
Host string `json:"host,omitempty"`
|
||||
}
|
||||
|
||||
// VanityNameserver contains the hostname as well as the list of IP addresses for nameservers.
|
||||
type VanityNameserver struct {
|
||||
// DomainName is the domain the nameserver is a subdomain of.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Hostname is the hostname of the nameserver.
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
// IPs is a list of IP addresses that are used for glue records for this nameserver.
|
||||
Ips []string `json:"ips,omitempty"`
|
||||
}
|
||||
|
||||
// ListVanityNameserversRequest passes the domain name as well as the pagination parameters to the ListVanityNameservers function.
|
||||
type ListVanityNameserversRequest struct {
|
||||
// DomainName is the domain to list the vanity nameservers for.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Per Page is the number of records to return per request. Per Page defaults to 1,000.
|
||||
PerPage int32 `json:"perPage,omitempty"`
|
||||
// Page is which page to return
|
||||
Page int32 `json:"page,omitempty"`
|
||||
}
|
||||
|
||||
// ListVanityNameserversResponse returns the list of vanity nameservers for the domain.
|
||||
type ListVanityNameserversResponse struct {
|
||||
// VanityNameservers is the list of vanity nameservers.
|
||||
VanityNameservers []*VanityNameserver `json:"vanityNameservers,omitempty"`
|
||||
// NextPage is the identifier for the next page of results. It is only populated if there is another page of results after the current page.
|
||||
NextPage int32 `json:"nextPage,omitempty"`
|
||||
// LastPage is the identifier for the final page of results. It is only populated if there is another page of results after the current page.
|
||||
LastPage int32 `json:"lastPage,omitempty"`
|
||||
}
|
||||
|
||||
// GetVanityNameserverRequest passes the hostname to get the details for.
|
||||
type GetVanityNameserverRequest struct {
|
||||
// DomainName is the domain to for the vanity nameserver.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Hostname is the hostname for the vanity nameserver.
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
}
|
||||
|
||||
// DeleteVanityNameserverRequest passes which hostname to remove from the registry.
|
||||
type DeleteVanityNameserverRequest struct {
|
||||
// DomainName is the domain of the vanity nameserver to delete.
|
||||
DomainName string `json:"domainName,omitempty"`
|
||||
// Hostname is the hostname of the vanity nameserver to delete.
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
}
|
108
vendor/github.com/namedotcom/go/namecom/transfers.go
generated
vendored
Normal file
108
vendor/github.com/namedotcom/go/namecom/transfers.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
package namecom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var _ = bytes.MinRead
|
||||
|
||||
// ListTransfers lists all pending transfer in requests. To get the information related to a non-pending transfer, you can use the GetTransfer function for that.
|
||||
func (n *NameCom) ListTransfers(request *ListTransfersRequest) (*ListTransfersResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/transfers")
|
||||
|
||||
values := url.Values{}
|
||||
if request.PerPage != 0 {
|
||||
values.Set("perPage", fmt.Sprintf("%d", request.PerPage))
|
||||
}
|
||||
if request.Page != 0 {
|
||||
values.Set("page", fmt.Sprintf("%d", request.Page))
|
||||
}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &ListTransfersResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetTransfer gets details for a transfer request.
|
||||
func (n *NameCom) GetTransfer(request *GetTransferRequest) (*Transfer, error) {
|
||||
endpoint := fmt.Sprintf("/v4/transfers/%s", request.DomainName)
|
||||
|
||||
values := url.Values{}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Transfer{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CreateTransfer purchases a new domain transfer request.
|
||||
func (n *NameCom) CreateTransfer(request *CreateTransferRequest) (*CreateTransferResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/transfers")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &CreateTransferResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CancelTransfer cancels a pending transfer request and refunds the amount to account credit.
|
||||
func (n *NameCom) CancelTransfer(request *CancelTransferRequest) (*Transfer, error) {
|
||||
endpoint := fmt.Sprintf("/v4/transfers")
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &Transfer{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
133
vendor/github.com/namedotcom/go/namecom/urlforwardings.go
generated
vendored
Normal file
133
vendor/github.com/namedotcom/go/namecom/urlforwardings.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
package namecom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var _ = bytes.MinRead
|
||||
|
||||
// ListURLForwardings returns a pagenated list of URL forwarding entries for a domain.
|
||||
func (n *NameCom) ListURLForwardings(request *ListURLForwardingsRequest) (*ListURLForwardingsResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/url/forwarding", request.DomainName)
|
||||
|
||||
values := url.Values{}
|
||||
if request.PerPage != 0 {
|
||||
values.Set("perPage", fmt.Sprintf("%d", request.PerPage))
|
||||
}
|
||||
if request.Page != 0 {
|
||||
values.Set("page", fmt.Sprintf("%d", request.Page))
|
||||
}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &ListURLForwardingsResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetURLForwarding returns an URL forwarding entry.
|
||||
func (n *NameCom) GetURLForwarding(request *GetURLForwardingRequest) (*URLForwarding, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/url/forwarding/%s", request.DomainName, request.Host)
|
||||
|
||||
values := url.Values{}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &URLForwarding{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CreateURLForwarding creates an URL forwarding entry. If this is the first URL forwarding entry, it may modify the A records for the domain accordingly.
|
||||
func (n *NameCom) CreateURLForwarding(request *URLForwarding) (*URLForwarding, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/url/forwarding", request.DomainName)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &URLForwarding{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UpdateURLForwarding updates which URL the host is being forwarded to.
|
||||
func (n *NameCom) UpdateURLForwarding(request *URLForwarding) (*URLForwarding, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/url/forwarding/%s", request.DomainName, request.Host)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.put(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &URLForwarding{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DeleteURLForwarding deletes the URL forwarding entry.
|
||||
func (n *NameCom) DeleteURLForwarding(request *DeleteURLForwardingRequest) (*EmptyResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/url/forwarding/%s", request.DomainName, request.Host)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.delete(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &EmptyResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
133
vendor/github.com/namedotcom/go/namecom/vanitynameservers.go
generated
vendored
Normal file
133
vendor/github.com/namedotcom/go/namecom/vanitynameservers.go
generated
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
package namecom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var _ = bytes.MinRead
|
||||
|
||||
// ListVanityNameservers lists all nameservers registered with the registry. It omits the IP addresses from the response. Those can be found from calling GetVanityNameserver.
|
||||
func (n *NameCom) ListVanityNameservers(request *ListVanityNameserversRequest) (*ListVanityNameserversResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/vanity_nameservers", request.DomainName)
|
||||
|
||||
values := url.Values{}
|
||||
if request.PerPage != 0 {
|
||||
values.Set("perPage", fmt.Sprintf("%d", request.PerPage))
|
||||
}
|
||||
if request.Page != 0 {
|
||||
values.Set("page", fmt.Sprintf("%d", request.Page))
|
||||
}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &ListVanityNameserversResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetVanityNameserver gets the details for a vanity nameserver registered with the registry.
|
||||
func (n *NameCom) GetVanityNameserver(request *GetVanityNameserverRequest) (*VanityNameserver, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/vanity_nameservers/%s", request.DomainName, request.Hostname)
|
||||
|
||||
values := url.Values{}
|
||||
|
||||
body, err := n.get(endpoint, values)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &VanityNameserver{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// CreateVanityNameserver registers a nameserver with the registry.
|
||||
func (n *NameCom) CreateVanityNameserver(request *VanityNameserver) (*VanityNameserver, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/vanity_nameservers", request.DomainName)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.post(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &VanityNameserver{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// UpdateVanityNameserver allows you to update the glue record IP addresses at the registry.
|
||||
func (n *NameCom) UpdateVanityNameserver(request *VanityNameserver) (*VanityNameserver, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/vanity_nameservers/%s", request.DomainName, request.Hostname)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.put(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &VanityNameserver{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// DeleteVanityNameserver unregisteres the nameserver at the registry. This might fail if the registry believes the nameserver is in use.
|
||||
func (n *NameCom) DeleteVanityNameserver(request *DeleteVanityNameserverRequest) (*EmptyResponse, error) {
|
||||
endpoint := fmt.Sprintf("/v4/domains/%s/vanity_nameservers/%s", request.DomainName, request.Hostname)
|
||||
|
||||
post := &bytes.Buffer{}
|
||||
err := json.NewEncoder(post).Encode(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := n.delete(endpoint, post)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &EmptyResponse{}
|
||||
|
||||
err = json.NewDecoder(body).Decode(resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
70
vendor/github.com/tuvistavie/securerandom/srandom.go
generated
vendored
Normal file
70
vendor/github.com/tuvistavie/securerandom/srandom.go
generated
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
package securerandom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func RandomBytes(n int) ([]byte, error) {
|
||||
bytes := make([]byte, n)
|
||||
_, err := rand.Read(bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
func Base64(n int, padded bool) (string, error) {
|
||||
bytes, err := RandomBytes(n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result := base64.StdEncoding.EncodeToString(bytes)
|
||||
result = strings.Replace(result, "\n", "", -1)
|
||||
if !padded {
|
||||
result = strings.Replace(result, "=", "", -1)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func UrlSafeBase64(n int, padded bool) (string, error) {
|
||||
result, err := Base64(n, padded)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result = strings.Replace(result, "+", "-", -1)
|
||||
result = strings.Replace(result, "/", "_", -1)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func Hex(n int) (string, error) {
|
||||
bytes, err := RandomBytes(n)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(bytes), nil
|
||||
}
|
||||
|
||||
func Uuid() (string, error) {
|
||||
var first, last uint32
|
||||
var middle [4]uint16
|
||||
randomBytes, err := RandomBytes(16)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buffer := bytes.NewBuffer(randomBytes)
|
||||
binary.Read(buffer, binary.BigEndian, &first)
|
||||
for i := 0; i < 4; i++ {
|
||||
binary.Read(buffer, binary.BigEndian, &middle[i])
|
||||
}
|
||||
binary.Read(buffer, binary.BigEndian, &last)
|
||||
middle[1] = (middle[1] & 0x0fff) | 0x4000
|
||||
middle[2] = (middle[2] & 0x3fff) | 0x8000
|
||||
return fmt.Sprintf("%08x-%04x-%04x-%04x-%04x%08x",
|
||||
first, middle[0], middle[1], middle[2], middle[3], last), nil
|
||||
}
|
16
vendor/github.com/xenolf/lego/acme/challenges.go
generated
vendored
16
vendor/github.com/xenolf/lego/acme/challenges.go
generated
vendored
|
@ -1,16 +0,0 @@
|
|||
package acme
|
||||
|
||||
// Challenge is a string that identifies a particular type and version of ACME challenge.
|
||||
type Challenge string
|
||||
|
||||
const (
|
||||
// HTTP01 is the "http-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#http
|
||||
// Note: HTTP01ChallengePath returns the URL path to fulfill this challenge
|
||||
HTTP01 = Challenge("http-01")
|
||||
// TLSSNI01 is the "tls-sni-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#tls-with-server-name-indication-tls-sni
|
||||
// Note: TLSSNI01ChallengeCert returns a certificate to fulfill this challenge
|
||||
TLSSNI01 = Challenge("tls-sni-01")
|
||||
// DNS01 is the "dns-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#dns
|
||||
// Note: DNS01Record returns a DNS record which will fulfill this challenge
|
||||
DNS01 = Challenge("dns-01")
|
||||
)
|
825
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
825
vendor/github.com/xenolf/lego/acme/client.go
generated
vendored
|
@ -1,825 +0,0 @@
|
|||
// Package acme implements the ACME protocol for Let's Encrypt and other conforming providers.
|
||||
package acme
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// Logger is an optional custom logger.
|
||||
Logger *log.Logger
|
||||
)
|
||||
|
||||
const (
|
||||
// maxBodySize is the maximum size of body that we will read.
|
||||
maxBodySize = 1024 * 1024
|
||||
|
||||
// overallRequestLimit is the overall number of request per second limited on the
|
||||
// “new-reg”, “new-authz” and “new-cert” endpoints. From the documentation the
|
||||
// limitation is 20 requests per second, but using 20 as value doesn't work but 18 do
|
||||
overallRequestLimit = 18
|
||||
)
|
||||
|
||||
// logf writes a log entry. It uses Logger if not
|
||||
// nil, otherwise it uses the default log.Logger.
|
||||
func logf(format string, args ...interface{}) {
|
||||
if Logger != nil {
|
||||
Logger.Printf(format, args...)
|
||||
} else {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// User interface is to be implemented by users of this library.
|
||||
// It is used by the client type to get user specific information.
|
||||
type User interface {
|
||||
GetEmail() string
|
||||
GetRegistration() *RegistrationResource
|
||||
GetPrivateKey() crypto.PrivateKey
|
||||
}
|
||||
|
||||
// Interface for all challenge solvers to implement.
|
||||
type solver interface {
|
||||
Solve(challenge challenge, domain string) error
|
||||
}
|
||||
|
||||
type validateFunc func(j *jws, domain, uri string, chlng challenge) error
|
||||
|
||||
// Client is the user-friendy way to ACME
|
||||
type Client struct {
|
||||
directory directory
|
||||
user User
|
||||
jws *jws
|
||||
keyType KeyType
|
||||
solvers map[Challenge]solver
|
||||
}
|
||||
|
||||
// NewClient creates a new ACME client on behalf of the user. The client will depend on
|
||||
// the ACME directory located at caDirURL for the rest of its actions. A private
|
||||
// key of type keyType (see KeyType contants) will be generated when requesting a new
|
||||
// certificate if one isn't provided.
|
||||
func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) {
|
||||
privKey := user.GetPrivateKey()
|
||||
if privKey == nil {
|
||||
return nil, errors.New("private key was nil")
|
||||
}
|
||||
|
||||
var dir directory
|
||||
if _, err := getJSON(caDirURL, &dir); err != nil {
|
||||
return nil, fmt.Errorf("get directory at '%s': %v", caDirURL, err)
|
||||
}
|
||||
|
||||
if dir.NewRegURL == "" {
|
||||
return nil, errors.New("directory missing new registration URL")
|
||||
}
|
||||
if dir.NewAuthzURL == "" {
|
||||
return nil, errors.New("directory missing new authz URL")
|
||||
}
|
||||
if dir.NewCertURL == "" {
|
||||
return nil, errors.New("directory missing new certificate URL")
|
||||
}
|
||||
if dir.RevokeCertURL == "" {
|
||||
return nil, errors.New("directory missing revoke certificate URL")
|
||||
}
|
||||
|
||||
jws := &jws{privKey: privKey, directoryURL: caDirURL}
|
||||
|
||||
// REVIEW: best possibility?
|
||||
// Add all available solvers with the right index as per ACME
|
||||
// spec to this map. Otherwise they won`t be found.
|
||||
solvers := make(map[Challenge]solver)
|
||||
solvers[HTTP01] = &httpChallenge{jws: jws, validate: validate, provider: &HTTPProviderServer{}}
|
||||
solvers[TLSSNI01] = &tlsSNIChallenge{jws: jws, validate: validate, provider: &TLSProviderServer{}}
|
||||
|
||||
return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil
|
||||
}
|
||||
|
||||
// SetChallengeProvider specifies a custom provider p that can solve the given challenge type.
|
||||
func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider) error {
|
||||
switch challenge {
|
||||
case HTTP01:
|
||||
c.solvers[challenge] = &httpChallenge{jws: c.jws, validate: validate, provider: p}
|
||||
case TLSSNI01:
|
||||
c.solvers[challenge] = &tlsSNIChallenge{jws: c.jws, validate: validate, provider: p}
|
||||
case DNS01:
|
||||
c.solvers[challenge] = &dnsChallenge{jws: c.jws, validate: validate, provider: p}
|
||||
default:
|
||||
return fmt.Errorf("Unknown challenge %v", challenge)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetHTTPAddress specifies a custom interface:port to be used for HTTP based challenges.
|
||||
// If this option is not used, the default port 80 and all interfaces will be used.
|
||||
// To only specify a port and no interface use the ":port" notation.
|
||||
//
|
||||
// NOTE: This REPLACES any custom HTTP provider previously set by calling
|
||||
// c.SetChallengeProvider with the default HTTP challenge provider.
|
||||
func (c *Client) SetHTTPAddress(iface string) error {
|
||||
host, port, err := net.SplitHostPort(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if chlng, ok := c.solvers[HTTP01]; ok {
|
||||
chlng.(*httpChallenge).provider = NewHTTPProviderServer(host, port)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTLSAddress specifies a custom interface:port to be used for TLS based challenges.
|
||||
// If this option is not used, the default port 443 and all interfaces will be used.
|
||||
// To only specify a port and no interface use the ":port" notation.
|
||||
//
|
||||
// NOTE: This REPLACES any custom TLS-SNI provider previously set by calling
|
||||
// c.SetChallengeProvider with the default TLS-SNI challenge provider.
|
||||
func (c *Client) SetTLSAddress(iface string) error {
|
||||
host, port, err := net.SplitHostPort(iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if chlng, ok := c.solvers[TLSSNI01]; ok {
|
||||
chlng.(*tlsSNIChallenge).provider = NewTLSProviderServer(host, port)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExcludeChallenges explicitly removes challenges from the pool for solving.
|
||||
func (c *Client) ExcludeChallenges(challenges []Challenge) {
|
||||
// Loop through all challenges and delete the requested one if found.
|
||||
for _, challenge := range challenges {
|
||||
delete(c.solvers, challenge)
|
||||
}
|
||||
}
|
||||
|
||||
// Register the current account to the ACME server.
|
||||
func (c *Client) Register() (*RegistrationResource, error) {
|
||||
if c == nil || c.user == nil {
|
||||
return nil, errors.New("acme: cannot register a nil client or user")
|
||||
}
|
||||
logf("[INFO] acme: Registering account for %s", c.user.GetEmail())
|
||||
|
||||
regMsg := registrationMessage{
|
||||
Resource: "new-reg",
|
||||
}
|
||||
if c.user.GetEmail() != "" {
|
||||
regMsg.Contact = []string{"mailto:" + c.user.GetEmail()}
|
||||
} else {
|
||||
regMsg.Contact = []string{}
|
||||
}
|
||||
|
||||
var serverReg Registration
|
||||
var regURI string
|
||||
hdr, err := postJSON(c.jws, c.directory.NewRegURL, regMsg, &serverReg)
|
||||
if err != nil {
|
||||
remoteErr, ok := err.(RemoteError)
|
||||
if ok && remoteErr.StatusCode == 409 {
|
||||
regURI = hdr.Get("Location")
|
||||
regMsg = registrationMessage{
|
||||
Resource: "reg",
|
||||
}
|
||||
if hdr, err = postJSON(c.jws, regURI, regMsg, &serverReg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
reg := &RegistrationResource{Body: serverReg}
|
||||
|
||||
links := parseLinks(hdr["Link"])
|
||||
|
||||
if regURI == "" {
|
||||
regURI = hdr.Get("Location")
|
||||
}
|
||||
reg.URI = regURI
|
||||
if links["terms-of-service"] != "" {
|
||||
reg.TosURL = links["terms-of-service"]
|
||||
}
|
||||
|
||||
if links["next"] != "" {
|
||||
reg.NewAuthzURL = links["next"]
|
||||
} else {
|
||||
return nil, errors.New("acme: The server did not return 'next' link to proceed")
|
||||
}
|
||||
|
||||
return reg, nil
|
||||
}
|
||||
|
||||
// DeleteRegistration deletes the client's user registration from the ACME
|
||||
// server.
|
||||
func (c *Client) DeleteRegistration() error {
|
||||
if c == nil || c.user == nil {
|
||||
return errors.New("acme: cannot unregister a nil client or user")
|
||||
}
|
||||
logf("[INFO] acme: Deleting account for %s", c.user.GetEmail())
|
||||
|
||||
regMsg := registrationMessage{
|
||||
Resource: "reg",
|
||||
Delete: true,
|
||||
}
|
||||
|
||||
_, err := postJSON(c.jws, c.user.GetRegistration().URI, regMsg, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// QueryRegistration runs a POST request on the client's registration and
|
||||
// returns the result.
|
||||
//
|
||||
// This is similar to the Register function, but acting on an existing
|
||||
// registration link and resource.
|
||||
func (c *Client) QueryRegistration() (*RegistrationResource, error) {
|
||||
if c == nil || c.user == nil {
|
||||
return nil, errors.New("acme: cannot query the registration of a nil client or user")
|
||||
}
|
||||
// Log the URL here instead of the email as the email may not be set
|
||||
logf("[INFO] acme: Querying account for %s", c.user.GetRegistration().URI)
|
||||
|
||||
regMsg := registrationMessage{
|
||||
Resource: "reg",
|
||||
}
|
||||
|
||||
var serverReg Registration
|
||||
hdr, err := postJSON(c.jws, c.user.GetRegistration().URI, regMsg, &serverReg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reg := &RegistrationResource{Body: serverReg}
|
||||
|
||||
links := parseLinks(hdr["Link"])
|
||||
// Location: header is not returned so this needs to be populated off of
|
||||
// existing URI
|
||||
reg.URI = c.user.GetRegistration().URI
|
||||
if links["terms-of-service"] != "" {
|
||||
reg.TosURL = links["terms-of-service"]
|
||||
}
|
||||
|
||||
if links["next"] != "" {
|
||||
reg.NewAuthzURL = links["next"]
|
||||
} else {
|
||||
return nil, errors.New("acme: No new-authz link in response to registration query")
|
||||
}
|
||||
|
||||
return reg, nil
|
||||
}
|
||||
|
||||
// AgreeToTOS updates the Client registration and sends the agreement to
|
||||
// the server.
|
||||
func (c *Client) AgreeToTOS() error {
|
||||
reg := c.user.GetRegistration()
|
||||
|
||||
reg.Body.Agreement = c.user.GetRegistration().TosURL
|
||||
reg.Body.Resource = "reg"
|
||||
_, err := postJSON(c.jws, c.user.GetRegistration().URI, c.user.GetRegistration().Body, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// ObtainCertificateForCSR tries to obtain a certificate matching the CSR passed into it.
|
||||
// The domains are inferred from the CommonName and SubjectAltNames, if any. The private key
|
||||
// for this CSR is not required.
|
||||
// If bundle is true, the []byte contains both the issuer certificate and
|
||||
// your issued certificate as a bundle.
|
||||
// This function will never return a partial certificate. If one domain in the list fails,
|
||||
// the whole certificate will fail.
|
||||
func (c *Client) ObtainCertificateForCSR(csr x509.CertificateRequest, bundle bool) (CertificateResource, map[string]error) {
|
||||
// figure out what domains it concerns
|
||||
// start with the common name
|
||||
domains := []string{csr.Subject.CommonName}
|
||||
|
||||
// loop over the SubjectAltName DNS names
|
||||
DNSNames:
|
||||
for _, sanName := range csr.DNSNames {
|
||||
for _, existingName := range domains {
|
||||
if existingName == sanName {
|
||||
// duplicate; skip this name
|
||||
continue DNSNames
|
||||
}
|
||||
}
|
||||
|
||||
// name is unique
|
||||
domains = append(domains, sanName)
|
||||
}
|
||||
|
||||
if bundle {
|
||||
logf("[INFO][%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||
} else {
|
||||
logf("[INFO][%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||
}
|
||||
|
||||
challenges, failures := c.getChallenges(domains)
|
||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||
if len(failures) > 0 {
|
||||
for _, auth := range challenges {
|
||||
c.disableAuthz(auth)
|
||||
}
|
||||
|
||||
return CertificateResource{}, failures
|
||||
}
|
||||
|
||||
errs := c.solveChallenges(challenges)
|
||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||
if len(errs) > 0 {
|
||||
return CertificateResource{}, errs
|
||||
}
|
||||
|
||||
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||
|
||||
cert, err := c.requestCertificateForCsr(challenges, bundle, csr.Raw, nil)
|
||||
if err != nil {
|
||||
for _, chln := range challenges {
|
||||
failures[chln.Domain] = err
|
||||
}
|
||||
}
|
||||
|
||||
// Add the CSR to the certificate so that it can be used for renewals.
|
||||
cert.CSR = pemEncode(&csr)
|
||||
|
||||
return cert, failures
|
||||
}
|
||||
|
||||
// ObtainCertificate tries to obtain a single certificate using all domains passed into it.
|
||||
// The first domain in domains is used for the CommonName field of the certificate, all other
|
||||
// domains are added using the Subject Alternate Names extension. A new private key is generated
|
||||
// for every invocation of this function. If you do not want that you can supply your own private key
|
||||
// in the privKey parameter. If this parameter is non-nil it will be used instead of generating a new one.
|
||||
// If bundle is true, the []byte contains both the issuer certificate and
|
||||
// your issued certificate as a bundle.
|
||||
// This function will never return a partial certificate. If one domain in the list fails,
|
||||
// the whole certificate will fail.
|
||||
func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, map[string]error) {
|
||||
if bundle {
|
||||
logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
|
||||
} else {
|
||||
logf("[INFO][%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
|
||||
}
|
||||
|
||||
challenges, failures := c.getChallenges(domains)
|
||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||
if len(failures) > 0 {
|
||||
for _, auth := range challenges {
|
||||
c.disableAuthz(auth)
|
||||
}
|
||||
|
||||
return CertificateResource{}, failures
|
||||
}
|
||||
|
||||
errs := c.solveChallenges(challenges)
|
||||
// If any challenge fails - return. Do not generate partial SAN certificates.
|
||||
if len(errs) > 0 {
|
||||
return CertificateResource{}, errs
|
||||
}
|
||||
|
||||
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||
|
||||
cert, err := c.requestCertificate(challenges, bundle, privKey, mustStaple)
|
||||
if err != nil {
|
||||
for _, chln := range challenges {
|
||||
failures[chln.Domain] = err
|
||||
}
|
||||
}
|
||||
|
||||
return cert, failures
|
||||
}
|
||||
|
||||
// RevokeCertificate takes a PEM encoded certificate or bundle and tries to revoke it at the CA.
|
||||
func (c *Client) RevokeCertificate(certificate []byte) error {
|
||||
certificates, err := parsePEMBundle(certificate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x509Cert := certificates[0]
|
||||
if x509Cert.IsCA {
|
||||
return fmt.Errorf("Certificate bundle starts with a CA certificate")
|
||||
}
|
||||
|
||||
encodedCert := base64.URLEncoding.EncodeToString(x509Cert.Raw)
|
||||
|
||||
_, err = postJSON(c.jws, c.directory.RevokeCertURL, revokeCertMessage{Resource: "revoke-cert", Certificate: encodedCert}, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// RenewCertificate takes a CertificateResource and tries to renew the certificate.
|
||||
// If the renewal process succeeds, the new certificate will ge returned in a new CertResource.
|
||||
// Please be aware that this function will return a new certificate in ANY case that is not an error.
|
||||
// If the server does not provide us with a new cert on a GET request to the CertURL
|
||||
// this function will start a new-cert flow where a new certificate gets generated.
|
||||
// If bundle is true, the []byte contains both the issuer certificate and
|
||||
// your issued certificate as a bundle.
|
||||
// For private key reuse the PrivateKey property of the passed in CertificateResource should be non-nil.
|
||||
func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple bool) (CertificateResource, error) {
|
||||
// Input certificate is PEM encoded. Decode it here as we may need the decoded
|
||||
// cert later on in the renewal process. The input may be a bundle or a single certificate.
|
||||
certificates, err := parsePEMBundle(cert.Certificate)
|
||||
if err != nil {
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
|
||||
x509Cert := certificates[0]
|
||||
if x509Cert.IsCA {
|
||||
return CertificateResource{}, fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", cert.Domain)
|
||||
}
|
||||
|
||||
// This is just meant to be informal for the user.
|
||||
timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
|
||||
logf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours()))
|
||||
|
||||
// We always need to request a new certificate to renew.
|
||||
// Start by checking to see if the certificate was based off a CSR, and
|
||||
// use that if it's defined.
|
||||
if len(cert.CSR) > 0 {
|
||||
csr, err := pemDecodeTox509CSR(cert.CSR)
|
||||
if err != nil {
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
newCert, failures := c.ObtainCertificateForCSR(*csr, bundle)
|
||||
return newCert, failures[cert.Domain]
|
||||
}
|
||||
|
||||
var privKey crypto.PrivateKey
|
||||
if cert.PrivateKey != nil {
|
||||
privKey, err = parsePEMPrivateKey(cert.PrivateKey)
|
||||
if err != nil {
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
}
|
||||
|
||||
var domains []string
|
||||
var failures map[string]error
|
||||
// check for SAN certificate
|
||||
if len(x509Cert.DNSNames) > 1 {
|
||||
domains = append(domains, x509Cert.Subject.CommonName)
|
||||
for _, sanDomain := range x509Cert.DNSNames {
|
||||
if sanDomain == x509Cert.Subject.CommonName {
|
||||
continue
|
||||
}
|
||||
domains = append(domains, sanDomain)
|
||||
}
|
||||
} else {
|
||||
domains = append(domains, x509Cert.Subject.CommonName)
|
||||
}
|
||||
|
||||
newCert, failures := c.ObtainCertificate(domains, bundle, privKey, mustStaple)
|
||||
return newCert, failures[cert.Domain]
|
||||
}
|
||||
|
||||
// Looks through the challenge combinations to find a solvable match.
|
||||
// Then solves the challenges in series and returns.
|
||||
func (c *Client) solveChallenges(challenges []authorizationResource) map[string]error {
|
||||
// loop through the resources, basically through the domains.
|
||||
failures := make(map[string]error)
|
||||
for _, authz := range challenges {
|
||||
if authz.Body.Status == "valid" {
|
||||
// Boulder might recycle recent validated authz (see issue #267)
|
||||
logf("[INFO][%s] acme: Authorization already valid; skipping challenge", authz.Domain)
|
||||
continue
|
||||
}
|
||||
// no solvers - no solving
|
||||
if solvers := c.chooseSolvers(authz.Body, authz.Domain); solvers != nil {
|
||||
for i, solver := range solvers {
|
||||
// TODO: do not immediately fail if one domain fails to validate.
|
||||
err := solver.Solve(authz.Body.Challenges[i], authz.Domain)
|
||||
if err != nil {
|
||||
c.disableAuthz(authz)
|
||||
failures[authz.Domain] = err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
c.disableAuthz(authz)
|
||||
failures[authz.Domain] = fmt.Errorf("[%s] acme: Could not determine solvers", authz.Domain)
|
||||
}
|
||||
}
|
||||
|
||||
return failures
|
||||
}
|
||||
|
||||
// Checks all combinations from the server and returns an array of
|
||||
// solvers which should get executed in series.
|
||||
func (c *Client) chooseSolvers(auth authorization, domain string) map[int]solver {
|
||||
for _, combination := range auth.Combinations {
|
||||
solvers := make(map[int]solver)
|
||||
for _, idx := range combination {
|
||||
if solver, ok := c.solvers[auth.Challenges[idx].Type]; ok {
|
||||
solvers[idx] = solver
|
||||
} else {
|
||||
logf("[INFO][%s] acme: Could not find solver for: %s", domain, auth.Challenges[idx].Type)
|
||||
}
|
||||
}
|
||||
|
||||
// If we can solve the whole combination, return the solvers
|
||||
if len(solvers) == len(combination) {
|
||||
return solvers
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get the challenges needed to proof our identifier to the ACME server.
|
||||
func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[string]error) {
|
||||
resc, errc := make(chan authorizationResource), make(chan domainError)
|
||||
|
||||
delay := time.Second / overallRequestLimit
|
||||
|
||||
for _, domain := range domains {
|
||||
time.Sleep(delay)
|
||||
|
||||
go func(domain string) {
|
||||
authMsg := authorization{Resource: "new-authz", Identifier: identifier{Type: "dns", Value: domain}}
|
||||
var authz authorization
|
||||
hdr, err := postJSON(c.jws, c.user.GetRegistration().NewAuthzURL, authMsg, &authz)
|
||||
if err != nil {
|
||||
errc <- domainError{Domain: domain, Error: err}
|
||||
return
|
||||
}
|
||||
|
||||
links := parseLinks(hdr["Link"])
|
||||
if links["next"] == "" {
|
||||
logf("[ERROR][%s] acme: Server did not provide next link to proceed", domain)
|
||||
errc <- domainError{Domain: domain, Error: errors.New("Server did not provide next link to proceed")}
|
||||
return
|
||||
}
|
||||
|
||||
resc <- authorizationResource{Body: authz, NewCertURL: links["next"], AuthURL: hdr.Get("Location"), Domain: domain}
|
||||
}(domain)
|
||||
}
|
||||
|
||||
responses := make(map[string]authorizationResource)
|
||||
failures := make(map[string]error)
|
||||
for i := 0; i < len(domains); i++ {
|
||||
select {
|
||||
case res := <-resc:
|
||||
responses[res.Domain] = res
|
||||
case err := <-errc:
|
||||
failures[err.Domain] = err.Error
|
||||
}
|
||||
}
|
||||
|
||||
challenges := make([]authorizationResource, 0, len(responses))
|
||||
for _, domain := range domains {
|
||||
if challenge, ok := responses[domain]; ok {
|
||||
challenges = append(challenges, challenge)
|
||||
}
|
||||
}
|
||||
|
||||
logAuthz(challenges)
|
||||
|
||||
close(resc)
|
||||
close(errc)
|
||||
|
||||
return challenges, failures
|
||||
}
|
||||
|
||||
func logAuthz(authz []authorizationResource) {
|
||||
for _, auth := range authz {
|
||||
logf("[INFO][%s] AuthURL: %s", auth.Domain, auth.AuthURL)
|
||||
}
|
||||
}
|
||||
|
||||
// cleanAuthz loops through the passed in slice and disables any auths which are not "valid"
|
||||
func (c *Client) disableAuthz(auth authorizationResource) error {
|
||||
var disabledAuth authorization
|
||||
_, err := postJSON(c.jws, auth.AuthURL, deactivateAuthMessage{Resource: "authz", Status: "deactivated"}, &disabledAuth)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, error) {
|
||||
if len(authz) == 0 {
|
||||
return CertificateResource{}, errors.New("Passed no authorizations to requestCertificate!")
|
||||
}
|
||||
|
||||
var err error
|
||||
if privKey == nil {
|
||||
privKey, err = generatePrivateKey(c.keyType)
|
||||
if err != nil {
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// determine certificate name(s) based on the authorization resources
|
||||
commonName := authz[0]
|
||||
var san []string
|
||||
for _, auth := range authz[1:] {
|
||||
san = append(san, auth.Domain)
|
||||
}
|
||||
|
||||
// TODO: should the CSR be customizable?
|
||||
csr, err := generateCsr(privKey, commonName.Domain, san, mustStaple)
|
||||
if err != nil {
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
|
||||
return c.requestCertificateForCsr(authz, bundle, csr, pemEncode(privKey))
|
||||
}
|
||||
|
||||
func (c *Client) requestCertificateForCsr(authz []authorizationResource, bundle bool, csr []byte, privateKeyPem []byte) (CertificateResource, error) {
|
||||
commonName := authz[0]
|
||||
|
||||
var authURLs []string
|
||||
for _, auth := range authz[1:] {
|
||||
authURLs = append(authURLs, auth.AuthURL)
|
||||
}
|
||||
|
||||
csrString := base64.URLEncoding.EncodeToString(csr)
|
||||
jsonBytes, err := json.Marshal(csrMessage{Resource: "new-cert", Csr: csrString, Authorizations: authURLs})
|
||||
if err != nil {
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
|
||||
resp, err := c.jws.post(commonName.NewCertURL, jsonBytes)
|
||||
if err != nil {
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
|
||||
certRes := CertificateResource{
|
||||
Domain: commonName.Domain,
|
||||
CertURL: resp.Header.Get("Location"),
|
||||
PrivateKey: privateKeyPem,
|
||||
}
|
||||
|
||||
maxChecks := 1000
|
||||
for i := 0; i < maxChecks; i++ {
|
||||
done, err := c.checkCertResponse(resp, &certRes, bundle)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
if done {
|
||||
break
|
||||
}
|
||||
if i == maxChecks-1 {
|
||||
return CertificateResource{}, fmt.Errorf("polled for certificate %d times; giving up", i)
|
||||
}
|
||||
resp, err = httpGet(certRes.CertURL)
|
||||
if err != nil {
|
||||
return CertificateResource{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return certRes, nil
|
||||
}
|
||||
|
||||
// checkCertResponse checks resp to see if a certificate is contained in the
|
||||
// response, and if so, loads it into certRes and returns true. If the cert
|
||||
// is not yet ready, it returns false. This function honors the waiting period
|
||||
// required by the Retry-After header of the response, if specified. This
|
||||
// function may read from resp.Body but does NOT close it. The certRes input
|
||||
// should already have the Domain (common name) field populated. If bundle is
|
||||
// true, the certificate will be bundled with the issuer's cert.
|
||||
func (c *Client) checkCertResponse(resp *http.Response, certRes *CertificateResource, bundle bool) (bool, error) {
|
||||
switch resp.StatusCode {
|
||||
case 201, 202:
|
||||
cert, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// The server returns a body with a length of zero if the
|
||||
// certificate was not ready at the time this request completed.
|
||||
// Otherwise the body is the certificate.
|
||||
if len(cert) > 0 {
|
||||
certRes.CertStableURL = resp.Header.Get("Content-Location")
|
||||
certRes.AccountRef = c.user.GetRegistration().URI
|
||||
|
||||
issuedCert := pemEncode(derCertificateBytes(cert))
|
||||
|
||||
// The issuer certificate link is always supplied via an "up" link
|
||||
// in the response headers of a new certificate.
|
||||
links := parseLinks(resp.Header["Link"])
|
||||
issuerCert, err := c.getIssuerCertificate(links["up"])
|
||||
if err != nil {
|
||||
// If we fail to acquire the issuer cert, return the issued certificate - do not fail.
|
||||
logf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err)
|
||||
} else {
|
||||
issuerCert = pemEncode(derCertificateBytes(issuerCert))
|
||||
|
||||
// If bundle is true, we want to return a certificate bundle.
|
||||
// To do this, we append the issuer cert to the issued cert.
|
||||
if bundle {
|
||||
issuedCert = append(issuedCert, issuerCert...)
|
||||
}
|
||||
}
|
||||
|
||||
certRes.Certificate = issuedCert
|
||||
certRes.IssuerCertificate = issuerCert
|
||||
logf("[INFO][%s] Server responded with a certificate.", certRes.Domain)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// The certificate was granted but is not yet issued.
|
||||
// Check retry-after and loop.
|
||||
ra := resp.Header.Get("Retry-After")
|
||||
retryAfter, err := strconv.Atoi(ra)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
logf("[INFO][%s] acme: Server responded with status 202; retrying after %ds", certRes.Domain, retryAfter)
|
||||
time.Sleep(time.Duration(retryAfter) * time.Second)
|
||||
|
||||
return false, nil
|
||||
default:
|
||||
return false, handleHTTPError(resp)
|
||||
}
|
||||
}
|
||||
|
||||
// getIssuerCertificate requests the issuer certificate
|
||||
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
|
||||
logf("[INFO] acme: Requesting issuer cert from %s", url)
|
||||
resp, err := httpGet(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = x509.ParseCertificate(issuerBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return issuerBytes, err
|
||||
}
|
||||
|
||||
func parseLinks(links []string) map[string]string {
|
||||
aBrkt := regexp.MustCompile("[<>]")
|
||||
slver := regexp.MustCompile("(.+) *= *\"(.+)\"")
|
||||
linkMap := make(map[string]string)
|
||||
|
||||
for _, link := range links {
|
||||
|
||||
link = aBrkt.ReplaceAllString(link, "")
|
||||
parts := strings.Split(link, ";")
|
||||
|
||||
matches := slver.FindStringSubmatch(parts[1])
|
||||
if len(matches) > 0 {
|
||||
linkMap[matches[2]] = parts[0]
|
||||
}
|
||||
}
|
||||
|
||||
return linkMap
|
||||
}
|
||||
|
||||
// validate makes the ACME server start validating a
|
||||
// challenge response, only returning once it is done.
|
||||
func validate(j *jws, domain, uri string, chlng challenge) error {
|
||||
var challengeResponse challenge
|
||||
|
||||
hdr, err := postJSON(j, uri, chlng, &challengeResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// After the path is sent, the ACME server will access our server.
|
||||
// Repeatedly check the server for an updated status on our request.
|
||||
for {
|
||||
switch challengeResponse.Status {
|
||||
case "valid":
|
||||
logf("[INFO][%s] The server validated our request", domain)
|
||||
return nil
|
||||
case "pending":
|
||||
break
|
||||
case "invalid":
|
||||
return handleChallengeError(challengeResponse)
|
||||
default:
|
||||
return errors.New("The server returned an unexpected state.")
|
||||
}
|
||||
|
||||
ra, err := strconv.Atoi(hdr.Get("Retry-After"))
|
||||
if err != nil {
|
||||
// The ACME server MUST return a Retry-After.
|
||||
// If it doesn't, we'll just poll hard.
|
||||
ra = 1
|
||||
}
|
||||
time.Sleep(time.Duration(ra) * time.Second)
|
||||
|
||||
hdr, err = getJSON(uri, &challengeResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
347
vendor/github.com/xenolf/lego/acme/crypto.go
generated
vendored
347
vendor/github.com/xenolf/lego/acme/crypto.go
generated
vendored
|
@ -1,347 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"encoding/asn1"
|
||||
|
||||
"golang.org/x/crypto/ocsp"
|
||||
)
|
||||
|
||||
// KeyType represents the key algo as well as the key size or curve to use.
|
||||
type KeyType string
|
||||
type derCertificateBytes []byte
|
||||
|
||||
// Constants for all key types we support.
|
||||
const (
|
||||
EC256 = KeyType("P256")
|
||||
EC384 = KeyType("P384")
|
||||
RSA2048 = KeyType("2048")
|
||||
RSA4096 = KeyType("4096")
|
||||
RSA8192 = KeyType("8192")
|
||||
)
|
||||
|
||||
const (
|
||||
// OCSPGood means that the certificate is valid.
|
||||
OCSPGood = ocsp.Good
|
||||
// OCSPRevoked means that the certificate has been deliberately revoked.
|
||||
OCSPRevoked = ocsp.Revoked
|
||||
// OCSPUnknown means that the OCSP responder doesn't know about the certificate.
|
||||
OCSPUnknown = ocsp.Unknown
|
||||
// OCSPServerFailed means that the OCSP responder failed to process the request.
|
||||
OCSPServerFailed = ocsp.ServerFailed
|
||||
)
|
||||
|
||||
// Constants for OCSP must staple
|
||||
var (
|
||||
tlsFeatureExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24}
|
||||
ocspMustStapleFeature = []byte{0x30, 0x03, 0x02, 0x01, 0x05}
|
||||
)
|
||||
|
||||
// GetOCSPForCert takes a PEM encoded cert or cert bundle returning the raw OCSP response,
|
||||
// the parsed response, and an error, if any. The returned []byte can be passed directly
|
||||
// into the OCSPStaple property of a tls.Certificate. If the bundle only contains the
|
||||
// issued certificate, this function will try to get the issuer certificate from the
|
||||
// IssuingCertificateURL in the certificate. If the []byte and/or ocsp.Response return
|
||||
// values are nil, the OCSP status may be assumed OCSPUnknown.
|
||||
func GetOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) {
|
||||
certificates, err := parsePEMBundle(bundle)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// We expect the certificate slice to be ordered downwards the chain.
|
||||
// SRV CRT -> CA. We need to pull the leaf and issuer certs out of it,
|
||||
// which should always be the first two certificates. If there's no
|
||||
// OCSP server listed in the leaf cert, there's nothing to do. And if
|
||||
// we have only one certificate so far, we need to get the issuer cert.
|
||||
issuedCert := certificates[0]
|
||||
if len(issuedCert.OCSPServer) == 0 {
|
||||
return nil, nil, errors.New("no OCSP server specified in cert")
|
||||
}
|
||||
if len(certificates) == 1 {
|
||||
// TODO: build fallback. If this fails, check the remaining array entries.
|
||||
if len(issuedCert.IssuingCertificateURL) == 0 {
|
||||
return nil, nil, errors.New("no issuing certificate URL")
|
||||
}
|
||||
|
||||
resp, err := httpGet(issuedCert.IssuingCertificateURL[0])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
issuerBytes, err := ioutil.ReadAll(limitReader(resp.Body, 1024*1024))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
issuerCert, err := x509.ParseCertificate(issuerBytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Insert it into the slice on position 0
|
||||
// We want it ordered right SRV CRT -> CA
|
||||
certificates = append(certificates, issuerCert)
|
||||
}
|
||||
issuerCert := certificates[1]
|
||||
|
||||
// Finally kick off the OCSP request.
|
||||
ocspReq, err := ocsp.CreateRequest(issuedCert, issuerCert, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(ocspReq)
|
||||
req, err := httpPost(issuedCert.OCSPServer[0], "application/ocsp-request", reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer req.Body.Close()
|
||||
|
||||
ocspResBytes, err := ioutil.ReadAll(limitReader(req.Body, 1024*1024))
|
||||
ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return ocspResBytes, ocspRes, nil
|
||||
}
|
||||
|
||||
func getKeyAuthorization(token string, key interface{}) (string, error) {
|
||||
var publicKey crypto.PublicKey
|
||||
switch k := key.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
publicKey = k.Public()
|
||||
case *rsa.PrivateKey:
|
||||
publicKey = k.Public()
|
||||
}
|
||||
|
||||
// Generate the Key Authorization for the challenge
|
||||
jwk := keyAsJWK(publicKey)
|
||||
if jwk == nil {
|
||||
return "", errors.New("Could not generate JWK from key.")
|
||||
}
|
||||
thumbBytes, err := jwk.Thumbprint(crypto.SHA256)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// unpad the base64URL
|
||||
keyThumb := base64.URLEncoding.EncodeToString(thumbBytes)
|
||||
index := strings.Index(keyThumb, "=")
|
||||
if index != -1 {
|
||||
keyThumb = keyThumb[:index]
|
||||
}
|
||||
|
||||
return token + "." + keyThumb, nil
|
||||
}
|
||||
|
||||
// parsePEMBundle parses a certificate bundle from top to bottom and returns
|
||||
// a slice of x509 certificates. This function will error if no certificates are found.
|
||||
func parsePEMBundle(bundle []byte) ([]*x509.Certificate, error) {
|
||||
var certificates []*x509.Certificate
|
||||
var certDERBlock *pem.Block
|
||||
|
||||
for {
|
||||
certDERBlock, bundle = pem.Decode(bundle)
|
||||
if certDERBlock == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if certDERBlock.Type == "CERTIFICATE" {
|
||||
cert, err := x509.ParseCertificate(certDERBlock.Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certificates = append(certificates, cert)
|
||||
}
|
||||
}
|
||||
|
||||
if len(certificates) == 0 {
|
||||
return nil, errors.New("No certificates were found while parsing the bundle.")
|
||||
}
|
||||
|
||||
return certificates, nil
|
||||
}
|
||||
|
||||
func parsePEMPrivateKey(key []byte) (crypto.PrivateKey, error) {
|
||||
keyBlock, _ := pem.Decode(key)
|
||||
|
||||
switch keyBlock.Type {
|
||||
case "RSA PRIVATE KEY":
|
||||
return x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
|
||||
case "EC PRIVATE KEY":
|
||||
return x509.ParseECPrivateKey(keyBlock.Bytes)
|
||||
default:
|
||||
return nil, errors.New("Unknown PEM header value")
|
||||
}
|
||||
}
|
||||
|
||||
func generatePrivateKey(keyType KeyType) (crypto.PrivateKey, error) {
|
||||
|
||||
switch keyType {
|
||||
case EC256:
|
||||
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
case EC384:
|
||||
return ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
case RSA2048:
|
||||
return rsa.GenerateKey(rand.Reader, 2048)
|
||||
case RSA4096:
|
||||
return rsa.GenerateKey(rand.Reader, 4096)
|
||||
case RSA8192:
|
||||
return rsa.GenerateKey(rand.Reader, 8192)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Invalid KeyType: %s", keyType)
|
||||
}
|
||||
|
||||
func generateCsr(privateKey crypto.PrivateKey, domain string, san []string, mustStaple bool) ([]byte, error) {
|
||||
template := x509.CertificateRequest{
|
||||
Subject: pkix.Name{
|
||||
CommonName: domain,
|
||||
},
|
||||
}
|
||||
|
||||
if len(san) > 0 {
|
||||
template.DNSNames = san
|
||||
}
|
||||
|
||||
if mustStaple {
|
||||
template.ExtraExtensions = append(template.ExtraExtensions, pkix.Extension{
|
||||
Id: tlsFeatureExtensionOID,
|
||||
Value: ocspMustStapleFeature,
|
||||
})
|
||||
}
|
||||
|
||||
return x509.CreateCertificateRequest(rand.Reader, &template, privateKey)
|
||||
}
|
||||
|
||||
func pemEncode(data interface{}) []byte {
|
||||
var pemBlock *pem.Block
|
||||
switch key := data.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
keyBytes, _ := x509.MarshalECPrivateKey(key)
|
||||
pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
|
||||
case *rsa.PrivateKey:
|
||||
pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
|
||||
break
|
||||
case *x509.CertificateRequest:
|
||||
pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw}
|
||||
break
|
||||
case derCertificateBytes:
|
||||
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.(derCertificateBytes))}
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(pemBlock)
|
||||
}
|
||||
|
||||
func pemDecode(data []byte) (*pem.Block, error) {
|
||||
pemBlock, _ := pem.Decode(data)
|
||||
if pemBlock == nil {
|
||||
return nil, fmt.Errorf("Pem decode did not yield a valid block. Is the certificate in the right format?")
|
||||
}
|
||||
|
||||
return pemBlock, nil
|
||||
}
|
||||
|
||||
func pemDecodeTox509(pem []byte) (*x509.Certificate, error) {
|
||||
pemBlock, err := pemDecode(pem)
|
||||
if pemBlock == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return x509.ParseCertificate(pemBlock.Bytes)
|
||||
}
|
||||
|
||||
func pemDecodeTox509CSR(pem []byte) (*x509.CertificateRequest, error) {
|
||||
pemBlock, err := pemDecode(pem)
|
||||
if pemBlock == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pemBlock.Type != "CERTIFICATE REQUEST" {
|
||||
return nil, fmt.Errorf("PEM block is not a certificate request")
|
||||
}
|
||||
|
||||
return x509.ParseCertificateRequest(pemBlock.Bytes)
|
||||
}
|
||||
|
||||
// GetPEMCertExpiration returns the "NotAfter" date of a PEM encoded certificate.
|
||||
// The certificate has to be PEM encoded. Any other encodings like DER will fail.
|
||||
func GetPEMCertExpiration(cert []byte) (time.Time, error) {
|
||||
pemBlock, err := pemDecode(cert)
|
||||
if pemBlock == nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
return getCertExpiration(pemBlock.Bytes)
|
||||
}
|
||||
|
||||
// getCertExpiration returns the "NotAfter" date of a DER encoded certificate.
|
||||
func getCertExpiration(cert []byte) (time.Time, error) {
|
||||
pCert, err := x509.ParseCertificate(cert)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
return pCert.NotAfter, nil
|
||||
}
|
||||
|
||||
func generatePemCert(privKey *rsa.PrivateKey, domain string) ([]byte, error) {
|
||||
derBytes, err := generateDerCert(privKey, time.Time{}, domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
|
||||
}
|
||||
|
||||
func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) {
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if expiration.IsZero() {
|
||||
expiration = time.Now().Add(365)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
CommonName: "ACME Challenge TEMP",
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: expiration,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment,
|
||||
BasicConstraintsValid: true,
|
||||
DNSNames: []string{domain},
|
||||
}
|
||||
|
||||
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
|
||||
}
|
||||
|
||||
func limitReader(rd io.ReadCloser, numBytes int64) io.ReadCloser {
|
||||
return http.MaxBytesReader(nil, rd, numBytes)
|
||||
}
|
309
vendor/github.com/xenolf/lego/acme/dns_challenge.go
generated
vendored
309
vendor/github.com/xenolf/lego/acme/dns_challenge.go
generated
vendored
|
@ -1,309 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type preCheckDNSFunc func(fqdn, value string) (bool, error)
|
||||
|
||||
var (
|
||||
// PreCheckDNS checks DNS propagation before notifying ACME that
|
||||
// the DNS challenge is ready.
|
||||
PreCheckDNS preCheckDNSFunc = checkDNSPropagation
|
||||
fqdnToZone = map[string]string{}
|
||||
)
|
||||
|
||||
const defaultResolvConf = "/etc/resolv.conf"
|
||||
|
||||
var defaultNameservers = []string{
|
||||
"google-public-dns-a.google.com:53",
|
||||
"google-public-dns-b.google.com:53",
|
||||
}
|
||||
|
||||
var RecursiveNameservers = getNameservers(defaultResolvConf, defaultNameservers)
|
||||
|
||||
// DNSTimeout is used to override the default DNS timeout of 10 seconds.
|
||||
var DNSTimeout = 10 * time.Second
|
||||
|
||||
// getNameservers attempts to get systems nameservers before falling back to the defaults
|
||||
func getNameservers(path string, defaults []string) []string {
|
||||
config, err := dns.ClientConfigFromFile(path)
|
||||
if err != nil || len(config.Servers) == 0 {
|
||||
return defaults
|
||||
}
|
||||
|
||||
systemNameservers := []string{}
|
||||
for _, server := range config.Servers {
|
||||
// ensure all servers have a port number
|
||||
if _, _, err := net.SplitHostPort(server); err != nil {
|
||||
systemNameservers = append(systemNameservers, net.JoinHostPort(server, "53"))
|
||||
} else {
|
||||
systemNameservers = append(systemNameservers, server)
|
||||
}
|
||||
}
|
||||
return systemNameservers
|
||||
}
|
||||
|
||||
// DNS01Record returns a DNS record which will fulfill the `dns-01` challenge
|
||||
func DNS01Record(domain, keyAuth string) (fqdn string, value string, ttl int) {
|
||||
keyAuthShaBytes := sha256.Sum256([]byte(keyAuth))
|
||||
// base64URL encoding without padding
|
||||
keyAuthSha := base64.URLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size])
|
||||
value = strings.TrimRight(keyAuthSha, "=")
|
||||
ttl = 120
|
||||
fqdn = fmt.Sprintf("_acme-challenge.%s.", domain)
|
||||
return
|
||||
}
|
||||
|
||||
// dnsChallenge implements the dns-01 challenge according to ACME 7.5
|
||||
type dnsChallenge struct {
|
||||
jws *jws
|
||||
validate validateFunc
|
||||
provider ChallengeProvider
|
||||
}
|
||||
|
||||
func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
|
||||
logf("[INFO][%s] acme: Trying to solve DNS-01", domain)
|
||||
|
||||
if s.provider == nil {
|
||||
return errors.New("No DNS Provider configured")
|
||||
}
|
||||
|
||||
// Generate the Key Authorization for the challenge
|
||||
keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.provider.Present(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error presenting token: %s", err)
|
||||
}
|
||||
defer func() {
|
||||
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
log.Printf("Error cleaning up %s: %v ", domain, err)
|
||||
}
|
||||
}()
|
||||
|
||||
fqdn, value, _ := DNS01Record(domain, keyAuth)
|
||||
|
||||
logf("[INFO][%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers)
|
||||
|
||||
var timeout, interval time.Duration
|
||||
switch provider := s.provider.(type) {
|
||||
case ChallengeProviderTimeout:
|
||||
timeout, interval = provider.Timeout()
|
||||
default:
|
||||
timeout, interval = 60*time.Second, 2*time.Second
|
||||
}
|
||||
|
||||
err = WaitFor(timeout, interval, func() (bool, error) {
|
||||
return PreCheckDNS(fqdn, value)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.validate(s.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||
}
|
||||
|
||||
// checkDNSPropagation checks if the expected TXT record has been propagated to all authoritative nameservers.
|
||||
func checkDNSPropagation(fqdn, value string) (bool, error) {
|
||||
// Initial attempt to resolve at the recursive NS
|
||||
r, err := dnsQuery(fqdn, dns.TypeTXT, RecursiveNameservers, true)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if r.Rcode == dns.RcodeSuccess {
|
||||
// If we see a CNAME here then use the alias
|
||||
for _, rr := range r.Answer {
|
||||
if cn, ok := rr.(*dns.CNAME); ok {
|
||||
if cn.Hdr.Name == fqdn {
|
||||
fqdn = cn.Target
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
authoritativeNss, err := lookupNameservers(fqdn)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return checkAuthoritativeNss(fqdn, value, authoritativeNss)
|
||||
}
|
||||
|
||||
// checkAuthoritativeNss queries each of the given nameservers for the expected TXT record.
|
||||
func checkAuthoritativeNss(fqdn, value string, nameservers []string) (bool, error) {
|
||||
for _, ns := range nameservers {
|
||||
r, err := dnsQuery(fqdn, dns.TypeTXT, []string{net.JoinHostPort(ns, "53")}, false)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return false, fmt.Errorf("NS %s returned %s for %s", ns, dns.RcodeToString[r.Rcode], fqdn)
|
||||
}
|
||||
|
||||
var found bool
|
||||
for _, rr := range r.Answer {
|
||||
if txt, ok := rr.(*dns.TXT); ok {
|
||||
if strings.Join(txt.Txt, "") == value {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return false, fmt.Errorf("NS %s did not return the expected TXT record", ns)
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// dnsQuery will query a nameserver, iterating through the supplied servers as it retries
|
||||
// The nameserver should include a port, to facilitate testing where we talk to a mock dns server.
|
||||
func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (in *dns.Msg, err error) {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(fqdn, rtype)
|
||||
m.SetEdns0(4096, false)
|
||||
|
||||
if !recursive {
|
||||
m.RecursionDesired = false
|
||||
}
|
||||
|
||||
// Will retry the request based on the number of servers (n+1)
|
||||
for i := 1; i <= len(nameservers)+1; i++ {
|
||||
ns := nameservers[i%len(nameservers)]
|
||||
udp := &dns.Client{Net: "udp", Timeout: DNSTimeout}
|
||||
in, _, err = udp.Exchange(m, ns)
|
||||
|
||||
if err == dns.ErrTruncated {
|
||||
tcp := &dns.Client{Net: "tcp", Timeout: DNSTimeout}
|
||||
// If the TCP request succeeds, the err will reset to nil
|
||||
in, _, err = tcp.Exchange(m, ns)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// lookupNameservers returns the authoritative nameservers for the given fqdn.
|
||||
func lookupNameservers(fqdn string) ([]string, error) {
|
||||
var authoritativeNss []string
|
||||
|
||||
zone, err := FindZoneByFqdn(fqdn, RecursiveNameservers)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not determine the zone: %v", err)
|
||||
}
|
||||
|
||||
r, err := dnsQuery(zone, dns.TypeNS, RecursiveNameservers, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, rr := range r.Answer {
|
||||
if ns, ok := rr.(*dns.NS); ok {
|
||||
authoritativeNss = append(authoritativeNss, strings.ToLower(ns.Ns))
|
||||
}
|
||||
}
|
||||
|
||||
if len(authoritativeNss) > 0 {
|
||||
return authoritativeNss, nil
|
||||
}
|
||||
return nil, fmt.Errorf("Could not determine authoritative nameservers")
|
||||
}
|
||||
|
||||
// FindZoneByFqdn determines the zone apex for the given fqdn by recursing up the
|
||||
// domain labels until the nameserver returns a SOA record in the answer section.
|
||||
func FindZoneByFqdn(fqdn string, nameservers []string) (string, error) {
|
||||
// Do we have it cached?
|
||||
if zone, ok := fqdnToZone[fqdn]; ok {
|
||||
return zone, nil
|
||||
}
|
||||
|
||||
labelIndexes := dns.Split(fqdn)
|
||||
for _, index := range labelIndexes {
|
||||
domain := fqdn[index:]
|
||||
|
||||
in, err := dnsQuery(domain, dns.TypeSOA, nameservers, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Any response code other than NOERROR and NXDOMAIN is treated as error
|
||||
if in.Rcode != dns.RcodeNameError && in.Rcode != dns.RcodeSuccess {
|
||||
return "", fmt.Errorf("Unexpected response code '%s' for %s",
|
||||
dns.RcodeToString[in.Rcode], domain)
|
||||
}
|
||||
|
||||
// Check if we got a SOA RR in the answer section
|
||||
if in.Rcode == dns.RcodeSuccess {
|
||||
|
||||
// CNAME records cannot/should not exist at the root of a zone.
|
||||
// So we skip a domain when a CNAME is found.
|
||||
if dnsMsgContainsCNAME(in) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, ans := range in.Answer {
|
||||
if soa, ok := ans.(*dns.SOA); ok {
|
||||
zone := soa.Hdr.Name
|
||||
fqdnToZone[fqdn] = zone
|
||||
return zone, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("Could not find the start of authority")
|
||||
}
|
||||
|
||||
// dnsMsgContainsCNAME checks for a CNAME answer in msg
|
||||
func dnsMsgContainsCNAME(msg *dns.Msg) bool {
|
||||
for _, ans := range msg.Answer {
|
||||
if _, ok := ans.(*dns.CNAME); ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ClearFqdnCache clears the cache of fqdn to zone mappings. Primarily used in testing.
|
||||
func ClearFqdnCache() {
|
||||
fqdnToZone = map[string]string{}
|
||||
}
|
||||
|
||||
// ToFqdn converts the name into a fqdn appending a trailing dot.
|
||||
func ToFqdn(name string) string {
|
||||
n := len(name)
|
||||
if n == 0 || name[n-1] == '.' {
|
||||
return name
|
||||
}
|
||||
return name + "."
|
||||
}
|
||||
|
||||
// UnFqdn converts the fqdn into a name removing the trailing dot.
|
||||
func UnFqdn(name string) string {
|
||||
n := len(name)
|
||||
if n != 0 && name[n-1] == '.' {
|
||||
return name[:n-1]
|
||||
}
|
||||
return name
|
||||
}
|
53
vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go
generated
vendored
53
vendor/github.com/xenolf/lego/acme/dns_challenge_manual.go
generated
vendored
|
@ -1,53 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
dnsTemplate = "%s %d IN TXT \"%s\""
|
||||
)
|
||||
|
||||
// DNSProviderManual is an implementation of the ChallengeProvider interface
|
||||
type DNSProviderManual struct{}
|
||||
|
||||
// NewDNSProviderManual returns a DNSProviderManual instance.
|
||||
func NewDNSProviderManual() (*DNSProviderManual, error) {
|
||||
return &DNSProviderManual{}, nil
|
||||
}
|
||||
|
||||
// Present prints instructions for manually creating the TXT record
|
||||
func (*DNSProviderManual) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := DNS01Record(domain, keyAuth)
|
||||
dnsRecord := fmt.Sprintf(dnsTemplate, fqdn, ttl, value)
|
||||
|
||||
authZone, err := FindZoneByFqdn(fqdn, RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logf("[INFO] acme: Please create the following TXT record in your %s zone:", authZone)
|
||||
logf("[INFO] acme: %s", dnsRecord)
|
||||
logf("[INFO] acme: Press 'Enter' when you are done")
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
_, _ = reader.ReadString('\n')
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp prints instructions for manually removing the TXT record
|
||||
func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, ttl := DNS01Record(domain, keyAuth)
|
||||
dnsRecord := fmt.Sprintf(dnsTemplate, fqdn, ttl, "...")
|
||||
|
||||
authZone, err := FindZoneByFqdn(fqdn, RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logf("[INFO] acme: You can now remove this TXT record from your %s zone:", authZone)
|
||||
logf("[INFO] acme: %s", dnsRecord)
|
||||
return nil
|
||||
}
|
94
vendor/github.com/xenolf/lego/acme/error.go
generated
vendored
94
vendor/github.com/xenolf/lego/acme/error.go
generated
vendored
|
@ -1,94 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
tosAgreementError = "Must agree to subscriber agreement before any further actions"
|
||||
invalidNonceError = "JWS has invalid anti-replay nonce"
|
||||
)
|
||||
|
||||
// RemoteError is the base type for all errors specific to the ACME protocol.
|
||||
type RemoteError struct {
|
||||
StatusCode int `json:"status,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Detail string `json:"detail"`
|
||||
}
|
||||
|
||||
func (e RemoteError) Error() string {
|
||||
return fmt.Sprintf("acme: Error %d - %s - %s", e.StatusCode, e.Type, e.Detail)
|
||||
}
|
||||
|
||||
// TOSError represents the error which is returned if the user needs to
|
||||
// accept the TOS.
|
||||
// TODO: include the new TOS url if we can somehow obtain it.
|
||||
type TOSError struct {
|
||||
RemoteError
|
||||
}
|
||||
|
||||
// NonceError represents the error which is returned if the
|
||||
// nonce sent by the client was not accepted by the server.
|
||||
type NonceError struct {
|
||||
RemoteError
|
||||
}
|
||||
|
||||
type domainError struct {
|
||||
Domain string
|
||||
Error error
|
||||
}
|
||||
|
||||
type challengeError struct {
|
||||
RemoteError
|
||||
records []validationRecord
|
||||
}
|
||||
|
||||
func (c challengeError) Error() string {
|
||||
|
||||
var errStr string
|
||||
for _, validation := range c.records {
|
||||
errStr = errStr + fmt.Sprintf("\tValidation for %s:%s\n\tResolved to:\n\t\t%s\n\tUsed: %s\n\n",
|
||||
validation.Hostname, validation.Port, strings.Join(validation.ResolvedAddresses, "\n\t\t"), validation.UsedAddress)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s\nError Detail:\n%s", c.RemoteError.Error(), errStr)
|
||||
}
|
||||
|
||||
func handleHTTPError(resp *http.Response) error {
|
||||
var errorDetail RemoteError
|
||||
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
if contentType == "application/json" || contentType == "application/problem+json" {
|
||||
err := json.NewDecoder(resp.Body).Decode(&errorDetail)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
detailBytes, err := ioutil.ReadAll(limitReader(resp.Body, maxBodySize))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errorDetail.Detail = string(detailBytes)
|
||||
}
|
||||
|
||||
errorDetail.StatusCode = resp.StatusCode
|
||||
|
||||
// Check for errors we handle specifically
|
||||
if errorDetail.StatusCode == http.StatusForbidden && errorDetail.Detail == tosAgreementError {
|
||||
return TOSError{errorDetail}
|
||||
}
|
||||
|
||||
if errorDetail.StatusCode == http.StatusBadRequest && strings.HasPrefix(errorDetail.Detail, invalidNonceError) {
|
||||
return NonceError{errorDetail}
|
||||
}
|
||||
|
||||
return errorDetail
|
||||
}
|
||||
|
||||
func handleChallengeError(chlng challenge) error {
|
||||
return challengeError{chlng.Error, chlng.ValidationRecords}
|
||||
}
|
160
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
160
vendor/github.com/xenolf/lego/acme/http.go
generated
vendored
|
@ -1,160 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UserAgent (if non-empty) will be tacked onto the User-Agent string in requests.
|
||||
var UserAgent string
|
||||
|
||||
// HTTPClient is an HTTP client with a reasonable timeout value.
|
||||
var HTTPClient = http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 15 * time.Second,
|
||||
ResponseHeaderTimeout: 15 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
// httpHead performs a HEAD request with a proper User-Agent string.
|
||||
// The response body (resp.Body) is already closed when this function returns.
|
||||
func httpHead(url string) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequest("HEAD", url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to head %q: %v", url, err)
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", userAgent())
|
||||
|
||||
resp, err = HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return resp, fmt.Errorf("failed to do head %q: %v", url, err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// httpPost performs a POST request with a proper User-Agent string.
|
||||
// Callers should close resp.Body when done reading from it.
|
||||
func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to post %q: %v", url, err)
|
||||
}
|
||||
req.Header.Set("Content-Type", bodyType)
|
||||
req.Header.Set("User-Agent", userAgent())
|
||||
|
||||
return HTTPClient.Do(req)
|
||||
}
|
||||
|
||||
// httpGet performs a GET request with a proper User-Agent string.
|
||||
// Callers should close resp.Body when done reading from it.
|
||||
func httpGet(url string) (resp *http.Response, err error) {
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get %q: %v", url, err)
|
||||
}
|
||||
req.Header.Set("User-Agent", userAgent())
|
||||
|
||||
return HTTPClient.Do(req)
|
||||
}
|
||||
|
||||
// getJSON performs an HTTP GET request and parses the response body
|
||||
// as JSON, into the provided respBody object.
|
||||
func getJSON(uri string, respBody interface{}) (http.Header, error) {
|
||||
resp, err := httpGet(uri)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get json %q: %v", uri, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
return resp.Header, handleHTTPError(resp)
|
||||
}
|
||||
|
||||
return resp.Header, json.NewDecoder(resp.Body).Decode(respBody)
|
||||
}
|
||||
|
||||
// postJSON performs an HTTP POST request and parses the response body
|
||||
// as JSON, into the provided respBody object.
|
||||
func postJSON(j *jws, uri string, reqBody, respBody interface{}) (http.Header, error) {
|
||||
jsonBytes, err := json.Marshal(reqBody)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
|
||||
err := handleHTTPError(resp)
|
||||
|
||||
switch err.(type) {
|
||||
|
||||
case NonceError:
|
||||
|
||||
// Retry once if the nonce was invalidated
|
||||
|
||||
retryResp, err := j.post(uri, jsonBytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to post JWS message. -> %v", err)
|
||||
}
|
||||
|
||||
defer retryResp.Body.Close()
|
||||
|
||||
if retryResp.StatusCode >= http.StatusBadRequest {
|
||||
return retryResp.Header, handleHTTPError(retryResp)
|
||||
}
|
||||
|
||||
if respBody == nil {
|
||||
return retryResp.Header, nil
|
||||
}
|
||||
|
||||
return retryResp.Header, json.NewDecoder(retryResp.Body).Decode(respBody)
|
||||
|
||||
default:
|
||||
return resp.Header, err
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if respBody == nil {
|
||||
return resp.Header, nil
|
||||
}
|
||||
|
||||
return resp.Header, 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", defaultGoUserAgent, runtime.GOOS, runtime.GOARCH, ourUserAgent, UserAgent)
|
||||
return strings.TrimSpace(ua)
|
||||
}
|
41
vendor/github.com/xenolf/lego/acme/http_challenge.go
generated
vendored
41
vendor/github.com/xenolf/lego/acme/http_challenge.go
generated
vendored
|
@ -1,41 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
type httpChallenge struct {
|
||||
jws *jws
|
||||
validate validateFunc
|
||||
provider ChallengeProvider
|
||||
}
|
||||
|
||||
// HTTP01ChallengePath returns the URL path for the `http-01` challenge
|
||||
func HTTP01ChallengePath(token string) string {
|
||||
return "/.well-known/acme-challenge/" + token
|
||||
}
|
||||
|
||||
func (s *httpChallenge) Solve(chlng challenge, domain string) error {
|
||||
|
||||
logf("[INFO][%s] acme: Trying to solve HTTP-01", domain)
|
||||
|
||||
// Generate the Key Authorization for the challenge
|
||||
keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = s.provider.Present(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("[%s] error presenting token: %v", domain, err)
|
||||
}
|
||||
defer func() {
|
||||
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
log.Printf("[%s] error cleaning up: %v", domain, err)
|
||||
}
|
||||
}()
|
||||
|
||||
return s.validate(s.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||
}
|
79
vendor/github.com/xenolf/lego/acme/http_challenge_server.go
generated
vendored
79
vendor/github.com/xenolf/lego/acme/http_challenge_server.go
generated
vendored
|
@ -1,79 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HTTPProviderServer implements ChallengeProvider for `http-01` challenge
|
||||
// It may be instantiated without using the NewHTTPProviderServer function if
|
||||
// you want only to use the default values.
|
||||
type HTTPProviderServer struct {
|
||||
iface string
|
||||
port string
|
||||
done chan bool
|
||||
listener net.Listener
|
||||
}
|
||||
|
||||
// NewHTTPProviderServer creates a new HTTPProviderServer on the selected interface and port.
|
||||
// Setting iface and / or port to an empty string will make the server fall back to
|
||||
// the "any" interface and port 80 respectively.
|
||||
func NewHTTPProviderServer(iface, port string) *HTTPProviderServer {
|
||||
return &HTTPProviderServer{iface: iface, port: port}
|
||||
}
|
||||
|
||||
// Present starts a web server and makes the token available at `HTTP01ChallengePath(token)` for web requests.
|
||||
func (s *HTTPProviderServer) Present(domain, token, keyAuth string) error {
|
||||
if s.port == "" {
|
||||
s.port = "80"
|
||||
}
|
||||
|
||||
var err error
|
||||
s.listener, err = net.Listen("tcp", net.JoinHostPort(s.iface, s.port))
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not start HTTP server for challenge -> %v", err)
|
||||
}
|
||||
|
||||
s.done = make(chan bool)
|
||||
go s.serve(domain, token, keyAuth)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp closes the HTTP server and removes the token from `HTTP01ChallengePath(token)`
|
||||
func (s *HTTPProviderServer) CleanUp(domain, token, keyAuth string) error {
|
||||
if s.listener == nil {
|
||||
return nil
|
||||
}
|
||||
s.listener.Close()
|
||||
<-s.done
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *HTTPProviderServer) serve(domain, token, keyAuth string) {
|
||||
path := HTTP01ChallengePath(token)
|
||||
|
||||
// The handler validates the HOST header and request type.
|
||||
// For validation it then writes the token the server returned with the challenge
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasPrefix(r.Host, domain) && r.Method == "GET" {
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
w.Write([]byte(keyAuth))
|
||||
logf("[INFO][%s] Served key authentication", domain)
|
||||
} else {
|
||||
logf("[WARN] Received request for domain %s with method %s but the domain did not match any challenge. Please ensure your are passing the HOST header properly.", r.Host, r.Method)
|
||||
w.Write([]byte("TEST"))
|
||||
}
|
||||
})
|
||||
|
||||
httpServer := &http.Server{
|
||||
Handler: mux,
|
||||
}
|
||||
// Once httpServer is shut down we don't want any lingering
|
||||
// connections, so disable KeepAlives.
|
||||
httpServer.SetKeepAlivesEnabled(false)
|
||||
httpServer.Serve(s.listener)
|
||||
s.done <- true
|
||||
}
|
131
vendor/github.com/xenolf/lego/acme/jws.go
generated
vendored
131
vendor/github.com/xenolf/lego/acme/jws.go
generated
vendored
|
@ -1,131 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"gopkg.in/square/go-jose.v1"
|
||||
)
|
||||
|
||||
type jws struct {
|
||||
directoryURL string
|
||||
privKey crypto.PrivateKey
|
||||
nonces nonceManager
|
||||
}
|
||||
|
||||
func keyAsJWK(key interface{}) *jose.JsonWebKey {
|
||||
switch k := key.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
return &jose.JsonWebKey{Key: k, Algorithm: "EC"}
|
||||
case *rsa.PublicKey:
|
||||
return &jose.JsonWebKey{Key: k, Algorithm: "RSA"}
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Posts a JWS signed message to the specified URL.
|
||||
// It does NOT close the response body, so the caller must
|
||||
// do that if no error was returned.
|
||||
func (j *jws) post(url string, content []byte) (*http.Response, error) {
|
||||
signedContent, err := j.signContent(content)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to sign content -> %s", err.Error())
|
||||
}
|
||||
|
||||
resp, err := httpPost(url, "application/jose+json", bytes.NewBuffer([]byte(signedContent.FullSerialize())))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to HTTP POST to %s -> %s", url, err.Error())
|
||||
}
|
||||
|
||||
nonce, nonceErr := getNonceFromResponse(resp)
|
||||
if nonceErr == nil {
|
||||
j.nonces.Push(nonce)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (j *jws) signContent(content []byte) (*jose.JsonWebSignature, error) {
|
||||
|
||||
var alg jose.SignatureAlgorithm
|
||||
switch k := j.privKey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
alg = jose.RS256
|
||||
case *ecdsa.PrivateKey:
|
||||
if k.Curve == elliptic.P256() {
|
||||
alg = jose.ES256
|
||||
} else if k.Curve == elliptic.P384() {
|
||||
alg = jose.ES384
|
||||
}
|
||||
}
|
||||
|
||||
signer, err := jose.NewSigner(alg, j.privKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to create jose signer -> %s", err.Error())
|
||||
}
|
||||
signer.SetNonceSource(j)
|
||||
|
||||
signed, err := signer.Sign(content)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to sign content -> %s", err.Error())
|
||||
}
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
func (j *jws) Nonce() (string, error) {
|
||||
if nonce, ok := j.nonces.Pop(); ok {
|
||||
return nonce, nil
|
||||
}
|
||||
|
||||
return getNonce(j.directoryURL)
|
||||
}
|
||||
|
||||
type nonceManager struct {
|
||||
nonces []string
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func (n *nonceManager) Pop() (string, bool) {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
|
||||
if len(n.nonces) == 0 {
|
||||
return "", false
|
||||
}
|
||||
|
||||
nonce := n.nonces[len(n.nonces)-1]
|
||||
n.nonces = n.nonces[:len(n.nonces)-1]
|
||||
return nonce, true
|
||||
}
|
||||
|
||||
func (n *nonceManager) Push(nonce string) {
|
||||
n.Lock()
|
||||
defer n.Unlock()
|
||||
n.nonces = append(n.nonces, nonce)
|
||||
}
|
||||
|
||||
func getNonce(url string) (string, error) {
|
||||
resp, err := httpHead(url)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Failed to get nonce from HTTP HEAD -> %s", err.Error())
|
||||
}
|
||||
|
||||
return getNonceFromResponse(resp)
|
||||
}
|
||||
|
||||
func getNonceFromResponse(resp *http.Response) (string, error) {
|
||||
nonce := resp.Header.Get("Replay-Nonce")
|
||||
if nonce == "" {
|
||||
return "", fmt.Errorf("Server did not respond with a proper nonce header.")
|
||||
}
|
||||
|
||||
return nonce, nil
|
||||
}
|
115
vendor/github.com/xenolf/lego/acme/messages.go
generated
vendored
115
vendor/github.com/xenolf/lego/acme/messages.go
generated
vendored
|
@ -1,115 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gopkg.in/square/go-jose.v1"
|
||||
)
|
||||
|
||||
type directory struct {
|
||||
NewAuthzURL string `json:"new-authz"`
|
||||
NewCertURL string `json:"new-cert"`
|
||||
NewRegURL string `json:"new-reg"`
|
||||
RevokeCertURL string `json:"revoke-cert"`
|
||||
}
|
||||
|
||||
type registrationMessage struct {
|
||||
Resource string `json:"resource"`
|
||||
Contact []string `json:"contact"`
|
||||
Delete bool `json:"delete,omitempty"`
|
||||
}
|
||||
|
||||
// Registration is returned by the ACME server after the registration
|
||||
// The client implementation should save this registration somewhere.
|
||||
type Registration struct {
|
||||
Resource string `json:"resource,omitempty"`
|
||||
ID int `json:"id"`
|
||||
Key jose.JsonWebKey `json:"key"`
|
||||
Contact []string `json:"contact"`
|
||||
Agreement string `json:"agreement,omitempty"`
|
||||
Authorizations string `json:"authorizations,omitempty"`
|
||||
Certificates string `json:"certificates,omitempty"`
|
||||
}
|
||||
|
||||
// RegistrationResource represents all important informations about a registration
|
||||
// of which the client needs to keep track itself.
|
||||
type RegistrationResource struct {
|
||||
Body Registration `json:"body,omitempty"`
|
||||
URI string `json:"uri,omitempty"`
|
||||
NewAuthzURL string `json:"new_authzr_uri,omitempty"`
|
||||
TosURL string `json:"terms_of_service,omitempty"`
|
||||
}
|
||||
|
||||
type authorizationResource struct {
|
||||
Body authorization
|
||||
Domain string
|
||||
NewCertURL string
|
||||
AuthURL string
|
||||
}
|
||||
|
||||
type authorization struct {
|
||||
Resource string `json:"resource,omitempty"`
|
||||
Identifier identifier `json:"identifier"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Expires time.Time `json:"expires,omitempty"`
|
||||
Challenges []challenge `json:"challenges,omitempty"`
|
||||
Combinations [][]int `json:"combinations,omitempty"`
|
||||
}
|
||||
|
||||
type identifier struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type validationRecord struct {
|
||||
URI string `json:"url,omitempty"`
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
Port string `json:"port,omitempty"`
|
||||
ResolvedAddresses []string `json:"addressesResolved,omitempty"`
|
||||
UsedAddress string `json:"addressUsed,omitempty"`
|
||||
}
|
||||
|
||||
type challenge struct {
|
||||
Resource string `json:"resource,omitempty"`
|
||||
Type Challenge `json:"type,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
URI string `json:"uri,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
KeyAuthorization string `json:"keyAuthorization,omitempty"`
|
||||
TLS bool `json:"tls,omitempty"`
|
||||
Iterations int `json:"n,omitempty"`
|
||||
Error RemoteError `json:"error,omitempty"`
|
||||
ValidationRecords []validationRecord `json:"validationRecord,omitempty"`
|
||||
}
|
||||
|
||||
type csrMessage struct {
|
||||
Resource string `json:"resource,omitempty"`
|
||||
Csr string `json:"csr"`
|
||||
Authorizations []string `json:"authorizations"`
|
||||
}
|
||||
|
||||
type revokeCertMessage struct {
|
||||
Resource string `json:"resource"`
|
||||
Certificate string `json:"certificate"`
|
||||
}
|
||||
|
||||
type deactivateAuthMessage struct {
|
||||
Resource string `json:"resource,omitempty"`
|
||||
Status string `jsom:"status"`
|
||||
}
|
||||
|
||||
// CertificateResource represents a CA issued certificate.
|
||||
// PrivateKey, Certificate and IssuerCertificate are all
|
||||
// already PEM encoded and can be directly written to disk.
|
||||
// Certificate may be a certificate bundle, depending on the
|
||||
// options supplied to create it.
|
||||
type CertificateResource struct {
|
||||
Domain string `json:"domain"`
|
||||
CertURL string `json:"certUrl"`
|
||||
CertStableURL string `json:"certStableUrl"`
|
||||
AccountRef string `json:"accountRef,omitempty"`
|
||||
PrivateKey []byte `json:"-"`
|
||||
Certificate []byte `json:"-"`
|
||||
IssuerCertificate []byte `json:"-"`
|
||||
CSR []byte `json:"-"`
|
||||
}
|
1
vendor/github.com/xenolf/lego/acme/pop_challenge.go
generated
vendored
1
vendor/github.com/xenolf/lego/acme/pop_challenge.go
generated
vendored
|
@ -1 +0,0 @@
|
|||
package acme
|
28
vendor/github.com/xenolf/lego/acme/provider.go
generated
vendored
28
vendor/github.com/xenolf/lego/acme/provider.go
generated
vendored
|
@ -1,28 +0,0 @@
|
|||
package acme
|
||||
|
||||
import "time"
|
||||
|
||||
// ChallengeProvider enables implementing a custom challenge
|
||||
// provider. Present presents the solution to a challenge available to
|
||||
// be solved. CleanUp will be called by the challenge if Present ends
|
||||
// in a non-error state.
|
||||
type ChallengeProvider interface {
|
||||
Present(domain, token, keyAuth string) error
|
||||
CleanUp(domain, token, keyAuth string) error
|
||||
}
|
||||
|
||||
// ChallengeProviderTimeout allows for implementing a
|
||||
// ChallengeProvider where an unusually long timeout is required when
|
||||
// waiting for an ACME challenge to be satisfied, such as when
|
||||
// checking for DNS record progagation. If an implementor of a
|
||||
// ChallengeProvider provides a Timeout method, then the return values
|
||||
// of the Timeout method will be used when appropriate by the acme
|
||||
// package. The interval value is the time between checks.
|
||||
//
|
||||
// The default values used for timeout and interval are 60 seconds and
|
||||
// 2 seconds respectively. These are used when no Timeout method is
|
||||
// defined for the ChallengeProvider.
|
||||
type ChallengeProviderTimeout interface {
|
||||
ChallengeProvider
|
||||
Timeout() (timeout, interval time.Duration)
|
||||
}
|
67
vendor/github.com/xenolf/lego/acme/tls_sni_challenge.go
generated
vendored
67
vendor/github.com/xenolf/lego/acme/tls_sni_challenge.go
generated
vendored
|
@ -1,67 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
type tlsSNIChallenge struct {
|
||||
jws *jws
|
||||
validate validateFunc
|
||||
provider ChallengeProvider
|
||||
}
|
||||
|
||||
func (t *tlsSNIChallenge) Solve(chlng challenge, domain string) error {
|
||||
// FIXME: https://github.com/ietf-wg-acme/acme/pull/22
|
||||
// Currently we implement this challenge to track boulder, not the current spec!
|
||||
|
||||
logf("[INFO][%s] acme: Trying to solve TLS-SNI-01", domain)
|
||||
|
||||
// Generate the Key Authorization for the challenge
|
||||
keyAuth, err := getKeyAuthorization(chlng.Token, t.jws.privKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = t.provider.Present(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("[%s] error presenting token: %v", domain, err)
|
||||
}
|
||||
defer func() {
|
||||
err := t.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||
if err != nil {
|
||||
log.Printf("[%s] error cleaning up: %v", domain, err)
|
||||
}
|
||||
}()
|
||||
return t.validate(t.jws, domain, chlng.URI, challenge{Resource: "challenge", Type: chlng.Type, Token: chlng.Token, KeyAuthorization: keyAuth})
|
||||
}
|
||||
|
||||
// TLSSNI01ChallengeCert returns a certificate and target domain for the `tls-sni-01` challenge
|
||||
func TLSSNI01ChallengeCert(keyAuth string) (tls.Certificate, string, error) {
|
||||
// generate a new RSA key for the certificates
|
||||
tempPrivKey, err := generatePrivateKey(RSA2048)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, "", err
|
||||
}
|
||||
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
|
||||
rsaPrivPEM := pemEncode(rsaPrivKey)
|
||||
|
||||
zBytes := sha256.Sum256([]byte(keyAuth))
|
||||
z := hex.EncodeToString(zBytes[:sha256.Size])
|
||||
domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:])
|
||||
tempCertPEM, err := generatePemCert(rsaPrivKey, domain)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, "", err
|
||||
}
|
||||
|
||||
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, "", err
|
||||
}
|
||||
|
||||
return certificate, domain, nil
|
||||
}
|
62
vendor/github.com/xenolf/lego/acme/tls_sni_challenge_server.go
generated
vendored
62
vendor/github.com/xenolf/lego/acme/tls_sni_challenge_server.go
generated
vendored
|
@ -1,62 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// TLSProviderServer implements ChallengeProvider for `TLS-SNI-01` challenge
|
||||
// It may be instantiated without using the NewTLSProviderServer function if
|
||||
// you want only to use the default values.
|
||||
type TLSProviderServer struct {
|
||||
iface string
|
||||
port string
|
||||
done chan bool
|
||||
listener net.Listener
|
||||
}
|
||||
|
||||
// NewTLSProviderServer creates a new TLSProviderServer on the selected interface and port.
|
||||
// Setting iface and / or port to an empty string will make the server fall back to
|
||||
// the "any" interface and port 443 respectively.
|
||||
func NewTLSProviderServer(iface, port string) *TLSProviderServer {
|
||||
return &TLSProviderServer{iface: iface, port: port}
|
||||
}
|
||||
|
||||
// Present makes the keyAuth available as a cert
|
||||
func (s *TLSProviderServer) Present(domain, token, keyAuth string) error {
|
||||
if s.port == "" {
|
||||
s.port = "443"
|
||||
}
|
||||
|
||||
cert, _, err := TLSSNI01ChallengeCert(keyAuth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConf := new(tls.Config)
|
||||
tlsConf.Certificates = []tls.Certificate{cert}
|
||||
|
||||
s.listener, err = tls.Listen("tcp", net.JoinHostPort(s.iface, s.port), tlsConf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not start HTTPS server for challenge -> %v", err)
|
||||
}
|
||||
|
||||
s.done = make(chan bool)
|
||||
go func() {
|
||||
http.Serve(s.listener, nil)
|
||||
s.done <- true
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp closes the HTTP server.
|
||||
func (s *TLSProviderServer) CleanUp(domain, token, keyAuth string) error {
|
||||
if s.listener == nil {
|
||||
return nil
|
||||
}
|
||||
s.listener.Close()
|
||||
<-s.done
|
||||
return nil
|
||||
}
|
29
vendor/github.com/xenolf/lego/acme/utils.go
generated
vendored
29
vendor/github.com/xenolf/lego/acme/utils.go
generated
vendored
|
@ -1,29 +0,0 @@
|
|||
package acme
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// WaitFor polls the given function 'f', once every 'interval', up to 'timeout'.
|
||||
func WaitFor(timeout, interval time.Duration, f func() (bool, error)) error {
|
||||
var lastErr string
|
||||
timeup := time.After(timeout)
|
||||
for {
|
||||
select {
|
||||
case <-timeup:
|
||||
return fmt.Errorf("Time limit exceeded. Last error: %s", lastErr)
|
||||
default:
|
||||
}
|
||||
|
||||
stop, err := f()
|
||||
if stop {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
lastErr = err.Error()
|
||||
}
|
||||
|
||||
time.Sleep(interval)
|
||||
}
|
||||
}
|
6
vendor/github.com/xenolf/lego/acmev2/challenges.go
generated
vendored
6
vendor/github.com/xenolf/lego/acmev2/challenges.go
generated
vendored
|
@ -1,13 +1,13 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
// Challenge is a string that identifies a particular type and version of ACME challenge.
|
||||
type Challenge string
|
||||
|
||||
const (
|
||||
// HTTP01 is the "http-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#http
|
||||
// HTTP01 is the "http-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acmev2.md#http
|
||||
// Note: HTTP01ChallengePath returns the URL path to fulfill this challenge
|
||||
HTTP01 = Challenge("http-01")
|
||||
// DNS01 is the "dns-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#dns
|
||||
// DNS01 is the "dns-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acmev2.md#dns
|
||||
// Note: DNS01Record returns a DNS record which will fulfill this challenge
|
||||
DNS01 = Challenge("dns-01")
|
||||
)
|
||||
|
|
4
vendor/github.com/xenolf/lego/acmev2/client.go
generated
vendored
4
vendor/github.com/xenolf/lego/acmev2/client.go
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
// Package acme implements the ACME protocol for Let's Encrypt and other conforming providers.
|
||||
package acme
|
||||
// package acmev2 implements the ACME protocol for Let's Encrypt and other conforming providers.
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/crypto.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/crypto.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/dns_challenge.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/dns_challenge.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/dns_challenge_manual.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/dns_challenge_manual.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/error.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/error.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/http.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/http.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/http_challenge.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/http_challenge.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/http_challenge_server.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/http_challenge_server.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/jws.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/jws.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/messages.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/messages.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/pop_challenge.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/pop_challenge.go
generated
vendored
|
@ -1 +1 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/provider.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/provider.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
import "time"
|
||||
|
||||
|
|
2
vendor/github.com/xenolf/lego/acmev2/utils.go
generated
vendored
2
vendor/github.com/xenolf/lego/acmev2/utils.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
package acme
|
||||
package acmev2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
19
vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go
generated
vendored
19
vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go
generated
vendored
|
@ -2,12 +2,13 @@ package auroradns
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/edeckers/auroradnsclient"
|
||||
"github.com/edeckers/auroradnsclient/records"
|
||||
"github.com/edeckers/auroradnsclient/zones"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"os"
|
||||
"sync"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider describes a provider for AuroraDNS
|
||||
|
@ -64,9 +65,9 @@ func (provider *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRe
|
|||
|
||||
// Present creates a record with a secret
|
||||
func (provider *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err)
|
||||
}
|
||||
|
@ -80,7 +81,7 @@ func (provider *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
subdomain := fqdn[0 : len(fqdn)-len(authZone)-1]
|
||||
|
||||
authZone = acme.UnFqdn(authZone)
|
||||
authZone = acmev2.UnFqdn(authZone)
|
||||
|
||||
zoneRecord, err := provider.getZoneInformationByName(authZone)
|
||||
|
||||
|
@ -106,7 +107,7 @@ func (provider *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes a given record that was generated by Present
|
||||
func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
provider.recordIDsMu.Lock()
|
||||
recordID, ok := provider.recordIDs[fqdn]
|
||||
|
@ -116,12 +117,12 @@ func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||
return fmt.Errorf("Unknown recordID for '%s'", fqdn)
|
||||
}
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err)
|
||||
}
|
||||
|
||||
authZone = acme.UnFqdn(authZone)
|
||||
authZone = acmev2.UnFqdn(authZone)
|
||||
|
||||
zoneRecord, err := provider.getZoneInformationByName(authZone)
|
||||
if err != nil {
|
||||
|
|
18
vendor/github.com/xenolf/lego/providers/dns/azure/azure.go
generated
vendored
18
vendor/github.com/xenolf/lego/providers/dns/azure/azure.go
generated
vendored
|
@ -16,10 +16,10 @@ import (
|
|||
"github.com/Azure/go-autorest/autorest/adal"
|
||||
"github.com/Azure/go-autorest/autorest/azure"
|
||||
"github.com/Azure/go-autorest/autorest/to"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
clientId string
|
||||
clientSecret string
|
||||
|
@ -64,7 +64,7 @@ func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
|||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
zone, err := c.getHostedZoneID(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -74,7 +74,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
|
||||
rsc.Authorizer = autorest.NewBearerAuthorizer(spt)
|
||||
|
||||
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
|
||||
relative := toRelativeRecord(fqdn, acmev2.ToFqdn(zone))
|
||||
rec := dns.RecordSet{
|
||||
Name: &relative,
|
||||
RecordSetProperties: &dns.RecordSetProperties{
|
||||
|
@ -93,19 +93,19 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// Returns the relative record to the domain
|
||||
func toRelativeRecord(domain, zone string) string {
|
||||
return acme.UnFqdn(strings.TrimSuffix(domain, zone))
|
||||
return acmev2.UnFqdn(strings.TrimSuffix(domain, zone))
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
zone, err := c.getHostedZoneID(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
|
||||
relative := toRelativeRecord(fqdn, acmev2.ToFqdn(zone))
|
||||
rsc := dns.NewRecordSetsClient(c.subscriptionId)
|
||||
spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
|
||||
rsc.Authorizer = autorest.NewBearerAuthorizer(spt)
|
||||
|
@ -119,7 +119,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||
|
||||
// Checks that azure has a zone for this domain name.
|
||||
func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
|||
dc := dns.NewZonesClient(c.subscriptionId)
|
||||
dc.Authorizer = autorest.NewBearerAuthorizer(spt)
|
||||
|
||||
zone, err := dc.Get(c.resourceGroup, acme.UnFqdn(authZone))
|
||||
zone, err := dc.Get(c.resourceGroup, acmev2.UnFqdn(authZone))
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
419
vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go
generated
vendored
Normal file
419
vendor/github.com/xenolf/lego/providers/dns/bluecat/bluecat.go
generated
vendored
Normal file
|
@ -0,0 +1,419 @@
|
|||
// Package bluecat implements a DNS provider for solving the DNS-01 challenge
|
||||
// using a self-hosted Bluecat Address Manager.
|
||||
package bluecat
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
const bluecatUrlTemplate = "%s/Services/REST/v1"
|
||||
const configType = "Configuration"
|
||||
const viewType = "View"
|
||||
const txtType = "TXTRecord"
|
||||
const zoneType = "Zone"
|
||||
|
||||
type entityResponse struct {
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Properties string `json:"properties"`
|
||||
}
|
||||
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface that uses
|
||||
// Bluecat's Address Manager REST API to manage TXT records for a domain.
|
||||
type DNSProvider struct {
|
||||
baseUrl string
|
||||
userName string
|
||||
password string
|
||||
configName string
|
||||
dnsView string
|
||||
token string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for Bluecat DNS.
|
||||
// Credentials must be passed in the environment variables: BLUECAT_SERVER_URL,
|
||||
// BLUECAT_USER_NAME and BLUECAT_PASSWORD. BLUECAT_SERVER_URL should have the
|
||||
// scheme, hostname, and port (if required) of the authoritative Bluecat BAM
|
||||
// server. The REST endpoint will be appended. In addition, the Configuration name
|
||||
// and external DNS View Name must be passed in BLUECAT_CONFIG_NAME and
|
||||
// BLUECAT_DNS_VIEW
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
server := os.Getenv("BLUECAT_SERVER_URL")
|
||||
userName := os.Getenv("BLUECAT_USER_NAME")
|
||||
password := os.Getenv("BLUECAT_PASSWORD")
|
||||
configName := os.Getenv("BLUECAT_CONFIG_NAME")
|
||||
dnsView := os.Getenv("BLUECAT_DNS_VIEW")
|
||||
httpClient := http.Client{Timeout: time.Duration(30 * time.Second)}
|
||||
return NewDNSProviderCredentials(server, userName, password, configName, dnsView, httpClient)
|
||||
}
|
||||
|
||||
// NewDNSProviderCredentials uses the supplied credentials to return a
|
||||
// DNSProvider instance configured for Bluecat DNS.
|
||||
func NewDNSProviderCredentials(server, userName, password, configName, dnsView string, httpClient http.Client) (*DNSProvider, error) {
|
||||
if server == "" || userName == "" || password == "" || configName == "" || dnsView == "" {
|
||||
return nil, fmt.Errorf("Bluecat credentials missing")
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
baseUrl: fmt.Sprintf(bluecatUrlTemplate, server),
|
||||
userName: userName,
|
||||
password: password,
|
||||
configName: configName,
|
||||
dnsView: dnsView,
|
||||
httpClient: http.DefaultClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Send a REST request, using query parameters specified. The Authorization
|
||||
// header will be set if we have an active auth token
|
||||
func (d *DNSProvider) sendRequest(method, resource string, payload interface{}, queryArgs map[string]string) (*http.Response, error) {
|
||||
url := fmt.Sprintf("%s/%s", d.baseUrl, resource)
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if len(d.token) > 0 {
|
||||
req.Header.Set("Authorization", d.token)
|
||||
}
|
||||
|
||||
// Add all query parameters
|
||||
q := req.URL.Query()
|
||||
for argName, argVal := range queryArgs {
|
||||
q.Add(argName, argVal)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
resp, err := d.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
errBytes, _ := ioutil.ReadAll(resp.Body)
|
||||
errResp := string(errBytes)
|
||||
return nil, fmt.Errorf("Bluecat API request failed with HTTP status code %d\n Full message: %s",
|
||||
resp.StatusCode, errResp)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Starts a new Bluecat API Session. Authenticates using customerName, userName,
|
||||
// password and receives a token to be used in for subsequent requests.
|
||||
func (d *DNSProvider) login() error {
|
||||
queryArgs := map[string]string{
|
||||
"username": d.userName,
|
||||
"password": d.password,
|
||||
}
|
||||
|
||||
resp, err := d.sendRequest("GET", "login", nil, queryArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
authBytes, _ := ioutil.ReadAll(resp.Body)
|
||||
authResp := string(authBytes)
|
||||
|
||||
if strings.Contains(authResp, "Authentication Error") {
|
||||
msg := strings.Trim(authResp, "\"")
|
||||
return fmt.Errorf("Bluecat API request failed: %s", msg)
|
||||
}
|
||||
// Upon success, API responds with "Session Token-> BAMAuthToken: dQfuRMTUxNjc3MjcyNDg1ODppcGFybXM= <- for User : username"
|
||||
re := regexp.MustCompile("BAMAuthToken: [^ ]+")
|
||||
token := re.FindString(authResp)
|
||||
d.token = token
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroys Bluecat Session
|
||||
func (d *DNSProvider) logout() error {
|
||||
if len(d.token) == 0 {
|
||||
// nothing to do
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err := d.sendRequest("GET", "logout", nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("Bluecat API request failed to delete session with HTTP status code %d", resp.StatusCode)
|
||||
} else {
|
||||
authBytes, _ := ioutil.ReadAll(resp.Body)
|
||||
authResp := string(authBytes)
|
||||
|
||||
if !strings.Contains(authResp, "successfully") {
|
||||
msg := strings.Trim(authResp, "\"")
|
||||
return fmt.Errorf("Bluecat API request failed to delete session: %s", msg)
|
||||
}
|
||||
}
|
||||
|
||||
d.token = ""
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Lookup the entity ID of the configuration named in our properties
|
||||
func (d *DNSProvider) lookupConfId() (uint, error) {
|
||||
queryArgs := map[string]string{
|
||||
"parentId": strconv.Itoa(0),
|
||||
"name": d.configName,
|
||||
"type": configType,
|
||||
}
|
||||
|
||||
resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var conf entityResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&conf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return conf.Id, nil
|
||||
}
|
||||
|
||||
// Find the DNS view with the given name within
|
||||
func (d *DNSProvider) lookupViewId(viewName string) (uint, error) {
|
||||
confId, err := d.lookupConfId()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
queryArgs := map[string]string{
|
||||
"parentId": strconv.FormatUint(uint64(confId), 10),
|
||||
"name": d.dnsView,
|
||||
"type": viewType,
|
||||
}
|
||||
|
||||
resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var view entityResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&view)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return view.Id, nil
|
||||
}
|
||||
|
||||
// Return the entityId of the parent zone by recursing from the root view
|
||||
// Also return the simple name of the host
|
||||
func (d *DNSProvider) lookupParentZoneId(viewId uint, fqdn string) (uint, string, error) {
|
||||
parentViewId := viewId
|
||||
name := ""
|
||||
|
||||
if fqdn != "" {
|
||||
zones := strings.Split(strings.Trim(fqdn, "."), ".")
|
||||
last := len(zones) - 1
|
||||
name = zones[0]
|
||||
|
||||
for i := last; i > -1; i-- {
|
||||
zoneId, err := d.getZone(parentViewId, zones[i])
|
||||
if err != nil || zoneId == 0 {
|
||||
return parentViewId, name, err
|
||||
}
|
||||
if i > 0 {
|
||||
name = strings.Join(zones[0:i], ".")
|
||||
}
|
||||
parentViewId = zoneId
|
||||
}
|
||||
}
|
||||
|
||||
return parentViewId, name, nil
|
||||
}
|
||||
|
||||
// Get the DNS zone with the specified name under the parentId
|
||||
func (d *DNSProvider) getZone(parentId uint, name string) (uint, error) {
|
||||
|
||||
queryArgs := map[string]string{
|
||||
"parentId": strconv.FormatUint(uint64(parentId), 10),
|
||||
"name": name,
|
||||
"type": zoneType,
|
||||
}
|
||||
|
||||
resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs)
|
||||
// Return an empty zone if the named zone doesn't exist
|
||||
if resp != nil && resp.StatusCode == 404 {
|
||||
return 0, fmt.Errorf("Bluecat API could not find zone named %s", name)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var zone entityResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&zone)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return zone.Id, nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record using the specified parameters
|
||||
// This will *not* create a subzone to contain the TXT record,
|
||||
// so make sure the FQDN specified is within an extant zone.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
err := d.login()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
viewId, err := d.lookupViewId(d.dnsView)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parentZoneId, name, err := d.lookupParentZoneId(viewId, fqdn)
|
||||
|
||||
queryArgs := map[string]string{
|
||||
"parentId": strconv.FormatUint(uint64(parentZoneId), 10),
|
||||
}
|
||||
|
||||
body := bluecatEntity{
|
||||
Name: name,
|
||||
Type: "TXTRecord",
|
||||
Properties: fmt.Sprintf("ttl=%d|absoluteName=%s|txt=%s|", ttl, fqdn, value),
|
||||
}
|
||||
|
||||
resp, err := d.sendRequest("POST", "addEntity", body, queryArgs)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
addTxtBytes, _ := ioutil.ReadAll(resp.Body)
|
||||
addTxtResp := string(addTxtBytes)
|
||||
// addEntity responds only with body text containing the ID of the created record
|
||||
_, err = strconv.ParseUint(addTxtResp, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Bluecat API addEntity request failed: %s", addTxtResp)
|
||||
}
|
||||
|
||||
err = d.deploy(uint(parentZoneId))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = d.logout()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deploy the DNS config for the specified entity to the authoritative servers
|
||||
func (d *DNSProvider) deploy(entityId uint) error {
|
||||
queryArgs := map[string]string{
|
||||
"entityId": strconv.FormatUint(uint64(entityId), 10),
|
||||
}
|
||||
|
||||
resp, err := d.sendRequest("POST", "quickDeploy", nil, queryArgs)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
err := d.login()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
viewId, err := d.lookupViewId(d.dnsView)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parentId, name, err := d.lookupParentZoneId(viewId, fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
queryArgs := map[string]string{
|
||||
"parentId": strconv.FormatUint(uint64(parentId), 10),
|
||||
"name": name,
|
||||
"type": txtType,
|
||||
}
|
||||
|
||||
resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var txtRec entityResponse
|
||||
err = json.NewDecoder(resp.Body).Decode(&txtRec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
queryArgs = map[string]string{
|
||||
"objectId": strconv.FormatUint(uint64(txtRec.Id), 10),
|
||||
}
|
||||
|
||||
resp, err = d.sendRequest("DELETE", "delete", nil, queryArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
err = d.deploy(parentId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = d.logout()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//JSON body for Bluecat entity requests and responses
|
||||
type bluecatEntity struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Properties string `json:"properties"`
|
||||
}
|
18
vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go
generated
vendored
18
vendor/github.com/xenolf/lego/providers/dns/cloudflare/cloudflare.go
generated
vendored
|
@ -11,14 +11,14 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// CloudFlareAPIURL represents the API endpoint to call.
|
||||
// TODO: Unexport?
|
||||
const CloudFlareAPIURL = "https://api.cloudflare.com/client/v4"
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
authEmail string
|
||||
authKey string
|
||||
|
@ -54,7 +54,7 @@ func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
|||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
zoneID, err := c.getHostedZoneID(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -62,7 +62,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
rec := cloudFlareRecord{
|
||||
Type: "TXT",
|
||||
Name: acme.UnFqdn(fqdn),
|
||||
Name: acmev2.UnFqdn(fqdn),
|
||||
Content: value,
|
||||
TTL: 120,
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
record, err := c.findTxtRecord(fqdn)
|
||||
if err != nil {
|
||||
|
@ -104,12 +104,12 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
|||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result, err := c.makeRequest("GET", "/zones?name="+acme.UnFqdn(authZone), nil)
|
||||
result, err := c.makeRequest("GET", "/zones?name="+acmev2.UnFqdn(authZone), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) {
|
|||
|
||||
result, err := c.makeRequest(
|
||||
"GET",
|
||||
fmt.Sprintf("/zones/%s/dns_records?per_page=1000&type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)),
|
||||
fmt.Sprintf("/zones/%s/dns_records?per_page=1000&type=TXT&name=%s", zoneID, acmev2.UnFqdn(fqdn)),
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -149,7 +149,7 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) {
|
|||
}
|
||||
|
||||
for _, rec := range records {
|
||||
if rec.Name == acme.UnFqdn(fqdn) {
|
||||
if rec.Name == acmev2.UnFqdn(fqdn) {
|
||||
return &rec, nil
|
||||
}
|
||||
}
|
||||
|
|
16
vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go
generated
vendored
16
vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go
generated
vendored
|
@ -13,12 +13,12 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
const cloudXNSBaseURL = "https://www.cloudxns.net/api2/"
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
apiKey string
|
||||
secretKey string
|
||||
|
@ -48,7 +48,7 @@ func NewDNSProviderCredentials(apiKey, secretKey string) (*DNSProvider, error) {
|
|||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
zoneID, err := c.getHostedZoneID(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -59,7 +59,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
zoneID, err := c.getHostedZoneID(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -79,7 +79,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
|||
Domain string `json:"domain"`
|
||||
}
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ func (c *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) {
|
|||
}
|
||||
|
||||
for _, record := range records {
|
||||
if record.Host == acme.UnFqdn(fqdn) && record.Type == "TXT" {
|
||||
if record.Host == acmev2.UnFqdn(fqdn) && record.Type == "TXT" {
|
||||
return record.RecordID, nil
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ func (c *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error {
|
|||
|
||||
payload := cloudXNSRecord{
|
||||
ID: id,
|
||||
Host: acme.UnFqdn(fqdn),
|
||||
Host: acmev2.UnFqdn(fqdn),
|
||||
Value: value,
|
||||
Type: "TXT",
|
||||
LineID: 1,
|
||||
|
@ -183,7 +183,7 @@ func (c *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMess
|
|||
req.Header.Set("API-HMAC", c.hmac(url, requestDate, string(body)))
|
||||
req.Header.Set("API-FORMAT", "json")
|
||||
|
||||
resp, err := acme.HTTPClient.Do(req)
|
||||
resp, err := acmev2.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
25
vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go
generated
vendored
25
vendor/github.com/xenolf/lego/providers/dns/digitalocean/digitalocean.go
generated
vendored
|
@ -11,10 +11,10 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
|
||||
// that uses DigitalOcean's REST API to manage TXT records for a domain.
|
||||
type DNSProvider struct {
|
||||
apiAuthToken string
|
||||
|
@ -49,6 +49,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
RecordType string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Data string `json:"data"`
|
||||
TTL int `json:"ttl"`
|
||||
}
|
||||
|
||||
// txtRecordResponse represents a response from DO's API after making a TXT record
|
||||
|
@ -61,17 +62,17 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
} `json:"domain_record"`
|
||||
}
|
||||
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err)
|
||||
}
|
||||
|
||||
authZone = acme.UnFqdn(authZone)
|
||||
authZone = acmev2.UnFqdn(authZone)
|
||||
|
||||
reqURL := fmt.Sprintf("%s/v2/domains/%s/records", digitalOceanBaseURL, authZone)
|
||||
reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value}
|
||||
reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value, TTL: 60}
|
||||
body, err := json.Marshal(reqData)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -112,7 +113,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
// get the record's unique ID from when we created it
|
||||
d.recordIDsMu.Lock()
|
||||
|
@ -122,12 +123,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||
return fmt.Errorf("unknown record ID for '%s'", fqdn)
|
||||
}
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err)
|
||||
}
|
||||
|
||||
authZone = acme.UnFqdn(authZone)
|
||||
authZone = acmev2.UnFqdn(authZone)
|
||||
|
||||
reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", digitalOceanBaseURL, authZone, recordID)
|
||||
req, err := http.NewRequest("DELETE", reqURL, nil)
|
||||
|
@ -164,3 +165,9 @@ type digitalOceanAPIError struct {
|
|||
}
|
||||
|
||||
var digitalOceanBaseURL = "https://api.digitalocean.com"
|
||||
|
||||
// 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 90 * time.Second, 5 * time.Second
|
||||
}
|
||||
|
|
31
vendor/github.com/xenolf/lego/providers/dns/dns_providers.go
generated
vendored
31
vendor/github.com/xenolf/lego/providers/dns/dns_providers.go
generated
vendored
|
@ -4,23 +4,30 @@ package dns
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
"github.com/xenolf/lego/providers/dns/auroradns"
|
||||
"github.com/xenolf/lego/providers/dns/azure"
|
||||
"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/digitalocean"
|
||||
"github.com/xenolf/lego/providers/dns/dnsimple"
|
||||
"github.com/xenolf/lego/providers/dns/dnsmadeeasy"
|
||||
"github.com/xenolf/lego/providers/dns/dnspod"
|
||||
"github.com/xenolf/lego/providers/dns/duckdns"
|
||||
"github.com/xenolf/lego/providers/dns/dyn"
|
||||
"github.com/xenolf/lego/providers/dns/exec"
|
||||
"github.com/xenolf/lego/providers/dns/exoscale"
|
||||
"github.com/xenolf/lego/providers/dns/fastdns"
|
||||
"github.com/xenolf/lego/providers/dns/gandi"
|
||||
"github.com/xenolf/lego/providers/dns/gandiv5"
|
||||
"github.com/xenolf/lego/providers/dns/googlecloud"
|
||||
"github.com/xenolf/lego/providers/dns/glesys"
|
||||
"github.com/xenolf/lego/providers/dns/godaddy"
|
||||
"github.com/xenolf/lego/providers/dns/googlecloud"
|
||||
"github.com/xenolf/lego/providers/dns/lightsail"
|
||||
"github.com/xenolf/lego/providers/dns/linode"
|
||||
"github.com/xenolf/lego/providers/dns/namecheap"
|
||||
"github.com/xenolf/lego/providers/dns/namedotcom"
|
||||
"github.com/xenolf/lego/providers/dns/ns1"
|
||||
"github.com/xenolf/lego/providers/dns/otc"
|
||||
"github.com/xenolf/lego/providers/dns/ovh"
|
||||
|
@ -31,14 +38,16 @@ import (
|
|||
"github.com/xenolf/lego/providers/dns/vultr"
|
||||
)
|
||||
|
||||
func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) {
|
||||
func NewDNSChallengeProviderByName(name string) (acmev2.ChallengeProvider, error) {
|
||||
var err error
|
||||
var provider acme.ChallengeProvider
|
||||
var provider acmev2.ChallengeProvider
|
||||
switch name {
|
||||
case "azure":
|
||||
provider, err = azure.NewDNSProvider()
|
||||
case "auroradns":
|
||||
provider, err = auroradns.NewDNSProvider()
|
||||
case "bluecat":
|
||||
provider, err = bluecat.NewDNSProvider()
|
||||
case "cloudflare":
|
||||
provider, err = cloudflare.NewDNSProvider()
|
||||
case "cloudxns":
|
||||
|
@ -51,24 +60,34 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
|||
provider, err = dnsmadeeasy.NewDNSProvider()
|
||||
case "dnspod":
|
||||
provider, err = dnspod.NewDNSProvider()
|
||||
case "duckdns":
|
||||
provider, err = duckdns.NewDNSProvider()
|
||||
case "dyn":
|
||||
provider, err = dyn.NewDNSProvider()
|
||||
case "fastdns":
|
||||
provider, err = fastdns.NewDNSProvider()
|
||||
case "exoscale":
|
||||
provider, err = exoscale.NewDNSProvider()
|
||||
case "gandi":
|
||||
provider, err = gandi.NewDNSProvider()
|
||||
case "gandiv5":
|
||||
provider, err = gandiv5.NewDNSProvider()
|
||||
case "glesys":
|
||||
provider, err = glesys.NewDNSProvider()
|
||||
case "gcloud":
|
||||
provider, err = googlecloud.NewDNSProvider()
|
||||
case "godaddy":
|
||||
provider, err = godaddy.NewDNSProvider()
|
||||
case "lightsail":
|
||||
provider, err = lightsail.NewDNSProvider()
|
||||
case "linode":
|
||||
provider, err = linode.NewDNSProvider()
|
||||
case "manual":
|
||||
provider, err = acme.NewDNSProviderManual()
|
||||
provider, err = acmev2.NewDNSProviderManual()
|
||||
case "namecheap":
|
||||
provider, err = namecheap.NewDNSProvider()
|
||||
case "namedotcom":
|
||||
provider, err = namedotcom.NewDNSProvider()
|
||||
case "rackspace":
|
||||
provider, err = rackspace.NewDNSProvider()
|
||||
case "route53":
|
||||
|
@ -85,6 +104,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
|
|||
provider, err = ns1.NewDNSProvider()
|
||||
case "otc":
|
||||
provider, err = otc.NewDNSProvider()
|
||||
case "exec":
|
||||
provider, err = exec.NewDNSProvider()
|
||||
default:
|
||||
err = fmt.Errorf("Unrecognised DNS provider: %s", name)
|
||||
}
|
||||
|
|
14
vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go
generated
vendored
14
vendor/github.com/xenolf/lego/providers/dns/dnsimple/dnsimple.go
generated
vendored
|
@ -9,10 +9,10 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/dnsimple/dnsimple-go/dnsimple"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
|
||||
type DNSProvider struct {
|
||||
client *dnsimple.Client
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ func NewDNSProviderCredentials(accessToken, baseUrl string) (*DNSProvider, error
|
|||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
zoneName, err := c.getHostedZone(domain)
|
||||
|
||||
|
@ -71,7 +71,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
records, err := c.findTxtRecords(domain, fqdn)
|
||||
if err != nil {
|
||||
|
@ -94,7 +94,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||
}
|
||||
|
||||
func (c *DNSProvider) getHostedZone(domain string) (string, error) {
|
||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) {
|
|||
return "", err
|
||||
}
|
||||
|
||||
zoneName := acme.UnFqdn(authZone)
|
||||
zoneName := acmev2.UnFqdn(authZone)
|
||||
|
||||
zones, err := c.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName})
|
||||
if err != nil {
|
||||
|
@ -159,7 +159,7 @@ func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsim
|
|||
}
|
||||
|
||||
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||
name := acme.UnFqdn(fqdn)
|
||||
name := acmev2.UnFqdn(fqdn)
|
||||
if idx := strings.Index(name, "."+domain); idx != -1 {
|
||||
return name[:idx]
|
||||
}
|
||||
|
|
12
vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go
generated
vendored
12
vendor/github.com/xenolf/lego/providers/dns/dnsmadeeasy/dnsmadeeasy.go
generated
vendored
|
@ -14,10 +14,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface that uses
|
||||
// DNSMadeEasy's DNS API to manage TXT records for a domain.
|
||||
type DNSProvider struct {
|
||||
baseURL string
|
||||
|
@ -77,9 +77,9 @@ func NewDNSProviderCredentials(baseURL, apiKey, apiSecret string) (*DNSProvider,
|
|||
|
||||
// Present creates a TXT record using the specified parameters
|
||||
func (d *DNSProvider) Present(domainName, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domainName, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domainName, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -104,9 +104,9 @@ func (d *DNSProvider) Present(domainName, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT records matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domainName, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domainName, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domainName, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
14
vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go
generated
vendored
14
vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go
generated
vendored
|
@ -8,10 +8,10 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/decker502/dnspod-go"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
|
||||
type DNSProvider struct {
|
||||
client *dnspod.Client
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ func NewDNSProviderCredentials(key string) (*DNSProvider, error) {
|
|||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
zoneID, zoneName, err := c.getHostedZone(domain)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -55,7 +55,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
records, err := c.findTxtRecords(domain, fqdn)
|
||||
if err != nil {
|
||||
|
@ -82,14 +82,14 @@ func (c *DNSProvider) getHostedZone(domain string) (string, string, error) {
|
|||
return "", "", fmt.Errorf("dnspod API call failed: %v", err)
|
||||
}
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var hostedZone dnspod.Domain
|
||||
for _, zone := range zones {
|
||||
if zone.Name == acme.UnFqdn(authZone) {
|
||||
if zone.Name == acmev2.UnFqdn(authZone) {
|
||||
hostedZone = zone
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, erro
|
|||
}
|
||||
|
||||
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||
name := acme.UnFqdn(fqdn)
|
||||
name := acmev2.UnFqdn(fqdn)
|
||||
if idx := strings.Index(name, "."+domain); idx != -1 {
|
||||
return name[:idx]
|
||||
}
|
||||
|
|
82
vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go
generated
vendored
Normal file
82
vendor/github.com/xenolf/lego/providers/dns/duckdns/duckdns.go
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Adds lego support for http://duckdns.org .
|
||||
//
|
||||
// See http://www.duckdns.org/spec.jsp for more info on updating TXT records.
|
||||
package duckdns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider adds and removes the record for the DNS challenge
|
||||
type DNSProvider struct {
|
||||
// The duckdns api token
|
||||
token string
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a new DNS provider using
|
||||
// environment variable DUCKDNS_TOKEN for adding and removing the DNS record.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
duckdnsToken := os.Getenv("DUCKDNS_TOKEN")
|
||||
|
||||
return NewDNSProviderCredentials(duckdnsToken)
|
||||
}
|
||||
|
||||
// NewDNSProviderCredentials uses the supplied credentials to return a
|
||||
// DNSProvider instance configured for http://duckdns.org .
|
||||
func NewDNSProviderCredentials(duckdnsToken string) (*DNSProvider, error) {
|
||||
if duckdnsToken == "" {
|
||||
return nil, errors.New("environment variable DUCKDNS_TOKEN not set")
|
||||
}
|
||||
|
||||
return &DNSProvider{token: duckdnsToken}, nil
|
||||
}
|
||||
|
||||
// makeDuckdnsURL creates a url to clear the set or unset the TXT record.
|
||||
// txt == "" will clear the TXT record.
|
||||
func makeDuckdnsURL(domain, token, txt string) string {
|
||||
requestBase := fmt.Sprintf("https://www.duckdns.org/update?domains=%s&token=%s", domain, token)
|
||||
if txt == "" {
|
||||
return requestBase + "&clear=true"
|
||||
}
|
||||
return requestBase + "&txt=" + txt
|
||||
}
|
||||
|
||||
func issueDuckdnsRequest(url string) error {
|
||||
response, err := acmev2.HTTPClient.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
bodyBytes, err := ioutil.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body := string(bodyBytes)
|
||||
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 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, _ := acmev2.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)
|
||||
}
|
24
vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go
generated
vendored
24
vendor/github.com/xenolf/lego/providers/dns/dyn/dyn.go
generated
vendored
|
@ -11,7 +11,7 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
var dynBaseURL = "https://api.dynect.net/REST"
|
||||
|
@ -30,7 +30,7 @@ type dynResponse struct {
|
|||
Messages json.RawMessage `json:"msgs"`
|
||||
}
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface that uses
|
||||
// Dyn's Managed DNS API to manage TXT records for a domain.
|
||||
type DNSProvider struct {
|
||||
customerName string
|
||||
|
@ -87,11 +87,8 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{})
|
|||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
if resp.StatusCode >= 500 {
|
||||
return nil, fmt.Errorf("Dyn API request failed with HTTP status code %d", resp.StatusCode)
|
||||
} else if resp.StatusCode == 307 {
|
||||
// TODO add support for HTTP 307 response and long running jobs
|
||||
return nil, fmt.Errorf("Dyn API request returned HTTP 307. This is currently unsupported")
|
||||
}
|
||||
|
||||
var dynRes dynResponse
|
||||
|
@ -100,6 +97,13 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{})
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return nil, fmt.Errorf("Dyn API request failed with HTTP status code %d: %s", resp.StatusCode, dynRes.Messages)
|
||||
} else if resp.StatusCode == 307 {
|
||||
// TODO add support for HTTP 307 response and long running jobs
|
||||
return nil, fmt.Errorf("Dyn API request returned HTTP 307. This is currently unsupported")
|
||||
}
|
||||
|
||||
if dynRes.Status == "failure" {
|
||||
// TODO add better error handling
|
||||
return nil, fmt.Errorf("Dyn API request failed: %s", dynRes.Messages)
|
||||
|
@ -172,9 +176,9 @@ func (d *DNSProvider) logout() error {
|
|||
|
||||
// Present creates a TXT record using the specified parameters
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -228,9 +232,9 @@ func (d *DNSProvider) publish(zone, notes string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
72
vendor/github.com/xenolf/lego/providers/dns/exec/exec.go
generated
vendored
Normal file
72
vendor/github.com/xenolf/lego/providers/dns/exec/exec.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
// 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
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider adds and removes the record for the DNS challenge by calling a
|
||||
// program with command-line parameters.
|
||||
type DNSProvider struct {
|
||||
program string
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a new DNS provider which runs the program in the
|
||||
// environment variable EXEC_PATH for adding and removing the DNS record.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
s := os.Getenv("EXEC_PATH")
|
||||
if s == "" {
|
||||
return nil, errors.New("environment variable EXEC_PATH not set")
|
||||
}
|
||||
|
||||
return &DNSProvider{program: s}, nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
cmd := exec.Command(d.program, "present", fqdn, value, strconv.Itoa(ttl))
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
cmd := exec.Command(d.program, "cleanup", fqdn, value, strconv.Itoa(ttl))
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
return cmd.Run()
|
||||
}
|
14
vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go
generated
vendored
14
vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go
generated
vendored
|
@ -8,10 +8,10 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/exoscale/egoscale"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
|
||||
type DNSProvider struct {
|
||||
client *egoscale.Client
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) {
|
|||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -78,7 +78,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the record matching the specified parameters.
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -116,12 +116,12 @@ func (c *DNSProvider) FindExistingRecordId(zone, recordName string) (int64, erro
|
|||
|
||||
// Extract DNS zone and DNS entry name
|
||||
func (c *DNSProvider) FindZoneAndRecordName(fqdn, domain string) (string, string, error) {
|
||||
zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||
zone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
zone = acme.UnFqdn(zone)
|
||||
name := acme.UnFqdn(fqdn)
|
||||
zone = acmev2.UnFqdn(zone)
|
||||
name := acmev2.UnFqdn(fqdn)
|
||||
name = name[:len(name)-len("."+zone)]
|
||||
|
||||
return zone, name, nil
|
||||
|
|
139
vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go
generated
vendored
Normal file
139
vendor/github.com/xenolf/lego/providers/dns/fastdns/fastdns.go
generated
vendored
Normal file
|
@ -0,0 +1,139 @@
|
|||
package fastdns
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
configdns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1"
|
||||
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
|
||||
type DNSProvider struct {
|
||||
config edgegrid.Config
|
||||
}
|
||||
|
||||
// NewDNSProvider uses the supplied environment variables to return a DNSProvider instance:
|
||||
// AKAMAI_HOST, AKAMAI_CLIENT_TOKEN, AKAMAI_CLIENT_SECRET, AKAMAI_ACCESS_TOKEN
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
host := os.Getenv("AKAMAI_HOST")
|
||||
clientToken := os.Getenv("AKAMAI_CLIENT_TOKEN")
|
||||
clientSecret := os.Getenv("AKAMAI_CLIENT_SECRET")
|
||||
accessToken := os.Getenv("AKAMAI_ACCESS_TOKEN")
|
||||
|
||||
return NewDNSProviderClient(host, clientToken, clientSecret, accessToken)
|
||||
}
|
||||
|
||||
// NewDNSProviderClient uses the supplied parameters to return a DNSProvider instance
|
||||
// configured for FastDNS.
|
||||
func NewDNSProviderClient(host, clientToken, clientSecret, accessToken string) (*DNSProvider, error) {
|
||||
if clientToken == "" || clientSecret == "" || accessToken == "" || host == "" {
|
||||
return nil, fmt.Errorf("Akamai FastDNS credentials missing")
|
||||
}
|
||||
config := edgegrid.Config{
|
||||
Host: host,
|
||||
ClientToken: clientToken,
|
||||
ClientSecret: clientSecret,
|
||||
AccessToken: accessToken,
|
||||
MaxBody: 131072,
|
||||
}
|
||||
|
||||
return &DNSProvider{
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fullfil the dns-01 challenge.
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
zoneName, recordName, err := c.findZoneAndRecordName(fqdn, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configdns.Init(c.config)
|
||||
|
||||
zone, err := configdns.GetZone(zoneName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record := configdns.NewTxtRecord()
|
||||
record.SetField("name", recordName)
|
||||
record.SetField("ttl", ttl)
|
||||
record.SetField("target", value)
|
||||
record.SetField("active", true)
|
||||
|
||||
existingRecord := c.findExistingRecord(zone, recordName)
|
||||
|
||||
if existingRecord != nil {
|
||||
if reflect.DeepEqual(existingRecord.ToMap(), record.ToMap()) {
|
||||
return nil
|
||||
}
|
||||
zone.RemoveRecord(existingRecord)
|
||||
return c.createRecord(zone, record)
|
||||
}
|
||||
|
||||
return c.createRecord(zone, record)
|
||||
}
|
||||
|
||||
// CleanUp removes the record matching the specified parameters.
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
zoneName, recordName, err := c.findZoneAndRecordName(fqdn, domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configdns.Init(c.config)
|
||||
|
||||
zone, err := configdns.GetZone(zoneName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existingRecord := c.findExistingRecord(zone, recordName)
|
||||
|
||||
if existingRecord != nil {
|
||||
err := zone.RemoveRecord(existingRecord)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return zone.Save()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string, error) {
|
||||
zone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
zone = acmev2.UnFqdn(zone)
|
||||
name := acmev2.UnFqdn(fqdn)
|
||||
name = name[:len(name)-len("."+zone)]
|
||||
|
||||
return zone, name, nil
|
||||
}
|
||||
|
||||
func (c *DNSProvider) findExistingRecord(zone *configdns.Zone, recordName string) *configdns.TxtRecord {
|
||||
for _, r := range zone.Zone.Txt {
|
||||
if r.Name == recordName {
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DNSProvider) createRecord(zone *configdns.Zone, record *configdns.TxtRecord) error {
|
||||
err := zone.AddRecord(record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return zone.Save()
|
||||
}
|
14
vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go
generated
vendored
14
vendor/github.com/xenolf/lego/providers/dns/gandi/gandi.go
generated
vendored
|
@ -14,7 +14,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// Gandi API reference: http://doc.rpc.gandi.net/index.html
|
||||
|
@ -26,7 +26,7 @@ var (
|
|||
endpoint = "https://rpc.gandi.net/xmlrpc/"
|
||||
// findZoneByFqdn determines the DNS zone of an fqdn. It is overridden
|
||||
// during tests.
|
||||
findZoneByFqdn = acme.FindZoneByFqdn
|
||||
findZoneByFqdn = acmev2.FindZoneByFqdn
|
||||
)
|
||||
|
||||
// inProgressInfo contains information about an in-progress challenge
|
||||
|
@ -37,7 +37,7 @@ type inProgressInfo struct {
|
|||
}
|
||||
|
||||
// DNSProvider is an implementation of the
|
||||
// acme.ChallengeProviderTimeout interface that uses Gandi's XML-RPC
|
||||
// acmev2.ChallengeProviderTimeout interface that uses Gandi's XML-RPC
|
||||
// API to manage TXT records for a domain.
|
||||
type DNSProvider struct {
|
||||
apiKey string
|
||||
|
@ -70,12 +70,12 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) {
|
|||
// does this by creating and activating a new temporary Gandi DNS
|
||||
// zone. This new zone contains the TXT record.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
if ttl < 300 {
|
||||
ttl = 300 // 300 is gandi minimum value for ttl
|
||||
}
|
||||
// find authZone and Gandi zone_id for fqdn
|
||||
authZone, err := findZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := findZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Gandi DNS: findZoneByFqdn failure: %v", err)
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
// containing the required TXT record
|
||||
newZoneName := fmt.Sprintf(
|
||||
"%s [ACME Challenge %s]",
|
||||
acme.UnFqdn(authZone), time.Now().Format(time.RFC822Z))
|
||||
acmev2.UnFqdn(authZone), time.Now().Format(time.RFC822Z))
|
||||
newZoneID, err := d.cloneZone(zoneID, newZoneName)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -138,7 +138,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
// parameters. It does this by restoring the old Gandi DNS zone and
|
||||
// removing the temporary one created by Present.
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
// acquire lock and retrieve zoneID, newZoneID and authZone
|
||||
d.inProgressMu.Lock()
|
||||
defer d.inProgressMu.Unlock()
|
||||
|
|
16
vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go
generated
vendored
16
vendor/github.com/xenolf/lego/providers/dns/gandiv5/gandiv5.go
generated
vendored
|
@ -12,7 +12,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// Gandi API reference: http://doc.livedns.gandi.net/
|
||||
|
@ -23,7 +23,7 @@ var (
|
|||
endpoint = "https://dns.api.gandi.net/api/v5"
|
||||
// findZoneByFqdn determines the DNS zone of an fqdn. It is overridden
|
||||
// during tests.
|
||||
findZoneByFqdn = acme.FindZoneByFqdn
|
||||
findZoneByFqdn = acmev2.FindZoneByFqdn
|
||||
)
|
||||
|
||||
// inProgressInfo contains information about an in-progress challenge
|
||||
|
@ -33,7 +33,7 @@ type inProgressInfo struct {
|
|||
}
|
||||
|
||||
// DNSProvider is an implementation of the
|
||||
// acme.ChallengeProviderTimeout interface that uses Gandi's LiveDNS
|
||||
// acmev2.ChallengeProviderTimeout interface that uses Gandi's LiveDNS
|
||||
// API to manage TXT records for a domain.
|
||||
type DNSProvider struct {
|
||||
apiKey string
|
||||
|
@ -62,12 +62,12 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) {
|
|||
|
||||
// Present creates a TXT record using the specified parameters.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
if ttl < 300 {
|
||||
ttl = 300 // 300 is gandi minimum value for ttl
|
||||
}
|
||||
// find authZone
|
||||
authZone, err := findZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := findZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Gandi DNS: findZoneByFqdn failure: %v", err)
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
d.inProgressMu.Lock()
|
||||
defer d.inProgressMu.Unlock()
|
||||
// add TXT record into authZone
|
||||
err = d.addTXTRecord(acme.UnFqdn(authZone), name, value, ttl)
|
||||
err = d.addTXTRecord(acmev2.UnFqdn(authZone), name, value, ttl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
// acquire lock and retrieve authZone
|
||||
d.inProgressMu.Lock()
|
||||
defer d.inProgressMu.Unlock()
|
||||
|
@ -109,7 +109,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||
authZone := d.inProgressFQDNs[fqdn].authZone
|
||||
delete(d.inProgressFQDNs, fqdn)
|
||||
// delete TXT record from authZone
|
||||
err := d.deleteTXTRecord(acme.UnFqdn(authZone), fieldName)
|
||||
err := d.deleteTXTRecord(acmev2.UnFqdn(authZone), fieldName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
211
vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go
generated
vendored
Normal file
211
vendor/github.com/xenolf/lego/providers/dns/glesys/glesys.go
generated
vendored
Normal file
|
@ -0,0 +1,211 @@
|
|||
// Package glesys implements a DNS provider for solving the DNS-01
|
||||
// challenge using GleSYS api.
|
||||
package glesys
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// GleSYS API reference: https://github.com/GleSYS/API/wiki/API-Documentation
|
||||
|
||||
// domainAPI is the GleSYS API endpoint used by Present and CleanUp.
|
||||
const domainAPI = "https://api.glesys.com/domain"
|
||||
|
||||
var (
|
||||
// Logger is used to log API communication results;
|
||||
// if nil, the default log.Logger is used.
|
||||
Logger *log.Logger
|
||||
)
|
||||
|
||||
// logf writes a log entry. It uses Logger if not
|
||||
// nil, otherwise it uses the default log.Logger.
|
||||
func logf(format string, args ...interface{}) {
|
||||
if Logger != nil {
|
||||
Logger.Printf(format, args...)
|
||||
} else {
|
||||
log.Printf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// DNSProvider is an implementation of the
|
||||
// acmev2.ChallengeProviderTimeout interface that uses GleSYS
|
||||
// API to manage TXT records for a domain.
|
||||
type DNSProvider struct {
|
||||
apiUser string
|
||||
apiKey string
|
||||
activeRecords map[string]int
|
||||
inProgressMu sync.Mutex
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for GleSYS.
|
||||
// Credentials must be passed in the environment variables: GLESYS_API_USER
|
||||
// and GLESYS_API_KEY.
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
apiUser := os.Getenv("GLESYS_API_USER")
|
||||
apiKey := os.Getenv("GLESYS_API_KEY")
|
||||
return NewDNSProviderCredentials(apiUser, apiKey)
|
||||
}
|
||||
|
||||
// NewDNSProviderCredentials uses the supplied credentials to return a
|
||||
// DNSProvider instance configured for GleSYS.
|
||||
func NewDNSProviderCredentials(apiUser string, apiKey string) (*DNSProvider, error) {
|
||||
if apiUser == "" || apiKey == "" {
|
||||
return nil, fmt.Errorf("GleSYS DNS: Incomplete credentials provided")
|
||||
}
|
||||
return &DNSProvider{
|
||||
apiUser: apiUser,
|
||||
apiKey: apiKey,
|
||||
activeRecords: make(map[string]int),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record using the specified parameters.
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
if ttl < 60 {
|
||||
ttl = 60 // 60 is GleSYS minimum value for ttl
|
||||
}
|
||||
// find authZone
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GleSYS DNS: findZoneByFqdn failure: %v", err)
|
||||
}
|
||||
// determine name of TXT record
|
||||
if !strings.HasSuffix(
|
||||
strings.ToLower(fqdn), strings.ToLower("."+authZone)) {
|
||||
return fmt.Errorf(
|
||||
"GleSYS DNS: unexpected authZone %s for fqdn %s", authZone, fqdn)
|
||||
}
|
||||
name := fqdn[:len(fqdn)-len("."+authZone)]
|
||||
// acquire lock and check there is not a challenge already in
|
||||
// progress for this value of authZone
|
||||
d.inProgressMu.Lock()
|
||||
defer d.inProgressMu.Unlock()
|
||||
// add TXT record into authZone
|
||||
recordId, err := d.addTXTRecord(domain, acmev2.UnFqdn(authZone), name, value, ttl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// save data necessary for CleanUp
|
||||
d.activeRecords[fqdn] = recordId
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
// acquire lock and retrieve authZone
|
||||
d.inProgressMu.Lock()
|
||||
defer d.inProgressMu.Unlock()
|
||||
if _, ok := d.activeRecords[fqdn]; !ok {
|
||||
// if there is no cleanup information then just return
|
||||
return nil
|
||||
}
|
||||
recordId := d.activeRecords[fqdn]
|
||||
delete(d.activeRecords, fqdn)
|
||||
// delete TXT record from authZone
|
||||
err := d.deleteTXTRecord(domain, recordId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Timeout returns the values (20*time.Minute, 20*time.Second) which
|
||||
// are used by the acme package as timeout and check interval values
|
||||
// when checking for DNS record propagation with GleSYS.
|
||||
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return 20 * time.Minute, 20 * time.Second
|
||||
}
|
||||
|
||||
// types for JSON method calls, parameters, and responses
|
||||
|
||||
type addRecordRequest struct {
|
||||
Domainname string `json:"domainname"`
|
||||
Host string `json:"host"`
|
||||
Type string `json:"type"`
|
||||
Data string `json:"data"`
|
||||
Ttl int `json:"ttl,omitempty"`
|
||||
}
|
||||
|
||||
type deleteRecordRequest struct {
|
||||
Recordid int `json:"recordid"`
|
||||
}
|
||||
|
||||
type responseStruct struct {
|
||||
Response struct {
|
||||
Status struct {
|
||||
Code int `json:"code"`
|
||||
} `json:"status"`
|
||||
Record deleteRecordRequest `json:"record"`
|
||||
} `json:"response"`
|
||||
}
|
||||
|
||||
// POSTing/Marshalling/Unmarshalling
|
||||
|
||||
func (d *DNSProvider) sendRequest(method string, resource string, payload interface{}) (*responseStruct, error) {
|
||||
url := fmt.Sprintf("%s/%s", domainAPI, resource)
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, err := http.NewRequest(method, url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.SetBasicAuth(d.apiUser, d.apiKey)
|
||||
|
||||
client := &http.Client{Timeout: time.Duration(10 * time.Second)}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
return nil, fmt.Errorf("GleSYS DNS: request failed with HTTP status code %d", resp.StatusCode)
|
||||
}
|
||||
var response responseStruct
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
|
||||
return &response, err
|
||||
}
|
||||
|
||||
// functions to perform API actions
|
||||
|
||||
func (d *DNSProvider) addTXTRecord(fqdn string, domain string, name string, value string, ttl int) (int, error) {
|
||||
response, err := d.sendRequest("POST", "addrecord", addRecordRequest{
|
||||
Domainname: domain,
|
||||
Host: name,
|
||||
Type: "TXT",
|
||||
Data: value,
|
||||
Ttl: ttl,
|
||||
})
|
||||
if response != nil && response.Response.Status.Code == 200 {
|
||||
logf("[INFO][%s] GleSYS DNS: Successfully created recordid %d", fqdn, response.Response.Record.Recordid)
|
||||
return response.Response.Record.Recordid, nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (d *DNSProvider) deleteTXTRecord(fqdn string, recordid int) error {
|
||||
response, err := d.sendRequest("POST", "deleterecord", deleteRecordRequest{
|
||||
Recordid: recordid,
|
||||
})
|
||||
if response != nil && response.Response.Status.Code == 200 {
|
||||
logf("[INFO][%s] GleSYS DNS: Successfully deleted recordid %d", fqdn, recordid)
|
||||
}
|
||||
return err
|
||||
}
|
15
vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go
generated
vendored
15
vendor/github.com/xenolf/lego/providers/dns/godaddy/godaddy.go
generated
vendored
|
@ -10,15 +10,16 @@ import (
|
|||
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// GoDaddyAPIURL represents the API endpoint to call.
|
||||
const apiURL = "https://api.godaddy.com"
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
apiKey string
|
||||
apiSecret string
|
||||
|
@ -50,7 +51,7 @@ func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
|||
}
|
||||
|
||||
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||
name := acme.UnFqdn(fqdn)
|
||||
name := acmev2.UnFqdn(fqdn)
|
||||
if idx := strings.Index(name, "."+domain); idx != -1 {
|
||||
return name[:idx]
|
||||
}
|
||||
|
@ -59,7 +60,7 @@ func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
|||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
domainZone, err := c.getZone(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -105,7 +106,7 @@ func (c *DNSProvider) updateRecords(records []DNSRecord, domainZone string, reco
|
|||
|
||||
// CleanUp sets null value in the TXT DNS record as GoDaddy has no proper DELETE record method
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
domainZone, err := c.getZone(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -124,12 +125,12 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||
}
|
||||
|
||||
func (c *DNSProvider) getZone(fqdn string) (string, error) {
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return acme.UnFqdn(authZone), nil
|
||||
return acmev2.UnFqdn(authZone), nil
|
||||
}
|
||||
|
||||
func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, error) {
|
||||
|
|
8
vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud.go
generated
vendored
8
vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud.go
generated
vendored
|
@ -8,7 +8,7 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/oauth2"
|
||||
|
@ -88,7 +88,7 @@ func NewDNSProviderServiceAccount(project string, saFile string) (*DNSProvider,
|
|||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
zone, err := c.getHostedZone(domain)
|
||||
if err != nil {
|
||||
|
@ -135,7 +135,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
zone, err := c.getHostedZone(domain)
|
||||
if err != nil {
|
||||
|
@ -167,7 +167,7 @@ func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
|||
|
||||
// getHostedZone returns the managed-zone
|
||||
func (c *DNSProvider) getHostedZone(domain string) (string, error) {
|
||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
107
vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go
generated
vendored
Normal file
107
vendor/github.com/xenolf/lego/providers/dns/lightsail/lightsail.go
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
// Package lightsail implements a DNS provider for solving the DNS-01 challenge
|
||||
// using AWS Lightsail DNS.
|
||||
package lightsail
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/lightsail"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
const (
|
||||
maxRetries = 5
|
||||
)
|
||||
|
||||
// DNSProvider implements the acmev2.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
client *lightsail.Lightsail
|
||||
}
|
||||
|
||||
// customRetryer implements the client.Retryer interface by composing the
|
||||
// DefaultRetryer. It controls the logic for retrying recoverable request
|
||||
// errors (e.g. when rate limits are exceeded).
|
||||
type customRetryer struct {
|
||||
client.DefaultRetryer
|
||||
}
|
||||
|
||||
// RetryRules overwrites the DefaultRetryer's method.
|
||||
// It uses a basic exponential backoff algorithm that returns an initial
|
||||
// delay of ~400ms with an upper limit of ~30 seconds which should prevent
|
||||
// causing a high number of consecutive throttling errors.
|
||||
// For reference: Route 53 enforces an account-wide(!) 5req/s query limit.
|
||||
func (d customRetryer) RetryRules(r *request.Request) time.Duration {
|
||||
retryCount := r.RetryCount
|
||||
if retryCount > 7 {
|
||||
retryCount = 7
|
||||
}
|
||||
|
||||
delay := (1 << uint(retryCount)) * (rand.Intn(50) + 200)
|
||||
return time.Duration(delay) * time.Millisecond
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for the AWS
|
||||
// Lightsail service.
|
||||
//
|
||||
// AWS Credentials are automatically detected in the following locations
|
||||
// and prioritized in the following order:
|
||||
// 1. Environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY,
|
||||
// [AWS_SESSION_TOKEN], [DNS_ZONE]
|
||||
// 2. Shared credentials file (defaults to ~/.aws/credentials)
|
||||
// 3. Amazon EC2 IAM role
|
||||
//
|
||||
// public hosted zone via the FQDN.
|
||||
//
|
||||
// See also: https://github.com/aws/aws-sdk-go/wiki/configuring-sdk
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
r := customRetryer{}
|
||||
r.NumMaxRetries = maxRetries
|
||||
config := request.WithRetryer(aws.NewConfig(), r)
|
||||
client := lightsail.New(session.New(config))
|
||||
|
||||
return &DNSProvider{
|
||||
client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record using the specified parameters
|
||||
func (r *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
value = `"` + value + `"`
|
||||
err := r.newTxtRecord(domain, fqdn, value)
|
||||
return err
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
value = `"` + value + `"`
|
||||
params := &lightsail.DeleteDomainEntryInput{
|
||||
DomainName: aws.String(domain),
|
||||
DomainEntry: &lightsail.DomainEntry{
|
||||
Name: aws.String(fqdn),
|
||||
Type: aws.String("TXT"),
|
||||
Target: aws.String(value),
|
||||
},
|
||||
}
|
||||
_, err := r.client.DeleteDomainEntry(params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *DNSProvider) newTxtRecord(domain string, fqdn string, value string) error {
|
||||
params := &lightsail.CreateDomainEntryInput{
|
||||
DomainName: aws.String(domain),
|
||||
DomainEntry: &lightsail.DomainEntry{
|
||||
Name: aws.String(fqdn),
|
||||
Target: aws.String(value),
|
||||
Type: aws.String("TXT"),
|
||||
},
|
||||
}
|
||||
_, err := r.client.CreateDomainEntry(params)
|
||||
return err
|
||||
}
|
14
vendor/github.com/xenolf/lego/providers/dns/linode/linode.go
generated
vendored
14
vendor/github.com/xenolf/lego/providers/dns/linode/linode.go
generated
vendored
|
@ -9,7 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/timewasted/linode/dns"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -23,7 +23,7 @@ type hostedZoneInfo struct {
|
|||
resourceName string
|
||||
}
|
||||
|
||||
// DNSProvider implements the acme.ChallengeProvider interface.
|
||||
// DNSProvider implements the acmev2.ChallengeProvider interface.
|
||||
type DNSProvider struct {
|
||||
linode *dns.DNS
|
||||
}
|
||||
|
@ -66,13 +66,13 @@ func (p *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
|||
|
||||
// Present creates a TXT record using the specified parameters.
|
||||
func (p *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
zone, err := p.getHostedZoneInfo(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err = p.linode.CreateDomainResourceTXT(zone.domainId, acme.UnFqdn(fqdn), value, 60); err != nil {
|
||||
if _, err = p.linode.CreateDomainResourceTXT(zone.domainId, acmev2.UnFqdn(fqdn), value, 60); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ func (p *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
zone, err := p.getHostedZoneInfo(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -112,14 +112,14 @@ func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||
|
||||
func (p *DNSProvider) getHostedZoneInfo(fqdn string) (*hostedZoneInfo, error) {
|
||||
// Lookup the zone that handles the specified FQDN.
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resourceName := strings.TrimSuffix(fqdn, "."+authZone)
|
||||
|
||||
// Query the authority zone.
|
||||
domain, err := p.linode.GetDomain(acme.UnFqdn(authZone))
|
||||
domain, err := p.linode.GetDomain(acmev2.UnFqdn(authZone))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
6
vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go
generated
vendored
6
vendor/github.com/xenolf/lego/providers/dns/namecheap/namecheap.go
generated
vendored
|
@ -12,7 +12,7 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// Notes about namecheap's tool API:
|
||||
|
@ -132,7 +132,7 @@ type challenge struct {
|
|||
// newChallenge builds a challenge record from a domain name, a challenge
|
||||
// authentication key, and a map of available TLDs.
|
||||
func newChallenge(domain, keyAuth string, tlds map[string]string) (*challenge, error) {
|
||||
domain = acme.UnFqdn(domain)
|
||||
domain = acmev2.UnFqdn(domain)
|
||||
parts := strings.Split(domain, ".")
|
||||
|
||||
// Find the longest matching TLD.
|
||||
|
@ -155,7 +155,7 @@ func newChallenge(domain, keyAuth string, tlds map[string]string) (*challenge, e
|
|||
host = strings.Join(parts[:longest-1], ".")
|
||||
}
|
||||
|
||||
key, keyValue, _ := acme.DNS01Record(domain, keyAuth)
|
||||
key, keyValue, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
return &challenge{
|
||||
domain: domain,
|
||||
|
|
124
vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go
generated
vendored
Normal file
124
vendor/github.com/xenolf/lego/providers/dns/namedotcom/namedotcom.go
generated
vendored
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Package namedotcom implements a DNS provider for solving the DNS-01 challenge
|
||||
// using Name.com's DNS service.
|
||||
package namedotcom
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/namedotcom/go/namecom"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
|
||||
type DNSProvider struct {
|
||||
client *namecom.NameCom
|
||||
}
|
||||
|
||||
// NewDNSProvider returns a DNSProvider instance configured for namedotcom.
|
||||
// Credentials must be passed in the environment variables: NAMECOM_USERNAME and NAMECOM_API_TOKEN
|
||||
func NewDNSProvider() (*DNSProvider, error) {
|
||||
username := os.Getenv("NAMECOM_USERNAME")
|
||||
apiToken := os.Getenv("NAMECOM_API_TOKEN")
|
||||
server := os.Getenv("NAMECOM_SERVER")
|
||||
|
||||
return NewDNSProviderCredentials(username, apiToken, server)
|
||||
}
|
||||
|
||||
// NewDNSProviderCredentials uses the supplied credentials to return a
|
||||
// DNSProvider instance configured for namedotcom.
|
||||
func NewDNSProviderCredentials(username, apiToken, server string) (*DNSProvider, error) {
|
||||
if username == "" {
|
||||
return nil, fmt.Errorf("Name.com Username is required")
|
||||
}
|
||||
if apiToken == "" {
|
||||
return nil, fmt.Errorf("Name.com API token is required")
|
||||
}
|
||||
|
||||
client := namecom.New(username, apiToken)
|
||||
|
||||
if server != "" {
|
||||
client.Server = server
|
||||
}
|
||||
|
||||
return &DNSProvider{client: client}, nil
|
||||
}
|
||||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
request := &namecom.Record{
|
||||
DomainName: domain,
|
||||
Host: c.extractRecordName(fqdn, domain),
|
||||
Type: "TXT",
|
||||
TTL: uint32(ttl),
|
||||
Answer: value,
|
||||
}
|
||||
|
||||
_, err := c.client.CreateRecord(request)
|
||||
if err != nil {
|
||||
return fmt.Errorf("namedotcom API call failed: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
records, err := c.getRecords(domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rec := range records {
|
||||
if rec.Fqdn == fqdn && rec.Type == "TXT" {
|
||||
request := &namecom.DeleteRecordRequest{
|
||||
DomainName: domain,
|
||||
ID: rec.ID,
|
||||
}
|
||||
_, err := c.client.DeleteRecord(request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) {
|
||||
var (
|
||||
err error
|
||||
records []*namecom.Record
|
||||
response *namecom.ListRecordsResponse
|
||||
)
|
||||
|
||||
request := &namecom.ListRecordsRequest{
|
||||
DomainName: domain,
|
||||
Page: 1,
|
||||
}
|
||||
|
||||
for request.Page > 0 {
|
||||
response, err = c.client.ListRecords(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
records = append(records, response.Records...)
|
||||
request.Page = response.NextPage
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||
name := acmev2.UnFqdn(fqdn)
|
||||
if idx := strings.Index(name, "."+domain); idx != -1 {
|
||||
return name[:idx]
|
||||
}
|
||||
return name
|
||||
}
|
12
vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go
generated
vendored
12
vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go
generated
vendored
|
@ -8,12 +8,12 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
"gopkg.in/ns1/ns1-go.v2/rest"
|
||||
"gopkg.in/ns1/ns1-go.v2/rest/model/dns"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
|
||||
type DNSProvider struct {
|
||||
client *rest.Client
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ func NewDNSProviderCredentials(key string) (*DNSProvider, error) {
|
|||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
zone, err := c.getHostedZone(domain)
|
||||
if err != nil {
|
||||
|
@ -61,14 +61,14 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
zone, err := c.getHostedZone(domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name := acme.UnFqdn(fqdn)
|
||||
name := acmev2.UnFqdn(fqdn)
|
||||
_, err = c.client.Records.Delete(zone.Zone, name, "TXT")
|
||||
return err
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ func (c *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) {
|
|||
}
|
||||
|
||||
func (c *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record {
|
||||
name := acme.UnFqdn(fqdn)
|
||||
name := acmev2.UnFqdn(fqdn)
|
||||
|
||||
return &dns.Record{
|
||||
Type: "TXT",
|
||||
|
|
12
vendor/github.com/xenolf/lego/providers/dns/otc/otc.go
generated
vendored
12
vendor/github.com/xenolf/lego/providers/dns/otc/otc.go
generated
vendored
|
@ -12,10 +12,10 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface that uses
|
||||
// OTC's Managed DNS API to manage TXT records for a domain.
|
||||
type DNSProvider struct {
|
||||
identityEndpoint string
|
||||
|
@ -313,13 +313,13 @@ func (d *DNSProvider) deleteRecordSet(zoneID, recordID string) error {
|
|||
|
||||
// Present creates a TXT record using the specified parameters
|
||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
if ttl < 300 {
|
||||
ttl = 300 // 300 is otc minimum value for ttl
|
||||
}
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -362,9 +362,9 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
18
vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go
generated
vendored
18
vendor/github.com/xenolf/lego/providers/dns/ovh/ovh.go
generated
vendored
|
@ -9,13 +9,13 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/ovh/go-ovh/ovh"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// OVH API reference: https://eu.api.ovh.com/
|
||||
// Create a Token: https://eu.api.ovh.com/createToken/
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
|
||||
// that uses OVH's REST API to manage TXT records for a domain.
|
||||
type DNSProvider struct {
|
||||
client *ovh.Client
|
||||
|
@ -78,15 +78,15 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
Zone string `json:"zone"`
|
||||
}
|
||||
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
// Parse domain name
|
||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err)
|
||||
}
|
||||
|
||||
authZone = acme.UnFqdn(authZone)
|
||||
authZone = acmev2.UnFqdn(authZone)
|
||||
subDomain := d.extractRecordName(fqdn, authZone)
|
||||
|
||||
reqURL := fmt.Sprintf("/domain/zone/%s/record", authZone)
|
||||
|
@ -117,7 +117,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
// get the record's unique ID from when we created it
|
||||
d.recordIDsMu.Lock()
|
||||
|
@ -127,12 +127,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||
return fmt.Errorf("unknown record ID for '%s'", fqdn)
|
||||
}
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err)
|
||||
}
|
||||
|
||||
authZone = acme.UnFqdn(authZone)
|
||||
authZone = acmev2.UnFqdn(authZone)
|
||||
|
||||
reqURL := fmt.Sprintf("/domain/zone/%s/record/%d", authZone, recordID)
|
||||
|
||||
|
@ -151,7 +151,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||
}
|
||||
|
||||
func (d *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||
name := acme.UnFqdn(fqdn)
|
||||
name := acmev2.UnFqdn(fqdn)
|
||||
if idx := strings.Index(name, "."+domain); idx != -1 {
|
||||
return name[:idx]
|
||||
}
|
||||
|
|
16
vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go
generated
vendored
16
vendor/github.com/xenolf/lego/providers/dns/pdns/pdns.go
generated
vendored
|
@ -14,10 +14,10 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
apiKey string
|
||||
host *url.URL
|
||||
|
@ -65,7 +65,7 @@ func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
|||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
zone, err := c.getHostedZone(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -75,7 +75,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// pre-v1 API wants non-fqdn
|
||||
if c.apiVersion == 0 {
|
||||
name = acme.UnFqdn(fqdn)
|
||||
name = acmev2.UnFqdn(fqdn)
|
||||
}
|
||||
|
||||
rec := pdnsRecord{
|
||||
|
@ -117,7 +117,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
zone, err := c.getHostedZone(fqdn)
|
||||
if err != nil {
|
||||
|
@ -153,7 +153,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
|||
|
||||
func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) {
|
||||
var zone hostedZone
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) {
|
|||
|
||||
url = ""
|
||||
for _, zone := range zones {
|
||||
if acme.UnFqdn(zone.Name) == acme.UnFqdn(authZone) {
|
||||
if acmev2.UnFqdn(zone.Name) == acmev2.UnFqdn(authZone) {
|
||||
url = zone.URL
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) {
|
|||
}
|
||||
|
||||
for _, set := range zone.RRSets {
|
||||
if (set.Name == acme.UnFqdn(fqdn) || set.Name == fqdn) && set.Type == "TXT" {
|
||||
if (set.Name == acmev2.UnFqdn(fqdn) || set.Name == fqdn) && set.Type == "TXT" {
|
||||
return &set, nil
|
||||
}
|
||||
}
|
||||
|
|
16
vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go
generated
vendored
16
vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go
generated
vendored
|
@ -11,13 +11,13 @@ import (
|
|||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// rackspaceAPIURL represents the Identity API endpoint to call
|
||||
var rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens"
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
|
||||
// used to store the reusable token and DNS API endpoint
|
||||
type DNSProvider struct {
|
||||
token string
|
||||
|
@ -126,7 +126,7 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) {
|
|||
|
||||
// Present creates a TXT record to fulfil the dns-01 challenge
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
zoneID, err := c.getHostedZoneID(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -134,7 +134,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
rec := RackspaceRecords{
|
||||
RackspaceRecord: []RackspaceRecord{{
|
||||
Name: acme.UnFqdn(fqdn),
|
||||
Name: acmev2.UnFqdn(fqdn),
|
||||
Type: "TXT",
|
||||
Data: value,
|
||||
TTL: 300,
|
||||
|
@ -156,7 +156,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
zoneID, err := c.getHostedZoneID(fqdn)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -187,12 +187,12 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
|
|||
} `json:"domains"`
|
||||
}
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil)
|
||||
result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acmev2.UnFqdn(authZone)), nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -213,7 +213,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
|
|||
|
||||
// findTxtRecord searches a DNS zone for a TXT record with a specific name
|
||||
func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*RackspaceRecord, error) {
|
||||
result, err := c.makeRequest("GET", fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil)
|
||||
result, err := c.makeRequest("GET", fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acmev2.UnFqdn(fqdn)), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
10
vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go
generated
vendored
10
vendor/github.com/xenolf/lego/providers/dns/rfc2136/rfc2136.go
generated
vendored
|
@ -10,10 +10,10 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface that
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface that
|
||||
// uses dynamic DNS updates (RFC 2136) to create TXT records on a nameserver.
|
||||
type DNSProvider struct {
|
||||
nameserver string
|
||||
|
@ -93,19 +93,19 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
|||
|
||||
// Present creates a TXT record using the specified parameters
|
||||
func (r *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
return r.changeRecord("INSERT", fqdn, value, ttl)
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
return r.changeRecord("REMOVE", fqdn, value, ttl)
|
||||
}
|
||||
|
||||
func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
|
||||
// Find the zone for the given fqdn
|
||||
zone, err := acme.FindZoneByFqdn(fqdn, []string{r.nameserver})
|
||||
zone, err := acmev2.FindZoneByFqdn(fqdn, []string{r.nameserver})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
14
vendor/github.com/xenolf/lego/providers/dns/route53/route53.go
generated
vendored
14
vendor/github.com/xenolf/lego/providers/dns/route53/route53.go
generated
vendored
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/route53"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -22,7 +22,7 @@ const (
|
|||
route53TTL = 10
|
||||
)
|
||||
|
||||
// DNSProvider implements the acme.ChallengeProvider interface
|
||||
// DNSProvider implements the acmev2.ChallengeProvider interface
|
||||
type DNSProvider struct {
|
||||
client *route53.Route53
|
||||
hostedZoneID string
|
||||
|
@ -80,14 +80,14 @@ func NewDNSProvider() (*DNSProvider, error) {
|
|||
|
||||
// Present creates a TXT record using the specified parameters
|
||||
func (r *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
value = `"` + value + `"`
|
||||
return r.changeRecord("UPSERT", fqdn, value, route53TTL)
|
||||
}
|
||||
|
||||
// CleanUp removes the TXT record matching the specified parameters
|
||||
func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
value = `"` + value + `"`
|
||||
return r.changeRecord("DELETE", fqdn, value, route53TTL)
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
|
|||
|
||||
statusID := resp.ChangeInfo.Id
|
||||
|
||||
return acme.WaitFor(120*time.Second, 4*time.Second, func() (bool, error) {
|
||||
return acmev2.WaitFor(120*time.Second, 4*time.Second, func() (bool, error) {
|
||||
reqParams := &route53.GetChangeInput{
|
||||
Id: statusID,
|
||||
}
|
||||
|
@ -139,14 +139,14 @@ func (r *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
|||
return r.hostedZoneID, nil
|
||||
}
|
||||
|
||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// .DNSName should not have a trailing dot
|
||||
reqParams := &route53.ListHostedZonesByNameInput{
|
||||
DNSName: aws.String(acme.UnFqdn(authZone)),
|
||||
DNSName: aws.String(acmev2.UnFqdn(authZone)),
|
||||
}
|
||||
resp, err := r.client.ListHostedZonesByName(reqParams)
|
||||
if err != nil {
|
||||
|
|
10
vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go
generated
vendored
10
vendor/github.com/xenolf/lego/providers/dns/vultr/vultr.go
generated
vendored
|
@ -9,10 +9,10 @@ import (
|
|||
"strings"
|
||||
|
||||
vultr "github.com/JamesClonk/vultr/lib"
|
||||
"github.com/xenolf/lego/acme"
|
||||
"github.com/xenolf/lego/acmev2"
|
||||
)
|
||||
|
||||
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
|
||||
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
|
||||
type DNSProvider struct {
|
||||
client *vultr.Client
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) {
|
|||
|
||||
// Present creates a TXT record to fulfil the DNS-01 challenge.
|
||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
zoneDomain, err := c.getHostedZone(domain)
|
||||
if err != nil {
|
||||
|
@ -59,7 +59,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
|||
|
||||
// CleanUp removes the TXT record matching the specified parameters.
|
||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
|
||||
|
||||
zoneDomain, records, err := c.findTxtRecords(domain, fqdn)
|
||||
if err != nil {
|
||||
|
@ -119,7 +119,7 @@ func (c *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRe
|
|||
}
|
||||
|
||||
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||
name := acme.UnFqdn(fqdn)
|
||||
name := acmev2.UnFqdn(fqdn)
|
||||
if idx := strings.Index(name, "."+domain); idx != -1 {
|
||||
return name[:idx]
|
||||
}
|
||||
|
|
46
vendor/gopkg.in/mattes/go-expand-tilde.v1/tilde.go
generated
vendored
Normal file
46
vendor/gopkg.in/mattes/go-expand-tilde.v1/tilde.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
package tilde
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoHome = errors.New("no home found")
|
||||
)
|
||||
|
||||
func Expand(path string) (string, error) {
|
||||
if !strings.HasPrefix(path, "~") {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
home, err := Home()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return home + path[1:], nil
|
||||
}
|
||||
|
||||
func Home() (string, error) {
|
||||
home := ""
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
home = filepath.Join(os.Getenv("HomeDrive"), os.Getenv("HomePath"))
|
||||
if home == "" {
|
||||
home = os.Getenv("UserProfile")
|
||||
}
|
||||
|
||||
default:
|
||||
home = os.Getenv("HOME")
|
||||
}
|
||||
|
||||
if home == "" {
|
||||
return "", ErrNoHome
|
||||
}
|
||||
return home, nil
|
||||
}
|
520
vendor/gopkg.in/square/go-jose.v1/asymmetric.go
generated
vendored
520
vendor/gopkg.in/square/go-jose.v1/asymmetric.go
generated
vendored
|
@ -1,520 +0,0 @@
|
|||
/*-
|
||||
* Copyright 2014 Square Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package jose
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"gopkg.in/square/go-jose.v1/cipher"
|
||||
)
|
||||
|
||||
// A generic RSA-based encrypter/verifier
|
||||
type rsaEncrypterVerifier struct {
|
||||
publicKey *rsa.PublicKey
|
||||
}
|
||||
|
||||
// A generic RSA-based decrypter/signer
|
||||
type rsaDecrypterSigner struct {
|
||||
privateKey *rsa.PrivateKey
|
||||
}
|
||||
|
||||
// A generic EC-based encrypter/verifier
|
||||
type ecEncrypterVerifier struct {
|
||||
publicKey *ecdsa.PublicKey
|
||||
}
|
||||
|
||||
// A key generator for ECDH-ES
|
||||
type ecKeyGenerator struct {
|
||||
size int
|
||||
algID string
|
||||
publicKey *ecdsa.PublicKey
|
||||
}
|
||||
|
||||
// A generic EC-based decrypter/signer
|
||||
type ecDecrypterSigner struct {
|
||||
privateKey *ecdsa.PrivateKey
|
||||
}
|
||||
|
||||
// newRSARecipient creates recipientKeyInfo based on the given key.
|
||||
func newRSARecipient(keyAlg KeyAlgorithm, publicKey *rsa.PublicKey) (recipientKeyInfo, error) {
|
||||
// Verify that key management algorithm is supported by this encrypter
|
||||
switch keyAlg {
|
||||
case RSA1_5, RSA_OAEP, RSA_OAEP_256:
|
||||
default:
|
||||
return recipientKeyInfo{}, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
if publicKey == nil {
|
||||
return recipientKeyInfo{}, errors.New("invalid public key")
|
||||
}
|
||||
|
||||
return recipientKeyInfo{
|
||||
keyAlg: keyAlg,
|
||||
keyEncrypter: &rsaEncrypterVerifier{
|
||||
publicKey: publicKey,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// newRSASigner creates a recipientSigInfo based on the given key.
|
||||
func newRSASigner(sigAlg SignatureAlgorithm, privateKey *rsa.PrivateKey) (recipientSigInfo, error) {
|
||||
// Verify that key management algorithm is supported by this encrypter
|
||||
switch sigAlg {
|
||||
case RS256, RS384, RS512, PS256, PS384, PS512:
|
||||
default:
|
||||
return recipientSigInfo{}, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
if privateKey == nil {
|
||||
return recipientSigInfo{}, errors.New("invalid private key")
|
||||
}
|
||||
|
||||
return recipientSigInfo{
|
||||
sigAlg: sigAlg,
|
||||
publicKey: &JsonWebKey{
|
||||
Key: &privateKey.PublicKey,
|
||||
},
|
||||
signer: &rsaDecrypterSigner{
|
||||
privateKey: privateKey,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// newECDHRecipient creates recipientKeyInfo based on the given key.
|
||||
func newECDHRecipient(keyAlg KeyAlgorithm, publicKey *ecdsa.PublicKey) (recipientKeyInfo, error) {
|
||||
// Verify that key management algorithm is supported by this encrypter
|
||||
switch keyAlg {
|
||||
case ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW:
|
||||
default:
|
||||
return recipientKeyInfo{}, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
if publicKey == nil || !publicKey.Curve.IsOnCurve(publicKey.X, publicKey.Y) {
|
||||
return recipientKeyInfo{}, errors.New("invalid public key")
|
||||
}
|
||||
|
||||
return recipientKeyInfo{
|
||||
keyAlg: keyAlg,
|
||||
keyEncrypter: &ecEncrypterVerifier{
|
||||
publicKey: publicKey,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// newECDSASigner creates a recipientSigInfo based on the given key.
|
||||
func newECDSASigner(sigAlg SignatureAlgorithm, privateKey *ecdsa.PrivateKey) (recipientSigInfo, error) {
|
||||
// Verify that key management algorithm is supported by this encrypter
|
||||
switch sigAlg {
|
||||
case ES256, ES384, ES512:
|
||||
default:
|
||||
return recipientSigInfo{}, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
if privateKey == nil {
|
||||
return recipientSigInfo{}, errors.New("invalid private key")
|
||||
}
|
||||
|
||||
return recipientSigInfo{
|
||||
sigAlg: sigAlg,
|
||||
publicKey: &JsonWebKey{
|
||||
Key: &privateKey.PublicKey,
|
||||
},
|
||||
signer: &ecDecrypterSigner{
|
||||
privateKey: privateKey,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encrypt the given payload and update the object.
|
||||
func (ctx rsaEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) {
|
||||
encryptedKey, err := ctx.encrypt(cek, alg)
|
||||
if err != nil {
|
||||
return recipientInfo{}, err
|
||||
}
|
||||
|
||||
return recipientInfo{
|
||||
encryptedKey: encryptedKey,
|
||||
header: &rawHeader{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encrypt the given payload. Based on the key encryption algorithm,
|
||||
// this will either use RSA-PKCS1v1.5 or RSA-OAEP (with SHA-1 or SHA-256).
|
||||
func (ctx rsaEncrypterVerifier) encrypt(cek []byte, alg KeyAlgorithm) ([]byte, error) {
|
||||
switch alg {
|
||||
case RSA1_5:
|
||||
return rsa.EncryptPKCS1v15(randReader, ctx.publicKey, cek)
|
||||
case RSA_OAEP:
|
||||
return rsa.EncryptOAEP(sha1.New(), randReader, ctx.publicKey, cek, []byte{})
|
||||
case RSA_OAEP_256:
|
||||
return rsa.EncryptOAEP(sha256.New(), randReader, ctx.publicKey, cek, []byte{})
|
||||
}
|
||||
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
// Decrypt the given payload and return the content encryption key.
|
||||
func (ctx rsaDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
|
||||
return ctx.decrypt(recipient.encryptedKey, KeyAlgorithm(headers.Alg), generator)
|
||||
}
|
||||
|
||||
// Decrypt the given payload. Based on the key encryption algorithm,
|
||||
// this will either use RSA-PKCS1v1.5 or RSA-OAEP (with SHA-1 or SHA-256).
|
||||
func (ctx rsaDecrypterSigner) decrypt(jek []byte, alg KeyAlgorithm, generator keyGenerator) ([]byte, error) {
|
||||
// Note: The random reader on decrypt operations is only used for blinding,
|
||||
// so stubbing is meanlingless (hence the direct use of rand.Reader).
|
||||
switch alg {
|
||||
case RSA1_5:
|
||||
defer func() {
|
||||
// DecryptPKCS1v15SessionKey sometimes panics on an invalid payload
|
||||
// because of an index out of bounds error, which we want to ignore.
|
||||
// This has been fixed in Go 1.3.1 (released 2014/08/13), the recover()
|
||||
// only exists for preventing crashes with unpatched versions.
|
||||
// See: https://groups.google.com/forum/#!topic/golang-dev/7ihX6Y6kx9k
|
||||
// See: https://code.google.com/p/go/source/detail?r=58ee390ff31602edb66af41ed10901ec95904d33
|
||||
_ = recover()
|
||||
}()
|
||||
|
||||
// Perform some input validation.
|
||||
keyBytes := ctx.privateKey.PublicKey.N.BitLen() / 8
|
||||
if keyBytes != len(jek) {
|
||||
// Input size is incorrect, the encrypted payload should always match
|
||||
// the size of the public modulus (e.g. using a 2048 bit key will
|
||||
// produce 256 bytes of output). Reject this since it's invalid input.
|
||||
return nil, ErrCryptoFailure
|
||||
}
|
||||
|
||||
cek, _, err := generator.genKey()
|
||||
if err != nil {
|
||||
return nil, ErrCryptoFailure
|
||||
}
|
||||
|
||||
// When decrypting an RSA-PKCS1v1.5 payload, we must take precautions to
|
||||
// prevent chosen-ciphertext attacks as described in RFC 3218, "Preventing
|
||||
// the Million Message Attack on Cryptographic Message Syntax". We are
|
||||
// therefore deliberately ignoring errors here.
|
||||
_ = rsa.DecryptPKCS1v15SessionKey(rand.Reader, ctx.privateKey, jek, cek)
|
||||
|
||||
return cek, nil
|
||||
case RSA_OAEP:
|
||||
// Use rand.Reader for RSA blinding
|
||||
return rsa.DecryptOAEP(sha1.New(), rand.Reader, ctx.privateKey, jek, []byte{})
|
||||
case RSA_OAEP_256:
|
||||
// Use rand.Reader for RSA blinding
|
||||
return rsa.DecryptOAEP(sha256.New(), rand.Reader, ctx.privateKey, jek, []byte{})
|
||||
}
|
||||
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
// Sign the given payload
|
||||
func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
|
||||
var hash crypto.Hash
|
||||
|
||||
switch alg {
|
||||
case RS256, PS256:
|
||||
hash = crypto.SHA256
|
||||
case RS384, PS384:
|
||||
hash = crypto.SHA384
|
||||
case RS512, PS512:
|
||||
hash = crypto.SHA512
|
||||
default:
|
||||
return Signature{}, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
hasher := hash.New()
|
||||
|
||||
// According to documentation, Write() on hash never fails
|
||||
_, _ = hasher.Write(payload)
|
||||
hashed := hasher.Sum(nil)
|
||||
|
||||
var out []byte
|
||||
var err error
|
||||
|
||||
switch alg {
|
||||
case RS256, RS384, RS512:
|
||||
out, err = rsa.SignPKCS1v15(randReader, ctx.privateKey, hash, hashed)
|
||||
case PS256, PS384, PS512:
|
||||
out, err = rsa.SignPSS(randReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthAuto,
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return Signature{}, err
|
||||
}
|
||||
|
||||
return Signature{
|
||||
Signature: out,
|
||||
protected: &rawHeader{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Verify the given payload
|
||||
func (ctx rsaEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
|
||||
var hash crypto.Hash
|
||||
|
||||
switch alg {
|
||||
case RS256, PS256:
|
||||
hash = crypto.SHA256
|
||||
case RS384, PS384:
|
||||
hash = crypto.SHA384
|
||||
case RS512, PS512:
|
||||
hash = crypto.SHA512
|
||||
default:
|
||||
return ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
hasher := hash.New()
|
||||
|
||||
// According to documentation, Write() on hash never fails
|
||||
_, _ = hasher.Write(payload)
|
||||
hashed := hasher.Sum(nil)
|
||||
|
||||
switch alg {
|
||||
case RS256, RS384, RS512:
|
||||
return rsa.VerifyPKCS1v15(ctx.publicKey, hash, hashed, signature)
|
||||
case PS256, PS384, PS512:
|
||||
return rsa.VerifyPSS(ctx.publicKey, hash, hashed, signature, nil)
|
||||
}
|
||||
|
||||
return ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
// Encrypt the given payload and update the object.
|
||||
func (ctx ecEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) {
|
||||
switch alg {
|
||||
case ECDH_ES:
|
||||
// ECDH-ES mode doesn't wrap a key, the shared secret is used directly as the key.
|
||||
return recipientInfo{
|
||||
header: &rawHeader{},
|
||||
}, nil
|
||||
case ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW:
|
||||
default:
|
||||
return recipientInfo{}, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
generator := ecKeyGenerator{
|
||||
algID: string(alg),
|
||||
publicKey: ctx.publicKey,
|
||||
}
|
||||
|
||||
switch alg {
|
||||
case ECDH_ES_A128KW:
|
||||
generator.size = 16
|
||||
case ECDH_ES_A192KW:
|
||||
generator.size = 24
|
||||
case ECDH_ES_A256KW:
|
||||
generator.size = 32
|
||||
}
|
||||
|
||||
kek, header, err := generator.genKey()
|
||||
if err != nil {
|
||||
return recipientInfo{}, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(kek)
|
||||
if err != nil {
|
||||
return recipientInfo{}, err
|
||||
}
|
||||
|
||||
jek, err := josecipher.KeyWrap(block, cek)
|
||||
if err != nil {
|
||||
return recipientInfo{}, err
|
||||
}
|
||||
|
||||
return recipientInfo{
|
||||
encryptedKey: jek,
|
||||
header: &header,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get key size for EC key generator
|
||||
func (ctx ecKeyGenerator) keySize() int {
|
||||
return ctx.size
|
||||
}
|
||||
|
||||
// Get a content encryption key for ECDH-ES
|
||||
func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) {
|
||||
priv, err := ecdsa.GenerateKey(ctx.publicKey.Curve, randReader)
|
||||
if err != nil {
|
||||
return nil, rawHeader{}, err
|
||||
}
|
||||
|
||||
out := josecipher.DeriveECDHES(ctx.algID, []byte{}, []byte{}, priv, ctx.publicKey, ctx.size)
|
||||
|
||||
headers := rawHeader{
|
||||
Epk: &JsonWebKey{
|
||||
Key: &priv.PublicKey,
|
||||
},
|
||||
}
|
||||
|
||||
return out, headers, nil
|
||||
}
|
||||
|
||||
// Decrypt the given payload and return the content encryption key.
|
||||
func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
|
||||
if headers.Epk == nil {
|
||||
return nil, errors.New("square/go-jose: missing epk header")
|
||||
}
|
||||
|
||||
publicKey, ok := headers.Epk.Key.(*ecdsa.PublicKey)
|
||||
if publicKey == nil || !ok {
|
||||
return nil, errors.New("square/go-jose: invalid epk header")
|
||||
}
|
||||
|
||||
if !ctx.privateKey.Curve.IsOnCurve(publicKey.X, publicKey.Y) {
|
||||
return nil, errors.New("square/go-jose: invalid public key in epk header")
|
||||
}
|
||||
|
||||
apuData := headers.Apu.bytes()
|
||||
apvData := headers.Apv.bytes()
|
||||
|
||||
deriveKey := func(algID string, size int) []byte {
|
||||
return josecipher.DeriveECDHES(algID, apuData, apvData, ctx.privateKey, publicKey, size)
|
||||
}
|
||||
|
||||
var keySize int
|
||||
|
||||
switch KeyAlgorithm(headers.Alg) {
|
||||
case ECDH_ES:
|
||||
// ECDH-ES uses direct key agreement, no key unwrapping necessary.
|
||||
return deriveKey(string(headers.Enc), generator.keySize()), nil
|
||||
case ECDH_ES_A128KW:
|
||||
keySize = 16
|
||||
case ECDH_ES_A192KW:
|
||||
keySize = 24
|
||||
case ECDH_ES_A256KW:
|
||||
keySize = 32
|
||||
default:
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
key := deriveKey(headers.Alg, keySize)
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return josecipher.KeyUnwrap(block, recipient.encryptedKey)
|
||||
}
|
||||
|
||||
// Sign the given payload
|
||||
func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
|
||||
var expectedBitSize int
|
||||
var hash crypto.Hash
|
||||
|
||||
switch alg {
|
||||
case ES256:
|
||||
expectedBitSize = 256
|
||||
hash = crypto.SHA256
|
||||
case ES384:
|
||||
expectedBitSize = 384
|
||||
hash = crypto.SHA384
|
||||
case ES512:
|
||||
expectedBitSize = 521
|
||||
hash = crypto.SHA512
|
||||
}
|
||||
|
||||
curveBits := ctx.privateKey.Curve.Params().BitSize
|
||||
if expectedBitSize != curveBits {
|
||||
return Signature{}, fmt.Errorf("square/go-jose: expected %d bit key, got %d bits instead", expectedBitSize, curveBits)
|
||||
}
|
||||
|
||||
hasher := hash.New()
|
||||
|
||||
// According to documentation, Write() on hash never fails
|
||||
_, _ = hasher.Write(payload)
|
||||
hashed := hasher.Sum(nil)
|
||||
|
||||
r, s, err := ecdsa.Sign(randReader, ctx.privateKey, hashed)
|
||||
if err != nil {
|
||||
return Signature{}, err
|
||||
}
|
||||
|
||||
keyBytes := curveBits / 8
|
||||
if curveBits%8 > 0 {
|
||||
keyBytes += 1
|
||||
}
|
||||
|
||||
// We serialize the outpus (r and s) into big-endian byte arrays and pad
|
||||
// them with zeros on the left to make sure the sizes work out. Both arrays
|
||||
// must be keyBytes long, and the output must be 2*keyBytes long.
|
||||
rBytes := r.Bytes()
|
||||
rBytesPadded := make([]byte, keyBytes)
|
||||
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
||||
|
||||
sBytes := s.Bytes()
|
||||
sBytesPadded := make([]byte, keyBytes)
|
||||
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
||||
|
||||
out := append(rBytesPadded, sBytesPadded...)
|
||||
|
||||
return Signature{
|
||||
Signature: out,
|
||||
protected: &rawHeader{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Verify the given payload
|
||||
func (ctx ecEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
|
||||
var keySize int
|
||||
var hash crypto.Hash
|
||||
|
||||
switch alg {
|
||||
case ES256:
|
||||
keySize = 32
|
||||
hash = crypto.SHA256
|
||||
case ES384:
|
||||
keySize = 48
|
||||
hash = crypto.SHA384
|
||||
case ES512:
|
||||
keySize = 66
|
||||
hash = crypto.SHA512
|
||||
default:
|
||||
return ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
if len(signature) != 2*keySize {
|
||||
return fmt.Errorf("square/go-jose: invalid signature size, have %d bytes, wanted %d", len(signature), 2*keySize)
|
||||
}
|
||||
|
||||
hasher := hash.New()
|
||||
|
||||
// According to documentation, Write() on hash never fails
|
||||
_, _ = hasher.Write(payload)
|
||||
hashed := hasher.Sum(nil)
|
||||
|
||||
r := big.NewInt(0).SetBytes(signature[:keySize])
|
||||
s := big.NewInt(0).SetBytes(signature[keySize:])
|
||||
|
||||
match := ecdsa.Verify(ctx.publicKey, hashed, r, s)
|
||||
if !match {
|
||||
return errors.New("square/go-jose: ecdsa signature failed to verify")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
196
vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac.go
generated
vendored
196
vendor/gopkg.in/square/go-jose.v1/cipher/cbc_hmac.go
generated
vendored
|
@ -1,196 +0,0 @@
|
|||
/*-
|
||||
* Copyright 2014 Square Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package josecipher
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash"
|
||||
)
|
||||
|
||||
const (
|
||||
nonceBytes = 16
|
||||
)
|
||||
|
||||
// NewCBCHMAC instantiates a new AEAD based on CBC+HMAC.
|
||||
func NewCBCHMAC(key []byte, newBlockCipher func([]byte) (cipher.Block, error)) (cipher.AEAD, error) {
|
||||
keySize := len(key) / 2
|
||||
integrityKey := key[:keySize]
|
||||
encryptionKey := key[keySize:]
|
||||
|
||||
blockCipher, err := newBlockCipher(encryptionKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var hash func() hash.Hash
|
||||
switch keySize {
|
||||
case 16:
|
||||
hash = sha256.New
|
||||
case 24:
|
||||
hash = sha512.New384
|
||||
case 32:
|
||||
hash = sha512.New
|
||||
}
|
||||
|
||||
return &cbcAEAD{
|
||||
hash: hash,
|
||||
blockCipher: blockCipher,
|
||||
authtagBytes: keySize,
|
||||
integrityKey: integrityKey,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// An AEAD based on CBC+HMAC
|
||||
type cbcAEAD struct {
|
||||
hash func() hash.Hash
|
||||
authtagBytes int
|
||||
integrityKey []byte
|
||||
blockCipher cipher.Block
|
||||
}
|
||||
|
||||
func (ctx *cbcAEAD) NonceSize() int {
|
||||
return nonceBytes
|
||||
}
|
||||
|
||||
func (ctx *cbcAEAD) Overhead() int {
|
||||
// Maximum overhead is block size (for padding) plus auth tag length, where
|
||||
// the length of the auth tag is equivalent to the key size.
|
||||
return ctx.blockCipher.BlockSize() + ctx.authtagBytes
|
||||
}
|
||||
|
||||
// Seal encrypts and authenticates the plaintext.
|
||||
func (ctx *cbcAEAD) Seal(dst, nonce, plaintext, data []byte) []byte {
|
||||
// Output buffer -- must take care not to mangle plaintext input.
|
||||
ciphertext := make([]byte, uint64(len(plaintext))+uint64(ctx.Overhead()))[:len(plaintext)]
|
||||
copy(ciphertext, plaintext)
|
||||
ciphertext = padBuffer(ciphertext, ctx.blockCipher.BlockSize())
|
||||
|
||||
cbc := cipher.NewCBCEncrypter(ctx.blockCipher, nonce)
|
||||
|
||||
cbc.CryptBlocks(ciphertext, ciphertext)
|
||||
authtag := ctx.computeAuthTag(data, nonce, ciphertext)
|
||||
|
||||
ret, out := resize(dst, uint64(len(dst))+uint64(len(ciphertext))+uint64(len(authtag)))
|
||||
copy(out, ciphertext)
|
||||
copy(out[len(ciphertext):], authtag)
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Open decrypts and authenticates the ciphertext.
|
||||
func (ctx *cbcAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
|
||||
if len(ciphertext) < ctx.authtagBytes {
|
||||
return nil, errors.New("square/go-jose: invalid ciphertext (too short)")
|
||||
}
|
||||
|
||||
offset := len(ciphertext) - ctx.authtagBytes
|
||||
expectedTag := ctx.computeAuthTag(data, nonce, ciphertext[:offset])
|
||||
match := subtle.ConstantTimeCompare(expectedTag, ciphertext[offset:])
|
||||
if match != 1 {
|
||||
return nil, errors.New("square/go-jose: invalid ciphertext (auth tag mismatch)")
|
||||
}
|
||||
|
||||
cbc := cipher.NewCBCDecrypter(ctx.blockCipher, nonce)
|
||||
|
||||
// Make copy of ciphertext buffer, don't want to modify in place
|
||||
buffer := append([]byte{}, []byte(ciphertext[:offset])...)
|
||||
|
||||
if len(buffer)%ctx.blockCipher.BlockSize() > 0 {
|
||||
return nil, errors.New("square/go-jose: invalid ciphertext (invalid length)")
|
||||
}
|
||||
|
||||
cbc.CryptBlocks(buffer, buffer)
|
||||
|
||||
// Remove padding
|
||||
plaintext, err := unpadBuffer(buffer, ctx.blockCipher.BlockSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, out := resize(dst, uint64(len(dst))+uint64(len(plaintext)))
|
||||
copy(out, plaintext)
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Compute an authentication tag
|
||||
func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte {
|
||||
buffer := make([]byte, uint64(len(aad))+uint64(len(nonce))+uint64(len(ciphertext))+8)
|
||||
n := 0
|
||||
n += copy(buffer, aad)
|
||||
n += copy(buffer[n:], nonce)
|
||||
n += copy(buffer[n:], ciphertext)
|
||||
binary.BigEndian.PutUint64(buffer[n:], uint64(len(aad))*8)
|
||||
|
||||
// According to documentation, Write() on hash.Hash never fails.
|
||||
hmac := hmac.New(ctx.hash, ctx.integrityKey)
|
||||
_, _ = hmac.Write(buffer)
|
||||
|
||||
return hmac.Sum(nil)[:ctx.authtagBytes]
|
||||
}
|
||||
|
||||
// resize ensures the the given slice has a capacity of at least n bytes.
|
||||
// If the capacity of the slice is less than n, a new slice is allocated
|
||||
// and the existing data will be copied.
|
||||
func resize(in []byte, n uint64) (head, tail []byte) {
|
||||
if uint64(cap(in)) >= n {
|
||||
head = in[:n]
|
||||
} else {
|
||||
head = make([]byte, n)
|
||||
copy(head, in)
|
||||
}
|
||||
|
||||
tail = head[len(in):]
|
||||
return
|
||||
}
|
||||
|
||||
// Apply padding
|
||||
func padBuffer(buffer []byte, blockSize int) []byte {
|
||||
missing := blockSize - (len(buffer) % blockSize)
|
||||
ret, out := resize(buffer, uint64(len(buffer))+uint64(missing))
|
||||
padding := bytes.Repeat([]byte{byte(missing)}, missing)
|
||||
copy(out, padding)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Remove padding
|
||||
func unpadBuffer(buffer []byte, blockSize int) ([]byte, error) {
|
||||
if len(buffer)%blockSize != 0 {
|
||||
return nil, errors.New("square/go-jose: invalid padding")
|
||||
}
|
||||
|
||||
last := buffer[len(buffer)-1]
|
||||
count := int(last)
|
||||
|
||||
if count == 0 || count > blockSize || count > len(buffer) {
|
||||
return nil, errors.New("square/go-jose: invalid padding")
|
||||
}
|
||||
|
||||
padding := bytes.Repeat([]byte{last}, count)
|
||||
if !bytes.HasSuffix(buffer, padding) {
|
||||
return nil, errors.New("square/go-jose: invalid padding")
|
||||
}
|
||||
|
||||
return buffer[:len(buffer)-count], nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue