Use to the stable version of Lego

This commit is contained in:
Ludovic Fernandez 2018-05-31 09:30:04 +02:00 committed by Traefiker Bot
parent 36e273714d
commit b2cf03fa5c
108 changed files with 3847 additions and 1152 deletions

View file

@ -1,13 +1,13 @@
package acmev2
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-acmev2.md#http
// 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")
// DNS01 is the "dns-01" ACME challenge https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acmev2.md#dns
// 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")
)

View file

@ -1,5 +1,5 @@
// package acmev2 implements the ACME protocol for Let's Encrypt and other conforming providers.
package acmev2
// Package acme implements the ACME protocol for Let's Encrypt and other conforming providers.
package acme
import (
"crypto"
@ -8,17 +8,13 @@ import (
"errors"
"fmt"
"io/ioutil"
"log"
"net"
"regexp"
"strconv"
"strings"
"time"
)
var (
// Logger is an optional custom logger.
Logger *log.Logger
"github.com/xenolf/lego/log"
)
const (
@ -31,16 +27,6 @@ const (
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 {
@ -86,9 +72,6 @@ func NewClient(caDirURL string, user User, keyType KeyType) (*Client, error) {
if dir.NewOrderURL == "" {
return nil, errors.New("directory missing new order URL")
}
/*if dir.RevokeCertURL == "" {
return nil, errors.New("directory missing revoke certificate URL")
}*/
jws := &jws{privKey: privKey, getNonceURL: dir.NewNonceURL}
if reg := user.GetRegistration(); reg != nil {
@ -149,12 +132,17 @@ func (c *Client) GetToSURL() string {
return c.directory.Meta.TermsOfService
}
// GetExternalAccountRequired returns the External Account Binding requirement of the Directory
func (c *Client) GetExternalAccountRequired() bool {
return c.directory.Meta.ExternalAccountRequired
}
// Register the current account to the ACME server.
func (c *Client) Register(tosAgreed bool) (*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())
log.Printf("[INFO] acme: Registering account for %s", c.user.GetEmail())
accMsg := accountMessage{}
if c.user.GetEmail() != "" {
@ -183,10 +171,58 @@ func (c *Client) Register(tosAgreed bool) (*RegistrationResource, error) {
return reg, nil
}
// RegisterWithExternalAccountBinding Register the current account to the ACME server.
func (c *Client) RegisterWithExternalAccountBinding(tosAgreed bool, kid string, hmacEncoded string) (*RegistrationResource, error) {
if c == nil || c.user == nil {
return nil, errors.New("acme: cannot register a nil client or user")
}
log.Printf("[INFO] acme: Registering account (EAB) for %s", c.user.GetEmail())
accMsg := accountMessage{}
if c.user.GetEmail() != "" {
accMsg.Contact = []string{"mailto:" + c.user.GetEmail()}
} else {
accMsg.Contact = []string{}
}
accMsg.TermsOfServiceAgreed = tosAgreed
hmac, err := base64.RawURLEncoding.DecodeString(hmacEncoded)
if err != nil {
return nil, fmt.Errorf("acme: could not decode hmac key: %s", err.Error())
}
eabJWS, err := c.jws.signEABContent(c.directory.NewAccountURL, kid, hmac)
if err != nil {
return nil, fmt.Errorf("acme: error signing eab content: %s", err.Error())
}
eabPayload := eabJWS.FullSerialize()
accMsg.ExternalAccountBinding = []byte(eabPayload)
var serverReg accountMessage
hdr, err := postJSON(c.jws, c.directory.NewAccountURL, accMsg, &serverReg)
if err != nil {
remoteErr, ok := err.(RemoteError)
if ok && remoteErr.StatusCode == 409 {
} else {
return nil, err
}
}
reg := &RegistrationResource{
URI: hdr.Get("Location"),
Body: serverReg,
}
c.jws.kid = reg.URI
return reg, nil
}
// ResolveAccountByKey will attempt to look up an account using the given account key
// and return its registration resource.
func (c *Client) ResolveAccountByKey() (*RegistrationResource, error) {
logf("[INFO] acme: Trying to resolve account by key")
log.Printf("[INFO] acme: Trying to resolve account by key")
acc := accountMessage{OnlyReturnExisting: true}
hdr, err := postJSON(c.jws, c.directory.NewAccountURL, acc, nil)
@ -201,7 +237,7 @@ func (c *Client) ResolveAccountByKey() (*RegistrationResource, error) {
var retAccount accountMessage
c.jws.kid = accountLink
hdr, err = postJSON(c.jws, accountLink, accountMessage{}, &retAccount)
_, err = postJSON(c.jws, accountLink, accountMessage{}, &retAccount)
if err != nil {
return nil, err
}
@ -215,18 +251,14 @@ 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())
log.Printf("[INFO] acme: Deleting account for %s", c.user.GetEmail())
accMsg := accountMessage{
Status: "deactivated",
}
_, err := postJSON(c.jws, c.user.GetRegistration().URI, accMsg, nil)
if err != nil {
return err
}
return nil
return err
}
// QueryRegistration runs a POST request on the client's registration and
@ -239,7 +271,7 @@ func (c *Client) QueryRegistration() (*RegistrationResource, error) {
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)
log.Printf("[INFO] acme: Querying account for %s", c.user.GetRegistration().URI)
accMsg := accountMessage{}
@ -265,7 +297,7 @@ func (c *Client) QueryRegistration() (*RegistrationResource, error) {
// 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, error) {
func (c *Client) ObtainCertificateForCSR(csr x509.CertificateRequest, bundle bool) (*CertificateResource, error) {
// figure out what domains it concerns
// start with the common name
domains := []string{csr.Subject.CommonName}
@ -285,14 +317,14 @@ DNSNames:
}
if bundle {
logf("[INFO][%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", "))
log.Printf("[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, ", "))
log.Printf("[INFO][%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
}
order, err := c.createOrderForIdentifiers(domains)
if err != nil {
return CertificateResource{}, err
return nil, err
}
authz, err := c.getAuthzForOrder(order)
if err != nil {
@ -300,16 +332,16 @@ DNSNames:
/*for _, auth := range authz {
c.disableAuthz(auth)
}*/
return CertificateResource{}, err
return nil, err
}
err = c.solveChallengeForAuthz(authz)
if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates.
return CertificateResource{}, err
return nil, err
}
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
log.Printf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
failures := make(ObtainError)
cert, err := c.requestCertificateForCsr(order, bundle, csr.Raw, nil)
@ -339,20 +371,20 @@ 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, mustStaple bool) (CertificateResource, error) {
func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (*CertificateResource, error) {
if len(domains) == 0 {
return CertificateResource{}, errors.New("No domains to obtain a certificate for")
return nil, errors.New("No domains to obtain a certificate for")
}
if bundle {
logf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
log.Printf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
} else {
logf("[INFO][%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
log.Printf("[INFO][%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
}
order, err := c.createOrderForIdentifiers(domains)
if err != nil {
return CertificateResource{}, err
return nil, err
}
authz, err := c.getAuthzForOrder(order)
if err != nil {
@ -360,16 +392,16 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
/*for _, auth := range authz {
c.disableAuthz(auth)
}*/
return CertificateResource{}, err
return nil, err
}
err = c.solveChallengeForAuthz(authz)
if err != nil {
// If any challenge fails, return. Do not generate partial SAN certificates.
return CertificateResource{}, err
return nil, err
}
logf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
log.Printf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
failures := make(ObtainError)
cert, err := c.requestCertificateForOrder(order, bundle, privKey, mustStaple)
@ -413,22 +445,22 @@ 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, mustStaple 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)
if err != nil {
return CertificateResource{}, err
return nil, err
}
x509Cert := certificates[0]
if x509Cert.IsCA {
return CertificateResource{}, fmt.Errorf("[%s] Certificate bundle starts with a CA certificate", cert.Domain)
return nil, 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()))
log.Printf("[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
@ -436,7 +468,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
if len(cert.CSR) > 0 {
csr, err := pemDecodeTox509CSR(cert.CSR)
if err != nil {
return CertificateResource{}, err
return nil, err
}
newCert, failures := c.ObtainCertificateForCSR(*csr, bundle)
return newCert, failures
@ -446,7 +478,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
if cert.PrivateKey != nil {
privKey, err = parsePEMPrivateKey(cert.PrivateKey)
if err != nil {
return CertificateResource{}, err
return nil, err
}
}
@ -502,7 +534,7 @@ func (c *Client) solveChallengeForAuthz(authorizations []authorization) error {
for _, authz := range authorizations {
if authz.Status == "valid" {
// Boulder might recycle recent validated authz (see issue #267)
logf("[INFO][%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value)
log.Printf("[INFO][%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value)
continue
}
@ -533,7 +565,7 @@ func (c *Client) chooseSolver(auth authorization, domain string) (int, solver) {
if solver, ok := c.solvers[Challenge(challenge.Type)]; ok {
return i, solver
}
logf("[INFO][%s] acme: Could not find solver for: %s", domain, challenge.Type)
log.Printf("[INFO][%s] acme: Could not find solver for: %s", domain, challenge.Type)
}
return 0, nil
}
@ -585,7 +617,7 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error)
func logAuthz(order orderResource) {
for i, auth := range order.Authorizations {
logf("[INFO][%s] AuthURL: %s", order.Identifiers[i].Value, auth)
log.Printf("[INFO][%s] AuthURL: %s", order.Identifiers[i].Value, auth)
}
}
@ -596,13 +628,13 @@ func (c *Client) disableAuthz(authURL string) error {
return err
}
func (c *Client) requestCertificateForOrder(order orderResource, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (CertificateResource, error) {
func (c *Client) requestCertificateForOrder(order orderResource, bundle bool, privKey crypto.PrivateKey, mustStaple bool) (*CertificateResource, error) {
var err error
if privKey == nil {
privKey, err = generatePrivateKey(c.keyType)
if err != nil {
return CertificateResource{}, err
return nil, err
}
}
@ -616,24 +648,24 @@ func (c *Client) requestCertificateForOrder(order orderResource, bundle bool, pr
// TODO: should the CSR be customizable?
csr, err := generateCsr(privKey, commonName, san, mustStaple)
if err != nil {
return CertificateResource{}, err
return nil, err
}
return c.requestCertificateForCsr(order, bundle, csr, pemEncode(privKey))
}
func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr []byte, privateKeyPem []byte) (CertificateResource, error) {
func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr []byte, privateKeyPem []byte) (*CertificateResource, error) {
commonName := order.Domains[0]
csrString := base64.RawURLEncoding.EncodeToString(csr)
var retOrder orderMessage
_, error := postJSON(c.jws, order.Finalize, csrMessage{Csr: csrString}, &retOrder)
if error != nil {
return CertificateResource{}, error
return nil, error
}
if retOrder.Status == "invalid" {
return CertificateResource{}, error
return nil, error
}
certRes := CertificateResource{
@ -646,11 +678,11 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
// if the certificate is available right away, short cut!
ok, err := c.checkCertResponse(retOrder, &certRes, bundle)
if err != nil {
return CertificateResource{}, err
return nil, err
}
if ok {
return certRes, nil
return &certRes, nil
}
}
@ -658,21 +690,21 @@ func (c *Client) requestCertificateForCsr(order orderResource, bundle bool, csr
for i := 0; i < maxChecks; i++ {
_, err := getJSON(order.URL, &retOrder)
if err != nil {
return CertificateResource{}, err
return nil, err
}
done, err := c.checkCertResponse(retOrder, &certRes, bundle)
if err != nil {
return CertificateResource{}, err
return nil, err
}
if done {
break
}
if i == maxChecks-1 {
return CertificateResource{}, fmt.Errorf("polled for certificate %d times; giving up", i)
return nil, fmt.Errorf("polled for certificate %d times; giving up", i)
}
}
return certRes, nil
return &certRes, nil
}
// checkCertResponse checks to see if the certificate is ready and a link is contained in the
@ -702,7 +734,7 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
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)
log.Printf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err)
} else {
issuerCert = pemEncode(derCertificateBytes(issuerCert))
@ -719,7 +751,7 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
certRes.Certificate = cert
certRes.CertURL = order.Certificate
certRes.CertStableURL = order.Certificate
logf("[INFO][%s] Server responded with a certificate.", certRes.Domain)
log.Printf("[INFO][%s] Server responded with a certificate.", certRes.Domain)
return true, nil
case "processing":
@ -733,7 +765,7 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
// getIssuerCertificate requests the issuer certificate
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
logf("[INFO] acme: Requesting issuer cert from %s", url)
log.Printf("[INFO] acme: Requesting issuer cert from %s", url)
resp, err := httpGet(url)
if err != nil {
return nil, err
@ -787,14 +819,13 @@ func validate(j *jws, domain, uri string, c challenge) error {
for {
switch chlng.Status {
case "valid":
logf("[INFO][%s] The server validated our request", domain)
log.Printf("[INFO][%s] The server validated our request", domain)
return nil
case "pending":
break
case "invalid":
return handleChallengeError(chlng)
default:
return errors.New("The server returned an unexpected state")
return errors.New("the server returned an unexpected state")
}
ra, err := strconv.Atoi(hdr.Get("Retry-After"))

View file

@ -1,4 +1,4 @@
package acmev2
package acme
import (
"bytes"
@ -9,6 +9,7 @@ import (
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/base64"
"encoding/pem"
"errors"
@ -19,8 +20,6 @@ import (
"net/http"
"time"
"encoding/asn1"
"golang.org/x/crypto/ocsp"
jose "gopkg.in/square/go-jose.v2"
)
@ -118,6 +117,10 @@ func GetOCSPForCert(bundle []byte) ([]byte, *ocsp.Response, error) {
defer req.Body.Close()
ocspResBytes, err := ioutil.ReadAll(limitReader(req.Body, 1024*1024))
if err != nil {
return nil, nil, err
}
ocspRes, err := ocsp.ParseResponse(ocspResBytes, issuerCert)
if err != nil {
return nil, nil, err
@ -138,7 +141,7 @@ func getKeyAuthorization(token string, key interface{}) (string, error) {
// Generate the Key Authorization for the challenge
jwk := &jose.JSONWebKey{Key: publicKey}
if jwk == nil {
return "", errors.New("Could not generate JWK from key")
return "", errors.New("could not generate JWK from key")
}
thumbBytes, err := jwk.Thumbprint(crypto.SHA256)
if err != nil {
@ -173,7 +176,7 @@ func parsePEMBundle(bundle []byte) ([]*x509.Certificate, error) {
}
if len(certificates) == 0 {
return nil, errors.New("No certificates were found while parsing the bundle")
return nil, errors.New("no certificates were found while parsing the bundle")
}
return certificates, nil
@ -188,7 +191,7 @@ func parsePEMPrivateKey(key []byte) (crypto.PrivateKey, error) {
case "EC PRIVATE KEY":
return x509.ParseECPrivateKey(keyBlock.Bytes)
default:
return nil, errors.New("Unknown PEM header value")
return nil, errors.New("unknown PEM header value")
}
}
@ -207,7 +210,7 @@ func generatePrivateKey(keyType KeyType) (crypto.PrivateKey, error) {
return rsa.GenerateKey(rand.Reader, 8192)
}
return nil, fmt.Errorf("Invalid KeyType: %s", keyType)
return nil, fmt.Errorf("invalid KeyType: %s", keyType)
}
func generateCsr(privateKey crypto.PrivateKey, domain string, san []string, mustStaple bool) ([]byte, error) {
@ -239,10 +242,8 @@ func pemEncode(data interface{}) []byte {
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))}
}

View file

@ -1,16 +1,16 @@
package acmev2
package acme
import (
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"log"
"net"
"strings"
"time"
"github.com/miekg/dns"
"github.com/xenolf/lego/log"
)
type preCheckDNSFunc func(fqdn, value string) (bool, error)
@ -72,7 +72,7 @@ type dnsChallenge struct {
}
func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
logf("[INFO][%s] acme: Trying to solve DNS-01", domain)
log.Printf("[INFO][%s] acme: Trying to solve DNS-01", domain)
if s.provider == nil {
return errors.New("No DNS Provider configured")
@ -97,7 +97,7 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
fqdn, value, _ := DNS01Record(domain, keyAuth)
logf("[INFO][%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers)
log.Printf("[INFO][%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers)
var timeout, interval time.Duration
switch provider := s.provider.(type) {

View file

@ -1,9 +1,11 @@
package acmev2
package acme
import (
"bufio"
"fmt"
"os"
"github.com/xenolf/lego/log"
)
const (
@ -28,9 +30,9 @@ func (*DNSProviderManual) Present(domain, token, keyAuth string) error {
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")
log.Printf("[INFO] acme: Please create the following TXT record in your %s zone:", authZone)
log.Printf("[INFO] acme: %s", dnsRecord)
log.Printf("[INFO] acme: Press 'Enter' when you are done")
reader := bufio.NewReader(os.Stdin)
_, _ = reader.ReadString('\n')
@ -47,7 +49,7 @@ func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error {
return err
}
logf("[INFO] acme: You can now remove this TXT record from your %s zone:", authZone)
logf("[INFO] acme: %s", dnsRecord)
log.Printf("[INFO] acme: You can now remove this TXT record from your %s zone:", authZone)
log.Printf("[INFO] acme: %s", dnsRecord)
return nil
}

View file

@ -1,4 +1,4 @@
package acmev2
package acme
import (
"bytes"
@ -14,18 +14,6 @@ const (
invalidNonceError = "urn:ietf:params:acme:error:badNonce"
)
// ObtainError is returned when there are specific errors available
// per domain. For example in ObtainCertificate
type ObtainError map[string]error
func (e ObtainError) Error() string {
buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n")
for dom, err := range e {
buffer.WriteString(fmt.Sprintf("[%s] %s\n", dom, err))
}
return buffer.String()
}
// RemoteError is the base type for all errors specific to the ACME protocol.
type RemoteError struct {
StatusCode int `json:"status,omitempty"`
@ -55,6 +43,18 @@ type domainError struct {
Error error
}
// ObtainError is returned when there are specific errors available
// per domain. For example in ObtainCertificate
type ObtainError map[string]error
func (e ObtainError) Error() string {
buffer := bytes.NewBufferString("acme: Error -> One or more domains had a problem:\n")
for dom, err := range e {
buffer.WriteString(fmt.Sprintf("[%s] %s\n", dom, err))
}
return buffer.String()
}
func handleHTTPError(resp *http.Response) error {
var errorDetail RemoteError

View file

@ -1,4 +1,4 @@
package acmev2
package acme
import (
"encoding/json"

View file

@ -1,8 +1,9 @@
package acmev2
package acme
import (
"fmt"
"log"
"github.com/xenolf/lego/log"
)
type httpChallenge struct {
@ -18,7 +19,7 @@ func HTTP01ChallengePath(token string) string {
func (s *httpChallenge) Solve(chlng challenge, domain string) error {
logf("[INFO][%s] acme: Trying to solve HTTP-01", domain)
log.Printf("[INFO][%s] acme: Trying to solve HTTP-01", domain)
// Generate the Key Authorization for the challenge
keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey)

View file

@ -1,10 +1,12 @@
package acmev2
package acme
import (
"fmt"
"net"
"net/http"
"strings"
"github.com/xenolf/lego/log"
)
// HTTPProviderServer implements ChallengeProvider for `http-01` challenge
@ -61,9 +63,9 @@ func (s *HTTPProviderServer) serve(domain, token, keyAuth string) {
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)
log.Printf("[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)
log.Printf("[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"))
}
})

View file

@ -1,4 +1,4 @@
package acmev2
package acme
import (
"bytes"
@ -26,13 +26,13 @@ type jws struct {
func (j *jws) post(url string, content []byte) (*http.Response, error) {
signedContent, err := j.signContent(url, content)
if err != nil {
return nil, fmt.Errorf("Failed to sign content -> %s", err.Error())
return nil, fmt.Errorf("failed to sign content -> %s", err.Error())
}
data := bytes.NewBuffer([]byte(signedContent.FullSerialize()))
resp, err := httpPost(url, "application/jose+json", data)
if err != nil {
return nil, fmt.Errorf("Failed to HTTP POST to %s -> %s", url, err.Error())
return nil, fmt.Errorf("failed to HTTP POST to %s -> %s", url, err.Error())
}
nonce, nonceErr := getNonceFromResponse(resp)
@ -77,16 +77,45 @@ func (j *jws) signContent(url string, content []byte) (*jose.JSONWebSignature, e
signer, err := jose.NewSigner(signKey, &options)
if err != nil {
return nil, fmt.Errorf("Failed to create jose signer -> %s", err.Error())
return nil, fmt.Errorf("failed to create jose signer -> %s", err.Error())
}
signed, err := signer.Sign(content)
if err != nil {
return nil, fmt.Errorf("Failed to sign content -> %s", err.Error())
return nil, fmt.Errorf("failed to sign content -> %s", err.Error())
}
return signed, nil
}
func (j *jws) signEABContent(url, kid string, hmac []byte) (*jose.JSONWebSignature, error) {
jwk := jose.JSONWebKey{Key: j.privKey}
jwkJSON, err := jwk.Public().MarshalJSON()
if err != nil {
return nil, fmt.Errorf("acme: error encoding eab jwk key: %s", err.Error())
}
signer, err := jose.NewSigner(
jose.SigningKey{Algorithm: jose.HS256, Key: hmac},
&jose.SignerOptions{
EmbedJWK: false,
ExtraHeaders: map[jose.HeaderKey]interface{}{
"kid": kid,
"url": url,
},
},
)
if err != nil {
return nil, fmt.Errorf("failed to create External Account Binding jose signer -> %s", err.Error())
}
signed, err := signer.Sign(jwkJSON)
if err != nil {
return nil, fmt.Errorf("failed to External Account Binding sign content -> %s", err.Error())
}
return signed, nil
}
func (j *jws) Nonce() (string, error) {
if nonce, ok := j.nonces.Pop(); ok {
return nonce, nil
@ -122,7 +151,7 @@ func (n *nonceManager) Push(nonce string) {
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 "", fmt.Errorf("failed to get nonce from HTTP HEAD -> %s", err.Error())
}
return getNonceFromResponse(resp)
@ -131,7 +160,7 @@ func getNonce(url string) (string, error) {
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 "", fmt.Errorf("server did not respond with a proper nonce header")
}
return nonce, nil

View file

@ -1,6 +1,7 @@
package acmev2
package acme
import (
"encoding/json"
"time"
)
@ -26,11 +27,12 @@ type directory struct {
}
type accountMessage struct {
Status string `json:"status,omitempty"`
Contact []string `json:"contact,omitempty"`
TermsOfServiceAgreed bool `json:"termsOfServiceAgreed,omitempty"`
Orders string `json:"orders,omitempty"`
OnlyReturnExisting bool `json:"onlyReturnExisting,omitempty"`
Status string `json:"status,omitempty"`
Contact []string `json:"contact,omitempty"`
TermsOfServiceAgreed bool `json:"termsOfServiceAgreed,omitempty"`
Orders string `json:"orders,omitempty"`
OnlyReturnExisting bool `json:"onlyReturnExisting,omitempty"`
ExternalAccountBinding json.RawMessage `json:"externalAccountBinding,omitempty"`
}
type orderResource struct {
@ -76,9 +78,6 @@ type csrMessage struct {
Csr string `json:"csr"`
}
type emptyObjectMessage struct {
}
type revokeCertMessage struct {
Certificate string `json:"certificate"`
}

View file

@ -1,4 +1,4 @@
package acmev2
package acme
import "time"

View file

@ -1,4 +1,4 @@
package acmev2
package acme
import (
"fmt"

View file

@ -1 +0,0 @@
package acmev2

59
vendor/github.com/xenolf/lego/log/logger.go generated vendored Normal file
View file

@ -0,0 +1,59 @@
package log
import (
"log"
"os"
)
// Logger is an optional custom logger.
var Logger *log.Logger
// Fatal writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Fatal(args ...interface{}) {
if Logger == nil {
Logger = log.New(os.Stderr, "", log.LstdFlags)
}
Logger.Fatal(args...)
}
// Fatalf writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Fatalf(format string, args ...interface{}) {
if Logger == nil {
Logger = log.New(os.Stderr, "", log.LstdFlags)
}
Logger.Fatalf(format, args...)
}
// Print writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Print(args ...interface{}) {
if Logger == nil {
Logger = log.New(os.Stdout, "", log.LstdFlags)
}
Logger.Print(args...)
}
// Println writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Println(args ...interface{}) {
if Logger == nil {
Logger = log.New(os.Stdout, "", log.LstdFlags)
}
Logger.Println(args...)
}
// Printf writes a log entry.
// It uses Logger if not nil, otherwise it uses the default log.Logger.
func Printf(format string, args ...interface{}) {
if Logger == nil {
Logger = log.New(os.Stdout, "", log.LstdFlags)
}
Logger.Printf(format, args...)
}

View file

@ -8,7 +8,7 @@ import (
"github.com/edeckers/auroradnsclient"
"github.com/edeckers/auroradnsclient/records"
"github.com/edeckers/auroradnsclient/zones"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider describes a provider for AuroraDNS
@ -60,14 +60,14 @@ func (provider *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRe
}
}
return zones.ZoneRecord{}, fmt.Errorf("Could not find Zone record")
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, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
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)
}
@ -81,9 +81,12 @@ func (provider *DNSProvider) Present(domain, token, keyAuth string) error {
subdomain := fqdn[0 : len(fqdn)-len(authZone)-1]
authZone = acmev2.UnFqdn(authZone)
authZone = acme.UnFqdn(authZone)
zoneRecord, err := provider.getZoneInformationByName(authZone)
if err != nil {
return fmt.Errorf("could not create record: %v", err)
}
reqData :=
records.CreateRecordRequest{
@ -95,7 +98,7 @@ func (provider *DNSProvider) Present(domain, token, keyAuth string) error {
respData, err := provider.client.CreateRecord(zoneRecord.ID, reqData)
if err != nil {
return fmt.Errorf("Could not create record: '%s'.", err)
return fmt.Errorf("could not create record: %v", err)
}
provider.recordIDsMu.Lock()
@ -107,22 +110,22 @@ 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, _, _ := acmev2.DNS01Record(domain, keyAuth)
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)
return fmt.Errorf("unknown recordID for %q", fqdn)
}
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
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)
return fmt.Errorf("could not determine zone for domain: %q. %v", domain, err)
}
authZone = acmev2.UnFqdn(authZone)
authZone = acme.UnFqdn(authZone)
zoneRecord, err := provider.getZoneInformationByName(authZone)
if err != nil {

View file

@ -15,44 +15,49 @@ 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/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
// DNSProvider is an implementation of the acme.ChallengeProvider interface
type DNSProvider struct {
clientId string
clientID string
clientSecret string
subscriptionId string
tenantId string
subscriptionID string
tenantID string
resourceGroup string
context context.Context
context context.Context
}
// 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, AZURE_RESOURCE_GROUP
func NewDNSProvider() (*DNSProvider, error) {
clientId := os.Getenv("AZURE_CLIENT_ID")
clientID := os.Getenv("AZURE_CLIENT_ID")
clientSecret := os.Getenv("AZURE_CLIENT_SECRET")
subscriptionId := os.Getenv("AZURE_SUBSCRIPTION_ID")
tenantId := os.Getenv("AZURE_TENANT_ID")
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)
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")
func NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID, resourceGroup string) (*DNSProvider, error) {
if clientID == "" || clientSecret == "" || subscriptionID == "" || tenantID == "" || resourceGroup == "" {
var missingEnvVars []string
for _, envVar := range []string{"AZURE_CLIENT_ID", "AZURE_CLIENT_SECRET", "AZURE_SUBSCRIPTION_ID", "AZURE_TENANT_ID", "AZURE_RESOURCE_GROUP"} {
if os.Getenv(envVar) == "" {
missingEnvVars = append(missingEnvVars, envVar)
}
}
return nil, fmt.Errorf("Azure configuration missing: %s", strings.Join(missingEnvVars, ","))
}
return &DNSProvider{
clientId: clientId,
clientID: clientID,
clientSecret: clientSecret,
subscriptionId: subscriptionId,
tenantId: tenantId,
subscriptionID: subscriptionID,
tenantID: tenantID,
resourceGroup: resourceGroup,
// TODO: A timeout can be added here for cancellation purposes.
context: context.Background(),
@ -67,74 +72,77 @@ 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, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
zone, err := c.getHostedZoneID(fqdn)
if err != nil {
return err
}
rsc := dns.NewRecordSetsClient(c.subscriptionId)
rsc := dns.NewRecordSetsClient(c.subscriptionID)
spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
if err != nil {
return err
}
rsc.Authorizer = autorest.NewBearerAuthorizer(spt)
relative := toRelativeRecord(fqdn, acmev2.ToFqdn(zone))
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
rec := dns.RecordSet{
Name: &relative,
RecordSetProperties: &dns.RecordSetProperties{
TTL: to.Int64Ptr(60),
TxtRecords: &[]dns.TxtRecord{dns.TxtRecord{Value: &[]string{value}}},
TxtRecords: &[]dns.TxtRecord{{Value: &[]string{value}}},
},
}
_, err = rsc.CreateOrUpdate(c.context, c.resourceGroup, zone, relative, dns.TXT, rec, "", "")
if err != nil {
return err
}
return nil
return err
}
// Returns the relative record to the domain
func toRelativeRecord(domain, zone string) string {
return acmev2.UnFqdn(strings.TrimSuffix(domain, zone))
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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
zone, err := c.getHostedZoneID(fqdn)
if err != nil {
return err
}
relative := toRelativeRecord(fqdn, acmev2.ToFqdn(zone))
rsc := dns.NewRecordSetsClient(c.subscriptionId)
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
rsc := dns.NewRecordSetsClient(c.subscriptionID)
spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
rsc.Authorizer = autorest.NewBearerAuthorizer(spt)
_, err = rsc.Delete(c.context, c.resourceGroup, zone, relative, dns.TXT, "")
if err != nil {
return err
}
return nil
rsc.Authorizer = autorest.NewBearerAuthorizer(spt)
_, err = rsc.Delete(c.context, c.resourceGroup, zone, relative, dns.TXT, "")
return err
}
// Checks that azure has a zone for this domain name.
func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return "", err
}
// Now we want to to Azure and get the zone.
spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
if err != nil {
return "", err
}
dc := dns.NewZonesClient(c.subscriptionId)
dc := dns.NewZonesClient(c.subscriptionID)
dc.Authorizer = autorest.NewBearerAuthorizer(spt)
zone, err := dc.Get(c.context, c.resourceGroup, acmev2.UnFqdn(authZone))
zone, err := dc.Get(c.context, c.resourceGroup, acme.UnFqdn(authZone))
if err != nil {
return "", err
}
@ -146,9 +154,9 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
// NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the
// passed credentials map.
func (c *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*adal.ServicePrincipalToken, error) {
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, c.tenantId)
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, c.tenantID)
if err != nil {
panic(err)
return nil, err
}
return adal.NewServicePrincipalToken(*oauthConfig, c.clientId, c.clientSecret, scope)
return adal.NewServicePrincipalToken(*oauthConfig, c.clientID, c.clientSecret, scope)
}

View file

@ -15,26 +15,26 @@ import (
"io/ioutil"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
const bluecatUrlTemplate = "%s/Services/REST/v1"
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"`
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
// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses
// Bluecat's Address Manager REST API to manage TXT records for a domain.
type DNSProvider struct {
baseUrl string
baseURL string
userName string
password string
configName string
@ -56,7 +56,7 @@ func NewDNSProvider() (*DNSProvider, error) {
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)}
httpClient := http.Client{Timeout: 30 * time.Second}
return NewDNSProviderCredentials(server, userName, password, configName, dnsView, httpClient)
}
@ -68,7 +68,7 @@ func NewDNSProviderCredentials(server, userName, password, configName, dnsView s
}
return &DNSProvider{
baseUrl: fmt.Sprintf(bluecatUrlTemplate, server),
baseURL: fmt.Sprintf(bluecatURLTemplate, server),
userName: userName,
password: password,
configName: configName,
@ -80,7 +80,7 @@ func NewDNSProviderCredentials(server, userName, password, configName, dnsView s
// 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)
url := fmt.Sprintf("%s/%s", d.baseURL, resource)
body, err := json.Marshal(payload)
if err != nil {
@ -160,14 +160,14 @@ func (d *DNSProvider) logout() error {
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)
}
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 = ""
@ -176,7 +176,7 @@ func (d *DNSProvider) logout() error {
}
// Lookup the entity ID of the configuration named in our properties
func (d *DNSProvider) lookupConfId() (uint, error) {
func (d *DNSProvider) lookupConfID() (uint, error) {
queryArgs := map[string]string{
"parentId": strconv.Itoa(0),
"name": d.configName,
@ -194,18 +194,18 @@ func (d *DNSProvider) lookupConfId() (uint, error) {
if err != nil {
return 0, err
}
return conf.Id, nil
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()
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),
"parentId": strconv.FormatUint(uint64(confID), 10),
"name": d.dnsView,
"type": viewType,
}
@ -222,13 +222,13 @@ func (d *DNSProvider) lookupViewId(viewName string) (uint, error) {
return 0, err
}
return view.Id, nil
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
func (d *DNSProvider) lookupParentZoneID(viewID uint, fqdn string) (uint, string, error) {
parentViewID := viewID
name := ""
if fqdn != "" {
@ -237,25 +237,24 @@ func (d *DNSProvider) lookupParentZoneId(viewId uint, fqdn string) (uint, string
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
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
parentViewID = zoneID
}
}
return parentViewId, name, nil
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) {
func (d *DNSProvider) getZone(parentID uint, name string) (uint, error) {
queryArgs := map[string]string{
"parentId": strconv.FormatUint(uint64(parentId), 10),
"parentId": strconv.FormatUint(uint64(parentID), 10),
"name": name,
"type": zoneType,
}
@ -276,29 +275,32 @@ func (d *DNSProvider) getZone(parentId uint, name string) (uint, error) {
return 0, err
}
return zone.Id, nil
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)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
err := d.login()
if err != nil {
return err
}
viewId, err := d.lookupViewId(d.dnsView)
viewID, err := d.lookupViewID(d.dnsView)
if err != nil {
return err
}
parentZoneId, name, err := d.lookupParentZoneId(viewId, fqdn)
parentZoneID, name, err := d.lookupParentZoneID(viewID, fqdn)
if err != nil {
return err
}
queryArgs := map[string]string{
"parentId": strconv.FormatUint(uint64(parentZoneId), 10),
"parentId": strconv.FormatUint(uint64(parentZoneID), 10),
}
body := bluecatEntity{
@ -322,23 +324,18 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
return fmt.Errorf("Bluecat API addEntity request failed: %s", addTxtResp)
}
err = d.deploy(uint(parentZoneId))
err = d.deploy(parentZoneID)
if err != nil {
return err
}
err = d.logout()
if err != nil {
return err
}
return nil
return d.logout()
}
// Deploy the DNS config for the specified entity to the authoritative servers
func (d *DNSProvider) deploy(entityId uint) error {
func (d *DNSProvider) deploy(entityID uint) error {
queryArgs := map[string]string{
"entityId": strconv.FormatUint(uint64(entityId), 10),
"entityId": strconv.FormatUint(uint64(entityID), 10),
}
resp, err := d.sendRequest("POST", "quickDeploy", nil, queryArgs)
@ -353,25 +350,25 @@ func (d *DNSProvider) deploy(entityId uint) error {
// CleanUp removes the TXT record matching the specified parameters
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
err := d.login()
if err != nil {
return err
}
viewId, err := d.lookupViewId(d.dnsView)
viewID, err := d.lookupViewID(d.dnsView)
if err != nil {
return err
}
parentId, name, err := d.lookupParentZoneId(viewId, fqdn)
parentID, name, err := d.lookupParentZoneID(viewID, fqdn)
if err != nil {
return err
}
queryArgs := map[string]string{
"parentId": strconv.FormatUint(uint64(parentId), 10),
"parentId": strconv.FormatUint(uint64(parentID), 10),
"name": name,
"type": txtType,
}
@ -388,7 +385,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
return err
}
queryArgs = map[string]string{
"objectId": strconv.FormatUint(uint64(txtRec.Id), 10),
"objectId": strconv.FormatUint(uint64(txtRec.ID), 10),
}
resp, err = d.sendRequest("DELETE", "delete", nil, queryArgs)
@ -397,20 +394,15 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
}
defer resp.Body.Close()
err = d.deploy(parentId)
err = d.deploy(parentID)
if err != nil {
return err
}
err = d.logout()
if err != nil {
return err
}
return nil
return d.logout()
}
//JSON body for Bluecat entity requests and responses
// JSON body for Bluecat entity requests and responses
type bluecatEntity struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`

View file

@ -7,18 +7,20 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// CloudFlareAPIURL represents the API endpoint to call.
// TODO: Unexport?
const CloudFlareAPIURL = "https://api.cloudflare.com/client/v4"
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
// DNSProvider is an implementation of the acme.ChallengeProvider interface
type DNSProvider struct {
authEmail string
authKey string
@ -37,7 +39,14 @@ func NewDNSProvider() (*DNSProvider, error) {
// DNSProvider instance configured for cloudflare.
func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) {
if email == "" || key == "" {
return nil, fmt.Errorf("CloudFlare credentials missing")
missingEnvVars := []string{}
if email == "" {
missingEnvVars = append(missingEnvVars, "CLOUDFLARE_EMAIL")
}
if key == "" {
missingEnvVars = append(missingEnvVars, "CLOUDFLARE_API_KEY")
}
return nil, fmt.Errorf("CloudFlare credentials missing: %s", strings.Join(missingEnvVars, ","))
}
return &DNSProvider{
@ -54,7 +63,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, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
zoneID, err := c.getHostedZoneID(fqdn)
if err != nil {
return err
@ -62,7 +71,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
rec := cloudFlareRecord{
Type: "TXT",
Name: acmev2.UnFqdn(fqdn),
Name: acme.UnFqdn(fqdn),
Content: value,
TTL: 120,
}
@ -73,16 +82,12 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
}
_, err = c.makeRequest("POST", fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body))
if err != nil {
return err
}
return nil
return err
}
// CleanUp removes the TXT record matching the specified parameters
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
record, err := c.findTxtRecord(fqdn)
if err != nil {
@ -90,11 +95,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
}
_, err = c.makeRequest("DELETE", fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil)
if err != nil {
return err
}
return nil
return err
}
func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
@ -104,12 +105,12 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
Name string `json:"name"`
}
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return "", err
}
result, err := c.makeRequest("GET", "/zones?name="+acmev2.UnFqdn(authZone), nil)
result, err := c.makeRequest("GET", "/zones?name="+acme.UnFqdn(authZone), nil)
if err != nil {
return "", err
}
@ -135,7 +136,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, acmev2.UnFqdn(fqdn)),
fmt.Sprintf("/zones/%s/dns_records?per_page=1000&type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)),
nil,
)
if err != nil {
@ -149,12 +150,12 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) {
}
for _, rec := range records {
if rec.Name == acmev2.UnFqdn(fqdn) {
if rec.Name == acme.UnFqdn(fqdn) {
return &rec, nil
}
}
return nil, fmt.Errorf("No existing record found for %s", fqdn)
return nil, fmt.Errorf("no existing record found for %s", fqdn)
}
func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
@ -179,7 +180,6 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
req.Header.Set("X-Auth-Email", c.authEmail)
req.Header.Set("X-Auth-Key", c.authKey)
//req.Header.Set("User-Agent", userAgent())
client := http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
@ -206,7 +206,11 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
}
return nil, fmt.Errorf("Cloudflare API Error \n%s", errStr)
}
return nil, fmt.Errorf("Cloudflare API error")
strBody := "Unreadable body"
if body, err := ioutil.ReadAll(resp.Body); err == nil {
strBody = string(body)
}
return nil, fmt.Errorf("Cloudflare API error: the request %s sent a response with a body which is not in JSON format: %s", req.URL.String(), strBody)
}
return r.Result, nil

View file

@ -13,12 +13,12 @@ import (
"strconv"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
const cloudXNSBaseURL = "https://www.cloudxns.net/api2/"
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
// DNSProvider is an implementation of the acme.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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.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 := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return "", err
}
@ -101,7 +101,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
}
}
return "", fmt.Errorf("Zone %s not found in cloudxns for domain %s", authZone, fqdn)
return "", fmt.Errorf("zone %s not found in cloudxns for domain %s", authZone, fqdn)
}
func (c *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) {
@ -117,12 +117,12 @@ func (c *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) {
}
for _, record := range records {
if record.Host == acmev2.UnFqdn(fqdn) && record.Type == "TXT" {
if record.Host == acme.UnFqdn(fqdn) && record.Type == "TXT" {
return record.RecordID, nil
}
}
return "", fmt.Errorf("No existing record found for %s", fqdn)
return "", fmt.Errorf("no existing record found for %s", fqdn)
}
func (c *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error {
@ -133,7 +133,7 @@ func (c *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error {
payload := cloudXNSRecord{
ID: id,
Host: acmev2.UnFqdn(fqdn),
Host: acme.UnFqdn(fqdn),
Value: value,
Type: "TXT",
LineID: 1,
@ -146,11 +146,7 @@ func (c *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error {
}
_, err = c.makeRequest("POST", "record", body)
if err != nil {
return err
}
return nil
return err
}
func (c *DNSProvider) delTxtRecord(recordID, zoneID string) error {
@ -183,7 +179,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 := acmev2.HTTPClient.Do(req)
resp, err := acme.HTTPClient.Do(req)
if err != nil {
return nil, err
}

View file

@ -11,10 +11,10 @@ import (
"sync"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
// DNSProvider is an implementation of the acme.ChallengeProvider interface
// that uses DigitalOcean's REST API to manage TXT records for a domain.
type DNSProvider struct {
apiAuthToken string
@ -22,6 +22,12 @@ type DNSProvider struct {
recordIDsMu sync.Mutex
}
// 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 60 * time.Second, 5 * time.Second
}
// NewDNSProvider returns a DNSProvider instance configured for Digital
// Ocean. Credentials must be passed in the environment variable:
// DO_AUTH_TOKEN.
@ -44,32 +50,14 @@ func NewDNSProviderCredentials(apiAuthToken string) (*DNSProvider, error) {
// Present creates a TXT record using the specified parameters
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
// txtRecordRequest represents the request body to DO's API to make a TXT record
type txtRecordRequest struct {
RecordType string `json:"type"`
Name string `json:"name"`
Data string `json:"data"`
TTL int `json:"ttl"`
}
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
// txtRecordResponse represents a response from DO's API after making a TXT record
type txtRecordResponse struct {
DomainRecord struct {
ID int `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Data string `json:"data"`
} `json:"domain_record"`
}
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
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)
return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err)
}
authZone = acmev2.UnFqdn(authZone)
authZone = acme.UnFqdn(authZone)
reqURL := fmt.Sprintf("%s/v2/domains/%s/records", digitalOceanBaseURL, authZone)
reqData := txtRecordRequest{RecordType: "TXT", Name: fqdn, Data: value, TTL: 30}
@ -113,7 +101,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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
// get the record's unique ID from when we created it
d.recordIDsMu.Lock()
@ -123,12 +111,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
return fmt.Errorf("unknown record ID for '%s'", fqdn)
}
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
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)
return fmt.Errorf("could not determine zone for domain: '%s'. %s", domain, err)
}
authZone = acmev2.UnFqdn(authZone)
authZone = acme.UnFqdn(authZone)
reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", digitalOceanBaseURL, authZone, recordID)
req, err := http.NewRequest("DELETE", reqURL, nil)
@ -166,8 +154,20 @@ 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 60 * time.Second, 5 * time.Second
// txtRecordRequest represents the request body to DO's API to make a TXT record
type txtRecordRequest struct {
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
type txtRecordResponse struct {
DomainRecord struct {
ID int `json:"id"`
Type string `json:"type"`
Name string `json:"name"`
Data string `json:"data"`
} `json:"domain_record"`
}

View file

@ -1,10 +1,9 @@
// Factory for DNS providers
package dns
import (
"fmt"
"github.com/xenolf/lego/acmev2"
"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/bluecat"
@ -21,9 +20,9 @@ import (
"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/gcloud"
"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"
@ -38,9 +37,10 @@ import (
"github.com/xenolf/lego/providers/dns/vultr"
)
func NewDNSChallengeProviderByName(name string) (acmev2.ChallengeProvider, error) {
// NewDNSChallengeProviderByName Factory for DNS providers
func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) {
var err error
var provider acmev2.ChallengeProvider
var provider acme.ChallengeProvider
switch name {
case "azure":
provider, err = azure.NewDNSProvider()
@ -75,7 +75,7 @@ func NewDNSChallengeProviderByName(name string) (acmev2.ChallengeProvider, error
case "glesys":
provider, err = glesys.NewDNSProvider()
case "gcloud":
provider, err = googlecloud.NewDNSProvider()
provider, err = gcloud.NewDNSProvider()
case "godaddy":
provider, err = godaddy.NewDNSProvider()
case "lightsail":
@ -83,7 +83,7 @@ func NewDNSChallengeProviderByName(name string) (acmev2.ChallengeProvider, error
case "linode":
provider, err = linode.NewDNSProvider()
case "manual":
provider, err = acmev2.NewDNSProviderManual()
provider, err = acme.NewDNSProviderManual()
case "namecheap":
provider, err = namecheap.NewDNSProvider()
case "namedotcom":
@ -107,7 +107,7 @@ func NewDNSChallengeProviderByName(name string) (acmev2.ChallengeProvider, error
case "exec":
provider, err = exec.NewDNSProvider()
default:
err = fmt.Errorf("Unrecognised DNS provider: %s", name)
err = fmt.Errorf("unrecognised DNS provider: %s", name)
}
return provider, err
}

View file

@ -9,10 +9,10 @@ import (
"strings"
"github.com/dnsimple/dnsimple-go/dnsimple"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
type DNSProvider struct {
client *dnsimple.Client
}
@ -23,14 +23,14 @@ type DNSProvider struct {
// See: https://developer.dnsimple.com/v2/#authentication
func NewDNSProvider() (*DNSProvider, error) {
accessToken := os.Getenv("DNSIMPLE_OAUTH_TOKEN")
baseUrl := os.Getenv("DNSIMPLE_BASE_URL")
baseURL := os.Getenv("DNSIMPLE_BASE_URL")
return NewDNSProviderCredentials(accessToken, baseUrl)
return NewDNSProviderCredentials(accessToken, baseURL)
}
// NewDNSProviderCredentials uses the supplied credentials to return a
// DNSProvider instance configured for dnsimple.
func NewDNSProviderCredentials(accessToken, baseUrl string) (*DNSProvider, error) {
func NewDNSProviderCredentials(accessToken, baseURL string) (*DNSProvider, error) {
if accessToken == "" {
return nil, fmt.Errorf("DNSimple OAuth token is missing")
}
@ -38,8 +38,8 @@ func NewDNSProviderCredentials(accessToken, baseUrl string) (*DNSProvider, error
client := dnsimple.NewClient(dnsimple.NewOauthTokenCredentials(accessToken))
client.UserAgent = "lego"
if baseUrl != "" {
client.BaseURL = baseUrl
if baseURL != "" {
client.BaseURL = baseURL
}
return &DNSProvider{client: client}, nil
@ -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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.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 := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
if err != nil {
return "", err
}
@ -104,7 +104,7 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) {
return "", err
}
zoneName := acmev2.UnFqdn(authZone)
zoneName := acme.UnFqdn(authZone)
zones, err := c.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName})
if err != nil {
@ -119,8 +119,7 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) {
}
if hostedZone.ID == 0 {
return "", fmt.Errorf("Zone %s not found in DNSimple for domain %s", authZone, domain)
return "", fmt.Errorf("zone %s not found in DNSimple for domain %s", authZone, domain)
}
return hostedZone.Name, nil
@ -159,7 +158,7 @@ func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsim
}
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
name := acmev2.UnFqdn(fqdn)
name := acme.UnFqdn(fqdn)
if idx := strings.Index(name, "."+domain); idx != -1 {
return name[:idx]
}
@ -173,8 +172,8 @@ func (c *DNSProvider) getAccountID() (string, error) {
}
if whoamiResponse.Data.Account == nil {
return "", fmt.Errorf("DNSimple user tokens are not supported, please use an account token.")
return "", fmt.Errorf("DNSimple user tokens are not supported, please use an account token")
}
return strconv.Itoa(whoamiResponse.Data.Account.ID), nil
return strconv.FormatInt(whoamiResponse.Data.Account.ID, 10), nil
}

View file

@ -14,10 +14,10 @@ import (
"strings"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface that uses
// DNSProvider is an implementation of the acme.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 := acmev2.DNS01Record(domainName, keyAuth)
fqdn, value, ttl := acme.DNS01Record(domainName, keyAuth)
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return err
}
@ -95,18 +95,14 @@ func (d *DNSProvider) Present(domainName, token, keyAuth string) error {
record := &Record{Type: "TXT", Name: name, Value: value, TTL: ttl}
err = d.createRecord(domain, record)
if err != nil {
return err
}
return nil
return err
}
// CleanUp removes the TXT records matching the specified parameters
func (d *DNSProvider) CleanUp(domainName, token, keyAuth string) error {
fqdn, _, _ := acmev2.DNS01Record(domainName, keyAuth)
fqdn, _, _ := acme.DNS01Record(domainName, keyAuth)
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return err
}
@ -226,7 +222,7 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{})
}
client := &http.Client{
Transport: transport,
Timeout: time.Duration(10 * time.Second),
Timeout: 10 * time.Second,
}
resp, err := client.Do(req)
if err != nil {

View file

@ -8,10 +8,10 @@ import (
"strings"
"github.com/decker502/dnspod-go"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
// DNSProvider is an implementation of the acme.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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.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 := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
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 == acmev2.UnFqdn(authZone) {
if zone.Name == acme.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 := acmev2.UnFqdn(fqdn)
name := acme.UnFqdn(fqdn)
if idx := strings.Index(name, "."+domain); idx != -1 {
return name[:idx]
}

View file

@ -1,5 +1,4 @@
// Adds lego support for http://duckdns.org .
//
// Package duckdns Adds lego support for http://duckdns.org .
// See http://www.duckdns.org/spec.jsp for more info on updating TXT records.
package duckdns
@ -9,7 +8,7 @@ import (
"io/ioutil"
"os"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider adds and removes the record for the DNS challenge
@ -47,7 +46,7 @@ func makeDuckdnsURL(domain, token, txt string) string {
}
func issueDuckdnsRequest(url string) error {
response, err := acmev2.HTTPClient.Get(url)
response, err := acme.HTTPClient.Get(url)
if err != nil {
return err
}
@ -70,7 +69,7 @@ func issueDuckdnsRequest(url string) error {
//
// 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)
_, txtRecord, _ := acme.DNS01Record(domain, keyAuth)
url := makeDuckdnsURL(domain, d.token, txtRecord)
return issueDuckdnsRequest(url)
}

View file

@ -11,7 +11,7 @@ import (
"strconv"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
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 acmev2.ChallengeProvider interface that uses
// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses
// Dyn's Managed DNS API to manage TXT records for a domain.
type DNSProvider struct {
customerName string
@ -80,7 +80,7 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{})
req.Header.Set("Auth-Token", d.token)
}
client := &http.Client{Timeout: time.Duration(10 * time.Second)}
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, err
@ -158,7 +158,7 @@ func (d *DNSProvider) logout() error {
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Auth-Token", d.token)
client := &http.Client{Timeout: time.Duration(10 * time.Second)}
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
@ -176,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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return err
}
@ -206,12 +206,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
return err
}
err = d.logout()
if err != nil {
return err
}
return nil
return d.logout()
}
func (d *DNSProvider) publish(zone, notes string) error {
@ -222,19 +217,16 @@ func (d *DNSProvider) publish(zone, notes string) error {
pub := &publish{Publish: true, Notes: notes}
resource := fmt.Sprintf("Zone/%s/", zone)
_, err := d.sendRequest("PUT", resource, pub)
if err != nil {
return err
}
return nil
_, err := d.sendRequest("PUT", resource, pub)
return err
}
// CleanUp removes the TXT record matching the specified parameters
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return err
}
@ -253,7 +245,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Auth-Token", d.token)
client := &http.Client{Timeout: time.Duration(10 * time.Second)}
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
@ -269,10 +261,5 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
return err
}
err = d.logout()
if err != nil {
return err
}
return nil
return d.logout()
}

View file

@ -31,7 +31,7 @@ import (
"os/exec"
"strconv"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider adds and removes the record for the DNS challenge by calling a
@ -53,7 +53,7 @@ func NewDNSProvider() (*DNSProvider, error) {
// 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)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
cmd := exec.Command(d.program, "present", fqdn, value, strconv.Itoa(ttl))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@ -63,7 +63,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, value, ttl := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
cmd := exec.Command(d.program, "cleanup", fqdn, value, strconv.Itoa(ttl))
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

View file

@ -8,15 +8,15 @@ import (
"os"
"github.com/exoscale/egoscale"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
type DNSProvider struct {
client *egoscale.Client
}
// Credentials must be passed in the environment variables:
// NewDNSProvider 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")
@ -25,7 +25,7 @@ func NewDNSProvider() (*DNSProvider, error) {
return NewDNSProviderClient(key, secret, endpoint)
}
// Uses the supplied parameters to return a DNSProvider instance
// NewDNSProviderClient Uses the supplied parameters to return a DNSProvider instance
// configured for Exoscale.
func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) {
if key == "" || secret == "" {
@ -42,13 +42,13 @@ 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 := acmev2.DNS01Record(domain, keyAuth)
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)
recordID, err := c.FindExistingRecordID(zone, recordName)
if err != nil {
return err
}
@ -78,13 +78,13 @@ 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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain)
if err != nil {
return err
}
recordID, err := c.FindExistingRecordId(zone, recordName)
recordID, err := c.FindExistingRecordID(zone, recordName)
if err != nil {
return err
}
@ -99,9 +99,9 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
return nil
}
// Query Exoscale to find an existing record for this name.
// FindExistingRecordID 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) {
func (c *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, error) {
records, err := c.client.GetRecords(zone)
if err != nil {
return -1, errors.New("Error while retrievening DNS records: " + err.Error())
@ -114,14 +114,14 @@ func (c *DNSProvider) FindExistingRecordId(zone, recordName string) (int64, erro
return 0, nil
}
// Extract DNS zone and DNS entry name
// FindZoneAndRecordName Extract DNS zone and DNS entry name
func (c *DNSProvider) FindZoneAndRecordName(fqdn, domain string) (string, string, error) {
zone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
if err != nil {
return "", "", err
}
zone = acmev2.UnFqdn(zone)
name := acmev2.UnFqdn(fqdn)
zone = acme.UnFqdn(zone)
name := acme.UnFqdn(fqdn)
name = name[:len(name)-len("."+zone)]
return zone, name, nil

View file

@ -7,10 +7,10 @@ import (
configdns "github.com/akamai/AkamaiOPEN-edgegrid-golang/configdns-v1"
"github.com/akamai/AkamaiOPEN-edgegrid-golang/edgegrid"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
type DNSProvider struct {
config edgegrid.Config
}
@ -47,7 +47,7 @@ func NewDNSProviderClient(host, clientToken, clientSecret, accessToken string) (
// 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)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
zoneName, recordName, err := c.findZoneAndRecordName(fqdn, domain)
if err != nil {
return err
@ -81,7 +81,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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
zoneName, recordName, err := c.findZoneAndRecordName(fqdn, domain)
if err != nil {
return err
@ -108,12 +108,12 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
}
func (c *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string, error) {
zone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
if err != nil {
return "", "", err
}
zone = acmev2.UnFqdn(zone)
name := acmev2.UnFqdn(fqdn)
zone = acme.UnFqdn(zone)
name := acme.UnFqdn(fqdn)
name = name[:len(name)-len("."+zone)]
return zone, name, nil

View file

@ -14,7 +14,7 @@ import (
"sync"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// 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 = acmev2.FindZoneByFqdn
findZoneByFqdn = acme.FindZoneByFqdn
)
// inProgressInfo contains information about an in-progress challenge
@ -37,7 +37,7 @@ type inProgressInfo struct {
}
// DNSProvider is an implementation of the
// acmev2.ChallengeProviderTimeout interface that uses Gandi's XML-RPC
// acme.ChallengeProviderTimeout interface that uses Gandi's XML-RPC
// API to manage TXT records for a domain.
type DNSProvider struct {
apiKey string
@ -70,19 +70,22 @@ 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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.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, acmev2.RecursiveNameservers)
authZone, err := findZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return fmt.Errorf("Gandi DNS: findZoneByFqdn failure: %v", err)
}
zoneID, err := d.getZoneID(authZone)
if err != nil {
return err
}
// determine name of TXT record
if !strings.HasSuffix(
strings.ToLower(fqdn), strings.ToLower("."+authZone)) {
@ -90,40 +93,49 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
"Gandi 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()
if _, ok := d.inProgressAuthZones[authZone]; ok {
return fmt.Errorf(
"Gandi DNS: challenge already in progress for authZone %s",
authZone)
}
// perform API actions to create and activate new gandi zone
// containing the required TXT record
newZoneName := fmt.Sprintf(
"%s [ACME Challenge %s]",
acmev2.UnFqdn(authZone), time.Now().Format(time.RFC822Z))
acme.UnFqdn(authZone), time.Now().Format(time.RFC822Z))
newZoneID, err := d.cloneZone(zoneID, newZoneName)
if err != nil {
return err
}
newZoneVersion, err := d.newZoneVersion(newZoneID)
if err != nil {
return err
}
err = d.addTXTRecord(newZoneID, newZoneVersion, name, value, ttl)
if err != nil {
return err
}
err = d.setZoneVersion(newZoneID, newZoneVersion)
if err != nil {
return err
}
err = d.setZone(authZone, newZoneID)
if err != nil {
return err
}
// save data necessary for CleanUp
d.inProgressFQDNs[fqdn] = inProgressInfo{
zoneID: zoneID,
@ -138,29 +150,29 @@ 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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
// acquire lock and retrieve zoneID, newZoneID and authZone
d.inProgressMu.Lock()
defer d.inProgressMu.Unlock()
if _, ok := d.inProgressFQDNs[fqdn]; !ok {
// if there is no cleanup information then just return
return nil
}
zoneID := d.inProgressFQDNs[fqdn].zoneID
newZoneID := d.inProgressFQDNs[fqdn].newZoneID
authZone := d.inProgressFQDNs[fqdn].authZone
delete(d.inProgressFQDNs, fqdn)
delete(d.inProgressAuthZones, authZone)
// perform API actions to restore old gandi zone for authZone
err := d.setZone(authZone, zoneID)
if err != nil {
return err
}
err = d.deleteZone(newZoneID)
if err != nil {
return err
}
return nil
return d.deleteZone(newZoneID)
}
// Timeout returns the values (40*time.Minute, 60*time.Second) which
@ -259,15 +271,18 @@ func (e rpcError) Error() string {
func httpPost(url string, bodyType string, body io.Reader) ([]byte, error) {
client := http.Client{Timeout: 60 * time.Second}
resp, err := client.Post(url, bodyType, body)
if err != nil {
return nil, fmt.Errorf("Gandi DNS: HTTP Post Error: %v", err)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("Gandi DNS: HTTP Post Error: %v", err)
}
return b, nil
}
@ -281,12 +296,14 @@ func rpcCall(call *methodCall, resp response) error {
if err != nil {
return fmt.Errorf("Gandi DNS: Marshal Error: %v", err)
}
// post
b = append([]byte(`<?xml version="1.0"?>`+"\n"), b...)
respBody, err := httpPost(endpoint, "text/xml", bytes.NewReader(b))
if err != nil {
return err
}
// unmarshal
err = xml.Unmarshal(respBody, resp)
if err != nil {
@ -313,12 +330,14 @@ func (d *DNSProvider) getZoneID(domain string) (int, error) {
if err != nil {
return 0, err
}
var zoneID int
for _, member := range resp.StructMembers {
if member.Name == "zone_id" {
zoneID = member.ValueInt
}
}
if zoneID == 0 {
return 0, fmt.Errorf(
"Gandi DNS: Could not determine zone_id for %s", domain)
@ -346,12 +365,14 @@ func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) {
if err != nil {
return 0, err
}
var newZoneID int
for _, member := range resp.StructMembers {
if member.Name == "id" {
newZoneID = member.ValueInt
}
}
if newZoneID == 0 {
return 0, fmt.Errorf("Gandi DNS: Could not determine cloned zone_id")
}
@ -370,6 +391,7 @@ func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) {
if err != nil {
return 0, err
}
if resp.Value == 0 {
return 0, fmt.Errorf("Gandi DNS: Could not create new zone version")
}
@ -402,10 +424,7 @@ func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value s
},
},
}, resp)
if err != nil {
return err
}
return nil
return err
}
func (d *DNSProvider) setZoneVersion(zoneID int, version int) error {
@ -421,6 +440,7 @@ func (d *DNSProvider) setZoneVersion(zoneID int, version int) error {
if err != nil {
return err
}
if !resp.Value {
return fmt.Errorf("Gandi DNS: could not set zone version")
}
@ -440,12 +460,14 @@ func (d *DNSProvider) setZone(domain string, zoneID int) error {
if err != nil {
return err
}
var respZoneID int
for _, member := range resp.StructMembers {
if member.Name == "zone_id" {
respZoneID = member.ValueInt
}
}
if respZoneID != zoneID {
return fmt.Errorf(
"Gandi DNS: Could not set new zone_id for %s", domain)
@ -465,6 +487,7 @@ func (d *DNSProvider) deleteZone(zoneID int) error {
if err != nil {
return err
}
if !resp.Value {
return fmt.Errorf("Gandi DNS: could not delete zone_id")
}

View file

@ -12,7 +12,7 @@ import (
"sync"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// Gandi API reference: http://doc.livedns.gandi.net/
@ -21,9 +21,10 @@ var (
// endpoint is the Gandi API endpoint used by Present and
// CleanUp. It is overridden during tests.
endpoint = "https://dns.api.gandi.net/api/v5"
// findZoneByFqdn determines the DNS zone of an fqdn. It is overridden
// during tests.
findZoneByFqdn = acmev2.FindZoneByFqdn
findZoneByFqdn = acme.FindZoneByFqdn
)
// inProgressInfo contains information about an in-progress challenge
@ -33,7 +34,7 @@ type inProgressInfo struct {
}
// DNSProvider is an implementation of the
// acmev2.ChallengeProviderTimeout interface that uses Gandi's LiveDNS
// acme.ChallengeProviderTimeout interface that uses Gandi's LiveDNS
// API to manage TXT records for a domain.
type DNSProvider struct {
apiKey string
@ -62,15 +63,17 @@ 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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
if ttl < 300 {
ttl = 300 // 300 is gandi minimum value for ttl
}
// find authZone
authZone, err := findZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := findZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return fmt.Errorf("Gandi DNS: findZoneByFqdn failure: %v", err)
}
// determine name of TXT record
if !strings.HasSuffix(
strings.ToLower(fqdn), strings.ToLower("."+authZone)) {
@ -78,15 +81,18 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
"Gandi 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
err = d.addTXTRecord(acmev2.UnFqdn(authZone), name, value, ttl)
err = d.addTXTRecord(acme.UnFqdn(authZone), name, value, ttl)
if err != nil {
return err
}
// save data necessary for CleanUp
d.inProgressFQDNs[fqdn] = inProgressInfo{
authZone: authZone,
@ -97,7 +103,8 @@ 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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
// acquire lock and retrieve authZone
d.inProgressMu.Lock()
defer d.inProgressMu.Unlock()
@ -105,15 +112,13 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
// if there is no cleanup information then just return
return nil
}
fieldName := d.inProgressFQDNs[fqdn].fieldName
authZone := d.inProgressFQDNs[fqdn].authZone
delete(d.inProgressFQDNs, fqdn)
// delete TXT record from authZone
err := d.deleteTXTRecord(acmev2.UnFqdn(authZone), fieldName)
if err != nil {
return err
}
return nil
return d.deleteTXTRecord(acme.UnFqdn(authZone), fieldName)
}
// Timeout returns the values (20*time.Minute, 20*time.Second) which
@ -149,16 +154,18 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf
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.apiKey) > 0 {
req.Header.Set("X-Api-Key", d.apiKey)
}
client := &http.Client{Timeout: time.Duration(10 * time.Second)}
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, err

View file

@ -1,17 +1,17 @@
// Package googlecloud implements a DNS provider for solving the DNS-01
// Package gcloud implements a DNS provider for solving the DNS-01
// challenge using Google Cloud DNS.
package googlecloud
package gcloud
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/dns/v1"
@ -28,10 +28,10 @@ type DNSProvider struct {
// A Service Account file can be passed in the environment variable:
// GCE_SERVICE_ACCOUNT_FILE
func NewDNSProvider() (*DNSProvider, error) {
project := os.Getenv("GCE_PROJECT")
if saFile, ok := os.LookupEnv("GCE_SERVICE_ACCOUNT_FILE"); ok {
return NewDNSProviderServiceAccount(project, saFile)
return NewDNSProviderServiceAccount(saFile)
}
project := os.Getenv("GCE_PROJECT")
return NewDNSProviderCredentials(project)
}
@ -58,10 +58,7 @@ func NewDNSProviderCredentials(project string) (*DNSProvider, error) {
// NewDNSProviderServiceAccount uses the supplied service account JSON file to
// return a DNSProvider instance configured for Google Cloud DNS.
func NewDNSProviderServiceAccount(project string, saFile string) (*DNSProvider, error) {
if project == "" {
return nil, fmt.Errorf("Google Cloud project name missing")
}
func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) {
if saFile == "" {
return nil, fmt.Errorf("Google Cloud Service Account file missing")
}
@ -70,11 +67,22 @@ func NewDNSProviderServiceAccount(project string, saFile string) (*DNSProvider,
if err != nil {
return nil, fmt.Errorf("Unable to read Service Account file: %v", err)
}
// read project id from service account file
var datJSON struct {
ProjectID string `json:"project_id"`
}
err = json.Unmarshal(dat, &datJSON)
if err != nil || datJSON.ProjectID == "" {
return nil, fmt.Errorf("Project ID not found in Google Cloud Service Account file")
}
project := datJSON.ProjectID
conf, err := google.JWTConfigFromJSON(dat, dns.NdevClouddnsReadwriteScope)
if err != nil {
return nil, fmt.Errorf("Unable to acquire config: %v", err)
}
client := conf.Client(oauth2.NoContext)
client := conf.Client(context.Background())
svc, err := dns.New(client)
if err != nil {
@ -88,7 +96,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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
zone, err := c.getHostedZone(domain)
if err != nil {
@ -135,7 +143,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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
zone, err := c.getHostedZone(domain)
if err != nil {
@ -167,7 +175,7 @@ func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
// getHostedZone returns the managed-zone
func (c *DNSProvider) getHostedZone(domain string) (string, error) {
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
if err != nil {
return "", err
}

View file

@ -6,14 +6,14 @@ import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"strings"
"sync"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
"github.com/xenolf/lego/log"
)
// GleSYS API reference: https://github.com/GleSYS/API/wiki/API-Documentation
@ -21,24 +21,8 @@ import (
// 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
// acme.ChallengeProviderTimeout interface that uses GleSYS
// API to manage TXT records for a domain.
type DNSProvider struct {
apiUser string
@ -71,15 +55,16 @@ func NewDNSProviderCredentials(apiUser string, apiKey string) (*DNSProvider, err
// 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)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
if ttl < 60 {
ttl = 60 // 60 is GleSYS minimum value for ttl
}
// find authZone
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.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)) {
@ -87,23 +72,27 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
"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)
recordID, err := d.addTXTRecord(domain, acme.UnFqdn(authZone), name, value, ttl)
if err != nil {
return err
}
// save data necessary for CleanUp
d.activeRecords[fqdn] = recordId
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)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
// acquire lock and retrieve authZone
d.inProgressMu.Lock()
defer d.inProgressMu.Unlock()
@ -111,14 +100,12 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
// if there is no cleanup information then just return
return nil
}
recordId := d.activeRecords[fqdn]
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
return d.deleteTXTRecord(domain, recordID)
}
// Timeout returns the values (20*time.Minute, 20*time.Second) which
@ -135,7 +122,7 @@ type addRecordRequest struct {
Host string `json:"host"`
Type string `json:"type"`
Data string `json:"data"`
Ttl int `json:"ttl,omitempty"`
TTL int `json:"ttl,omitempty"`
}
type deleteRecordRequest struct {
@ -160,14 +147,16 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf
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)}
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, err
@ -177,6 +166,7 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf
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)
@ -191,10 +181,10 @@ func (d *DNSProvider) addTXTRecord(fqdn string, domain string, name string, valu
Host: name,
Type: "TXT",
Data: value,
Ttl: ttl,
TTL: ttl,
})
if response != nil && response.Response.Status.Code == 200 {
logf("[INFO][%s] GleSYS DNS: Successfully created recordid %d", fqdn, response.Response.Record.Recordid)
log.Printf("[INFO][%s] GleSYS DNS: Successfully created recordid %d", fqdn, response.Response.Record.Recordid)
return response.Response.Record.Recordid, nil
}
return 0, err
@ -205,7 +195,7 @@ func (d *DNSProvider) deleteTXTRecord(fqdn string, recordid int) error {
Recordid: recordid,
})
if response != nil && response.Response.Status.Code == 200 {
logf("[INFO][%s] GleSYS DNS: Successfully deleted recordid %d", fqdn, recordid)
log.Printf("[INFO][%s] GleSYS DNS: Successfully deleted recordid %d", fqdn, recordid)
}
return err
}

View file

@ -13,13 +13,13 @@ import (
"io/ioutil"
"strings"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// GoDaddyAPIURL represents the API endpoint to call.
const apiURL = "https://api.godaddy.com"
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
// DNSProvider is an implementation of the acme.ChallengeProvider interface
type DNSProvider struct {
apiKey string
apiSecret string
@ -51,7 +51,7 @@ func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
}
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
name := acmev2.UnFqdn(fqdn)
name := acme.UnFqdn(fqdn)
if idx := strings.Index(name, "."+domain); idx != -1 {
return name[:idx]
}
@ -60,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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
domainZone, err := c.getZone(fqdn)
if err != nil {
return err
@ -76,7 +76,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
Type: "TXT",
Name: recordName,
Data: value,
Ttl: ttl,
TTL: ttl,
},
}
@ -99,14 +99,14 @@ func (c *DNSProvider) updateRecords(records []DNSRecord, domainZone string, reco
if resp.StatusCode != http.StatusOK {
bodyBytes, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("Could not create record %v; Status: %v; Body: %s\n", string(body), resp.StatusCode, string(bodyBytes))
return fmt.Errorf("could not create record %v; Status: %v; Body: %s", string(body), resp.StatusCode, string(bodyBytes))
}
return nil
}
// 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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
domainZone, err := c.getZone(fqdn)
if err != nil {
return err
@ -125,12 +125,12 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
}
func (c *DNSProvider) getZone(fqdn string) (string, error) {
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return "", err
}
return acmev2.UnFqdn(authZone), nil
return acme.UnFqdn(authZone), nil
}
func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, error) {
@ -147,10 +147,11 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Res
return client.Do(req)
}
// DNSRecord a DNS record
type DNSRecord struct {
Type string `json:"type"`
Name string `json:"name"`
Data string `json:"data"`
Priority int `json:"priority,omitempty"`
Ttl int `json:"ttl,omitempty"`
TTL int `json:"ttl,omitempty"`
}

View file

@ -11,14 +11,14 @@ import (
"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"
"github.com/xenolf/lego/acme"
)
const (
maxRetries = 5
)
// DNSProvider implements the acmev2.ChallengeProvider interface
// DNSProvider implements the acme.ChallengeProvider interface
type DNSProvider struct {
client *lightsail.Lightsail
}
@ -71,7 +71,7 @@ func NewDNSProvider() (*DNSProvider, error) {
// Present creates a TXT record using the specified parameters
func (r *DNSProvider) Present(domain, token, keyAuth string) error {
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
value = `"` + value + `"`
err := r.newTxtRecord(domain, fqdn, value)
return err
@ -79,7 +79,7 @@ func (r *DNSProvider) Present(domain, token, keyAuth string) error {
// CleanUp removes the TXT record matching the specified parameters
func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, value, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
value = `"` + value + `"`
params := &lightsail.DeleteDomainEntryInput{
DomainName: aws.String(domain),

View file

@ -9,7 +9,7 @@ import (
"time"
"github.com/timewasted/linode/dns"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
const (
@ -19,11 +19,11 @@ const (
)
type hostedZoneInfo struct {
domainId int
domainID int
resourceName string
}
// DNSProvider implements the acmev2.ChallengeProvider interface.
// DNSProvider implements the acme.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, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
zone, err := p.getHostedZoneInfo(fqdn)
if err != nil {
return err
}
if _, err = p.linode.CreateDomainResourceTXT(zone.domainId, acmev2.UnFqdn(fqdn), value, 60); err != nil {
if _, err = p.linode.CreateDomainResourceTXT(zone.domainID, acme.UnFqdn(fqdn), value, 60); err != nil {
return err
}
@ -81,14 +81,14 @@ 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, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
zone, err := p.getHostedZoneInfo(fqdn)
if err != nil {
return err
}
// Get all TXT records for the specified domain.
resources, err := p.linode.GetResourcesByType(zone.domainId, "TXT")
resources, err := p.linode.GetResourcesByType(zone.domainID, "TXT")
if err != nil {
return err
}
@ -101,7 +101,7 @@ func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error {
return err
}
if resp.ResourceID != resource.ResourceID {
return errors.New("Error deleting resource: resource IDs do not match!")
return errors.New("error deleting resource: resource IDs do not match")
}
break
}
@ -112,20 +112,20 @@ 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 := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return nil, err
}
resourceName := strings.TrimSuffix(fqdn, "."+authZone)
// Query the authority zone.
domain, err := p.linode.GetDomain(acmev2.UnFqdn(authZone))
domain, err := p.linode.GetDomain(acme.UnFqdn(authZone))
if err != nil {
return nil, err
}
return &hostedZoneInfo{
domainId: domain.DomainID,
domainID: domain.DomainID,
resourceName: resourceName,
}, nil
}

View file

@ -12,7 +12,7 @@ import (
"strings"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// 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 = acmev2.UnFqdn(domain)
domain = acme.UnFqdn(domain)
parts := strings.Split(domain, ".")
// Find the longest matching TLD.
@ -144,7 +144,7 @@ func newChallenge(domain, keyAuth string, tlds map[string]string) (*challenge, e
}
}
if longest < 1 {
return nil, fmt.Errorf("Invalid domain name '%s'", domain)
return nil, fmt.Errorf("invalid domain name %q", domain)
}
tld := strings.Join(parts[longest:], ".")
@ -155,7 +155,7 @@ func newChallenge(domain, keyAuth string, tlds map[string]string) (*challenge, e
host = strings.Join(parts[:longest-1], ".")
}
key, keyValue, _ := acmev2.DNS01Record(domain, keyAuth)
key, keyValue, _ := acme.DNS01Record(domain, keyAuth)
return &challenge{
domain: domain,
@ -318,7 +318,7 @@ func (d *DNSProvider) setHosts(ch *challenge, hosts []host) error {
shr.Errors[0].Description, shr.Errors[0].Number)
}
if shr.Result.IsSuccess != "true" {
return fmt.Errorf("Namecheap setHosts failed.")
return fmt.Errorf("Namecheap setHosts failed")
}
return nil

View file

@ -8,10 +8,10 @@ import (
"strings"
"github.com/namedotcom/go/namecom"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
// DNSProvider is an implementation of the acme.ChallengeProvider interface.
type DNSProvider struct {
client *namecom.NameCom
}
@ -47,7 +47,7 @@ func NewDNSProviderCredentials(username, apiToken, server 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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
request := &namecom.Record{
DomainName: domain,
@ -67,7 +67,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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
records, err := c.getRecords(domain)
if err != nil {
@ -116,7 +116,7 @@ func (c *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) {
}
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
name := acmev2.UnFqdn(fqdn)
name := acme.UnFqdn(fqdn)
if idx := strings.Index(name, "."+domain); idx != -1 {
return name[:idx]
}

View file

@ -8,12 +8,12 @@ import (
"os"
"time"
"github.com/xenolf/lego/acmev2"
"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 acmev2.ChallengeProvider interface.
// DNSProvider is an implementation of the acme.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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
zone, err := c.getHostedZone(domain)
if err != nil {
return err
}
name := acmev2.UnFqdn(fqdn)
name := acme.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 := acmev2.UnFqdn(fqdn)
name := acme.UnFqdn(fqdn)
return &dns.Record{
Type: "TXT",

View file

@ -2,11 +2,12 @@ package otc
import (
"fmt"
"github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
var fakeOTCUserName = "test"
@ -15,12 +16,14 @@ var fakeOTCDomainName = "test"
var fakeOTCProjectName = "test"
var fakeOTCToken = "62244bc21da68d03ebac94e6636ff01f"
// DNSMock mock
type DNSMock struct {
t *testing.T
Server *httptest.Server
Mux *http.ServeMux
}
// NewDNSMock create a new DNSMock
func NewDNSMock(t *testing.T) *DNSMock {
return &DNSMock{
t: t,
@ -38,6 +41,7 @@ func (m *DNSMock) ShutdownServer() {
m.Server.Close()
}
// HandleAuthSuccessfully Handle auth successfully
func (m *DNSMock) HandleAuthSuccessfully() {
m.Mux.HandleFunc("/v3/auth/token", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Subject-Token", fakeOTCToken)
@ -64,6 +68,7 @@ func (m *DNSMock) HandleAuthSuccessfully() {
})
}
// HandleListZonesSuccessfully Handle list zones successfully
func (m *DNSMock) HandleListZonesSuccessfully() {
m.Mux.HandleFunc("/v2/zones", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{
@ -79,6 +84,7 @@ func (m *DNSMock) HandleListZonesSuccessfully() {
})
}
// HandleListZonesEmpty Handle list zones empty
func (m *DNSMock) HandleListZonesEmpty() {
m.Mux.HandleFunc("/v2/zones", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{
@ -93,6 +99,7 @@ func (m *DNSMock) HandleListZonesEmpty() {
})
}
// HandleDeleteRecordsetsSuccessfully Handle delete recordsets successfully
func (m *DNSMock) HandleDeleteRecordsetsSuccessfully() {
m.Mux.HandleFunc("/v2/zones/123123/recordsets/321321", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{
@ -107,6 +114,7 @@ func (m *DNSMock) HandleDeleteRecordsetsSuccessfully() {
})
}
// HandleListRecordsetsEmpty Handle list recordsets empty
func (m *DNSMock) HandleListRecordsetsEmpty() {
m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{
@ -118,6 +126,8 @@ func (m *DNSMock) HandleListRecordsetsEmpty() {
assert.Equal(m.t, r.URL.RawQuery, "type=TXT&name=_acme-challenge.example.com.")
})
}
// HandleListRecordsetsSuccessfully Handle list recordsets successfully
func (m *DNSMock) HandleListRecordsetsSuccessfully() {
m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {

View file

@ -12,10 +12,10 @@ import (
"os"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface that uses
// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses
// OTC's Managed DNS API to manage TXT records for a domain.
type DNSProvider struct {
identityEndpoint string
@ -59,6 +59,7 @@ func NewDNSProviderCredentials(domainName, userName, password, projectName, iden
}, nil
}
// SendRequest send request
func (d *DNSProvider) SendRequest(method, resource string, payload interface{}) (io.Reader, error) {
url := fmt.Sprintf("%s/%s", d.otcBaseURL, resource)
@ -81,7 +82,7 @@ func (d *DNSProvider) SendRequest(method, resource string, payload interface{})
tr.DisableKeepAlives = true
client := &http.Client{
Timeout: time.Duration(10 * time.Second),
Timeout: 10 * time.Second,
Transport: tr,
}
resp, err := client.Do(req)
@ -168,7 +169,7 @@ func (d *DNSProvider) loginRequest() error {
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: time.Duration(10 * time.Second)}
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
@ -221,12 +222,7 @@ func (d *DNSProvider) loginRequest() error {
// Starts a new OTC API Session. Authenticates using userName, password
// and receives a token to be used in for subsequent requests.
func (d *DNSProvider) login() error {
err := d.loginRequest()
if err != nil {
return err
}
return nil
return d.loginRequest()
}
func (d *DNSProvider) getZoneID(zone string) (string, error) {
@ -305,21 +301,18 @@ func (d *DNSProvider) deleteRecordSet(zoneID, recordID string) error {
resource := fmt.Sprintf("zones/%s/recordsets/%s", zoneID, recordID)
_, err := d.SendRequest("DELETE", resource, nil)
if err != nil {
return err
}
return nil
return err
}
// 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)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
if ttl < 300 {
ttl = 300 // 300 is otc minimum value for ttl
}
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return err
}
@ -340,7 +333,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
Ttl int `json:"ttl"`
TTL int `json:"ttl"`
Records []string `json:"records"`
}
@ -348,23 +341,18 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
Name: fqdn,
Description: "Added TXT record for ACME dns-01 challenge using lego client",
Type: "TXT",
Ttl: 300,
TTL: ttl,
Records: []string{fmt.Sprintf("\"%s\"", value)},
}
_, err = d.SendRequest("POST", resource, r1)
if err != nil {
return err
}
return nil
return err
}
// CleanUp removes the TXT record matching the specified parameters
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return err
}
@ -375,7 +363,6 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
}
zoneID, err := d.getZoneID(authZone)
if err != nil {
return err
}
@ -384,5 +371,6 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
if err != nil {
return fmt.Errorf("unable go get record %s for zone %s: %s", fqdn, domain, err)
}
return d.deleteRecordSet(zoneID, recordID)
}

View file

@ -1,4 +1,4 @@
// Package OVH implements a DNS provider for solving the DNS-01
// Package ovh implements a DNS provider for solving the DNS-01
// challenge using OVH DNS.
package ovh
@ -9,13 +9,13 @@ import (
"sync"
"github.com/ovh/go-ovh/ovh"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// OVH API reference: https://eu.api.ovh.com/
// Create a Token: https://eu.api.ovh.com/createToken/
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
// DNSProvider is an implementation of the acme.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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
// Parse domain name
authZone, err := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
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 = acmev2.UnFqdn(authZone)
authZone = acme.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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.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 := acmev2.FindZoneByFqdn(acmev2.ToFqdn(domain), acmev2.RecursiveNameservers)
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 = acmev2.UnFqdn(authZone)
authZone = acme.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 := acmev2.UnFqdn(fqdn)
name := acme.UnFqdn(fqdn)
if idx := strings.Index(name, "."+domain); idx != -1 {
return name[:idx]
}

View file

@ -14,10 +14,10 @@ import (
"strings"
"time"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface
// DNSProvider is an implementation of the acme.ChallengeProvider interface
type DNSProvider struct {
apiKey string
host *url.URL
@ -29,12 +29,12 @@ type DNSProvider struct {
// PDNS_API_URL and PDNS_API_KEY.
func NewDNSProvider() (*DNSProvider, error) {
key := os.Getenv("PDNS_API_KEY")
hostUrl, err := url.Parse(os.Getenv("PDNS_API_URL"))
hostURL, err := url.Parse(os.Getenv("PDNS_API_URL"))
if err != nil {
return nil, err
}
return NewDNSProviderCredentials(hostUrl, key)
return NewDNSProviderCredentials(hostURL, key)
}
// NewDNSProviderCredentials uses the supplied credentials to return a
@ -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, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, _ := acme.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 = acmev2.UnFqdn(fqdn)
name = acme.UnFqdn(fqdn)
}
rec := pdnsRecord{
@ -90,7 +90,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
rrsets := rrSets{
RRSets: []rrSet{
rrSet{
{
Name: name,
ChangeType: "REPLACE",
Type: "TXT",
@ -107,17 +107,12 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
}
_, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body))
if err != nil {
fmt.Println("here")
return err
}
return nil
return err
}
// CleanUp removes the TXT record matching the specified parameters
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
zone, err := c.getHostedZone(fqdn)
if err != nil {
@ -131,7 +126,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
rrsets := rrSets{
RRSets: []rrSet{
rrSet{
{
Name: set.Name,
Type: set.Type,
ChangeType: "DELETE",
@ -144,16 +139,12 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
}
_, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body))
if err != nil {
return err
}
return nil
return err
}
func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) {
var zone hostedZone
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return nil, err
}
@ -172,7 +163,7 @@ func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) {
url = ""
for _, zone := range zones {
if acmev2.UnFqdn(zone.Name) == acmev2.UnFqdn(authZone) {
if acme.UnFqdn(zone.Name) == acme.UnFqdn(authZone) {
url = zone.URL
}
}
@ -215,12 +206,12 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) {
}
for _, set := range zone.RRSets {
if (set.Name == acmev2.UnFqdn(fqdn) || set.Name == fqdn) && set.Type == "TXT" {
if (set.Name == acme.UnFqdn(fqdn) || set.Name == fqdn) && set.Type == "TXT" {
return &set, nil
}
}
return nil, fmt.Errorf("No existing record found for %s", fqdn)
return nil, fmt.Errorf("no existing record found for %s", fqdn)
}
func (c *DNSProvider) getAPIVersion() {

View file

@ -11,13 +11,13 @@ import (
"os"
"time"
"github.com/xenolf/lego/acmev2"
"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 acmev2.ChallengeProvider interface
// DNSProvider is an implementation of the acme.ChallengeProvider interface
// used to store the reusable token and DNS API endpoint
type DNSProvider struct {
token string
@ -92,7 +92,7 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) {
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)
return nil, fmt.Errorf("error querying Rackspace Identity API: %v", err)
}
defer resp.Body.Close()
@ -115,7 +115,7 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) {
}
}
if dnsEndpoint == "" {
return nil, fmt.Errorf("Failed to populate DNS endpoint, check Rackspace API for changes.")
return nil, fmt.Errorf("failed to populate DNS endpoint, check Rackspace API for changes")
}
return &DNSProvider{
@ -126,15 +126,15 @@ 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, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
zoneID, err := c.getHostedZoneID(fqdn)
if err != nil {
return err
}
rec := RackspaceRecords{
RackspaceRecord: []RackspaceRecord{{
Name: acmev2.UnFqdn(fqdn),
rec := Records{
Record: []Record{{
Name: acme.UnFqdn(fqdn),
Type: "TXT",
Data: value,
TTL: 300,
@ -147,16 +147,12 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
}
_, err = c.makeRequest("POST", fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body))
if err != nil {
return err
}
return nil
return err
}
// CleanUp removes the TXT record matching the specified parameters
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
zoneID, err := c.getHostedZoneID(fqdn)
if err != nil {
return err
@ -168,11 +164,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
}
_, err = c.makeRequest("DELETE", fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil)
if err != nil {
return err
}
return nil
return err
}
// getHostedZoneID performs a lookup to get the DNS zone which needs
@ -187,12 +179,12 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
} `json:"domains"`
}
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return 0, err
}
result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acmev2.UnFqdn(authZone)), nil)
result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil)
if err != nil {
return 0, err
}
@ -205,36 +197,35 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
// 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 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, acmev2.UnFqdn(fqdn)), nil)
func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*Record, 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
var records Records
err = json.Unmarshal(result, &records)
if err != nil {
return nil, err
}
recordsLength := len(records.RackspaceRecord)
recordsLength := len(records.Record)
switch recordsLength {
case 1:
break
case 0:
return nil, fmt.Errorf("No TXT record found for %s", fqdn)
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 nil, fmt.Errorf("more than 1 TXT record found for %s", fqdn)
}
return &records.RackspaceRecord[0], nil
return &records.Record[0], nil
}
// makeRequest is a wrapper function used for making DNS API requests
@ -251,13 +242,13 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
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)
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)
return nil, fmt.Errorf("request failed for %s %s. Response code: %d", method, url, resp.StatusCode)
}
var r json.RawMessage
@ -269,13 +260,13 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
return r, nil
}
// RackspaceRecords is the list of records sent/received from the DNS API
type RackspaceRecords struct {
RackspaceRecord []RackspaceRecord `json:"records"`
// Records is the list of records sent/received from the DNS API
type Records struct {
Record []Record `json:"records"`
}
// RackspaceRecord represents a Rackspace DNS record
type RackspaceRecord struct {
// Record represents a Rackspace DNS record
type Record struct {
Name string `json:"name"`
Type string `json:"type"`
Data string `json:"data"`

View file

@ -10,10 +10,10 @@ import (
"time"
"github.com/miekg/dns"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface that
// DNSProvider is an implementation of the acme.ChallengeProvider interface that
// uses dynamic DNS updates (RFC 2136) to create TXT records on a nameserver.
type DNSProvider struct {
nameserver string
@ -77,7 +77,7 @@ func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, t
if err != nil {
return nil, err
} else if t < 0 {
return nil, fmt.Errorf("Invalid/negative RFC2136_TIMEOUT: %v", timeout)
return nil, fmt.Errorf("invalid/negative RFC2136_TIMEOUT: %v", timeout)
} else {
d.timeout = t
}
@ -86,26 +86,26 @@ func NewDNSProviderCredentials(nameserver, tsigAlgorithm, tsigKey, tsigSecret, t
return d, nil
}
// Returns the timeout configured with RFC2136_TIMEOUT, or 60s.
// Timeout Returns the timeout configured with RFC2136_TIMEOUT, or 60s.
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
return d.timeout, 2 * time.Second
}
// Present creates a TXT record using the specified parameters
func (r *DNSProvider) Present(domain, token, keyAuth string) error {
fqdn, value, ttl := acmev2.DNS01Record(domain, keyAuth)
return r.changeRecord("INSERT", fqdn, value, ttl)
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
return d.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 := acmev2.DNS01Record(domain, keyAuth)
return r.changeRecord("REMOVE", fqdn, value, ttl)
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
return d.changeRecord("REMOVE", fqdn, value, ttl)
}
func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
func (d *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
// Find the zone for the given fqdn
zone, err := acmev2.FindZoneByFqdn(fqdn, []string{r.nameserver})
zone, err := acme.FindZoneByFqdn(fqdn, []string{d.nameserver})
if err != nil {
return err
}
@ -127,20 +127,20 @@ func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
case "REMOVE":
m.Remove(rrs)
default:
return fmt.Errorf("Unexpected action: %s", action)
return fmt.Errorf("unexpected action: %s", action)
}
// Setup client
c := new(dns.Client)
c.SingleInflight = true
// TSIG authentication / msg signing
if len(r.tsigKey) > 0 && len(r.tsigSecret) > 0 {
m.SetTsig(dns.Fqdn(r.tsigKey), r.tsigAlgorithm, 300, time.Now().Unix())
c.TsigSecret = map[string]string{dns.Fqdn(r.tsigKey): r.tsigSecret}
if len(d.tsigKey) > 0 && len(d.tsigSecret) > 0 {
m.SetTsig(dns.Fqdn(d.tsigKey), d.tsigAlgorithm, 300, time.Now().Unix())
c.TsigSecret = map[string]string{dns.Fqdn(d.tsigKey): d.tsigSecret}
}
// Send the query
reply, _, err := c.Exchange(m, r.nameserver)
reply, _, err := c.Exchange(m, d.nameserver)
if err != nil {
return fmt.Errorf("DNS update failed: %v", err)
}

View file

@ -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/acmev2"
"github.com/xenolf/lego/acme"
)
const (
@ -22,7 +22,7 @@ const (
route53TTL = 10
)
// DNSProvider implements the acmev2.ChallengeProvider interface
// DNSProvider implements the acme.ChallengeProvider interface
type DNSProvider struct {
client *route53.Route53
hostedZoneID string
@ -70,7 +70,11 @@ func NewDNSProvider() (*DNSProvider, error) {
r := customRetryer{}
r.NumMaxRetries = maxRetries
config := request.WithRetryer(aws.NewConfig(), r)
client := route53.New(session.New(config))
session, err := session.NewSessionWithOptions(session.Options{Config: *config})
if err != nil {
return nil, err
}
client := route53.New(session)
return &DNSProvider{
client: client,
@ -80,14 +84,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, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, _ := acme.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, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
value = `"` + value + `"`
return r.changeRecord("DELETE", fqdn, value, route53TTL)
}
@ -95,7 +99,7 @@ func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
hostedZoneID, err := r.getHostedZoneID(fqdn)
if err != nil {
return fmt.Errorf("Failed to determine Route 53 hosted zone ID: %v", err)
return fmt.Errorf("failed to determine Route 53 hosted zone ID: %v", err)
}
recordSet := newTXTRecordSet(fqdn, value, ttl)
@ -114,20 +118,20 @@ func (r *DNSProvider) changeRecord(action, fqdn, value string, ttl int) error {
resp, err := r.client.ChangeResourceRecordSets(reqParams)
if err != nil {
return fmt.Errorf("Failed to change Route 53 record set: %v", err)
return fmt.Errorf("failed to change Route 53 record set: %v", err)
}
statusID := resp.ChangeInfo.Id
return acmev2.WaitFor(120*time.Second, 4*time.Second, func() (bool, error) {
return acme.WaitFor(120*time.Second, 4*time.Second, func() (bool, error) {
reqParams := &route53.GetChangeInput{
Id: statusID,
}
resp, err := r.client.GetChange(reqParams)
if err != nil {
return false, fmt.Errorf("Failed to query Route 53 change status: %v", err)
return false, fmt.Errorf("failed to query Route 53 change status: %v", err)
}
if *resp.ChangeInfo.Status == route53.ChangeStatusInsync {
if aws.StringValue(resp.ChangeInfo.Status) == route53.ChangeStatusInsync {
return true, nil
}
return false, nil
@ -139,14 +143,14 @@ func (r *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
return r.hostedZoneID, nil
}
authZone, err := acmev2.FindZoneByFqdn(fqdn, acmev2.RecursiveNameservers)
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
if err != nil {
return "", err
}
// .DNSName should not have a trailing dot
reqParams := &route53.ListHostedZonesByNameInput{
DNSName: aws.String(acmev2.UnFqdn(authZone)),
DNSName: aws.String(acme.UnFqdn(authZone)),
}
resp, err := r.client.ListHostedZonesByName(reqParams)
if err != nil {
@ -156,14 +160,14 @@ func (r *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
var hostedZoneID string
for _, hostedZone := range resp.HostedZones {
// .Name has a trailing dot
if !*hostedZone.Config.PrivateZone && *hostedZone.Name == authZone {
hostedZoneID = *hostedZone.Id
if !aws.BoolValue(hostedZone.Config.PrivateZone) && aws.StringValue(hostedZone.Name) == authZone {
hostedZoneID = aws.StringValue(hostedZone.Id)
break
}
}
if len(hostedZoneID) == 0 {
return "", fmt.Errorf("Zone %s not found in Route 53 for domain %s", authZone, fqdn)
return "", fmt.Errorf("zone %s not found in Route 53 for domain %s", authZone, fqdn)
}
if strings.HasPrefix(hostedZoneID, "/hostedzone/") {

View file

@ -9,10 +9,10 @@ import (
"strings"
vultr "github.com/JamesClonk/vultr/lib"
"github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/acme"
)
// DNSProvider is an implementation of the acmev2.ChallengeProvider interface.
// DNSProvider is an implementation of the acme.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 := acmev2.DNS01Record(domain, keyAuth)
fqdn, value, ttl := acme.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, _, _ := acmev2.DNS01Record(domain, keyAuth)
fqdn, _, _ := acme.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 := acmev2.UnFqdn(fqdn)
name := acme.UnFqdn(fqdn)
if idx := strings.Index(name, "."+domain); idx != -1 {
return name[:idx]
}