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,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2014-2017 Aetrion LLC dba DNSimple
Copyright (c) 2014-2018 Aetrion LLC dba DNSimple
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,15 +1,12 @@
package dnsimple
import (
)
type AccountsService struct {
client *Client
}
// Account represents a DNSimple account.
type Account struct {
ID int `json:"id,omitempty"`
ID int64 `json:"id,omitempty"`
Email string `json:"email,omitempty"`
PlanIdentifier string `json:"plan_identifier,omitempty"`
CreatedAt string `json:"created_at,omitempty"`

View file

@ -2,29 +2,31 @@ package dnsimple
import (
"fmt"
"strconv"
)
// CertificatesService handles communication with the certificate related
// methods of the DNSimple API.
//
// See https://developer.dnsimple.com/v2/domains/certificates
// See https://developer.dnsimple.com/v2/certificates
type CertificatesService struct {
client *Client
}
// Certificate represents a Certificate in DNSimple.
type Certificate struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
CommonName string `json:"common_name,omitempty"`
Years int `json:"years,omitempty"`
State string `json:"state,omitempty"`
AuthorityIdentifier string `json:"authority_identifier,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
ExpiresOn string `json:"expires_on,omitempty"`
CertificateRequest string `json:"csr,omitempty"`
ID int64 `json:"id,omitempty"`
DomainID int64 `json:"domain_id,omitempty"`
ContactID int64 `json:"contact_id,omitempty"`
CommonName string `json:"common_name,omitempty"`
AlternateNames []string `json:"alternate_names,omitempty"`
Years int `json:"years,omitempty"`
State string `json:"state,omitempty"`
AuthorityIdentifier string `json:"authority_identifier,omitempty"`
AutoRenew bool `json:"auto_renew"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
ExpiresOn string `json:"expires_on,omitempty"`
CertificateRequest string `json:"csr,omitempty"`
}
// CertificateBundle represents a container for all the PEM-encoded X509 certificate entities,
@ -37,9 +39,46 @@ type CertificateBundle struct {
IntermediateCertificates []string `json:"chain,omitempty"`
}
func certificatePath(accountID, domainIdentifier, certificateID string) (path string) {
// CertificatePurchase represents a Certificate Purchase in DNSimple.
type CertificatePurchase struct {
ID int64 `json:"id,omitempty"`
CertificateID int64 `json:"new_certificate_id,omitempty"`
State string `json:"state,omitempty"`
AutoRenew bool `json:"auto_renew,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
// CertificateRenewal represents a Certificate Renewal in DNSimple.
type CertificateRenewal struct {
ID int64 `json:"id,omitempty"`
OldCertificateID int64 `json:"old_certificate_id,omitempty"`
NewCertificateID int64 `json:"new_certificate_id,omitempty"`
State string `json:"state,omitempty"`
AutoRenew bool `json:"auto_renew,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
// LetsencryptCertificateAttributes is a set of attributes to purchase a Let's Encrypt certificate.
type LetsencryptCertificateAttributes struct {
ContactID int64 `json:"contact_id,omitempty"`
Name string `json:"name,omitempty"`
AutoRenew bool `json:"auto_renew,omitempty"`
AlternateNames []string `json:"alternate_names,omitempty"`
}
func certificatePath(accountID, domainIdentifier string, certificateID int64) (path string) {
path = fmt.Sprintf("%v/certificates", domainPath(accountID, domainIdentifier))
if certificateID != "" {
if certificateID != 0 {
path += fmt.Sprintf("/%v", certificateID)
}
return
}
func letsencryptCertificatePath(accountID, domainIdentifier string, certificateID int64) (path string) {
path = fmt.Sprintf("%v/certificates/letsencrypt", domainPath(accountID, domainIdentifier))
if certificateID != 0 {
path += fmt.Sprintf("/%v", certificateID)
}
return
@ -63,11 +102,23 @@ type certificatesResponse struct {
Data []Certificate `json:"data"`
}
// ListCertificates list the certificates for a domain.
// certificatePurchaseResponse represents a response from an API method that returns a CertificatePurchase struct.
type certificatePurchaseResponse struct {
Response
Data *CertificatePurchase `json:"data"`
}
// certificateRenewalResponse represents a response from an API method that returns a CertificateRenewal struct.
type certificateRenewalResponse struct {
Response
Data *CertificateRenewal `json:"data"`
}
// ListCertificates lists the certificates for a domain in the account.
//
// See https://developer.dnsimple.com/v2/domains/certificates#list
// See https://developer.dnsimple.com/v2/certificates#listCertificates
func (s *CertificatesService) ListCertificates(accountID, domainIdentifier string, options *ListOptions) (*certificatesResponse, error) {
path := versioned(certificatePath(accountID, domainIdentifier, ""))
path := versioned(certificatePath(accountID, domainIdentifier, 0))
certificatesResponse := &certificatesResponse{}
path, err := addURLQueryOptions(path, options)
@ -84,11 +135,11 @@ func (s *CertificatesService) ListCertificates(accountID, domainIdentifier strin
return certificatesResponse, nil
}
// GetCertificate fetches the certificate.
// GetCertificate gets the details of a certificate.
//
// See https://developer.dnsimple.com/v2/domains/certificates#get
func (s *CertificatesService) GetCertificate(accountID, domainIdentifier string, certificateID int) (*certificateResponse, error) {
path := versioned(certificatePath(accountID, domainIdentifier, strconv.Itoa(certificateID)))
// See https://developer.dnsimple.com/v2/certificates#getCertificate
func (s *CertificatesService) GetCertificate(accountID, domainIdentifier string, certificateID int64) (*certificateResponse, error) {
path := versioned(certificatePath(accountID, domainIdentifier, certificateID))
certificateResponse := &certificateResponse{}
resp, err := s.client.get(path, certificateResponse)
@ -100,12 +151,12 @@ func (s *CertificatesService) GetCertificate(accountID, domainIdentifier string,
return certificateResponse, nil
}
// DownloadCertificate download the issued server certificate,
// as well the root certificate and the intermediate chain.
// DownloadCertificate gets the PEM-encoded certificate,
// along with the root certificate and intermediate chain.
//
// See https://developer.dnsimple.com/v2/domains/certificates#download
func (s *CertificatesService) DownloadCertificate(accountID, domainIdentifier string, certificateID int) (*certificateBundleResponse, error) {
path := versioned(certificatePath(accountID, domainIdentifier, strconv.Itoa(certificateID)) + "/download")
// See https://developer.dnsimple.com/v2/certificates#downloadCertificate
func (s *CertificatesService) DownloadCertificate(accountID, domainIdentifier string, certificateID int64) (*certificateBundleResponse, error) {
path := versioned(certificatePath(accountID, domainIdentifier, certificateID) + "/download")
certificateBundleResponse := &certificateBundleResponse{}
resp, err := s.client.get(path, certificateBundleResponse)
@ -117,11 +168,11 @@ func (s *CertificatesService) DownloadCertificate(accountID, domainIdentifier st
return certificateBundleResponse, nil
}
// GetCertificatePrivateKey fetches the certificate private key.
// GetCertificatePrivateKey gets the PEM-encoded certificate private key.
//
// See https://developer.dnsimple.com/v2/domains/certificates#get-private-key
func (s *CertificatesService) GetCertificatePrivateKey(accountID, domainIdentifier string, certificateID int) (*certificateBundleResponse, error) {
path := versioned(certificatePath(accountID, domainIdentifier, strconv.Itoa(certificateID)) + "/private_key")
// See https://developer.dnsimple.com/v2/certificates#getCertificatePrivateKey
func (s *CertificatesService) GetCertificatePrivateKey(accountID, domainIdentifier string, certificateID int64) (*certificateBundleResponse, error) {
path := versioned(certificatePath(accountID, domainIdentifier, certificateID) + "/private_key")
certificateBundleResponse := &certificateBundleResponse{}
resp, err := s.client.get(path, certificateBundleResponse)
@ -132,3 +183,67 @@ func (s *CertificatesService) GetCertificatePrivateKey(accountID, domainIdentifi
certificateBundleResponse.HttpResponse = resp
return certificateBundleResponse, nil
}
// PurchaseLetsencryptCertificate purchases a Let's Encrypt certificate.
//
// See https://developer.dnsimple.com/v2/certificates/#purchaseLetsencryptCertificate
func (s *CertificatesService) PurchaseLetsencryptCertificate(accountID, domainIdentifier string, certificateAttributes LetsencryptCertificateAttributes) (*certificatePurchaseResponse, error) {
path := versioned(letsencryptCertificatePath(accountID, domainIdentifier, 0))
certificatePurchaseResponse := &certificatePurchaseResponse{}
resp, err := s.client.post(path, certificateAttributes, certificatePurchaseResponse)
if err != nil {
return nil, err
}
certificatePurchaseResponse.HttpResponse = resp
return certificatePurchaseResponse, nil
}
// IssueLetsencryptCertificate issues a pending Let's Encrypt certificate purchase order.
//
// See https://developer.dnsimple.com/v2/certificates/#issueLetsencryptCertificate
func (s *CertificatesService) IssueLetsencryptCertificate(accountID, domainIdentifier string, certificateID int64) (*certificateResponse, error) {
path := versioned(letsencryptCertificatePath(accountID, domainIdentifier, certificateID) + "/issue")
certificateResponse := &certificateResponse{}
resp, err := s.client.post(path, nil, certificateResponse)
if err != nil {
return nil, err
}
certificateResponse.HttpResponse = resp
return certificateResponse, nil
}
// PurchaseLetsencryptCertificateRenewal purchases a Let's Encrypt certificate renewal.
//
// See https://developer.dnsimple.com/v2/certificates/#purchaseRenewalLetsencryptCertificate
func (s *CertificatesService) PurchaseLetsencryptCertificateRenewal(accountID, domainIdentifier string, certificateID int64, certificateAttributes LetsencryptCertificateAttributes) (*certificateRenewalResponse, error) {
path := versioned(letsencryptCertificatePath(accountID, domainIdentifier, certificateID) + "/renewals")
certificateRenewalResponse := &certificateRenewalResponse{}
resp, err := s.client.post(path, certificateAttributes, certificateRenewalResponse)
if err != nil {
return nil, err
}
certificateRenewalResponse.HttpResponse = resp
return certificateRenewalResponse, nil
}
// IssueLetsencryptCertificateRenewal issues a pending Let's Encrypt certificate renewal order.
//
// See https://developer.dnsimple.com/v2/certificates/#issueRenewalLetsencryptCertificate
func (s *CertificatesService) IssueLetsencryptCertificateRenewal(accountID, domainIdentifier string, certificateID, certificateRenewalID int64) (*certificateResponse, error) {
path := versioned(letsencryptCertificatePath(accountID, domainIdentifier, certificateID) + fmt.Sprintf("/renewals/%d/issue", certificateRenewalID))
certificateResponse := &certificateResponse{}
resp, err := s.client.post(path, nil, certificateResponse)
if err != nil {
return nil, err
}
certificateResponse.HttpResponse = resp
return certificateResponse, nil
}

View file

@ -14,8 +14,8 @@ type ContactsService struct {
// Contact represents a Contact in DNSimple.
type Contact struct {
ID int `json:"id,omitempty"`
AccountID int `json:"account_id,omitempty"`
ID int64 `json:"id,omitempty"`
AccountID int64 `json:"account_id,omitempty"`
Label string `json:"label,omitempty"`
FirstName string `json:"first_name,omitempty"`
LastName string `json:"last_name,omitempty"`
@ -34,7 +34,7 @@ type Contact struct {
UpdatedAt string `json:"updated_at,omitempty"`
}
func contactPath(accountID string, contactID int) (path string) {
func contactPath(accountID string, contactID int64) (path string) {
path = fmt.Sprintf("/%v/contacts", accountID)
if contactID != 0 {
path += fmt.Sprintf("/%v", contactID)
@ -94,7 +94,7 @@ func (s *ContactsService) CreateContact(accountID string, contactAttributes Cont
// GetContact fetches a contact.
//
// See https://developer.dnsimple.com/v2/contacts/#get
func (s *ContactsService) GetContact(accountID string, contactID int) (*contactResponse, error) {
func (s *ContactsService) GetContact(accountID string, contactID int64) (*contactResponse, error) {
path := versioned(contactPath(accountID, contactID))
contactResponse := &contactResponse{}
@ -110,7 +110,7 @@ func (s *ContactsService) GetContact(accountID string, contactID int) (*contactR
// UpdateContact updates a contact.
//
// See https://developer.dnsimple.com/v2/contacts/#update
func (s *ContactsService) UpdateContact(accountID string, contactID int, contactAttributes Contact) (*contactResponse, error) {
func (s *ContactsService) UpdateContact(accountID string, contactID int64, contactAttributes Contact) (*contactResponse, error) {
path := versioned(contactPath(accountID, contactID))
contactResponse := &contactResponse{}
@ -126,7 +126,7 @@ func (s *ContactsService) UpdateContact(accountID string, contactID int, contact
// DeleteContact PERMANENTLY deletes a contact from the account.
//
// See https://developer.dnsimple.com/v2/contacts/#delete
func (s *ContactsService) DeleteContact(accountID string, contactID int) (*contactResponse, error) {
func (s *ContactsService) DeleteContact(accountID string, contactID int64) (*contactResponse, error) {
path := versioned(contactPath(accountID, contactID))
contactResponse := &contactResponse{}

View file

@ -23,7 +23,7 @@ const (
// This is a pro-forma convention given that Go dependencies
// tends to be fetched directly from the repo.
// It is also used in the user-agent identify the client.
Version = "0.14.0"
Version = "0.16.0"
// defaultBaseURL to the DNSimple production API.
defaultBaseURL = "https://api.dnsimple.com"

View file

@ -14,9 +14,9 @@ type DomainsService struct {
// Domain represents a domain in DNSimple.
type Domain struct {
ID int `json:"id,omitempty"`
AccountID int `json:"account_id,omitempty"`
RegistrantID int `json:"registrant_id,omitempty"`
ID int64 `json:"id,omitempty"`
AccountID int64 `json:"account_id,omitempty"`
RegistrantID int64 `json:"registrant_id,omitempty"`
Name string `json:"name,omitempty"`
UnicodeName string `json:"unicode_name,omitempty"`
Token string `json:"token,omitempty"`

View file

@ -6,10 +6,10 @@ import (
// Collaborator represents a Collaborator in DNSimple.
type Collaborator struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
ID int64 `json:"id,omitempty"`
DomainID int64 `json:"domain_id,omitempty"`
DomainName string `json:"domain_name,omitempty"`
UserID int `json:"user_id,omitempty"`
UserID int64 `json:"user_id,omitempty"`
UserEmail string `json:"user_email,omitempty"`
Invitation bool `json:"invitation,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
@ -17,9 +17,9 @@ type Collaborator struct {
AcceptedAt string `json:"accepted_at,omitempty"`
}
func collaboratorPath(accountID, domainIdentifier, collaboratorID string) (path string) {
func collaboratorPath(accountID, domainIdentifier string, collaboratorID int64) (path string) {
path = fmt.Sprintf("%v/collaborators", domainPath(accountID, domainIdentifier))
if collaboratorID != "" {
if collaboratorID != 0 {
path += fmt.Sprintf("/%v", collaboratorID)
}
return
@ -46,7 +46,7 @@ type collaboratorsResponse struct {
//
// See https://developer.dnsimple.com/v2/domains/collaborators#list
func (s *DomainsService) ListCollaborators(accountID, domainIdentifier string, options *ListOptions) (*collaboratorsResponse, error) {
path := versioned(collaboratorPath(accountID, domainIdentifier, ""))
path := versioned(collaboratorPath(accountID, domainIdentifier, 0))
collaboratorsResponse := &collaboratorsResponse{}
path, err := addURLQueryOptions(path, options)
@ -67,7 +67,7 @@ func (s *DomainsService) ListCollaborators(accountID, domainIdentifier string, o
//
// See https://developer.dnsimple.com/v2/domains/collaborators#add
func (s *DomainsService) AddCollaborator(accountID string, domainIdentifier string, attributes CollaboratorAttributes) (*collaboratorResponse, error) {
path := versioned(collaboratorPath(accountID, domainIdentifier, ""))
path := versioned(collaboratorPath(accountID, domainIdentifier, 0))
collaboratorResponse := &collaboratorResponse{}
resp, err := s.client.post(path, attributes, collaboratorResponse)
@ -81,8 +81,8 @@ func (s *DomainsService) AddCollaborator(accountID string, domainIdentifier stri
// RemoveCollaborator PERMANENTLY deletes a domain from the account.
//
// See https://developer.dnsimple.com/v2/domains/collaborators#add
func (s *DomainsService) RemoveCollaborator(accountID string, domainIdentifier string, collaboratorID string) (*collaboratorResponse, error) {
// See https://developer.dnsimple.com/v2/domains/collaborators#remove
func (s *DomainsService) RemoveCollaborator(accountID string, domainIdentifier string, collaboratorID int64) (*collaboratorResponse, error) {
path := versioned(collaboratorPath(accountID, domainIdentifier, collaboratorID))
collaboratorResponse := &collaboratorResponse{}

View file

@ -4,8 +4,8 @@ import "fmt"
// DelegationSignerRecord represents a delegation signer record for a domain in DNSimple.
type DelegationSignerRecord struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
ID int64 `json:"id,omitempty"`
DomainID int64 `json:"domain_id,omitempty"`
Algorithm string `json:"algorithm"`
Digest string `json:"digest"`
DigestType string `json:"digest_type"`
@ -14,10 +14,10 @@ type DelegationSignerRecord struct {
UpdatedAt string `json:"updated_at,omitempty"`
}
func delegationSignerRecordPath(accountID string, domainIdentifier string, dsRecordID int) (path string) {
func delegationSignerRecordPath(accountID string, domainIdentifier string, dsRecordID int64) (path string) {
path = fmt.Sprintf("%v/ds_records", domainPath(accountID, domainIdentifier))
if dsRecordID != 0 {
path += fmt.Sprintf("/%d", dsRecordID)
path += fmt.Sprintf("/%v", dsRecordID)
}
return
}
@ -74,7 +74,7 @@ func (s *DomainsService) CreateDelegationSignerRecord(accountID string, domainId
// GetDelegationSignerRecord fetches a delegation signer record.
//
// See https://developer.dnsimple.com/v2/domains/dnssec/#ds-record-get
func (s *DomainsService) GetDelegationSignerRecord(accountID string, domainIdentifier string, dsRecordID int) (*delegationSignerRecordResponse, error) {
func (s *DomainsService) GetDelegationSignerRecord(accountID string, domainIdentifier string, dsRecordID int64) (*delegationSignerRecordResponse, error) {
path := versioned(delegationSignerRecordPath(accountID, domainIdentifier, dsRecordID))
dsRecordResponse := &delegationSignerRecordResponse{}
@ -91,7 +91,7 @@ func (s *DomainsService) GetDelegationSignerRecord(accountID string, domainIdent
// from the domain.
//
// See https://developer.dnsimple.com/v2/domains/dnssec/#ds-record-delete
func (s *DomainsService) DeleteDelegationSignerRecord(accountID string, domainIdentifier string, dsRecordID int) (*delegationSignerRecordResponse, error) {
func (s *DomainsService) DeleteDelegationSignerRecord(accountID string, domainIdentifier string, dsRecordID int64) (*delegationSignerRecordResponse, error) {
path := versioned(delegationSignerRecordPath(accountID, domainIdentifier, dsRecordID))
dsRecordResponse := &delegationSignerRecordResponse{}

View file

@ -1,6 +1,8 @@
package dnsimple
import "fmt"
import (
"fmt"
)
// Dnssec represents the current DNSSEC settings for a domain in DNSimple.
type Dnssec struct {

View file

@ -6,18 +6,18 @@ import (
// EmailForward represents an email forward in DNSimple.
type EmailForward struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
ID int64 `json:"id,omitempty"`
DomainID int64 `json:"domain_id,omitempty"`
From string `json:"from,omitempty"`
To string `json:"to,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
func emailForwardPath(accountID string, domainIdentifier string, forwardID int) (path string) {
func emailForwardPath(accountID string, domainIdentifier string, forwardID int64) (path string) {
path = fmt.Sprintf("%v/email_forwards", domainPath(accountID, domainIdentifier))
if forwardID != 0 {
path += fmt.Sprintf("/%d", forwardID)
path += fmt.Sprintf("/%v", forwardID)
}
return
}
@ -38,7 +38,7 @@ type emailForwardsResponse struct {
//
// See https://developer.dnsimple.com/v2/domains/email-forwards/#list
func (s *DomainsService) ListEmailForwards(accountID string, domainIdentifier string, options *ListOptions) (*emailForwardsResponse, error) {
path := versioned(emailForwardPath(accountID, domainIdentifier , 0))
path := versioned(emailForwardPath(accountID, domainIdentifier, 0))
forwardsResponse := &emailForwardsResponse{}
path, err := addURLQueryOptions(path, options)
@ -74,7 +74,7 @@ func (s *DomainsService) CreateEmailForward(accountID string, domainIdentifier s
// GetEmailForward fetches an email forward.
//
// See https://developer.dnsimple.com/v2/domains/email-forwards/#get
func (s *DomainsService) GetEmailForward(accountID string, domainIdentifier string, forwardID int) (*emailForwardResponse, error) {
func (s *DomainsService) GetEmailForward(accountID string, domainIdentifier string, forwardID int64) (*emailForwardResponse, error) {
path := versioned(emailForwardPath(accountID, domainIdentifier, forwardID))
forwardResponse := &emailForwardResponse{}
@ -90,7 +90,7 @@ func (s *DomainsService) GetEmailForward(accountID string, domainIdentifier stri
// DeleteEmailForward PERMANENTLY deletes an email forward from the domain.
//
// See https://developer.dnsimple.com/v2/domains/email-forwards/#delete
func (s *DomainsService) DeleteEmailForward(accountID string, domainIdentifier string, forwardID int) (*emailForwardResponse, error) {
func (s *DomainsService) DeleteEmailForward(accountID string, domainIdentifier string, forwardID int64) (*emailForwardResponse, error) {
path := versioned(emailForwardPath(accountID, domainIdentifier, forwardID))
forwardResponse := &emailForwardResponse{}

View file

@ -6,19 +6,19 @@ import (
// DomainPush represents a domain push in DNSimple.
type DomainPush struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
ContactID int `json:"contact_id,omitempty"`
AccountID int `json:"account_id,omitempty"`
ID int64 `json:"id,omitempty"`
DomainID int64 `json:"domain_id,omitempty"`
ContactID int64 `json:"contact_id,omitempty"`
AccountID int64 `json:"account_id,omitempty"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
AcceptedAt string `json:"accepted_at,omitempty"`
}
func domainPushPath(accountID string, pushID int) (path string) {
func domainPushPath(accountID string, pushID int64) (path string) {
path = fmt.Sprintf("/%v/pushes", accountID)
if pushID != 0 {
path += fmt.Sprintf("/%d", pushID)
path += fmt.Sprintf("/%v", pushID)
}
return
}
@ -38,13 +38,13 @@ type domainPushesResponse struct {
// DomainPushAttributes represent a domain push payload (see initiate).
type DomainPushAttributes struct {
NewAccountEmail string `json:"new_account_email,omitempty"`
ContactID string `json:"contact_id,omitempty"`
ContactID int64 `json:"contact_id,omitempty"`
}
// InitiatePush initiate a new domain push.
//
// See https://developer.dnsimple.com/v2/domains/pushes/#initiate
func (s *DomainsService) InitiatePush(accountID string, domainID string, pushAttributes DomainPushAttributes) (*domainPushResponse, error) {
func (s *DomainsService) InitiatePush(accountID, domainID string, pushAttributes DomainPushAttributes) (*domainPushResponse, error) {
path := versioned(fmt.Sprintf("/%v/pushes", domainPath(accountID, domainID)))
pushResponse := &domainPushResponse{}
@ -81,7 +81,7 @@ func (s *DomainsService) ListPushes(accountID string, options *ListOptions) (*do
// AcceptPush accept a push for a domain.
//
// See https://developer.dnsimple.com/v2/domains/pushes/#accept
func (s *DomainsService) AcceptPush(accountID string, pushID int, pushAttributes DomainPushAttributes) (*domainPushResponse, error) {
func (s *DomainsService) AcceptPush(accountID string, pushID int64, pushAttributes DomainPushAttributes) (*domainPushResponse, error) {
path := versioned(domainPushPath(accountID, pushID))
pushResponse := &domainPushResponse{}
@ -97,7 +97,7 @@ func (s *DomainsService) AcceptPush(accountID string, pushID int, pushAttributes
// RejectPush reject a push for a domain.
//
// See https://developer.dnsimple.com/v2/domains/pushes/#reject
func (s *DomainsService) RejectPush(accountID string, pushID int) (*domainPushResponse, error) {
func (s *DomainsService) RejectPush(accountID string, pushID int64) (*domainPushResponse, error) {
path := versioned(domainPushPath(accountID, pushID))
pushResponse := &domainPushResponse{}

View file

@ -28,7 +28,7 @@ type domainCheckResponse struct {
// CheckDomain checks a domain name.
//
// See https://developer.dnsimple.com/v2/registrar/#check
func (s *RegistrarService) CheckDomain(accountID, domainName string) (*domainCheckResponse, error) {
func (s *RegistrarService) CheckDomain(accountID string, domainName string) (*domainCheckResponse, error) {
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/check", accountID, domainName))
checkResponse := &domainCheckResponse{}
@ -70,7 +70,7 @@ type DomainPremiumPriceOptions struct {
// - renewal
//
// See https://developer.dnsimple.com/v2/registrar/#premium-price
func (s *RegistrarService) GetDomainPremiumPrice(accountID, domainName string, options *DomainPremiumPriceOptions) (*domainPremiumPriceResponse, error) {
func (s *RegistrarService) GetDomainPremiumPrice(accountID string, domainName string, options *DomainPremiumPriceOptions) (*domainPremiumPriceResponse, error) {
var err error
path := versioned(fmt.Sprintf("/%v/registrar/domains/%v/premium_price", accountID, domainName))
priceResponse := &domainPremiumPriceResponse{}
@ -100,7 +100,6 @@ type DomainRegistration struct {
State string `json:"state"`
AutoRenew bool `json:"auto_renew"`
WhoisPrivacy bool `json:"whois_privacy"`
PremiumPrice string `json:"premium_price"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
@ -122,6 +121,8 @@ type DomainRegisterRequest struct {
// Set to true to enable the auto-renewal of the domain.
// Default to true.
EnableAutoRenewal bool `json:"auto_renew,omitempty"`
// Required as confirmation of the price, only if the domain is premium.
PremiumPrice string `json:"premium_price,omitempty"`
}
// RegisterDomain registers a domain name.
@ -150,7 +151,6 @@ type DomainTransfer struct {
State string `json:"state"`
AutoRenew bool `json:"auto_renew"`
WhoisPrivacy bool `json:"whois_privacy"`
PremiumPrice string `json:"premium_price"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
@ -175,6 +175,8 @@ type DomainTransferRequest struct {
// Set to true to enable the auto-renewal of the domain.
// Default to true.
EnableAutoRenewal bool `json:"auto_renew,omitempty"`
// Required as confirmation of the price, only if the domain is premium.
PremiumPrice string `json:"premium_price,omitempty"`
}
// TransferDomain transfers a domain name.
@ -219,13 +221,12 @@ func (s *RegistrarService) TransferDomainOut(accountID string, domainName string
// DomainRenewal represents the result of a domain renewal call.
type DomainRenewal struct {
ID int `json:"id"`
DomainID int `json:"domain_id"`
Period int `json:"period"`
State string `json:"state"`
PremiumPrice string `json:"premium_price"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
ID int `json:"id"`
DomainID int `json:"domain_id"`
Period int `json:"period"`
State string `json:"state"`
CreatedAt string `json:"created_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
}
// domainRenewalResponse represents a response from an API method that returns a domain renewal.
@ -239,6 +240,8 @@ type domainRenewalResponse struct {
type DomainRenewRequest struct {
// The number of years
Period int `json:"period"`
// Required as confirmation of the price, only if the domain is premium.
PremiumPrice string `json:"premium_price,omitempty"`
}
// RenewDomain renews a domain name.

View file

@ -6,8 +6,8 @@ import (
// WhoisPrivacy represents a whois privacy in DNSimple.
type WhoisPrivacy struct {
ID int `json:"id,omitempty"`
DomainID int `json:"domain_id,omitempty"`
ID int64 `json:"id,omitempty"`
DomainID int64 `json:"domain_id,omitempty"`
Enabled bool `json:"enabled,omitempty"`
ExpiresOn string `json:"expires_on,omitempty"`
CreatedAt string `json:"created_at,omitempty"`

View file

@ -14,7 +14,7 @@ type ServicesService struct {
// Service represents a Service in DNSimple.
type Service struct {
ID int `json:"id,omitempty"`
ID int64 `json:"id,omitempty"`
SID string `json:"sid,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
@ -36,10 +36,10 @@ type ServiceSetting struct {
Password bool `json:"password,omitempty"`
}
func servicePath(serviceID string) (path string) {
func servicePath(serviceIdentifier string) (path string) {
path = "/services"
if serviceID != "" {
path += fmt.Sprintf("/%v", serviceID)
if serviceIdentifier != "" {
path += fmt.Sprintf("/%v", serviceIdentifier)
}
return
}

View file

@ -4,11 +4,11 @@ import (
"fmt"
)
func domainServicesPath(accountID string, domainID string, serviceIdentifier string) string {
func domainServicesPath(accountID string, domainIdentifier string, serviceIdentifier string) string {
if serviceIdentifier != "" {
return fmt.Sprintf("/%v/domains/%v/services/%v", accountID, domainID, serviceIdentifier)
return fmt.Sprintf("/%v/domains/%v/services/%v", accountID, domainIdentifier, serviceIdentifier)
}
return fmt.Sprintf("/%v/domains/%v/services", accountID, domainID)
return fmt.Sprintf("/%v/domains/%v/services", accountID, domainIdentifier)
}
// DomainServiceSettings represents optional settings when applying a DNSimple one-click service to a domain.
@ -19,8 +19,8 @@ type DomainServiceSettings struct {
// AppliedServices lists the applied one-click services for a domain.
//
// See https://developer.dnsimple.com/v2/services/domains/#applied
func (s *ServicesService) AppliedServices(accountID string, domainID string, options *ListOptions) (*servicesResponse, error) {
path := versioned(domainServicesPath(accountID, domainID, ""))
func (s *ServicesService) AppliedServices(accountID string, domainIdentifier string, options *ListOptions) (*servicesResponse, error) {
path := versioned(domainServicesPath(accountID, domainIdentifier, ""))
servicesResponse := &servicesResponse{}
path, err := addURLQueryOptions(path, options)
@ -40,8 +40,8 @@ func (s *ServicesService) AppliedServices(accountID string, domainID string, opt
// ApplyService applies a one-click services to a domain.
//
// See https://developer.dnsimple.com/v2/services/domains/#apply
func (s *ServicesService) ApplyService(accountID string, serviceIdentifier string, domainID string, settings DomainServiceSettings) (*serviceResponse, error) {
path := versioned(domainServicesPath(accountID, domainID, serviceIdentifier))
func (s *ServicesService) ApplyService(accountID string, serviceIdentifier string, domainIdentifier string, settings DomainServiceSettings) (*serviceResponse, error) {
path := versioned(domainServicesPath(accountID, domainIdentifier, serviceIdentifier))
serviceResponse := &serviceResponse{}
resp, err := s.client.post(path, settings, nil)
@ -56,8 +56,8 @@ func (s *ServicesService) ApplyService(accountID string, serviceIdentifier strin
// UnapplyService unapplies a one-click services from a domain.
//
// See https://developer.dnsimple.com/v2/services/domains/#unapply
func (s *ServicesService) UnapplyService(accountID string, serviceIdentifier string, domainID string) (*serviceResponse, error) {
path := versioned(domainServicesPath(accountID, domainID, serviceIdentifier))
func (s *ServicesService) UnapplyService(accountID string, serviceIdentifier string, domainIdentifier string) (*serviceResponse, error) {
path := versioned(domainServicesPath(accountID, domainIdentifier, serviceIdentifier))
serviceResponse := &serviceResponse{}
resp, err := s.client.delete(path, nil, nil)

View file

@ -14,9 +14,9 @@ type TemplatesService struct {
// Template represents a Template in DNSimple.
type Template struct {
ID int `json:"id,omitempty"`
ID int64 `json:"id,omitempty"`
SID string `json:"sid,omitempty"`
AccountID int `json:"account_id,omitempty"`
AccountID int64 `json:"account_id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
CreatedAt string `json:"created_at,omitempty"`

View file

@ -7,8 +7,8 @@ import (
// ApplyTemplate applies a template to the given domain.
//
// See https://developer.dnsimple.com/v2/templates/domains/#apply
func (s *TemplatesService) ApplyTemplate(accountID string, templateIdentifier string, domainID string) (*templateResponse, error) {
path := versioned(fmt.Sprintf("%v/templates/%v", domainPath(accountID, domainID), templateIdentifier))
func (s *TemplatesService) ApplyTemplate(accountID string, templateIdentifier string, domainIdentifier string) (*templateResponse, error) {
path := versioned(fmt.Sprintf("%v/templates/%v", domainPath(accountID, domainIdentifier), templateIdentifier))
templateResponse := &templateResponse{}
resp, err := s.client.post(path, nil, nil)

View file

@ -6,8 +6,8 @@ import (
// TemplateRecord represents a DNS record for a template in DNSimple.
type TemplateRecord struct {
ID int `json:"id,omitempty"`
TemplateID int `json:"template_id,omitempty"`
ID int64 `json:"id,omitempty"`
TemplateID int64 `json:"template_id,omitempty"`
Name string `json:"name"`
Content string `json:"content,omitempty"`
TTL int `json:"ttl,omitempty"`
@ -17,8 +17,8 @@ type TemplateRecord struct {
UpdatedAt string `json:"updated_at,omitempty"`
}
func templateRecordPath(accountID string, templateIdentifier string, templateRecordID string) string {
if templateRecordID != "" {
func templateRecordPath(accountID string, templateIdentifier string, templateRecordID int64) string {
if templateRecordID != 0 {
return fmt.Sprintf("%v/records/%v", templatePath(accountID, templateIdentifier), templateRecordID)
}
@ -41,7 +41,7 @@ type templateRecordsResponse struct {
//
// See https://developer.dnsimple.com/v2/templates/records/#list
func (s *TemplatesService) ListTemplateRecords(accountID string, templateIdentifier string, options *ListOptions) (*templateRecordsResponse, error) {
path := versioned(templateRecordPath(accountID, templateIdentifier, ""))
path := versioned(templateRecordPath(accountID, templateIdentifier, 0))
templateRecordsResponse := &templateRecordsResponse{}
path, err := addURLQueryOptions(path, options)
@ -62,7 +62,7 @@ func (s *TemplatesService) ListTemplateRecords(accountID string, templateIdentif
//
// See https://developer.dnsimple.com/v2/templates/records/#create
func (s *TemplatesService) CreateTemplateRecord(accountID string, templateIdentifier string, templateRecordAttributes TemplateRecord) (*templateRecordResponse, error) {
path := versioned(templateRecordPath(accountID, templateIdentifier, ""))
path := versioned(templateRecordPath(accountID, templateIdentifier, 0))
templateRecordResponse := &templateRecordResponse{}
resp, err := s.client.post(path, templateRecordAttributes, templateRecordResponse)
@ -77,7 +77,7 @@ func (s *TemplatesService) CreateTemplateRecord(accountID string, templateIdenti
// GetTemplateRecord fetches a template record.
//
// See https://developer.dnsimple.com/v2/templates/records/#get
func (s *TemplatesService) GetTemplateRecord(accountID string, templateIdentifier string, templateRecordID string) (*templateRecordResponse, error) {
func (s *TemplatesService) GetTemplateRecord(accountID string, templateIdentifier string, templateRecordID int64) (*templateRecordResponse, error) {
path := versioned(templateRecordPath(accountID, templateIdentifier, templateRecordID))
templateRecordResponse := &templateRecordResponse{}
@ -93,7 +93,7 @@ func (s *TemplatesService) GetTemplateRecord(accountID string, templateIdentifie
// DeleteTemplateRecord deletes a template record.
//
// See https://developer.dnsimple.com/v2/templates/records/#delete
func (s *TemplatesService) DeleteTemplateRecord(accountID string, templateIdentifier string, templateRecordID string) (*templateRecordResponse, error) {
func (s *TemplatesService) DeleteTemplateRecord(accountID string, templateIdentifier string, templateRecordID int64) (*templateRecordResponse, error) {
path := versioned(templateRecordPath(accountID, templateIdentifier, templateRecordID))
templateRecordResponse := &templateRecordResponse{}

View file

@ -2,6 +2,6 @@ package dnsimple
// User represents a DNSimple user.
type User struct {
ID int `json:"id,omitempty"`
ID int64 `json:"id,omitempty"`
Email string `json:"email,omitempty"`
}

View file

@ -14,7 +14,7 @@ type VanityNameServersService struct {
// VanityNameServer represents data for a single vanity name server
type VanityNameServer struct {
ID int `json:"id,omitempty"`
ID int64 `json:"id,omitempty"`
Name string `json:"name,omitempty"`
IPv4 string `json:"ipv4,omitempty"`
IPv6 string `json:"ipv6,omitempty"`
@ -22,8 +22,8 @@ type VanityNameServer struct {
UpdatedAt string `json:"updated_at,omitempty"`
}
func vanityNameServerPath(accountID string, domainID string) string {
return fmt.Sprintf("/%v/vanity/%v", accountID, domainID)
func vanityNameServerPath(accountID string, domainIdentifier string) string {
return fmt.Sprintf("/%v/vanity/%v", accountID, domainIdentifier)
}
// vanityNameServerResponse represents a response for vanity name server enable and disable operations.
@ -35,8 +35,8 @@ type vanityNameServerResponse struct {
// EnableVanityNameServers Vanity Name Servers for the given domain
//
// See https://developer.dnsimple.com/v2/vanity/#enable
func (s *VanityNameServersService) EnableVanityNameServers(accountID string, domainID string) (*vanityNameServerResponse, error) {
path := versioned(vanityNameServerPath(accountID, domainID))
func (s *VanityNameServersService) EnableVanityNameServers(accountID string, domainIdentifier string) (*vanityNameServerResponse, error) {
path := versioned(vanityNameServerPath(accountID, domainIdentifier))
vanityNameServerResponse := &vanityNameServerResponse{}
resp, err := s.client.put(path, nil, vanityNameServerResponse)
@ -51,8 +51,8 @@ func (s *VanityNameServersService) EnableVanityNameServers(accountID string, dom
// DisableVanityNameServers Vanity Name Servers for the given domain
//
// See https://developer.dnsimple.com/v2/vanity/#disable
func (s *VanityNameServersService) DisableVanityNameServers(accountID string, domainID string) (*vanityNameServerResponse, error) {
path := versioned(vanityNameServerPath(accountID, domainID))
func (s *VanityNameServersService) DisableVanityNameServers(accountID string, domainIdentifier string) (*vanityNameServerResponse, error) {
path := versioned(vanityNameServerPath(accountID, domainIdentifier))
vanityNameServerResponse := &vanityNameServerResponse{}
resp, err := s.client.delete(path, nil, nil)

View file

@ -14,11 +14,11 @@ type WebhooksService struct {
// Webhook represents a DNSimple webhook.
type Webhook struct {
ID int `json:"id,omitempty"`
ID int64 `json:"id,omitempty"`
URL string `json:"url,omitempty"`
}
func webhookPath(accountID string, webhookID int) (path string) {
func webhookPath(accountID string, webhookID int64) (path string) {
path = fmt.Sprintf("/%v/webhooks", accountID)
if webhookID != 0 {
path = fmt.Sprintf("%v/%v", path, webhookID)
@ -73,7 +73,7 @@ func (s *WebhooksService) CreateWebhook(accountID string, webhookAttributes Webh
// GetWebhook fetches a webhook.
//
// See https://developer.dnsimple.com/v2/webhooks#get
func (s *WebhooksService) GetWebhook(accountID string, webhookID int) (*webhookResponse, error) {
func (s *WebhooksService) GetWebhook(accountID string, webhookID int64) (*webhookResponse, error) {
path := versioned(webhookPath(accountID, webhookID))
webhookResponse := &webhookResponse{}
@ -89,7 +89,7 @@ func (s *WebhooksService) GetWebhook(accountID string, webhookID int) (*webhookR
// DeleteWebhook PERMANENTLY deletes a webhook from the account.
//
// See https://developer.dnsimple.com/v2/webhooks#delete
func (s *WebhooksService) DeleteWebhook(accountID string, webhookID int) (*webhookResponse, error) {
func (s *WebhooksService) DeleteWebhook(accountID string, webhookID int64) (*webhookResponse, error) {
path := versioned(webhookPath(accountID, webhookID))
webhookResponse := &webhookResponse{}

View file

@ -14,8 +14,8 @@ type ZonesService struct {
// Zone represents a Zone in DNSimple.
type Zone struct {
ID int `json:"id,omitempty"`
AccountID int `json:"account_id,omitempty"`
ID int64 `json:"id,omitempty"`
AccountID int64 `json:"account_id,omitempty"`
Name string `json:"name,omitempty"`
Reverse bool `json:"reverse,omitempty"`
CreatedAt string `json:"created_at,omitempty"`

View file

@ -6,9 +6,9 @@ import (
// ZoneRecord represents a DNS record in DNSimple.
type ZoneRecord struct {
ID int `json:"id,omitempty"`
ID int64 `json:"id,omitempty"`
ZoneID string `json:"zone_id,omitempty"`
ParentID int `json:"parent_id,omitempty"`
ParentID int64 `json:"parent_id,omitempty"`
Type string `json:"type,omitempty"`
Name string `json:"name"`
Content string `json:"content,omitempty"`
@ -20,10 +20,10 @@ type ZoneRecord struct {
UpdatedAt string `json:"updated_at,omitempty"`
}
func zoneRecordPath(accountID string, zoneID string, recordID int) (path string) {
path = fmt.Sprintf("/%v/zones/%v/records", accountID, zoneID)
func zoneRecordPath(accountID string, zoneName string, recordID int64) (path string) {
path = fmt.Sprintf("/%v/zones/%v/records", accountID, zoneName)
if recordID != 0 {
path += fmt.Sprintf("/%d", recordID)
path += fmt.Sprintf("/%v", recordID)
}
return
}
@ -51,16 +51,16 @@ type ZoneRecordListOptions struct {
// Select records of given type.
// Eg. TXT, A, NS.
Type string `url:"record_type,omitempty"`
Type string `url:"type,omitempty"`
ListOptions
}
// ListRecords lists the zone records for a zone.
//
// See https://developer.dnsimple.com/v2/zones/#list
func (s *ZonesService) ListRecords(accountID string, zoneID string, options *ZoneRecordListOptions) (*zoneRecordsResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneID, 0))
// See https://developer.dnsimple.com/v2/zones/records/#listZoneRecords
func (s *ZonesService) ListRecords(accountID string, zoneName string, options *ZoneRecordListOptions) (*zoneRecordsResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneName, 0))
recordsResponse := &zoneRecordsResponse{}
path, err := addURLQueryOptions(path, options)
@ -79,9 +79,9 @@ func (s *ZonesService) ListRecords(accountID string, zoneID string, options *Zon
// CreateRecord creates a zone record.
//
// See https://developer.dnsimple.com/v2/zones/#create
func (s *ZonesService) CreateRecord(accountID string, zoneID string, recordAttributes ZoneRecord) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneID, 0))
// See https://developer.dnsimple.com/v2/zones/records/#createZoneRecord
func (s *ZonesService) CreateRecord(accountID string, zoneName string, recordAttributes ZoneRecord) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneName, 0))
recordResponse := &zoneRecordResponse{}
resp, err := s.client.post(path, recordAttributes, recordResponse)
@ -95,9 +95,9 @@ func (s *ZonesService) CreateRecord(accountID string, zoneID string, recordAttri
// GetRecord fetches a zone record.
//
// See https://developer.dnsimple.com/v2/zones/#get
func (s *ZonesService) GetRecord(accountID string, zoneID string, recordID int) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneID, recordID))
// See https://developer.dnsimple.com/v2/zones/records/#getZoneRecord
func (s *ZonesService) GetRecord(accountID string, zoneName string, recordID int64) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneName, recordID))
recordResponse := &zoneRecordResponse{}
resp, err := s.client.get(path, recordResponse)
@ -111,9 +111,9 @@ func (s *ZonesService) GetRecord(accountID string, zoneID string, recordID int)
// UpdateRecord updates a zone record.
//
// See https://developer.dnsimple.com/v2/zones/#update
func (s *ZonesService) UpdateRecord(accountID string, zoneID string, recordID int, recordAttributes ZoneRecord) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneID, recordID))
// See https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
func (s *ZonesService) UpdateRecord(accountID string, zoneName string, recordID int64, recordAttributes ZoneRecord) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneName, recordID))
recordResponse := &zoneRecordResponse{}
resp, err := s.client.patch(path, recordAttributes, recordResponse)
@ -127,9 +127,9 @@ func (s *ZonesService) UpdateRecord(accountID string, zoneID string, recordID in
// DeleteRecord PERMANENTLY deletes a zone record from the zone.
//
// See https://developer.dnsimple.com/v2/zones/#delete
func (s *ZonesService) DeleteRecord(accountID string, zoneID string, recordID int) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneID, recordID))
// See https://developer.dnsimple.com/v2/zones/records/#deleteZoneRecord
func (s *ZonesService) DeleteRecord(accountID string, zoneName string, recordID int64) (*zoneRecordResponse, error) {
path := versioned(zoneRecordPath(accountID, zoneName, recordID))
recordResponse := &zoneRecordResponse{}
resp, err := s.client.delete(path, nil, nil)

View file

@ -19,11 +19,18 @@ var (
// currentUserHome attempts to get current user's home directory
func currentUserHome() (string, error) {
userHome := ""
usr, err := user.Current()
if err != nil {
return "", err
// Fallback by trying to read $HOME
userHome = os.Getenv("HOME")
if userHome != "" {
err = nil
}
} else {
userHome = usr.HomeDir
}
return usr.HomeDir, nil
return userHome, nil
}
// appendConfigurationFile only if it exists. We need to do this because
@ -88,13 +95,13 @@ func (c *Client) loadConfig(endpointName string) error {
// If we still have no valid endpoint, AppKey or AppSecret, return an error
if c.endpoint == "" {
return fmt.Errorf("Unknown endpoint '%s'. Consider checking 'Endpoints' list of using an URL.", endpointName)
return fmt.Errorf("unknown endpoint '%s', consider checking 'Endpoints' list of using an URL", endpointName)
}
if c.AppKey == "" {
return fmt.Errorf("Missing application key. Please check your configuration or consult the documentation to create one.")
return fmt.Errorf("missing application key, please check your configuration or consult the documentation to create one")
}
if c.AppSecret == "" {
return fmt.Errorf("Missing application secret. Please check your configuration or consult the documentation to create one.")
return fmt.Errorf("missing application secret, please check your configuration or consult the documentation to create one")
}
return nil

View file

@ -21,6 +21,7 @@ const DefaultTimeout = 180 * time.Second
const (
OvhEU = "https://eu.api.ovh.com/1.0"
OvhCA = "https://ca.api.ovh.com/1.0"
OvhUS = "https://api.ovh.us/1.0"
KimsufiEU = "https://eu.api.kimsufi.com/1.0"
KimsufiCA = "https://ca.api.kimsufi.com/1.0"
SoyoustartEU = "https://eu.api.soyoustart.com/1.0"
@ -32,6 +33,7 @@ const (
var Endpoints = map[string]string{
"ovh-eu": OvhEU,
"ovh-ca": OvhCA,
"ovh-us": OvhUS,
"kimsufi-eu": KimsufiEU,
"kimsufi-ca": KimsufiCA,
"soyoustart-eu": SoyoustartEU,
@ -170,39 +172,6 @@ func (c *Client) DeleteUnAuth(url string, resType interface{}) error {
return c.CallAPI("DELETE", url, nil, resType, false)
}
//
// Low level API access
//
// getResult check the response and unmarshals it into the response type if needed.
// Helper function, called from CallAPI.
func (c *Client) getResponse(response *http.Response, resType interface{}) error {
// Read all the response body
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
// < 200 && >= 300 : API error
if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices {
apiError := &APIError{Code: response.StatusCode}
if err = json.Unmarshal(body, apiError); err != nil {
apiError.Message = string(body)
}
apiError.QueryID = response.Header.Get("X-Ovh-QueryID")
return apiError
}
// Nothing to unmarshal
if len(body) == 0 || resType == nil {
return nil
}
return json.Unmarshal(body, &resType)
}
// timeDelta returns the time delta between the host and the remote API
func (c *Client) getTimeDelta() (time.Duration, error) {
@ -210,6 +179,9 @@ func (c *Client) getTimeDelta() (time.Duration, error) {
// Ensure only one thread is updating
c.timeDeltaMutex.Lock()
// Ensure that the mutex will be released on return
defer c.timeDeltaMutex.Unlock()
// Did we wait ? Maybe no more needed
if !c.timeDeltaDone {
ovhTime, err := c.getTime()
@ -220,7 +192,6 @@ func (c *Client) getTimeDelta() (time.Duration, error) {
c.timeDelta = time.Since(*ovhTime)
c.timeDeltaDone = true
}
c.timeDeltaMutex.Unlock()
}
return c.timeDelta, nil
@ -251,39 +222,22 @@ var getEndpointForSignature = func(c *Client) string {
return c.endpoint
}
// CallAPI is the lowest level call helper. If needAuth is true,
// inject authentication headers and sign the request.
//
// Request signature is a sha1 hash on following fields, joined by '+':
// - applicationSecret (from Client instance)
// - consumerKey (from Client instance)
// - capitalized method (from arguments)
// - full request url, including any query string argument
// - full serialized request body
// - server current time (takes time delta into account)
//
// Call will automatically assemble the target url from the endpoint
// configured in the client instance and the path argument. If the reqBody
// argument is not nil, it will also serialize it as json and inject
// the required Content-Type header.
//
// If everyrthing went fine, unmarshall response into resType and return nil
// otherwise, return the error
func (c *Client) CallAPI(method, path string, reqBody, resType interface{}, needAuth bool) error {
// NewRequest returns a new HTTP request
func (c *Client) NewRequest(method, path string, reqBody interface{}, needAuth bool) (*http.Request, error) {
var body []byte
var err error
if reqBody != nil {
body, err = json.Marshal(reqBody)
if err != nil {
return err
return nil, err
}
}
target := fmt.Sprintf("%s%s", c.endpoint, path)
req, err := http.NewRequest(method, target, bytes.NewReader(body))
if err != nil {
return err
return nil, err
}
// Inject headers
@ -298,7 +252,7 @@ func (c *Client) CallAPI(method, path string, reqBody, resType interface{}, need
if needAuth {
timeDelta, err := c.TimeDelta()
if err != nil {
return err
return nil, err
}
timestamp := getLocalTime().Add(-timeDelta).Unix()
@ -321,12 +275,71 @@ func (c *Client) CallAPI(method, path string, reqBody, resType interface{}, need
// Send the request with requested timeout
c.Client.Timeout = c.Timeout
response, err := c.Client.Do(req)
return req, nil
}
// Do sends an HTTP request and returns an HTTP response
func (c *Client) Do(req *http.Request) (*http.Response, error) {
return c.Client.Do(req)
}
// CallAPI is the lowest level call helper. If needAuth is true,
// inject authentication headers and sign the request.
//
// Request signature is a sha1 hash on following fields, joined by '+':
// - applicationSecret (from Client instance)
// - consumerKey (from Client instance)
// - capitalized method (from arguments)
// - full request url, including any query string argument
// - full serialized request body
// - server current time (takes time delta into account)
//
// Call will automatically assemble the target url from the endpoint
// configured in the client instance and the path argument. If the reqBody
// argument is not nil, it will also serialize it as json and inject
// the required Content-Type header.
//
// If everything went fine, unmarshall response into resType and return nil
// otherwise, return the error
func (c *Client) CallAPI(method, path string, reqBody, resType interface{}, needAuth bool) error {
req, err := c.NewRequest(method, path, reqBody, needAuth)
if err != nil {
return err
}
response, err := c.Do(req)
if err != nil {
return err
}
return c.UnmarshalResponse(response, resType)
}
// UnmarshalResponse checks the response and unmarshals it into the response
// type if needed Helper function, called from CallAPI
func (c *Client) UnmarshalResponse(response *http.Response, resType interface{}) error {
// Read all the response body
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
// Unmarshal the result into the resType if possible
return c.getResponse(response, resType)
// < 200 && >= 300 : API error
if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices {
apiError := &APIError{Code: response.StatusCode}
if err = json.Unmarshal(body, apiError); err != nil {
apiError.Message = string(body)
}
apiError.QueryID = response.Header.Get("X-Ovh-QueryID")
return apiError
}
// Nothing to unmarshal
if len(body) == 0 || resType == nil {
return nil
}
return json.Unmarshal(body, &resType)
}

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]
}