Этот коммит содержится в:
Christopher Speller
2016-11-16 19:28:52 -05:00
коммит произвёл GitHub
родитель 0b296dd8c2
Коммит 0135904f7d
445 изменённых файлов: 38624 добавлений и 14161 удалений

4
vendor/github.com/xenolf/lego/.travis.yml сгенерированный поставляемый
Просмотреть файл

@@ -3,6 +3,10 @@ go:
- 1.6.3
- 1.7
- tip
services:
- memcached
env:
- MEMCACHED_HOSTS=localhost:11211
install:
- go get -t ./...
script:

2
vendor/github.com/xenolf/lego/Dockerfile сгенерированный поставляемый
Просмотреть файл

@@ -7,7 +7,7 @@ RUN apk update && apk add ca-certificates go git && \
go get -u github.com/xenolf/lego && \
cd /go/src/github.com/xenolf/lego && \
go build -o /usr/bin/lego . && \
apk del ca-certificates go git && \
apk del go git && \
rm -rf /var/cache/apk/* && \
rm -rf /go

65
vendor/github.com/xenolf/lego/acme/client.go сгенерированный поставляемый
Просмотреть файл

@@ -97,7 +97,7 @@ func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) {
return &Client{directory: dir, user: user, jws: jws, keyType: keyType, solvers: solvers}, nil
}
// SetChallengeProvider specifies a custom provider that will make the solution available
// 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:
@@ -115,6 +115,9 @@ func (c *Client) SetChallengeProvider(challenge Challenge, p ChallengeProvider)
// 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 {
@@ -131,6 +134,9 @@ func (c *Client) SetHTTPAddress(iface string) error {
// 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 {
@@ -347,7 +353,7 @@ DNSNames:
// 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) (CertificateResource, map[string]error) {
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 {
@@ -368,7 +374,7 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
cert, err := c.requestCertificate(challenges, bundle, privKey)
cert, err := c.requestCertificate(challenges, bundle, privKey, mustStaple)
if err != nil {
for _, chln := range challenges {
failures[chln.Domain] = err
@@ -404,7 +410,7 @@ func (c *Client) RevokeCertificate(certificate []byte) error {
// 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 bool) (CertificateResource, error) {
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)
@@ -421,50 +427,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (Certif
timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
logf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours()))
// The first step of renewal is to check if we get a renewed cert
// directly from the cert URL.
resp, err := httpGet(cert.CertURL)
if err != nil {
return CertificateResource{}, err
}
defer resp.Body.Close()
serverCertBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return CertificateResource{}, err
}
serverCert, err := x509.ParseCertificate(serverCertBytes)
if err != nil {
return CertificateResource{}, err
}
// If the server responds with a different certificate we are effectively renewed.
// TODO: Further test if we can actually use the new certificate (Our private key works)
if !x509Cert.Equal(serverCert) {
logf("[INFO][%s] acme: Server responded with renewed certificate", cert.Domain)
issuedCert := pemEncode(derCertificateBytes(serverCertBytes))
// If bundle is true, we want to return a certificate bundle.
// To do this, we need the issuer certificate.
if bundle {
// 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("[ERROR][%s] acme: Could not bundle issuer certificate: %v", cert.Domain, err)
} else {
// Success - append the issuer cert to the issued cert.
issuerCert = pemEncode(derCertificateBytes(issuerCert))
issuedCert = append(issuedCert, issuerCert...)
}
}
cert.Certificate = issuedCert
return cert, nil
}
// If the certificate is the same, then we need to request a new certificate.
// 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 {
@@ -499,7 +462,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle bool) (Certif
domains = append(domains, x509Cert.Subject.CommonName)
}
newCert, failures := c.ObtainCertificate(domains, bundle, privKey)
newCert, failures := c.ObtainCertificate(domains, bundle, privKey, mustStaple)
return newCert, failures[cert.Domain]
}
@@ -600,7 +563,7 @@ func (c *Client) getChallenges(domains []string) ([]authorizationResource, map[s
return challenges, failures
}
func (c *Client) requestCertificate(authz []authorizationResource, bundle bool, privKey crypto.PrivateKey) (CertificateResource, error) {
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!")
}
@@ -621,7 +584,7 @@ func (c *Client) requestCertificate(authz []authorizationResource, bundle bool,
}
// TODO: should the CSR be customizable?
csr, err := generateCsr(privKey, commonName.Domain, san)
csr, err := generateCsr(privKey, commonName.Domain, san, mustStaple)
if err != nil {
return CertificateResource{}, err
}

17
vendor/github.com/xenolf/lego/acme/crypto.go сгенерированный поставляемый
Просмотреть файл

@@ -20,6 +20,8 @@ import (
"strings"
"time"
"encoding/asn1"
"golang.org/x/crypto/ocsp"
)
@@ -47,6 +49,12 @@ const (
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
@@ -206,7 +214,7 @@ func generatePrivateKey(keyType KeyType) (crypto.PrivateKey, error) {
return nil, fmt.Errorf("Invalid KeyType: %s", keyType)
}
func generateCsr(privateKey crypto.PrivateKey, domain string, san []string) ([]byte, error) {
func generateCsr(privateKey crypto.PrivateKey, domain string, san []string, mustStaple bool) ([]byte, error) {
template := x509.CertificateRequest{
Subject: pkix.Name{
CommonName: domain,
@@ -217,6 +225,13 @@ func generateCsr(privateKey crypto.PrivateKey, domain string, san []string) ([]b
template.DNSNames = san
}
if mustStaple {
template.Extensions = append(template.Extensions, pkix.Extension{
Id: tlsFeatureExtensionOID,
Value: ocspMustStapleFeature,
})
}
return x509.CreateCertificateRequest(rand.Reader, &template, privateKey)
}

2
vendor/github.com/xenolf/lego/acme/crypto_test.go сгенерированный поставляемый
Просмотреть файл

@@ -24,7 +24,7 @@ func TestGenerateCSR(t *testing.T) {
t.Fatal("Error generating private key:", err)
}
csr, err := generateCsr(key, "fizz.buzz", nil)
csr, err := generateCsr(key, "fizz.buzz", nil, true)
if err != nil {
t.Error("Error generating CSR:", err)
}

27
vendor/github.com/xenolf/lego/acme/dns_challenge.go сгенерированный поставляемый
Просмотреть файл

@@ -23,14 +23,37 @@ var (
fqdnToZone = map[string]string{}
)
var RecursiveNameservers = []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))
@@ -75,7 +98,7 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
fqdn, value, _ := DNS01Record(domain, keyAuth)
logf("[INFO][%s] Checking DNS record propagation...", domain)
logf("[INFO][%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers)
var timeout, interval time.Duration
switch provider := s.provider.(type) {

21
vendor/github.com/xenolf/lego/acme/dns_challenge_test.go сгенерированный поставляемый
Просмотреть файл

@@ -85,6 +85,15 @@ var checkAuthoritativeNssTestsErr = []struct {
},
}
var checkResolvConfServersTests = []struct {
fixture string
expected []string
defaults []string
}{
{"testdata/resolv.conf.1", []string{"10.200.3.249:53", "10.200.3.250:5353", "[2001:4860:4860::8844]:53", "[10.0.0.1]:5353"}, []string{"127.0.0.1:53"}},
{"testdata/resolv.conf.nonexistant", []string{"127.0.0.1:53"}, []string{"127.0.0.1:53"}},
}
func TestDNSValidServerResponse(t *testing.T) {
PreCheckDNS = func(fqdn, value string) (bool, error) {
return true, nil
@@ -183,3 +192,15 @@ func TestCheckAuthoritativeNssErr(t *testing.T) {
}
}
}
func TestResolveConfServers(t *testing.T) {
for _, tt := range checkResolvConfServersTests {
result := getNameservers(tt.fixture, tt.defaults)
sort.Strings(result)
sort.Strings(tt.expected)
if !reflect.DeepEqual(result, tt.expected) {
t.Errorf("#%s: expected %q; got %q", tt.fixture, tt.expected, result)
}
}
}

2
vendor/github.com/xenolf/lego/acme/http_challenge_server.go сгенерированный поставляемый
Просмотреть файл

@@ -63,7 +63,7 @@ func (s *HTTPProviderServer) serve(domain, token, keyAuth string) {
w.Write([]byte(keyAuth))
logf("[INFO][%s] Served key authentication", domain)
} else {
logf("[INFO] Received request for domain %s with method %s", r.Host, r.Method)
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"))
}
})

2
vendor/github.com/xenolf/lego/acme/http_challenge_test.go сгенерированный поставляемый
Просмотреть файл

@@ -51,7 +51,7 @@ func TestHTTPChallengeInvalidPort(t *testing.T) {
if err := solver.Solve(clientChallenge, "localhost:123456"); err == nil {
t.Errorf("Solve error: got %v, want error", err)
} else if want := "invalid port 123456"; !strings.HasSuffix(err.Error(), want) {
} else if want, want18 := "invalid port 123456", "123456: invalid port"; !strings.HasSuffix(err.Error(), want) && !strings.HasSuffix(err.Error(), want18) {
t.Errorf("Solve error: got %q, want suffix %q", err.Error(), want)
}
}

8
vendor/github.com/xenolf/lego/acme/messages.go сгенерированный поставляемый
Просмотреть файл

@@ -13,17 +13,10 @@ type directory struct {
RevokeCertURL string `json:"revoke-cert"`
}
type recoveryKeyMessage struct {
Length int `json:"length,omitempty"`
Client jose.JsonWebKey `json:"client,omitempty"`
Server jose.JsonWebKey `json:"client,omitempty"`
}
type registrationMessage struct {
Resource string `json:"resource"`
Contact []string `json:"contact"`
Delete bool `json:"delete,omitempty"`
// RecoveryKey recoveryKeyMessage `json:"recoveryKey,omitempty"`
}
// Registration is returned by the ACME server after the registration
@@ -36,7 +29,6 @@ type Registration struct {
Agreement string `json:"agreement,omitempty"`
Authorizations string `json:"authorizations,omitempty"`
Certificates string `json:"certificates,omitempty"`
// RecoveryKey recoveryKeyMessage `json:"recoveryKey,omitempty"`
}
// RegistrationResource represents all important informations about a registration

5
vendor/github.com/xenolf/lego/acme/testdata/resolv.conf.1 сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,5 @@
domain company.com
nameserver 10.200.3.249
nameserver 10.200.3.250:5353
nameserver 2001:4860:4860::8844
nameserver [10.0.0.1]:5353

2
vendor/github.com/xenolf/lego/acme/tls_sni_challenge_test.go сгенерированный поставляемый
Просмотреть файл

@@ -59,7 +59,7 @@ func TestTLSSNIChallengeInvalidPort(t *testing.T) {
if err := solver.Solve(clientChallenge, "localhost:123456"); err == nil {
t.Errorf("Solve error: got %v, want error", err)
} else if want := "invalid port 123456"; !strings.HasSuffix(err.Error(), want) {
} else if want, want18 := "invalid port 123456", "123456: invalid port"; !strings.HasSuffix(err.Error(), want) && !strings.HasSuffix(err.Error(), want18) {
t.Errorf("Solve error: got %q, want suffix %q", err.Error(), want)
}
}

17
vendor/github.com/xenolf/lego/cli.go сгенерированный поставляемый
Просмотреть файл

@@ -64,6 +64,10 @@ func main() {
Name: "no-bundle",
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
},
cli.BoolFlag{
Name: "must-staple",
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.",
},
},
},
{
@@ -89,6 +93,10 @@ func main() {
Name: "no-bundle",
Usage: "Do not create a certificate bundle by adding the issuers certificate to the new certificate.",
},
cli.BoolFlag{
Name: "must-staple",
Usage: "Include the OCSP must staple TLS extension in the CSR and generated certificate. Only works if the CSR is generated by lego.",
},
},
},
{
@@ -138,6 +146,10 @@ func main() {
Name: "webroot",
Usage: "Set the webroot folder to use for HTTP based challenges to write directly in a file in .well-known/acme-challenge",
},
cli.StringSliceFlag{
Name: "memcached-host",
Usage: "Set the memcached host(s) to use for HTTP based challenges. Challenges will be written to all specified hosts.",
},
cli.StringFlag{
Name: "http",
Usage: "Set the port and interface to use for HTTP based challenges to listen on. Supported: interface:port or :port",
@@ -189,21 +201,26 @@ Here is an example bash command using the CloudFlare DNS provider:
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
fmt.Fprintln(w, "Valid providers and their associated credential environment variables:")
fmt.Fprintln(w)
fmt.Fprintln(w, "\tazure:\tAZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID, AZURE_RESROUCE_GROUP")
fmt.Fprintln(w, "\tauroradns:\tAURORA_USER_ID, AURORA_KEY, AURORA_ENDPOINT")
fmt.Fprintln(w, "\tcloudflare:\tCLOUDFLARE_EMAIL, CLOUDFLARE_API_KEY")
fmt.Fprintln(w, "\tdigitalocean:\tDO_AUTH_TOKEN")
fmt.Fprintln(w, "\tdnsimple:\tDNSIMPLE_EMAIL, DNSIMPLE_API_KEY")
fmt.Fprintln(w, "\tdnsmadeeasy:\tDNSMADEEASY_API_KEY, DNSMADEEASY_API_SECRET")
fmt.Fprintln(w, "\texoscale:\tEXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT")
fmt.Fprintln(w, "\tgandi:\tGANDI_API_KEY")
fmt.Fprintln(w, "\tgcloud:\tGCE_PROJECT")
fmt.Fprintln(w, "\tlinode:\tLINODE_API_KEY")
fmt.Fprintln(w, "\tmanual:\tnone")
fmt.Fprintln(w, "\tnamecheap:\tNAMECHEAP_API_USER, NAMECHEAP_API_KEY")
fmt.Fprintln(w, "\trackspace:\tRACKSPACE_USER, RACKSPACE_API_KEY")
fmt.Fprintln(w, "\trfc2136:\tRFC2136_TSIG_KEY, RFC2136_TSIG_SECRET,\n\t\tRFC2136_TSIG_ALGORITHM, RFC2136_NAMESERVER")
fmt.Fprintln(w, "\troute53:\tAWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION")
fmt.Fprintln(w, "\tdyn:\tDYN_CUSTOMER_NAME, DYN_USER_NAME, DYN_PASSWORD")
fmt.Fprintln(w, "\tvultr:\tVULTR_API_KEY")
fmt.Fprintln(w, "\tovh:\tOVH_ENDPOINT, OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET, OVH_CONSUMER_KEY")
fmt.Fprintln(w, "\tpdns:\tPDNS_API_KEY, PDNS_API_URL")
fmt.Fprintln(w, "\tdnspod:\tDNSPOD_API_KEY")
w.Flush()
fmt.Println(`

35
vendor/github.com/xenolf/lego/cli_handlers.go сгенерированный поставляемый
Просмотреть файл

@@ -15,20 +15,27 @@ import (
"github.com/urfave/cli"
"github.com/xenolf/lego/acme"
"github.com/xenolf/lego/providers/dns/auroradns"
"github.com/xenolf/lego/providers/dns/azure"
"github.com/xenolf/lego/providers/dns/cloudflare"
"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/dyn"
"github.com/xenolf/lego/providers/dns/exoscale"
"github.com/xenolf/lego/providers/dns/gandi"
"github.com/xenolf/lego/providers/dns/googlecloud"
"github.com/xenolf/lego/providers/dns/linode"
"github.com/xenolf/lego/providers/dns/namecheap"
"github.com/xenolf/lego/providers/dns/ns1"
"github.com/xenolf/lego/providers/dns/ovh"
"github.com/xenolf/lego/providers/dns/pdns"
"github.com/xenolf/lego/providers/dns/rackspace"
"github.com/xenolf/lego/providers/dns/rfc2136"
"github.com/xenolf/lego/providers/dns/route53"
"github.com/xenolf/lego/providers/dns/vultr"
"github.com/xenolf/lego/providers/http/memcached"
"github.com/xenolf/lego/providers/http/webroot"
)
@@ -99,6 +106,18 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) {
// infer that the user also wants to exclude all other challenges
client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01})
}
if c.GlobalIsSet("memcached-host") {
provider, err := memcached.NewMemcachedProvider(c.GlobalStringSlice("memcached-host"))
if err != nil {
logger().Fatal(err)
}
client.SetChallengeProvider(acme.HTTP01, provider)
// --memcached-host=foo:11211 indicates that the user specifically want to do a HTTP challenge
// infer that the user also wants to exclude all other challenges
client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01})
}
if c.GlobalIsSet("http") {
if strings.Index(c.GlobalString("http"), ":") == -1 {
logger().Fatalf("The --http switch only accepts interface:port or :port for its argument.")
@@ -117,6 +136,10 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) {
var err error
var provider acme.ChallengeProvider
switch c.GlobalString("dns") {
case "azure":
provider, err = azure.NewDNSProvider()
case "auroradns":
provider, err = auroradns.NewDNSProvider()
case "cloudflare":
provider, err = cloudflare.NewDNSProvider()
case "digitalocean":
@@ -125,6 +148,8 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) {
provider, err = dnsimple.NewDNSProvider()
case "dnsmadeeasy":
provider, err = dnsmadeeasy.NewDNSProvider()
case "exoscale":
provider, err = exoscale.NewDNSProvider()
case "dyn":
provider, err = dyn.NewDNSProvider()
case "gandi":
@@ -137,6 +162,8 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) {
provider, err = acme.NewDNSProviderManual()
case "namecheap":
provider, err = namecheap.NewDNSProvider()
case "rackspace":
provider, err = rackspace.NewDNSProvider()
case "route53":
provider, err = route53.NewDNSProvider()
case "rfc2136":
@@ -147,6 +174,10 @@ func setup(c *cli.Context) (*Configuration, *Account, *acme.Client) {
provider, err = ovh.NewDNSProvider()
case "pdns":
provider, err = pdns.NewDNSProvider()
case "ns1":
provider, err = ns1.NewDNSProvider()
case "dnspod":
provider, err = dnspod.NewDNSProvider()
}
if err != nil {
@@ -320,7 +351,7 @@ func run(c *cli.Context) error {
if hasDomains {
// obtain a certificate, generating a new private key
cert, failures = client.ObtainCertificate(c.GlobalStringSlice("domains"), !c.Bool("no-bundle"), nil)
cert, failures = client.ObtainCertificate(c.GlobalStringSlice("domains"), !c.Bool("no-bundle"), nil, c.Bool("must-staple"))
} else {
// read the CSR
csr, err := readCSRFile(c.GlobalString("csr"))
@@ -433,7 +464,7 @@ func renew(c *cli.Context) error {
certRes.Certificate = certBytes
newCert, err := client.RenewCertificate(certRes, !c.Bool("no-bundle"))
newCert, err := client.RenewCertificate(certRes, !c.Bool("no-bundle"), c.Bool("must-staple"))
if err != nil {
logger().Fatalf("%s", err.Error())
}

141
vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,141 @@
package auroradns
import (
"fmt"
"github.com/edeckers/auroradnsclient"
"github.com/edeckers/auroradnsclient/records"
"github.com/edeckers/auroradnsclient/zones"
"github.com/xenolf/lego/acme"
"os"
"sync"
)
// DNSProvider describes a provider for AuroraDNS
type DNSProvider struct {
recordIDs map[string]string
recordIDsMu sync.Mutex
client *auroradnsclient.AuroraDNSClient
}
// NewDNSProvider returns a DNSProvider instance configured for AuroraDNS.
// Credentials must be passed in the environment variables: AURORA_USER_ID
// and AURORA_KEY.
func NewDNSProvider() (*DNSProvider, error) {
userID := os.Getenv("AURORA_USER_ID")
key := os.Getenv("AURORA_KEY")
endpoint := os.Getenv("AURORA_ENDPOINT")
if endpoint == "" {
endpoint = "https://api.auroradns.eu"
}
return NewDNSProviderCredentials(endpoint, userID, key)
}
// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for AuroraDNS.
func NewDNSProviderCredentials(baseURL string, userID string, key string) (*DNSProvider, error) {
client, err := auroradnsclient.NewAuroraDNSClient(baseURL, userID, key)
if err != nil {
return nil, err
}
return &DNSProvider{
client: client,
recordIDs: make(map[string]string),
}, nil
}
func (provider *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, error) {
zs, err := provider.client.GetZones()
if err != nil {
return zones.ZoneRecord{}, err
}
for _, element := range zs {
if element.Name == name {
return element, nil
}
}
return zones.ZoneRecord{}, fmt.Errorf("Could not find Zone record")
}
// Present creates a record with a secret
func (provider *DNSProvider) Present(domain, token, keyAuth string) error {
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
if err != nil {
return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err)
}
// 1. Aurora will happily create the TXT record when it is provided a fqdn,
// but it will only appear in the control panel and will not be
// propagated to DNS servers. Extract and use subdomain instead.
// 2. A trailing dot in the fqdn will cause Aurora to add a trailing dot to
// the subdomain, resulting in _acme-challenge..<domain> rather
// than _acme-challenge.<domain>
subdomain := fqdn[0 : len(fqdn)-len(authZone)-1]
authZone = acme.UnFqdn(authZone)
zoneRecord, err := provider.getZoneInformationByName(authZone)
reqData :=
records.CreateRecordRequest{
RecordType: "TXT",
Name: subdomain,
Content: value,
TTL: 300,
}
respData, err := provider.client.CreateRecord(zoneRecord.ID, reqData)
if err != nil {
return fmt.Errorf("Could not create record: '%s'.", err)
}
provider.recordIDsMu.Lock()
provider.recordIDs[fqdn] = respData.ID
provider.recordIDsMu.Unlock()
return nil
}
// CleanUp removes a given record that was generated by Present
func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
provider.recordIDsMu.Lock()
recordID, ok := provider.recordIDs[fqdn]
provider.recordIDsMu.Unlock()
if !ok {
return fmt.Errorf("Unknown recordID for '%s'", fqdn)
}
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
if err != nil {
return fmt.Errorf("Could not determine zone for domain: '%s'. %s", domain, err)
}
authZone = acme.UnFqdn(authZone)
zoneRecord, err := provider.getZoneInformationByName(authZone)
if err != nil {
return err
}
_, err = provider.client.RemoveRecord(zoneRecord.ID, recordID)
if err != nil {
return err
}
provider.recordIDsMu.Lock()
delete(provider.recordIDs, fqdn)
provider.recordIDsMu.Unlock()
return nil
}

148
vendor/github.com/xenolf/lego/providers/dns/auroradns/auroradns_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,148 @@
package auroradns
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
)
var fakeAuroraDNSUserId = "asdf1234"
var fakeAuroraDNSKey = "key"
func TestAuroraDNSPresent(t *testing.T) {
var requestReceived bool
mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" && r.URL.Path == "/zones" {
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `[{
"id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
"name": "example.com"
}]`)
return
}
requestReceived = true
if got, want := r.Method, "POST"; got != want {
t.Errorf("Expected method to be '%s' but got '%s'", want, got)
}
if got, want := r.URL.Path, "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records"; got != want {
t.Errorf("Expected path to be '%s' but got '%s'", want, got)
}
if got, want := r.Header.Get("Content-Type"), "application/json"; got != want {
t.Errorf("Expected Content-Type to be '%s' but got '%s'", want, got)
}
reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Fatalf("Error reading request body: %v", err)
}
if got, want := string(reqBody),
`{"type":"TXT","name":"_acme-challenge","content":"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI","ttl":300}`; got != want {
t.Errorf("Expected body data to be: `%s` but got `%s`", want, got)
}
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `{
"id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
"type": "TXT",
"name": "_acme-challenge",
"ttl": 300
}`)
}))
defer mock.Close()
auroraProvider, err := NewDNSProviderCredentials(mock.URL, fakeAuroraDNSUserId, fakeAuroraDNSKey)
if auroraProvider == nil {
t.Fatal("Expected non-nil AuroraDNS provider, but was nil")
}
if err != nil {
t.Fatalf("Expected no error creating provider, but got: %v", err)
}
err = auroraProvider.Present("example.com", "", "foobar")
if err != nil {
t.Fatalf("Expected no error creating TXT record, but got: %v", err)
}
if !requestReceived {
t.Error("Expected request to be received by mock backend, but it wasn't")
}
}
func TestAuroraDNSCleanUp(t *testing.T) {
var requestReceived bool
mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" && r.URL.Path == "/zones" {
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `[{
"id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
"name": "example.com"
}]`)
return
}
if r.Method == "POST" && r.URL.Path == "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records" {
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `{
"id": "ec56a4180-65aa-42ec-a945-5fd21dec0538",
"type": "TXT",
"name": "_acme-challenge",
"ttl": 300
}`)
return
}
requestReceived = true
if got, want := r.Method, "DELETE"; got != want {
t.Errorf("Expected method to be '%s' but got '%s'", want, got)
}
if got, want := r.URL.Path,
"/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records/ec56a4180-65aa-42ec-a945-5fd21dec0538"; got != want {
t.Errorf("Expected path to be '%s' but got '%s'", want, got)
}
if got, want := r.Header.Get("Content-Type"), "application/json"; got != want {
t.Errorf("Expected Content-Type to be '%s' but got '%s'", want, got)
}
w.WriteHeader(http.StatusCreated)
fmt.Fprintf(w, `{}`)
}))
defer mock.Close()
auroraProvider, err := NewDNSProviderCredentials(mock.URL, fakeAuroraDNSUserId, fakeAuroraDNSKey)
if auroraProvider == nil {
t.Fatal("Expected non-nil AuroraDNS provider, but was nil")
}
if err != nil {
t.Fatalf("Expected no error creating provider, but got: %v", err)
}
err = auroraProvider.Present("example.com", "", "foobar")
if err != nil {
t.Fatalf("Expected no error creating TXT record, but got: %v", err)
}
err = auroraProvider.CleanUp("example.com", "", "foobar")
if err != nil {
t.Fatalf("Expected no error removing TXT record, but got: %v", err)
}
if !requestReceived {
t.Error("Expected request to be received by mock backend, but it wasn't")
}
}

142
vendor/github.com/xenolf/lego/providers/dns/azure/azure.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,142 @@
// Package azure implements a DNS provider for solving the DNS-01
// challenge using azure DNS.
// Azure doesn't like trailing dots on domain names, most of the acme code does.
package azure
import (
"fmt"
"os"
"time"
"github.com/Azure/azure-sdk-for-go/arm/dns"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to"
"github.com/xenolf/lego/acme"
"strings"
)
// DNSProvider is an implementation of the acme.ChallengeProvider interface
type DNSProvider struct {
clientId string
clientSecret string
subscriptionId string
tenantId string
resourceGroup string
}
// NewDNSProvider returns a DNSProvider instance configured for azure.
// Credentials must be passed in the environment variables: AZURE_CLIENT_ID,
// AZURE_CLIENT_SECRET, AZURE_SUBSCRIPTION_ID, AZURE_TENANT_ID
func NewDNSProvider() (*DNSProvider, error) {
clientId := os.Getenv("AZURE_CLIENT_ID")
clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
subscriptionId := os.Getenv("AZURE_SUBSCRIPTION_ID")
tenantId := os.Getenv("AZURE_TENANT_ID")
resourceGroup := os.Getenv("AZURE_RESOURCE_GROUP")
return NewDNSProviderCredentials(clientId, clientSecret, subscriptionId, tenantId, resourceGroup)
}
// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for azure.
func NewDNSProviderCredentials(clientId, clientSecret, subscriptionId, tenantId, resourceGroup string) (*DNSProvider, error) {
if clientId == "" || clientSecret == "" || subscriptionId == "" || tenantId == "" || resourceGroup == "" {
return nil, fmt.Errorf("Azure configuration missing")
}
return &DNSProvider{
clientId: clientId,
clientSecret: clientSecret,
subscriptionId: subscriptionId,
tenantId: tenantId,
resourceGroup: resourceGroup,
}, nil
}
// Timeout returns the timeout and interval to use when checking for DNS
// propagation. Adjusting here to cope with spikes in propagation times.
func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
return 120 * time.Second, 2 * time.Second
}
// 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)
zone, err := c.getHostedZoneID(fqdn)
if err != nil {
return err
}
rsc := dns.NewRecordSetsClient(c.subscriptionId)
rsc.Authorizer, err = c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
rec := dns.RecordSet{
Name: &relative,
Properties: &dns.RecordSetProperties{
TTL: to.Int64Ptr(60),
TXTRecords: &[]dns.TxtRecord{dns.TxtRecord{Value: &[]string{value}}},
},
}
_, err = rsc.CreateOrUpdate(c.resourceGroup, zone, relative, dns.TXT, rec, "", "")
if err != nil {
return err
}
return nil
}
// Returns the relative record to the domain
func toRelativeRecord(domain, zone string) string {
return acme.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)
zone, err := c.getHostedZoneID(fqdn)
if err != nil {
return err
}
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
rsc := dns.NewRecordSetsClient(c.subscriptionId)
rsc.Authorizer, err = c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
_, err = rsc.Delete(c.resourceGroup, zone, relative, dns.TXT, "", "")
if err != nil {
return err
}
return nil
}
// 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)
if err != nil {
return "", err
}
// Now we want to to Azure and get the zone.
dc := dns.NewZonesClient(c.subscriptionId)
dc.Authorizer, err = c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
zone, err := dc.Get(c.resourceGroup, acme.UnFqdn(authZone))
if err != nil {
return "", err
}
// zone.Name shouldn't have a trailing dot(.)
return to.String(zone.Name), nil
}
// NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the
// passed credentials map.
func (c *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*azure.ServicePrincipalToken, error) {
oauthConfig, err := azure.PublicCloud.OAuthConfigForTenant(c.tenantId)
if err != nil {
panic(err)
}
return azure.NewServicePrincipalToken(*oauthConfig, c.clientId, c.clientSecret, scope)
}

89
vendor/github.com/xenolf/lego/providers/dns/azure/azure_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,89 @@
package azure
import (
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
var (
azureLiveTest bool
azureClientID string
azureClientSecret string
azureSubscriptionID string
azureTenantID string
azureResourceGroup string
azureDomain string
)
func init() {
azureClientID = os.Getenv("AZURE_CLIENT_ID")
azureClientSecret = os.Getenv("AZURE_CLIENT_SECRET")
azureSubscriptionID = os.Getenv("AZURE_SUBSCRIPTION_ID")
azureTenantID = os.Getenv("AZURE_TENANT_ID")
azureResourceGroup = os.Getenv("AZURE_RESOURCE_GROUP")
azureDomain = os.Getenv("AZURE_DOMAIN")
if len(azureClientID) > 0 && len(azureClientSecret) > 0 {
azureLiveTest = true
}
}
func restoreAzureEnv() {
os.Setenv("AZURE_CLIENT_ID", azureClientID)
os.Setenv("AZURE_SUBSCRIPTION_ID", azureSubscriptionID)
}
func TestNewDNSProviderValid(t *testing.T) {
if !azureLiveTest {
t.Skip("skipping live test (requires credentials)")
}
os.Setenv("AZURE_CLIENT_ID", "")
_, err := NewDNSProviderCredentials(azureClientID, azureClientSecret, azureSubscriptionID, azureTenantID, azureResourceGroup)
assert.NoError(t, err)
restoreAzureEnv()
}
func TestNewDNSProviderValidEnv(t *testing.T) {
if !azureLiveTest {
t.Skip("skipping live test (requires credentials)")
}
os.Setenv("AZURE_CLIENT_ID", "other")
_, err := NewDNSProvider()
assert.NoError(t, err)
restoreAzureEnv()
}
func TestNewDNSProviderMissingCredErr(t *testing.T) {
os.Setenv("AZURE_SUBSCRIPTION_ID", "")
_, err := NewDNSProvider()
assert.EqualError(t, err, "Azure configuration missing")
restoreAzureEnv()
}
func TestLiveAzurePresent(t *testing.T) {
if !azureLiveTest {
t.Skip("skipping live test")
}
provider, err := NewDNSProviderCredentials(azureClientID, azureClientSecret, azureSubscriptionID, azureTenantID, azureResourceGroup)
assert.NoError(t, err)
err = provider.Present(azureDomain, "", "123d==")
assert.NoError(t, err)
}
func TestLiveAzureCleanUp(t *testing.T) {
if !azureLiveTest {
t.Skip("skipping live test")
}
provider, err := NewDNSProviderCredentials(azureClientID, azureClientSecret, azureSubscriptionID, azureTenantID, azureResourceGroup)
time.Sleep(time.Second * 1)
assert.NoError(t, err)
err = provider.CleanUp(azureDomain, "", "123d==")
assert.NoError(t, err)
}

146
vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,146 @@
// Package dnspod implements a DNS provider for solving the DNS-01 challenge
// using dnspod DNS.
package dnspod
import (
"fmt"
"os"
"strings"
"github.com/decker502/dnspod-go"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
type DNSProvider struct {
client *dnspod.Client
}
// NewDNSProvider returns a DNSProvider instance configured for dnspod.
// Credentials must be passed in the environment variables: DNSPOD_API_KEY.
func NewDNSProvider() (*DNSProvider, error) {
key := os.Getenv("DNSPOD_API_KEY")
return NewDNSProviderCredentials(key)
}
// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for dnspod.
func NewDNSProviderCredentials(key string) (*DNSProvider, error) {
if key == "" {
return nil, fmt.Errorf("dnspod credentials missing")
}
params := dnspod.CommonParams{LoginToken: key, Format: "json"}
return &DNSProvider{
client: dnspod.NewClient(params),
}, nil
}
// 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)
zoneID, zoneName, err := c.getHostedZone(domain)
if err != nil {
return err
}
recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl)
_, _, err = c.client.Domains.CreateRecord(zoneID, *recordAttributes)
if err != nil {
return fmt.Errorf("dnspod 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, _, _ := acme.DNS01Record(domain, keyAuth)
records, err := c.findTxtRecords(domain, fqdn)
if err != nil {
return err
}
zoneID, _, err := c.getHostedZone(domain)
if err != nil {
return err
}
for _, rec := range records {
_, err := c.client.Domains.DeleteRecord(zoneID, rec.ID)
if err != nil {
return err
}
}
return nil
}
func (c *DNSProvider) getHostedZone(domain string) (string, string, error) {
zones, _, err := c.client.Domains.List()
if err != nil {
return "", "", fmt.Errorf("dnspod API call failed: %v", err)
}
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
if err != nil {
return "", "", err
}
var hostedZone dnspod.Domain
for _, zone := range zones {
if zone.Name == acme.UnFqdn(authZone) {
hostedZone = zone
}
}
if hostedZone.ID == 0 {
return "", "", fmt.Errorf("Zone %s not found in dnspod for domain %s", authZone, domain)
}
return fmt.Sprintf("%v", hostedZone.ID), hostedZone.Name, nil
}
func (c *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Record {
name := c.extractRecordName(fqdn, zone)
return &dnspod.Record{
Type: "TXT",
Name: name,
Value: value,
Line: "默认",
TTL: "600",
}
}
func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, error) {
zoneID, zoneName, err := c.getHostedZone(domain)
if err != nil {
return nil, err
}
var records []dnspod.Record
result, _, err := c.client.Domains.ListRecords(zoneID, "")
if err != nil {
return records, fmt.Errorf("dnspod API call has failed: %v", err)
}
recordName := c.extractRecordName(fqdn, zoneName)
for _, record := range result {
if record.Name == recordName {
records = append(records, record)
}
}
return records, nil
}
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
name := acme.UnFqdn(fqdn)
if idx := strings.Index(name, "."+domain); idx != -1 {
return name[:idx]
}
return name
}

72
vendor/github.com/xenolf/lego/providers/dns/dnspod/dnspod_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,72 @@
package dnspod
import (
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
)
var (
dnspodLiveTest bool
dnspodAPIKey string
dnspodDomain string
)
func init() {
dnspodAPIKey = os.Getenv("DNSPOD_API_KEY")
dnspodDomain = os.Getenv("DNSPOD_DOMAIN")
if len(dnspodAPIKey) > 0 && len(dnspodDomain) > 0 {
dnspodLiveTest = true
}
}
func restorednspodEnv() {
os.Setenv("DNSPOD_API_KEY", dnspodAPIKey)
}
func TestNewDNSProviderValid(t *testing.T) {
os.Setenv("DNSPOD_API_KEY", "")
_, err := NewDNSProviderCredentials("123")
assert.NoError(t, err)
restorednspodEnv()
}
func TestNewDNSProviderValidEnv(t *testing.T) {
os.Setenv("DNSPOD_API_KEY", "123")
_, err := NewDNSProvider()
assert.NoError(t, err)
restorednspodEnv()
}
func TestNewDNSProviderMissingCredErr(t *testing.T) {
os.Setenv("DNSPOD_API_KEY", "")
_, err := NewDNSProvider()
assert.EqualError(t, err, "dnspod credentials missing")
restorednspodEnv()
}
func TestLivednspodPresent(t *testing.T) {
if !dnspodLiveTest {
t.Skip("skipping live test")
}
provider, err := NewDNSProviderCredentials(dnspodAPIKey)
assert.NoError(t, err)
err = provider.Present(dnspodDomain, "", "123d==")
assert.NoError(t, err)
}
func TestLivednspodCleanUp(t *testing.T) {
if !dnspodLiveTest {
t.Skip("skipping live test")
}
time.Sleep(time.Second * 1)
provider, err := NewDNSProviderCredentials(dnspodAPIKey)
assert.NoError(t, err)
err = provider.CleanUp(dnspodDomain, "", "123d==")
assert.NoError(t, err)
}

132
vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,132 @@
// Package exoscale implements a DNS provider for solving the DNS-01 challenge
// using exoscale DNS.
package exoscale
import (
"errors"
"fmt"
"os"
"github.com/pyr/egoscale/src/egoscale"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
type DNSProvider struct {
client *egoscale.Client
}
// Credentials must be passed in the environment variables:
// EXOSCALE_API_KEY, EXOSCALE_API_SECRET, EXOSCALE_ENDPOINT.
func NewDNSProvider() (*DNSProvider, error) {
key := os.Getenv("EXOSCALE_API_KEY")
secret := os.Getenv("EXOSCALE_API_SECRET")
endpoint := os.Getenv("EXOSCALE_ENDPOINT")
return NewDNSProviderClient(key, secret, endpoint)
}
// Uses the supplied parameters to return a DNSProvider instance
// configured for Exoscale.
func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) {
if key == "" || secret == "" {
return nil, fmt.Errorf("Exoscale credentials missing")
}
if endpoint == "" {
endpoint = "https://api.exoscale.ch/dns"
}
return &DNSProvider{
client: egoscale.NewClient(endpoint, key, secret),
}, nil
}
// 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)
zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain)
if err != nil {
return err
}
recordId, err := c.FindExistingRecordId(zone, recordName)
if err != nil {
return err
}
record := egoscale.DNSRecord{
Name: recordName,
Ttl: ttl,
Content: value,
RecordType: "TXT",
}
if recordId == 0 {
_, err := c.client.CreateRecord(zone, record)
if err != nil {
return errors.New("Error while creating DNS record: " + err.Error())
}
} else {
record.Id = recordId
_, err := c.client.UpdateRecord(zone, record)
if err != nil {
return errors.New("Error while updating DNS record: " + err.Error())
}
}
return nil
}
// CleanUp removes the record matching the specified parameters.
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain)
if err != nil {
return err
}
recordId, err := c.FindExistingRecordId(zone, recordName)
if err != nil {
return err
}
if recordId != 0 {
record := egoscale.DNSRecord{
Id: recordId,
}
err = c.client.DeleteRecord(zone, record)
if err != nil {
return errors.New("Error while deleting DNS record: " + err.Error())
}
}
return nil
}
// Query Exoscale to find an existing record for this name.
// Returns nil if no record could be found
func (c *DNSProvider) FindExistingRecordId(zone, recordName string) (int64, error) {
responses, err := c.client.GetRecords(zone)
if err != nil {
return -1, errors.New("Error while retrievening DNS records: " + err.Error())
}
for _, response := range responses {
if response.Record.Name == recordName {
return response.Record.Id, nil
}
}
return 0, nil
}
// 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)
if err != nil {
return "", "", err
}
zone = acme.UnFqdn(zone)
name := acme.UnFqdn(fqdn)
name = name[:len(name)-len("."+zone)]
return zone, name, nil
}

103
vendor/github.com/xenolf/lego/providers/dns/exoscale/exoscale_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,103 @@
package exoscale
import (
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
var (
exoscaleLiveTest bool
exoscaleAPIKey string
exoscaleAPISecret string
exoscaleDomain string
)
func init() {
exoscaleAPISecret = os.Getenv("EXOSCALE_API_SECRET")
exoscaleAPIKey = os.Getenv("EXOSCALE_API_KEY")
exoscaleDomain = os.Getenv("EXOSCALE_DOMAIN")
if len(exoscaleAPIKey) > 0 && len(exoscaleAPISecret) > 0 && len(exoscaleDomain) > 0 {
exoscaleLiveTest = true
}
}
func restoreExoscaleEnv() {
os.Setenv("EXOSCALE_API_KEY", exoscaleAPIKey)
os.Setenv("EXOSCALE_API_SECRET", exoscaleAPISecret)
}
func TestNewDNSProviderValid(t *testing.T) {
os.Setenv("EXOSCALE_API_KEY", "")
os.Setenv("EXOSCALE_API_SECRET", "")
_, err := NewDNSProviderClient("example@example.com", "123", "")
assert.NoError(t, err)
restoreExoscaleEnv()
}
func TestNewDNSProviderValidEnv(t *testing.T) {
os.Setenv("EXOSCALE_API_KEY", "example@example.com")
os.Setenv("EXOSCALE_API_SECRET", "123")
_, err := NewDNSProvider()
assert.NoError(t, err)
restoreExoscaleEnv()
}
func TestNewDNSProviderMissingCredErr(t *testing.T) {
os.Setenv("EXOSCALE_API_KEY", "")
os.Setenv("EXOSCALE_API_SECRET", "")
_, err := NewDNSProvider()
assert.EqualError(t, err, "Exoscale credentials missing")
restoreExoscaleEnv()
}
func TestExtractRootRecordName(t *testing.T) {
provider, err := NewDNSProviderClient("example@example.com", "123", "")
assert.NoError(t, err)
zone, recordName, err := provider.FindZoneAndRecordName("_acme-challenge.bar.com.", "bar.com")
assert.NoError(t, err)
assert.Equal(t, "bar.com", zone)
assert.Equal(t, "_acme-challenge", recordName)
}
func TestExtractSubRecordName(t *testing.T) {
provider, err := NewDNSProviderClient("example@example.com", "123", "")
assert.NoError(t, err)
zone, recordName, err := provider.FindZoneAndRecordName("_acme-challenge.foo.bar.com.", "foo.bar.com")
assert.NoError(t, err)
assert.Equal(t, "bar.com", zone)
assert.Equal(t, "_acme-challenge.foo", recordName)
}
func TestLiveExoscalePresent(t *testing.T) {
if !exoscaleLiveTest {
t.Skip("skipping live test")
}
provider, err := NewDNSProviderClient(exoscaleAPIKey, exoscaleAPISecret, "")
assert.NoError(t, err)
err = provider.Present(exoscaleDomain, "", "123d==")
assert.NoError(t, err)
// Present Twice to handle create / update
err = provider.Present(exoscaleDomain, "", "123d==")
assert.NoError(t, err)
}
func TestLiveExoscaleCleanUp(t *testing.T) {
if !exoscaleLiveTest {
t.Skip("skipping live test")
}
time.Sleep(time.Second * 1)
provider, err := NewDNSProviderClient(exoscaleAPIKey, exoscaleAPISecret, "")
assert.NoError(t, err)
err = provider.CleanUp(exoscaleDomain, "", "123d==")
assert.NoError(t, err)
}

4
vendor/github.com/xenolf/lego/providers/dns/gandi/gandi_test.go сгенерированный поставляемый
Просмотреть файл

@@ -141,7 +141,7 @@ func TestDNSProviderLive(t *testing.T) {
}
// complete the challenge
bundle := false
_, failures := client.ObtainCertificate([]string{domain}, bundle, nil)
_, failures := client.ObtainCertificate([]string{domain}, bundle, nil, false)
if len(failures) > 0 {
t.Fatal(failures)
}
@@ -496,7 +496,7 @@ var serverResponses = map[string]string{
</member>
<member>
<name>id</name>
<value><int>3333333333</int></value>
<value><int>333333333</int></value>
</member>
<member>
<name>value</name>

10
vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud.go сгенерированный поставляемый
Просмотреть файл

@@ -68,6 +68,16 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
Additions: []*dns.ResourceRecordSet{rec},
}
// Look for existing records.
list, err := c.client.ResourceRecordSets.List(c.project, zone).Name(fqdn).Type("TXT").Do()
if err != nil {
return err
}
if len(list.Rrsets) > 0 {
// Attempt to delete the existing records when adding our new one.
change.Deletions = list.Rrsets
}
chg, err := c.client.Changes.Create(c.project, zone, change).Do()
if err != nil {
return err

14
vendor/github.com/xenolf/lego/providers/dns/googlecloud/googlecloud_test.go сгенерированный поставляемый
Просмотреть файл

@@ -70,6 +70,20 @@ func TestLiveGoogleCloudPresent(t *testing.T) {
assert.NoError(t, err)
}
func TestLiveGoogleCloudPresentMultiple(t *testing.T) {
if !gcloudLiveTest {
t.Skip("skipping live test")
}
provider, err := NewDNSProviderCredentials(gcloudProject)
assert.NoError(t, err)
// Check that we're able to create multiple entries
err = provider.Present(gcloudDomain, "1", "123d==")
err = provider.Present(gcloudDomain, "2", "123d==")
assert.NoError(t, err)
}
func TestLiveGoogleCloudCleanUp(t *testing.T) {
if !gcloudLiveTest {
t.Skip("skipping live test")

97
vendor/github.com/xenolf/lego/providers/dns/ns1/ns1.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,97 @@
// Package ns1 implements a DNS provider for solving the DNS-01 challenge
// using NS1 DNS.
package ns1
import (
"fmt"
"net/http"
"os"
"time"
"github.com/xenolf/lego/acme"
"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.
type DNSProvider struct {
client *rest.Client
}
// NewDNSProvider returns a DNSProvider instance configured for NS1.
// Credentials must be passed in the environment variables: NS1_API_KEY.
func NewDNSProvider() (*DNSProvider, error) {
key := os.Getenv("NS1_API_KEY")
if key == "" {
return nil, fmt.Errorf("NS1 credentials missing")
}
return NewDNSProviderCredentials(key)
}
// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for NS1.
func NewDNSProviderCredentials(key string) (*DNSProvider, error) {
if key == "" {
return nil, fmt.Errorf("NS1 credentials missing")
}
httpClient := &http.Client{Timeout: time.Second * 10}
client := rest.NewClient(httpClient, rest.SetAPIKey(key))
return &DNSProvider{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 := acme.DNS01Record(domain, keyAuth)
zone, err := c.getHostedZone(domain)
if err != nil {
return err
}
record := c.newTxtRecord(zone, fqdn, value, ttl)
_, err = c.client.Records.Create(record)
if err != nil && err != rest.ErrRecordExists {
return err
}
return nil
}
// CleanUp removes the TXT record matching the specified parameters.
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
zone, err := c.getHostedZone(domain)
if err != nil {
return err
}
name := acme.UnFqdn(fqdn)
_, err = c.client.Records.Delete(zone.Zone, name, "TXT")
return err
}
func (c *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) {
zone, _, err := c.client.Zones.Get(domain)
if err != nil {
return nil, err
}
return zone, nil
}
func (c *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record {
name := acme.UnFqdn(fqdn)
return &dns.Record{
Type: "TXT",
Zone: zone.Zone,
Domain: name,
TTL: ttl,
Answers: []*dns.Answer{
{Rdata: []string{value}},
},
}
}

67
vendor/github.com/xenolf/lego/providers/dns/ns1/ns1_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,67 @@
package ns1
import (
"os"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
var (
liveTest bool
apiKey string
domain string
)
func init() {
apiKey = os.Getenv("NS1_API_KEY")
domain = os.Getenv("NS1_DOMAIN")
if len(apiKey) > 0 && len(domain) > 0 {
liveTest = true
}
}
func restoreNS1Env() {
os.Setenv("NS1_API_KEY", apiKey)
}
func TestNewDNSProviderValid(t *testing.T) {
os.Setenv("NS1_API_KEY", "")
_, err := NewDNSProviderCredentials("123")
assert.NoError(t, err)
restoreNS1Env()
}
func TestNewDNSProviderMissingCredErr(t *testing.T) {
os.Setenv("NS1_API_KEY", "")
_, err := NewDNSProvider()
assert.EqualError(t, err, "NS1 credentials missing")
restoreNS1Env()
}
func TestLivePresent(t *testing.T) {
if !liveTest {
t.Skip("skipping live test")
}
provider, err := NewDNSProviderCredentials(apiKey)
assert.NoError(t, err)
err = provider.Present(domain, "", "123d==")
assert.NoError(t, err)
}
func TestLiveCleanUp(t *testing.T) {
if !liveTest {
t.Skip("skipping live test")
}
time.Sleep(time.Second * 1)
provider, err := NewDNSProviderCredentials(apiKey)
assert.NoError(t, err)
err = provider.CleanUp(domain, "", "123d==")
assert.NoError(t, err)
}

284
vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,284 @@
// Package rackspace implements a DNS provider for solving the DNS-01
// challenge using rackspace DNS.
package rackspace
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
"github.com/xenolf/lego/acme"
)
// 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
// used to store the reusable token and DNS API endpoint
type DNSProvider struct {
token string
cloudDNSEndpoint string
}
// NewDNSProvider returns a DNSProvider instance configured for Rackspace.
// Credentials must be passed in the environment variables: RACKSPACE_USER
// and RACKSPACE_API_KEY.
func NewDNSProvider() (*DNSProvider, error) {
user := os.Getenv("RACKSPACE_USER")
key := os.Getenv("RACKSPACE_API_KEY")
return NewDNSProviderCredentials(user, key)
}
// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for Rackspace. It authenticates against
// the API, also grabbing the DNS Endpoint.
func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) {
if user == "" || key == "" {
return nil, fmt.Errorf("Rackspace credentials missing")
}
type APIKeyCredentials struct {
Username string `json:"username"`
APIKey string `json:"apiKey"`
}
type Auth struct {
APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"`
}
type RackspaceAuthData struct {
Auth `json:"auth"`
}
type RackspaceIdentity struct {
Access struct {
ServiceCatalog []struct {
Endpoints []struct {
PublicURL string `json:"publicURL"`
TenantID string `json:"tenantId"`
} `json:"endpoints"`
Name string `json:"name"`
} `json:"serviceCatalog"`
Token struct {
ID string `json:"id"`
} `json:"token"`
} `json:"access"`
}
authData := RackspaceAuthData{
Auth: Auth{
APIKeyCredentials: APIKeyCredentials{
Username: user,
APIKey: key,
},
},
}
body, err := json.Marshal(authData)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", rackspaceAPIURL, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
client := http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("Error querying Rackspace Identity API: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("Rackspace Authentication failed. Response code: %d", resp.StatusCode)
}
var rackspaceIdentity RackspaceIdentity
err = json.NewDecoder(resp.Body).Decode(&rackspaceIdentity)
if err != nil {
return nil, err
}
// Iterate through the Service Catalog to get the DNS Endpoint
var dnsEndpoint string
for _, service := range rackspaceIdentity.Access.ServiceCatalog {
if service.Name == "cloudDNS" {
dnsEndpoint = service.Endpoints[0].PublicURL
break
}
}
if dnsEndpoint == "" {
return nil, fmt.Errorf("Failed to populate DNS endpoint, check Rackspace API for changes.")
}
return &DNSProvider{
token: rackspaceIdentity.Access.Token.ID,
cloudDNSEndpoint: dnsEndpoint,
}, nil
}
// 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)
zoneID, err := c.getHostedZoneID(fqdn)
if err != nil {
return err
}
rec := RackspaceRecords{
RackspaceRecord: []RackspaceRecord{{
Name: acme.UnFqdn(fqdn),
Type: "TXT",
Data: value,
TTL: 300,
}},
}
body, err := json.Marshal(rec)
if err != nil {
return err
}
_, err = c.makeRequest("POST", fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body))
if err != nil {
return err
}
return nil
}
// CleanUp removes the TXT record matching the specified parameters
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
zoneID, err := c.getHostedZoneID(fqdn)
if err != nil {
return err
}
record, err := c.findTxtRecord(fqdn, zoneID)
if err != nil {
return err
}
_, err = c.makeRequest("DELETE", fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil)
if err != nil {
return err
}
return nil
}
// getHostedZoneID performs a lookup to get the DNS zone which needs
// modifying for a given FQDN
func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
// HostedZones represents the response when querying Rackspace DNS zones
type ZoneSearchResponse struct {
TotalEntries int `json:"totalEntries"`
HostedZones []struct {
ID int `json:"id"`
Name string `json:"name"`
} `json:"domains"`
}
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return 0, err
}
result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil)
if err != nil {
return 0, err
}
var zoneSearchResponse ZoneSearchResponse
err = json.Unmarshal(result, &zoneSearchResponse)
if err != nil {
return 0, err
}
// If nothing was returned, or for whatever reason more than 1 was returned (the search uses exact match, so should not occur)
if zoneSearchResponse.TotalEntries != 1 {
return 0, fmt.Errorf("Found %d zones for %s in Rackspace for domain %s", zoneSearchResponse.TotalEntries, authZone, fqdn)
}
return zoneSearchResponse.HostedZones[0].ID, nil
}
// 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)
if err != nil {
return nil, err
}
var records RackspaceRecords
err = json.Unmarshal(result, &records)
if err != nil {
return nil, err
}
recordsLength := len(records.RackspaceRecord)
switch recordsLength {
case 1:
break
case 0:
return nil, fmt.Errorf("No TXT record found for %s", fqdn)
default:
return nil, fmt.Errorf("More than 1 TXT record found for %s", fqdn)
}
return &records.RackspaceRecord[0], nil
}
// makeRequest is a wrapper function used for making DNS API requests
func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
url := c.cloudDNSEndpoint + uri
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
req.Header.Set("X-Auth-Token", c.token)
req.Header.Set("Content-Type", "application/json")
client := http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("Error querying DNS API: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted {
return nil, fmt.Errorf("Request failed for %s %s. Response code: %d", method, url, resp.StatusCode)
}
var r json.RawMessage
err = json.NewDecoder(resp.Body).Decode(&r)
if err != nil {
return nil, fmt.Errorf("JSON decode failed for %s %s. Response code: %d", method, url, resp.StatusCode)
}
return r, nil
}
// RackspaceRecords is the list of records sent/recieved from the DNS API
type RackspaceRecords struct {
RackspaceRecord []RackspaceRecord `json:"records"`
}
// RackspaceRecord represents a Rackspace DNS record
type RackspaceRecord struct {
Name string `json:"name"`
Type string `json:"type"`
Data string `json:"data"`
TTL int `json:"ttl,omitempty"`
ID string `json:"id,omitempty"`
}

220
vendor/github.com/xenolf/lego/providers/dns/rackspace/rackspace_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,220 @@
package rackspace
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
var (
rackspaceLiveTest bool
rackspaceUser string
rackspaceAPIKey string
rackspaceDomain string
testAPIURL string
)
func init() {
rackspaceUser = os.Getenv("RACKSPACE_USER")
rackspaceAPIKey = os.Getenv("RACKSPACE_API_KEY")
rackspaceDomain = os.Getenv("RACKSPACE_DOMAIN")
if len(rackspaceUser) > 0 && len(rackspaceAPIKey) > 0 && len(rackspaceDomain) > 0 {
rackspaceLiveTest = true
}
}
func testRackspaceEnv() {
rackspaceAPIURL = testAPIURL
os.Setenv("RACKSPACE_USER", "testUser")
os.Setenv("RACKSPACE_API_KEY", "testKey")
}
func liveRackspaceEnv() {
rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens"
os.Setenv("RACKSPACE_USER", rackspaceUser)
os.Setenv("RACKSPACE_API_KEY", rackspaceAPIKey)
}
func startTestServers() (identityAPI, dnsAPI *httptest.Server) {
dnsAPI = httptest.NewServer(dnsMux())
dnsEndpoint := dnsAPI.URL + "/123456"
identityAPI = httptest.NewServer(identityHandler(dnsEndpoint))
testAPIURL = identityAPI.URL + "/"
return
}
func closeTestServers(identityAPI, dnsAPI *httptest.Server) {
identityAPI.Close()
dnsAPI.Close()
}
func identityHandler(dnsEndpoint string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, found := jsonMap[string(reqBody)]
if !found {
w.WriteHeader(http.StatusBadRequest)
return
}
resp = strings.Replace(resp, "https://dns.api.rackspacecloud.com/v1.0/123456", dnsEndpoint, 1)
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, resp)
})
}
func dnsMux() *http.ServeMux {
mux := http.NewServeMux()
// Used by `getHostedZoneID()` finding `zoneID` "?name=example.com"
mux.HandleFunc("/123456/domains", func(w http.ResponseWriter, r *http.Request) {
if r.URL.Query().Get("name") == "example.com" {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["zoneDetails"])
return
}
w.WriteHeader(http.StatusBadRequest)
return
})
mux.HandleFunc("/123456/domains/112233/records", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
// Used by `Present()` creating the TXT record
case http.MethodPost:
reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
resp, found := jsonMap[string(reqBody)]
if !found {
w.WriteHeader(http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusAccepted)
fmt.Fprintf(w, resp)
// Used by `findTxtRecord()` finding `record.ID` "?type=TXT&name=_acme-challenge.example.com"
case http.MethodGet:
if r.URL.Query().Get("type") == "TXT" && r.URL.Query().Get("name") == "_acme-challenge.example.com" {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["recordDetails"])
return
}
w.WriteHeader(http.StatusBadRequest)
return
// Used by `CleanUp()` deleting the TXT record "?id=445566"
case http.MethodDelete:
if r.URL.Query().Get("id") == "TXT-654321" {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, jsonMap["recordDelete"])
return
}
w.WriteHeader(http.StatusBadRequest)
}
})
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
fmt.Printf("Not Found for Request: (%+v)\n\n", r)
})
return mux
}
func TestNewDNSProviderMissingCredErr(t *testing.T) {
testRackspaceEnv()
_, err := NewDNSProviderCredentials("", "")
assert.EqualError(t, err, "Rackspace credentials missing")
}
func TestOfflineRackspaceValid(t *testing.T) {
testRackspaceEnv()
provider, err := NewDNSProviderCredentials(os.Getenv("RACKSPACE_USER"), os.Getenv("RACKSPACE_API_KEY"))
assert.NoError(t, err)
assert.Equal(t, provider.token, "testToken", "The token should match")
}
func TestOfflineRackspacePresent(t *testing.T) {
testRackspaceEnv()
provider, err := NewDNSProvider()
if assert.NoError(t, err) {
err = provider.Present("example.com", "token", "keyAuth")
assert.NoError(t, err)
}
}
func TestOfflineRackspaceCleanUp(t *testing.T) {
testRackspaceEnv()
provider, err := NewDNSProvider()
if assert.NoError(t, err) {
err = provider.CleanUp("example.com", "token", "keyAuth")
assert.NoError(t, err)
}
}
func TestNewDNSProviderValidEnv(t *testing.T) {
if !rackspaceLiveTest {
t.Skip("skipping live test")
}
liveRackspaceEnv()
provider, err := NewDNSProvider()
assert.NoError(t, err)
assert.Contains(t, provider.cloudDNSEndpoint, "https://dns.api.rackspacecloud.com/v1.0/", "The endpoint URL should contain the base")
}
func TestRackspacePresent(t *testing.T) {
if !rackspaceLiveTest {
t.Skip("skipping live test")
}
liveRackspaceEnv()
provider, err := NewDNSProvider()
assert.NoError(t, err)
err = provider.Present(rackspaceDomain, "", "112233445566==")
assert.NoError(t, err)
}
func TestRackspaceCleanUp(t *testing.T) {
if !rackspaceLiveTest {
t.Skip("skipping live test")
}
time.Sleep(time.Second * 15)
liveRackspaceEnv()
provider, err := NewDNSProvider()
assert.NoError(t, err)
err = provider.CleanUp(rackspaceDomain, "", "112233445566==")
assert.NoError(t, err)
}
func TestMain(m *testing.M) {
identityAPI, dnsAPI := startTestServers()
defer closeTestServers(identityAPI, dnsAPI)
os.Exit(m.Run())
}
var jsonMap = map[string]string{
`{"auth":{"RAX-KSKEY:apiKeyCredentials":{"username":"testUser","apiKey":"testKey"}}}`: `{"access":{"token":{"id":"testToken","expires":"1970-01-01T00:00:00.000Z","tenant":{"id":"123456","name":"123456"},"RAX-AUTH:authenticatedBy":["APIKEY"]},"serviceCatalog":[{"type":"rax:dns","endpoints":[{"publicURL":"https://dns.api.rackspacecloud.com/v1.0/123456","tenantId":"123456"}],"name":"cloudDNS"}],"user":{"id":"fakeUseID","name":"testUser"}}}`,
"zoneDetails": `{"domains":[{"name":"example.com","id":112233,"emailAddress":"hostmaster@example.com","updated":"1970-01-01T00:00:00.000+0000","created":"1970-01-01T00:00:00.000+0000"}],"totalEntries":1}`,
`{"records":[{"name":"_acme-challenge.example.com","type":"TXT","data":"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM","ttl":300}]}`: `{"request":"{\"records\":[{\"name\":\"_acme-challenge.example.com\",\"type\":\"TXT\",\"data\":\"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM\",\"ttl\":300}]}","status":"RUNNING","verb":"POST","jobId":"00000000-0000-0000-0000-0000000000","callbackUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/status/00000000-0000-0000-0000-0000000000","requestUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/domains/112233/records"}`,
"recordDetails": `{"records":[{"name":"_acme-challenge.example.com","id":"TXT-654321","type":"TXT","data":"pW9ZKG0xz_PCriK-nCMOjADy9eJcgGWIzkkj2fN4uZM","ttl":300,"updated":"1970-01-01T00:00:00.000+0000","created":"1970-01-01T00:00:00.000+0000"}]}`,
"recordDelete": `{"status":"RUNNING","verb":"DELETE","jobId":"00000000-0000-0000-0000-0000000000","callbackUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/status/00000000-0000-0000-0000-0000000000","requestUrl":"https://dns.api.rackspacecloud.com/v1.0/123456/domains/112233/recordsid=TXT-654321"}`,
}

15
vendor/github.com/xenolf/lego/providers/http/memcached/README.md сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,15 @@
# Memcached http provider
Publishes challenges into memcached where they can be retrieved by nginx. Allows
specifying multiple memcached servers and the responses will be published to all
of them, making it easier to verify when your domain is hosted on a cluster of
servers.
Example nginx config:
```
location /.well-known/acme-challenge/ {
set $memcached_key "$uri";
memcached_pass 127.0.0.1:11211;
}
```

59
vendor/github.com/xenolf/lego/providers/http/memcached/memcached.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,59 @@
// Package webroot implements a HTTP provider for solving the HTTP-01 challenge using web server's root path.
package memcached
import (
"fmt"
"path"
"github.com/rainycape/memcache"
"github.com/xenolf/lego/acme"
)
// HTTPProvider implements ChallengeProvider for `http-01` challenge
type MemcachedProvider struct {
hosts []string
}
// NewHTTPProvider returns a HTTPProvider instance with a configured webroot path
func NewMemcachedProvider(hosts []string) (*MemcachedProvider, error) {
if len(hosts) == 0 {
return nil, fmt.Errorf("No memcached hosts provided")
}
c := &MemcachedProvider{
hosts: hosts,
}
return c, nil
}
// Present makes the token available at `HTTP01ChallengePath(token)` by creating a file in the given webroot path
func (w *MemcachedProvider) Present(domain, token, keyAuth string) error {
var errs []error
challengePath := path.Join("/", acme.HTTP01ChallengePath(token))
for _, host := range w.hosts {
mc, err := memcache.New(host)
if err != nil {
errs = append(errs, err)
continue
}
mc.Add(&memcache.Item{
Key: challengePath,
Value: []byte(keyAuth),
Expiration: 60,
})
}
if len(errs) == len(w.hosts) {
return fmt.Errorf("Unable to store key in any of the memcache hosts -> %v", errs)
}
return nil
}
// CleanUp removes the file created for the challenge
func (w *MemcachedProvider) CleanUp(domain, token, keyAuth string) error {
// Memcached will clean up itself, that's what expiration is for.
return nil
}

111
vendor/github.com/xenolf/lego/providers/http/memcached/memcached_test.go сгенерированный поставляемый Обычный файл
Просмотреть файл

@@ -0,0 +1,111 @@
package memcached
import (
"os"
"path"
"strings"
"testing"
"github.com/rainycape/memcache"
"github.com/stretchr/testify/assert"
"github.com/xenolf/lego/acme"
)
var (
memcachedHosts []string
)
const (
domain = "lego.test"
token = "foo"
keyAuth = "bar"
)
func init() {
memcachedHostsStr := os.Getenv("MEMCACHED_HOSTS")
if len(memcachedHostsStr) > 0 {
memcachedHosts = strings.Split(memcachedHostsStr, ",")
}
}
func TestNewMemcachedProviderEmpty(t *testing.T) {
emptyHosts := make([]string, 0)
_, err := NewMemcachedProvider(emptyHosts)
assert.EqualError(t, err, "No memcached hosts provided")
}
func TestNewMemcachedProviderValid(t *testing.T) {
if len(memcachedHosts) == 0 {
t.Skip("Skipping memcached tests")
}
_, err := NewMemcachedProvider(memcachedHosts)
assert.NoError(t, err)
}
func TestMemcachedPresentSingleHost(t *testing.T) {
if len(memcachedHosts) == 0 {
t.Skip("Skipping memcached tests")
}
p, err := NewMemcachedProvider(memcachedHosts[0:1])
assert.NoError(t, err)
challengePath := path.Join("/", acme.HTTP01ChallengePath(token))
err = p.Present(domain, token, keyAuth)
assert.NoError(t, err)
mc, err := memcache.New(memcachedHosts[0])
assert.NoError(t, err)
i, err := mc.Get(challengePath)
assert.NoError(t, err)
assert.Equal(t, i.Value, []byte(keyAuth))
}
func TestMemcachedPresentMultiHost(t *testing.T) {
if len(memcachedHosts) <= 1 {
t.Skip("Skipping memcached multi-host tests")
}
p, err := NewMemcachedProvider(memcachedHosts)
assert.NoError(t, err)
challengePath := path.Join("/", acme.HTTP01ChallengePath(token))
err = p.Present(domain, token, keyAuth)
assert.NoError(t, err)
for _, host := range memcachedHosts {
mc, err := memcache.New(host)
assert.NoError(t, err)
i, err := mc.Get(challengePath)
assert.NoError(t, err)
assert.Equal(t, i.Value, []byte(keyAuth))
}
}
func TestMemcachedPresentPartialFailureMultiHost(t *testing.T) {
if len(memcachedHosts) == 0 {
t.Skip("Skipping memcached tests")
}
hosts := append(memcachedHosts, "5.5.5.5:11211")
p, err := NewMemcachedProvider(hosts)
assert.NoError(t, err)
challengePath := path.Join("/", acme.HTTP01ChallengePath(token))
err = p.Present(domain, token, keyAuth)
assert.NoError(t, err)
for _, host := range memcachedHosts {
mc, err := memcache.New(host)
assert.NoError(t, err)
i, err := mc.Get(challengePath)
assert.NoError(t, err)
assert.Equal(t, i.Value, []byte(keyAuth))
}
}
func TestMemcachedCleanup(t *testing.T) {
if len(memcachedHosts) == 0 {
t.Skip("Skipping memcached tests")
}
p, err := NewMemcachedProvider(memcachedHosts)
assert.NoError(t, err)
assert.NoError(t, p.CleanUp(domain, token, keyAuth))
}