forked from TrueCloudLab/lego
Review DNS providers (#580)
* refactor: create log.Infof and log.Warnf * refactor: review DNS providers. - use one `http.Client` by provider instead of one client by request - use the same receiver name `d` for all `DNSProvider` - use `http.MethodXXX` * refactor: logger init.
This commit is contained in:
parent
57782ac3c1
commit
a1585a7b9a
42 changed files with 573 additions and 549 deletions
|
@ -164,7 +164,7 @@ func (c *Client) Register(tosAgreed bool) (*RegistrationResource, error) {
|
||||||
if c == nil || c.user == nil {
|
if c == nil || c.user == nil {
|
||||||
return nil, errors.New("acme: cannot register a nil client or user")
|
return nil, errors.New("acme: cannot register a nil client or user")
|
||||||
}
|
}
|
||||||
log.Printf("[INFO] acme: Registering account for %s", c.user.GetEmail())
|
log.Infof("acme: Registering account for %s", c.user.GetEmail())
|
||||||
|
|
||||||
accMsg := accountMessage{}
|
accMsg := accountMessage{}
|
||||||
if c.user.GetEmail() != "" {
|
if c.user.GetEmail() != "" {
|
||||||
|
@ -198,7 +198,7 @@ func (c *Client) RegisterWithExternalAccountBinding(tosAgreed bool, kid string,
|
||||||
if c == nil || c.user == nil {
|
if c == nil || c.user == nil {
|
||||||
return nil, errors.New("acme: cannot register a nil client or user")
|
return nil, errors.New("acme: cannot register a nil client or user")
|
||||||
}
|
}
|
||||||
log.Printf("[INFO] acme: Registering account (EAB) for %s", c.user.GetEmail())
|
log.Infof("acme: Registering account (EAB) for %s", c.user.GetEmail())
|
||||||
|
|
||||||
accMsg := accountMessage{}
|
accMsg := accountMessage{}
|
||||||
if c.user.GetEmail() != "" {
|
if c.user.GetEmail() != "" {
|
||||||
|
@ -244,7 +244,7 @@ func (c *Client) RegisterWithExternalAccountBinding(tosAgreed bool, kid string,
|
||||||
// ResolveAccountByKey will attempt to look up an account using the given account key
|
// ResolveAccountByKey will attempt to look up an account using the given account key
|
||||||
// and return its registration resource.
|
// and return its registration resource.
|
||||||
func (c *Client) ResolveAccountByKey() (*RegistrationResource, error) {
|
func (c *Client) ResolveAccountByKey() (*RegistrationResource, error) {
|
||||||
log.Printf("[INFO] acme: Trying to resolve account by key")
|
log.Infof("acme: Trying to resolve account by key")
|
||||||
|
|
||||||
acc := accountMessage{OnlyReturnExisting: true}
|
acc := accountMessage{OnlyReturnExisting: true}
|
||||||
hdr, err := postJSON(c.jws, c.directory.NewAccountURL, acc, nil)
|
hdr, err := postJSON(c.jws, c.directory.NewAccountURL, acc, nil)
|
||||||
|
@ -273,7 +273,7 @@ func (c *Client) DeleteRegistration() error {
|
||||||
if c == nil || c.user == nil {
|
if c == nil || c.user == nil {
|
||||||
return errors.New("acme: cannot unregister a nil client or user")
|
return errors.New("acme: cannot unregister a nil client or user")
|
||||||
}
|
}
|
||||||
log.Printf("[INFO] acme: Deleting account for %s", c.user.GetEmail())
|
log.Infof("acme: Deleting account for %s", c.user.GetEmail())
|
||||||
|
|
||||||
accMsg := accountMessage{
|
accMsg := accountMessage{
|
||||||
Status: "deactivated",
|
Status: "deactivated",
|
||||||
|
@ -293,7 +293,7 @@ func (c *Client) QueryRegistration() (*RegistrationResource, error) {
|
||||||
return nil, errors.New("acme: cannot query the registration of a nil client or user")
|
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
|
// Log the URL here instead of the email as the email may not be set
|
||||||
log.Printf("[INFO] acme: Querying account for %s", c.user.GetRegistration().URI)
|
log.Infof("acme: Querying account for %s", c.user.GetRegistration().URI)
|
||||||
|
|
||||||
accMsg := accountMessage{}
|
accMsg := accountMessage{}
|
||||||
|
|
||||||
|
@ -339,9 +339,9 @@ DNSNames:
|
||||||
}
|
}
|
||||||
|
|
||||||
if bundle {
|
if bundle {
|
||||||
log.Printf("[INFO][%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", "))
|
log.Infof("[%s] acme: Obtaining bundled SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[INFO][%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
|
log.Infof("[%s] acme: Obtaining SAN certificate given a CSR", strings.Join(domains, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
order, err := c.createOrderForIdentifiers(domains)
|
order, err := c.createOrderForIdentifiers(domains)
|
||||||
|
@ -363,7 +363,7 @@ DNSNames:
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||||
|
|
||||||
failures := make(ObtainError)
|
failures := make(ObtainError)
|
||||||
cert, err := c.requestCertificateForCsr(order, bundle, csr.Raw, nil)
|
cert, err := c.requestCertificateForCsr(order, bundle, csr.Raw, nil)
|
||||||
|
@ -399,9 +399,9 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
|
||||||
}
|
}
|
||||||
|
|
||||||
if bundle {
|
if bundle {
|
||||||
log.Printf("[INFO][%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
|
log.Infof("[%s] acme: Obtaining bundled SAN certificate", strings.Join(domains, ", "))
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[INFO][%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
|
log.Infof("[%s] acme: Obtaining SAN certificate", strings.Join(domains, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
order, err := c.createOrderForIdentifiers(domains)
|
order, err := c.createOrderForIdentifiers(domains)
|
||||||
|
@ -423,7 +423,7 @@ func (c *Client) ObtainCertificate(domains []string, bundle bool, privKey crypto
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO][%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
log.Infof("[%s] acme: Validations succeeded; requesting certificates", strings.Join(domains, ", "))
|
||||||
|
|
||||||
failures := make(ObtainError)
|
failures := make(ObtainError)
|
||||||
cert, err := c.requestCertificateForOrder(order, bundle, privKey, mustStaple)
|
cert, err := c.requestCertificateForOrder(order, bundle, privKey, mustStaple)
|
||||||
|
@ -482,7 +482,7 @@ func (c *Client) RenewCertificate(cert CertificateResource, bundle, mustStaple b
|
||||||
|
|
||||||
// This is just meant to be informal for the user.
|
// This is just meant to be informal for the user.
|
||||||
timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
|
timeLeft := x509Cert.NotAfter.Sub(time.Now().UTC())
|
||||||
log.Printf("[INFO][%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours()))
|
log.Infof("[%s] acme: Trying renewal with %d hours remaining", cert.Domain, int(timeLeft.Hours()))
|
||||||
|
|
||||||
// We always need to request a new certificate to renew.
|
// We always need to request a new certificate to renew.
|
||||||
// Start by checking to see if the certificate was based off a CSR, and
|
// Start by checking to see if the certificate was based off a CSR, and
|
||||||
|
@ -556,7 +556,7 @@ func (c *Client) solveChallengeForAuthz(authorizations []authorization) error {
|
||||||
for _, authz := range authorizations {
|
for _, authz := range authorizations {
|
||||||
if authz.Status == "valid" {
|
if authz.Status == "valid" {
|
||||||
// Boulder might recycle recent validated authz (see issue #267)
|
// Boulder might recycle recent validated authz (see issue #267)
|
||||||
log.Printf("[INFO][%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value)
|
log.Infof("[%s] acme: Authorization already valid; skipping challenge", authz.Identifier.Value)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -587,7 +587,7 @@ func (c *Client) chooseSolver(auth authorization, domain string) (int, solver) {
|
||||||
if solver, ok := c.solvers[Challenge(challenge.Type)]; ok {
|
if solver, ok := c.solvers[Challenge(challenge.Type)]; ok {
|
||||||
return i, solver
|
return i, solver
|
||||||
}
|
}
|
||||||
log.Printf("[INFO][%s] acme: Could not find solver for: %s", domain, challenge.Type)
|
log.Infof("[%s] acme: Could not find solver for: %s", domain, challenge.Type)
|
||||||
}
|
}
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
@ -639,7 +639,7 @@ func (c *Client) getAuthzForOrder(order orderResource) ([]authorization, error)
|
||||||
|
|
||||||
func logAuthz(order orderResource) {
|
func logAuthz(order orderResource) {
|
||||||
for i, auth := range order.Authorizations {
|
for i, auth := range order.Authorizations {
|
||||||
log.Printf("[INFO][%s] AuthURL: %s", order.Identifiers[i].Value, auth)
|
log.Infof("[%s] AuthURL: %s", order.Identifiers[i].Value, auth)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -756,7 +756,7 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If we fail to acquire the issuer cert, return the issued certificate - do not fail.
|
// If we fail to acquire the issuer cert, return the issued certificate - do not fail.
|
||||||
log.Printf("[WARNING][%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err)
|
log.Warnf("[%s] acme: Could not bundle issuer certificate: %v", certRes.Domain, err)
|
||||||
} else {
|
} else {
|
||||||
issuerCert = pemEncode(derCertificateBytes(issuerCert))
|
issuerCert = pemEncode(derCertificateBytes(issuerCert))
|
||||||
|
|
||||||
|
@ -773,21 +773,21 @@ func (c *Client) checkCertResponse(order orderMessage, certRes *CertificateResou
|
||||||
certRes.Certificate = cert
|
certRes.Certificate = cert
|
||||||
certRes.CertURL = order.Certificate
|
certRes.CertURL = order.Certificate
|
||||||
certRes.CertStableURL = order.Certificate
|
certRes.CertStableURL = order.Certificate
|
||||||
log.Printf("[INFO][%s] Server responded with a certificate.", certRes.Domain)
|
log.Infof("[%s] Server responded with a certificate.", certRes.Domain)
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
||||||
case "processing":
|
case "processing":
|
||||||
return false, nil
|
return false, nil
|
||||||
case "invalid":
|
case "invalid":
|
||||||
return false, errors.New("Order has invalid state: invalid")
|
return false, errors.New("order has invalid state: invalid")
|
||||||
|
default:
|
||||||
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getIssuerCertificate requests the issuer certificate
|
// getIssuerCertificate requests the issuer certificate
|
||||||
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
|
func (c *Client) getIssuerCertificate(url string) ([]byte, error) {
|
||||||
log.Printf("[INFO] acme: Requesting issuer cert from %s", url)
|
log.Infof("acme: Requesting issuer cert from %s", url)
|
||||||
resp, err := httpGet(url)
|
resp, err := httpGet(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -841,7 +841,7 @@ func validate(j *jws, domain, uri string, c challenge) error {
|
||||||
for {
|
for {
|
||||||
switch chlng.Status {
|
switch chlng.Status {
|
||||||
case "valid":
|
case "valid":
|
||||||
log.Printf("[INFO][%s] The server validated our request", domain)
|
log.Infof("[%s] The server validated our request", domain)
|
||||||
return nil
|
return nil
|
||||||
case "pending":
|
case "pending":
|
||||||
case "processing":
|
case "processing":
|
||||||
|
|
|
@ -152,13 +152,13 @@ func TestValidate(t *testing.T) {
|
||||||
w.Header().Add("Replay-Nonce", "12345")
|
w.Header().Add("Replay-Nonce", "12345")
|
||||||
w.Header().Add("Retry-After", "0")
|
w.Header().Add("Retry-After", "0")
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case "HEAD":
|
case http.MethodHead:
|
||||||
case "POST":
|
case http.MethodPost:
|
||||||
st := statuses[0]
|
st := statuses[0]
|
||||||
statuses = statuses[1:]
|
statuses = statuses[1:]
|
||||||
writeJSONResponse(w, &challenge{Type: "http-01", Status: st, URL: "http://example.com/", Token: "token"})
|
writeJSONResponse(w, &challenge{Type: "http-01", Status: st, URL: "http://example.com/", Token: "token"})
|
||||||
|
|
||||||
case "GET":
|
case http.MethodGet:
|
||||||
st := statuses[0]
|
st := statuses[0]
|
||||||
statuses = statuses[1:]
|
statuses = statuses[1:]
|
||||||
writeJSONResponse(w, &challenge{Type: "http-01", Status: st, URL: "http://example.com/", Token: "token"})
|
writeJSONResponse(w, &challenge{Type: "http-01", Status: st, URL: "http://example.com/", Token: "token"})
|
||||||
|
@ -199,7 +199,7 @@ func TestGetChallenges(t *testing.T) {
|
||||||
var ts *httptest.Server
|
var ts *httptest.Server
|
||||||
ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
ts = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case "GET", "HEAD":
|
case http.MethodGet, http.MethodHead:
|
||||||
w.Header().Add("Replay-Nonce", "12345")
|
w.Header().Add("Replay-Nonce", "12345")
|
||||||
w.Header().Add("Retry-After", "0")
|
w.Header().Add("Retry-After", "0")
|
||||||
writeJSONResponse(w, directory{
|
writeJSONResponse(w, directory{
|
||||||
|
@ -209,7 +209,7 @@ func TestGetChallenges(t *testing.T) {
|
||||||
RevokeCertURL: ts.URL,
|
RevokeCertURL: ts.URL,
|
||||||
KeyChangeURL: ts.URL,
|
KeyChangeURL: ts.URL,
|
||||||
})
|
})
|
||||||
case "POST":
|
case http.MethodPost:
|
||||||
writeJSONResponse(w, orderMessage{})
|
writeJSONResponse(w, orderMessage{})
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -72,10 +72,10 @@ type dnsChallenge struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
|
func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
|
||||||
log.Printf("[INFO][%s] acme: Trying to solve DNS-01", domain)
|
log.Infof("[%s] acme: Trying to solve DNS-01", domain)
|
||||||
|
|
||||||
if s.provider == nil {
|
if s.provider == nil {
|
||||||
return errors.New("No DNS Provider configured")
|
return errors.New("no DNS Provider configured")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the Key Authorization for the challenge
|
// Generate the Key Authorization for the challenge
|
||||||
|
@ -86,18 +86,18 @@ func (s *dnsChallenge) Solve(chlng challenge, domain string) error {
|
||||||
|
|
||||||
err = s.provider.Present(domain, chlng.Token, keyAuth)
|
err = s.provider.Present(domain, chlng.Token, keyAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error presenting token: %s", err)
|
return fmt.Errorf("error presenting token: %s", err)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error cleaning up %s: %v ", domain, err)
|
log.Warnf("Error cleaning up %s: %v ", domain, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fqdn, value, _ := DNS01Record(domain, keyAuth)
|
fqdn, value, _ := DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
log.Printf("[INFO][%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers)
|
log.Infof("[%s] Checking DNS record propagation using %+v", domain, RecursiveNameservers)
|
||||||
|
|
||||||
var timeout, interval time.Duration
|
var timeout, interval time.Duration
|
||||||
switch provider := s.provider.(type) {
|
switch provider := s.provider.(type) {
|
||||||
|
|
|
@ -30,9 +30,9 @@ func (*DNSProviderManual) Present(domain, token, keyAuth string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO] acme: Please create the following TXT record in your %s zone:", authZone)
|
log.Infof("acme: Please create the following TXT record in your %s zone:", authZone)
|
||||||
log.Printf("[INFO] acme: %s", dnsRecord)
|
log.Infof("acme: %s", dnsRecord)
|
||||||
log.Printf("[INFO] acme: Press 'Enter' when you are done")
|
log.Infof("acme: Press 'Enter' when you are done")
|
||||||
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
_, _ = reader.ReadString('\n')
|
_, _ = reader.ReadString('\n')
|
||||||
|
@ -49,7 +49,7 @@ func (*DNSProviderManual) CleanUp(domain, token, keyAuth string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[INFO] acme: You can now remove this TXT record from your %s zone:", authZone)
|
log.Infof("acme: You can now remove this TXT record from your %s zone:", authZone)
|
||||||
log.Printf("[INFO] acme: %s", dnsRecord)
|
log.Infof("acme: %s", dnsRecord)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ func initCertPool() *x509.CertPool {
|
||||||
// httpHead performs a HEAD request with a proper User-Agent string.
|
// httpHead performs a HEAD request with a proper User-Agent string.
|
||||||
// The response body (resp.Body) is already closed when this function returns.
|
// The response body (resp.Body) is already closed when this function returns.
|
||||||
func httpHead(url string) (resp *http.Response, err error) {
|
func httpHead(url string) (resp *http.Response, err error) {
|
||||||
req, err := http.NewRequest("HEAD", url, nil)
|
req, err := http.NewRequest(http.MethodHead, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to head %q: %v", url, err)
|
return nil, fmt.Errorf("failed to head %q: %v", url, err)
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ func httpHead(url string) (resp *http.Response, err error) {
|
||||||
// httpPost performs a POST request with a proper User-Agent string.
|
// httpPost performs a POST request with a proper User-Agent string.
|
||||||
// Callers should close resp.Body when done reading from it.
|
// Callers should close resp.Body when done reading from it.
|
||||||
func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, err error) {
|
func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response, err error) {
|
||||||
req, err := http.NewRequest("POST", url, body)
|
req, err := http.NewRequest(http.MethodPost, url, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to post %q: %v", url, err)
|
return nil, fmt.Errorf("failed to post %q: %v", url, err)
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ func httpPost(url string, bodyType string, body io.Reader) (resp *http.Response,
|
||||||
// httpGet performs a GET request with a proper User-Agent string.
|
// httpGet performs a GET request with a proper User-Agent string.
|
||||||
// Callers should close resp.Body when done reading from it.
|
// Callers should close resp.Body when done reading from it.
|
||||||
func httpGet(url string) (resp *http.Response, err error) {
|
func httpGet(url string) (resp *http.Response, err error) {
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get %q: %v", url, err)
|
return nil, fmt.Errorf("failed to get %q: %v", url, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ func HTTP01ChallengePath(token string) string {
|
||||||
|
|
||||||
func (s *httpChallenge) Solve(chlng challenge, domain string) error {
|
func (s *httpChallenge) Solve(chlng challenge, domain string) error {
|
||||||
|
|
||||||
log.Printf("[INFO][%s] acme: Trying to solve HTTP-01", domain)
|
log.Infof("[%s] acme: Trying to solve HTTP-01", domain)
|
||||||
|
|
||||||
// Generate the Key Authorization for the challenge
|
// Generate the Key Authorization for the challenge
|
||||||
keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey)
|
keyAuth, err := getKeyAuthorization(chlng.Token, s.jws.privKey)
|
||||||
|
@ -34,7 +34,7 @@ func (s *httpChallenge) Solve(chlng challenge, domain string) error {
|
||||||
defer func() {
|
defer func() {
|
||||||
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
err := s.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[%s] error cleaning up: %v", domain, err)
|
log.Warnf("[%s] error cleaning up: %v", domain, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -60,12 +60,12 @@ func (s *HTTPProviderServer) serve(domain, token, keyAuth string) {
|
||||||
// For validation it then writes the token the server returned with the challenge
|
// For validation it then writes the token the server returned with the challenge
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
|
||||||
if strings.HasPrefix(r.Host, domain) && r.Method == "GET" {
|
if strings.HasPrefix(r.Host, domain) && r.Method == http.MethodGet {
|
||||||
w.Header().Add("Content-Type", "text/plain")
|
w.Header().Add("Content-Type", "text/plain")
|
||||||
w.Write([]byte(keyAuth))
|
w.Write([]byte(keyAuth))
|
||||||
log.Printf("[INFO][%s] Served key authentication", domain)
|
log.Infof("[%s] Served key authentication", domain)
|
||||||
} else {
|
} else {
|
||||||
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)
|
log.Warnf("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"))
|
w.Write([]byte("TEST"))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,7 +22,7 @@ func TestHTTPHeadUserAgent(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if method != "HEAD" {
|
if method != http.MethodHead {
|
||||||
t.Errorf("Expected method to be HEAD, got %s", method)
|
t.Errorf("Expected method to be HEAD, got %s", method)
|
||||||
}
|
}
|
||||||
if !strings.Contains(ua, ourUserAgent) {
|
if !strings.Contains(ua, ourUserAgent) {
|
||||||
|
@ -44,7 +44,7 @@ func TestHTTPGetUserAgent(t *testing.T) {
|
||||||
}
|
}
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
|
|
||||||
if method != "GET" {
|
if method != http.MethodGet {
|
||||||
t.Errorf("Expected method to be GET, got %s", method)
|
t.Errorf("Expected method to be GET, got %s", method)
|
||||||
}
|
}
|
||||||
if !strings.Contains(ua, ourUserAgent) {
|
if !strings.Contains(ua, ourUserAgent) {
|
||||||
|
@ -66,7 +66,7 @@ func TestHTTPPostUserAgent(t *testing.T) {
|
||||||
}
|
}
|
||||||
res.Body.Close()
|
res.Body.Close()
|
||||||
|
|
||||||
if method != "POST" {
|
if method != http.MethodPost {
|
||||||
t.Errorf("Expected method to be POST, got %s", method)
|
t.Errorf("Expected method to be POST, got %s", method)
|
||||||
}
|
}
|
||||||
if !strings.Contains(ua, ourUserAgent) {
|
if !strings.Contains(ua, ourUserAgent) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ type tlsALPNChallenge struct {
|
||||||
|
|
||||||
// Solve manages the provider to validate and solve the challenge.
|
// Solve manages the provider to validate and solve the challenge.
|
||||||
func (t *tlsALPNChallenge) Solve(chlng challenge, domain string) error {
|
func (t *tlsALPNChallenge) Solve(chlng challenge, domain string) error {
|
||||||
log.Printf("[INFO][%s] acme: Trying to solve TLS-ALPN-01", domain)
|
log.Infof("[%s] acme: Trying to solve TLS-ALPN-01", domain)
|
||||||
|
|
||||||
// Generate the Key Authorization for the challenge
|
// Generate the Key Authorization for the challenge
|
||||||
keyAuth, err := getKeyAuthorization(chlng.Token, t.jws.privKey)
|
keyAuth, err := getKeyAuthorization(chlng.Token, t.jws.privKey)
|
||||||
|
@ -38,7 +38,7 @@ func (t *tlsALPNChallenge) Solve(chlng challenge, domain string) error {
|
||||||
defer func() {
|
defer func() {
|
||||||
err := t.provider.CleanUp(domain, chlng.Token, keyAuth)
|
err := t.provider.CleanUp(domain, chlng.Token, keyAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[%s] error cleaning up: %v", domain, err)
|
log.Warnf("[%s] error cleaning up: %v", domain, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
|
@ -6,54 +6,44 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger is an optional custom logger.
|
// Logger is an optional custom logger.
|
||||||
var Logger *log.Logger
|
var Logger = log.New(os.Stdout, "", log.LstdFlags)
|
||||||
|
|
||||||
// Fatal writes a log entry.
|
// Fatal writes a log entry.
|
||||||
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
||||||
func Fatal(args ...interface{}) {
|
func Fatal(args ...interface{}) {
|
||||||
if Logger == nil {
|
|
||||||
Logger = log.New(os.Stderr, "", log.LstdFlags)
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Fatal(args...)
|
Logger.Fatal(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatalf writes a log entry.
|
// Fatalf writes a log entry.
|
||||||
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
||||||
func Fatalf(format string, args ...interface{}) {
|
func Fatalf(format string, args ...interface{}) {
|
||||||
if Logger == nil {
|
|
||||||
Logger = log.New(os.Stderr, "", log.LstdFlags)
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Fatalf(format, args...)
|
Logger.Fatalf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print writes a log entry.
|
// Print writes a log entry.
|
||||||
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
||||||
func Print(args ...interface{}) {
|
func Print(args ...interface{}) {
|
||||||
if Logger == nil {
|
|
||||||
Logger = log.New(os.Stdout, "", log.LstdFlags)
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Print(args...)
|
Logger.Print(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Println writes a log entry.
|
// Println writes a log entry.
|
||||||
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
||||||
func Println(args ...interface{}) {
|
func Println(args ...interface{}) {
|
||||||
if Logger == nil {
|
|
||||||
Logger = log.New(os.Stdout, "", log.LstdFlags)
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Println(args...)
|
Logger.Println(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Printf writes a log entry.
|
// Printf writes a log entry.
|
||||||
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
// It uses Logger if not nil, otherwise it uses the default log.Logger.
|
||||||
func Printf(format string, args ...interface{}) {
|
func Printf(format string, args ...interface{}) {
|
||||||
if Logger == nil {
|
|
||||||
Logger = log.New(os.Stdout, "", log.LstdFlags)
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Printf(format, args...)
|
Logger.Printf(format, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Warnf writes a log entry.
|
||||||
|
func Warnf(format string, args ...interface{}) {
|
||||||
|
Printf("[WARN] "+format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof writes a log entry.
|
||||||
|
func Infof(format string, args ...interface{}) {
|
||||||
|
Printf("[INFO] "+format, args...)
|
||||||
|
}
|
||||||
|
|
|
@ -51,8 +51,8 @@ func NewDNSProviderCredentials(baseURL string, userID string, key string) (*DNSP
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (provider *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, error) {
|
func (d *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRecord, error) {
|
||||||
zs, err := provider.client.GetZones()
|
zs, err := d.client.GetZones()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return zones.ZoneRecord{}, err
|
return zones.ZoneRecord{}, err
|
||||||
|
@ -68,7 +68,7 @@ func (provider *DNSProvider) getZoneInformationByName(name string) (zones.ZoneRe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a record with a secret
|
// Present creates a record with a secret
|
||||||
func (provider *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||||
|
@ -87,7 +87,7 @@ func (provider *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
|
||||||
authZone = acme.UnFqdn(authZone)
|
authZone = acme.UnFqdn(authZone)
|
||||||
|
|
||||||
zoneRecord, err := provider.getZoneInformationByName(authZone)
|
zoneRecord, err := d.getZoneInformationByName(authZone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create record: %v", err)
|
return fmt.Errorf("could not create record: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -100,25 +100,25 @@ func (provider *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
TTL: 300,
|
TTL: 300,
|
||||||
}
|
}
|
||||||
|
|
||||||
respData, err := provider.client.CreateRecord(zoneRecord.ID, reqData)
|
respData, err := d.client.CreateRecord(zoneRecord.ID, reqData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not create record: %v", err)
|
return fmt.Errorf("could not create record: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.recordIDsMu.Lock()
|
d.recordIDsMu.Lock()
|
||||||
provider.recordIDs[fqdn] = respData.ID
|
d.recordIDs[fqdn] = respData.ID
|
||||||
provider.recordIDsMu.Unlock()
|
d.recordIDsMu.Unlock()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes a given record that was generated by Present
|
// CleanUp removes a given record that was generated by Present
|
||||||
func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
provider.recordIDsMu.Lock()
|
d.recordIDsMu.Lock()
|
||||||
recordID, ok := provider.recordIDs[fqdn]
|
recordID, ok := d.recordIDs[fqdn]
|
||||||
provider.recordIDsMu.Unlock()
|
d.recordIDsMu.Unlock()
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("unknown recordID for %q", fqdn)
|
return fmt.Errorf("unknown recordID for %q", fqdn)
|
||||||
|
@ -131,19 +131,19 @@ func (provider *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
|
||||||
authZone = acme.UnFqdn(authZone)
|
authZone = acme.UnFqdn(authZone)
|
||||||
|
|
||||||
zoneRecord, err := provider.getZoneInformationByName(authZone)
|
zoneRecord, err := d.getZoneInformationByName(authZone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = provider.client.RemoveRecord(zoneRecord.ID, recordID)
|
_, err = d.client.RemoveRecord(zoneRecord.ID, recordID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.recordIDsMu.Lock()
|
d.recordIDsMu.Lock()
|
||||||
delete(provider.recordIDs, fqdn)
|
delete(d.recordIDs, fqdn)
|
||||||
provider.recordIDsMu.Unlock()
|
d.recordIDsMu.Unlock()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ func TestAuroraDNSPresent(t *testing.T) {
|
||||||
var requestReceived bool
|
var requestReceived bool
|
||||||
|
|
||||||
mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "GET" && r.URL.Path == "/zones" {
|
if r.Method == http.MethodGet && r.URL.Path == "/zones" {
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
fmt.Fprintf(w, `[{
|
fmt.Fprintf(w, `[{
|
||||||
"id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
|
"id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||||
|
@ -62,7 +62,7 @@ func TestAuroraDNSCleanUp(t *testing.T) {
|
||||||
var requestReceived bool
|
var requestReceived bool
|
||||||
|
|
||||||
mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
mock := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "GET" && r.URL.Path == "/zones" {
|
if r.Method == http.MethodGet && r.URL.Path == "/zones" {
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
fmt.Fprintf(w, `[{
|
fmt.Fprintf(w, `[{
|
||||||
"id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
|
"id": "c56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||||
|
@ -71,7 +71,7 @@ func TestAuroraDNSCleanUp(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Method == "POST" && r.URL.Path == "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records" {
|
if r.Method == http.MethodPost && r.URL.Path == "/zones/c56a4180-65aa-42ec-a945-5fd21dec0538/records" {
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
fmt.Fprintf(w, `{
|
fmt.Fprintf(w, `{
|
||||||
"id": "ec56a4180-65aa-42ec-a945-5fd21dec0538",
|
"id": "ec56a4180-65aa-42ec-a945-5fd21dec0538",
|
||||||
|
|
|
@ -67,20 +67,20 @@ func NewDNSProviderCredentials(clientID, clientSecret, subscriptionID, tenantID,
|
||||||
|
|
||||||
// Timeout returns the timeout and interval to use when checking for DNS
|
// Timeout returns the timeout and interval to use when checking for DNS
|
||||||
// propagation. Adjusting here to cope with spikes in propagation times.
|
// propagation. Adjusting here to cope with spikes in propagation times.
|
||||||
func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
return 120 * time.Second, 2 * time.Second
|
return 120 * time.Second, 2 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge
|
// Present creates a TXT record to fulfil the dns-01 challenge
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
zone, err := c.getHostedZoneID(fqdn)
|
zone, err := d.getHostedZoneID(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rsc := dns.NewRecordSetsClient(c.subscriptionID)
|
rsc := dns.NewRecordSetsClient(d.subscriptionID)
|
||||||
spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
|
spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = rsc.CreateOrUpdate(c.context, c.resourceGroup, zone, relative, dns.TXT, rec, "", "")
|
_, err = rsc.CreateOrUpdate(d.context, d.resourceGroup, zone, relative, dns.TXT, rec, "", "")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,44 +106,44 @@ func toRelativeRecord(domain, zone string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
zone, err := c.getHostedZoneID(fqdn)
|
zone, err := d.getHostedZoneID(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
|
relative := toRelativeRecord(fqdn, acme.ToFqdn(zone))
|
||||||
rsc := dns.NewRecordSetsClient(c.subscriptionID)
|
rsc := dns.NewRecordSetsClient(d.subscriptionID)
|
||||||
spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
|
spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rsc.Authorizer = autorest.NewBearerAuthorizer(spt)
|
rsc.Authorizer = autorest.NewBearerAuthorizer(spt)
|
||||||
|
|
||||||
_, err = rsc.Delete(c.context, c.resourceGroup, zone, relative, dns.TXT, "")
|
_, err = rsc.Delete(d.context, d.resourceGroup, zone, relative, dns.TXT, "")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks that azure has a zone for this domain name.
|
// Checks that azure has a zone for this domain name.
|
||||||
func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
||||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we want to to Azure and get the zone.
|
// Now we want to to Azure and get the zone.
|
||||||
spt, err := c.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
|
spt, err := d.newServicePrincipalTokenFromCredentials(azure.PublicCloud.ResourceManagerEndpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
dc := dns.NewZonesClient(c.subscriptionID)
|
dc := dns.NewZonesClient(d.subscriptionID)
|
||||||
dc.Authorizer = autorest.NewBearerAuthorizer(spt)
|
dc.Authorizer = autorest.NewBearerAuthorizer(spt)
|
||||||
|
|
||||||
zone, err := dc.Get(c.context, c.resourceGroup, acme.UnFqdn(authZone))
|
zone, err := dc.Get(d.context, d.resourceGroup, acme.UnFqdn(authZone))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -154,10 +154,10 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
||||||
|
|
||||||
// NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the
|
// NewServicePrincipalTokenFromCredentials creates a new ServicePrincipalToken using values of the
|
||||||
// passed credentials map.
|
// passed credentials map.
|
||||||
func (c *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*adal.ServicePrincipalToken, error) {
|
func (d *DNSProvider) newServicePrincipalTokenFromCredentials(scope string) (*adal.ServicePrincipalToken, error) {
|
||||||
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, c.tenantID)
|
oauthConfig, err := adal.NewOAuthConfig(azure.PublicCloud.ActiveDirectoryEndpoint, d.tenantID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return adal.NewServicePrincipalToken(*oauthConfig, c.clientID, c.clientSecret, scope)
|
return adal.NewServicePrincipalToken(*oauthConfig, d.clientID, d.clientSecret, scope)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ type DNSProvider struct {
|
||||||
configName string
|
configName string
|
||||||
dnsView string
|
dnsView string
|
||||||
token string
|
token string
|
||||||
httpClient *http.Client
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a DNSProvider instance configured for Bluecat DNS.
|
// NewDNSProvider returns a DNSProvider instance configured for Bluecat DNS.
|
||||||
|
@ -85,7 +85,7 @@ func NewDNSProviderCredentials(server, userName, password, configName, dnsView s
|
||||||
password: password,
|
password: password,
|
||||||
configName: configName,
|
configName: configName,
|
||||||
dnsView: dnsView,
|
dnsView: dnsView,
|
||||||
httpClient: client,
|
client: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{},
|
||||||
q.Add(argName, argVal)
|
q.Add(argName, argVal)
|
||||||
}
|
}
|
||||||
req.URL.RawQuery = q.Encode()
|
req.URL.RawQuery = q.Encode()
|
||||||
resp, err := d.httpClient.Do(req)
|
resp, err := d.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -137,13 +137,16 @@ func (d *DNSProvider) login() error {
|
||||||
"password": d.password,
|
"password": d.password,
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := d.sendRequest("GET", "login", nil, queryArgs)
|
resp, err := d.sendRequest(http.MethodGet, "login", nil, queryArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
authBytes, _ := ioutil.ReadAll(resp.Body)
|
authBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
authResp := string(authBytes)
|
authResp := string(authBytes)
|
||||||
|
|
||||||
if strings.Contains(authResp, "Authentication Error") {
|
if strings.Contains(authResp, "Authentication Error") {
|
||||||
|
@ -164,7 +167,7 @@ func (d *DNSProvider) logout() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := d.sendRequest("GET", "logout", nil, nil)
|
resp, err := d.sendRequest(http.MethodGet, "logout", nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -174,7 +177,10 @@ func (d *DNSProvider) logout() error {
|
||||||
return fmt.Errorf("Bluecat API request failed to delete session with HTTP status code %d", resp.StatusCode)
|
return fmt.Errorf("Bluecat API request failed to delete session with HTTP status code %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
authBytes, _ := ioutil.ReadAll(resp.Body)
|
authBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
authResp := string(authBytes)
|
authResp := string(authBytes)
|
||||||
|
|
||||||
if !strings.Contains(authResp, "successfully") {
|
if !strings.Contains(authResp, "successfully") {
|
||||||
|
@ -195,7 +201,7 @@ func (d *DNSProvider) lookupConfID() (uint, error) {
|
||||||
"type": configType,
|
"type": configType,
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs)
|
resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -222,7 +228,7 @@ func (d *DNSProvider) lookupViewID(viewName string) (uint, error) {
|
||||||
"type": viewType,
|
"type": viewType,
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs)
|
resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -271,7 +277,7 @@ func (d *DNSProvider) getZone(parentID uint, name string) (uint, error) {
|
||||||
"type": zoneType,
|
"type": zoneType,
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs)
|
resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs)
|
||||||
// Return an empty zone if the named zone doesn't exist
|
// Return an empty zone if the named zone doesn't exist
|
||||||
if resp != nil && resp.StatusCode == 404 {
|
if resp != nil && resp.StatusCode == 404 {
|
||||||
return 0, fmt.Errorf("Bluecat API could not find zone named %s", name)
|
return 0, fmt.Errorf("Bluecat API could not find zone named %s", name)
|
||||||
|
@ -321,8 +327,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
Properties: fmt.Sprintf("ttl=%d|absoluteName=%s|txt=%s|", ttl, fqdn, value),
|
Properties: fmt.Sprintf("ttl=%d|absoluteName=%s|txt=%s|", ttl, fqdn, value),
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := d.sendRequest("POST", "addEntity", body, queryArgs)
|
resp, err := d.sendRequest(http.MethodPost, "addEntity", body, queryArgs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -350,8 +355,7 @@ func (d *DNSProvider) deploy(entityID uint) error {
|
||||||
"entityId": strconv.FormatUint(uint64(entityID), 10),
|
"entityId": strconv.FormatUint(uint64(entityID), 10),
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := d.sendRequest("POST", "quickDeploy", nil, queryArgs)
|
resp, err := d.sendRequest(http.MethodPost, "quickDeploy", nil, queryArgs)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -385,7 +389,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
"type": txtType,
|
"type": txtType,
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := d.sendRequest("GET", "getEntityByName", nil, queryArgs)
|
resp, err := d.sendRequest(http.MethodGet, "getEntityByName", nil, queryArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -400,7 +404,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
"objectId": strconv.FormatUint(uint64(txtRec.ID), 10),
|
"objectId": strconv.FormatUint(uint64(txtRec.ID), 10),
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = d.sendRequest("DELETE", "delete", nil, queryArgs)
|
resp, err = d.sendRequest(http.MethodDelete, http.MethodDelete, nil, queryArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ const CloudFlareAPIURL = "https://api.cloudflare.com/client/v4"
|
||||||
type DNSProvider struct {
|
type DNSProvider struct {
|
||||||
authEmail string
|
authEmail string
|
||||||
authKey string
|
authKey string
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a DNSProvider instance configured for cloudflare.
|
// NewDNSProvider returns a DNSProvider instance configured for cloudflare.
|
||||||
|
@ -48,19 +49,20 @@ func NewDNSProviderCredentials(email, key string) (*DNSProvider, error) {
|
||||||
return &DNSProvider{
|
return &DNSProvider{
|
||||||
authEmail: email,
|
authEmail: email,
|
||||||
authKey: key,
|
authKey: key,
|
||||||
|
client: &http.Client{Timeout: 30 * time.Second},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timeout returns the timeout and interval to use when checking for DNS
|
// Timeout returns the timeout and interval to use when checking for DNS
|
||||||
// propagation. Adjusting here to cope with spikes in propagation times.
|
// propagation. Adjusting here to cope with spikes in propagation times.
|
||||||
func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
return 120 * time.Second, 2 * time.Second
|
return 120 * time.Second, 2 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge
|
// Present creates a TXT record to fulfil the dns-01 challenge
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
zoneID, err := c.getHostedZoneID(fqdn)
|
zoneID, err := d.getHostedZoneID(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -77,24 +79,24 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.makeRequest("POST", fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body))
|
_, err = d.doRequest(http.MethodPost, fmt.Sprintf("/zones/%s/dns_records", zoneID), bytes.NewReader(body))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
record, err := c.findTxtRecord(fqdn)
|
record, err := d.findTxtRecord(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.makeRequest("DELETE", fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil)
|
_, err = d.doRequest(http.MethodDelete, fmt.Sprintf("/zones/%s/dns_records/%s", record.ZoneID, record.ID), nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
||||||
// HostedZone represents a CloudFlare DNS zone
|
// HostedZone represents a CloudFlare DNS zone
|
||||||
type HostedZone struct {
|
type HostedZone struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
@ -106,7 +108,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := c.makeRequest("GET", "/zones?name="+acme.UnFqdn(authZone), nil)
|
result, err := d.doRequest(http.MethodGet, "/zones?name="+acme.UnFqdn(authZone), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -124,14 +126,14 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
||||||
return hostedZone[0].ID, nil
|
return hostedZone[0].ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) {
|
func (d *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) {
|
||||||
zoneID, err := c.getHostedZoneID(fqdn)
|
zoneID, err := d.getHostedZoneID(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := c.makeRequest(
|
result, err := d.doRequest(
|
||||||
"GET",
|
http.MethodGet,
|
||||||
fmt.Sprintf("/zones/%s/dns_records?per_page=1000&type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)),
|
fmt.Sprintf("/zones/%s/dns_records?per_page=1000&type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)),
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
@ -154,31 +156,16 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*cloudFlareRecord, error) {
|
||||||
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) {
|
func (d *DNSProvider) doRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
|
||||||
// APIError contains error details for failed requests
|
|
||||||
type APIError struct {
|
|
||||||
Code int `json:"code,omitempty"`
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
ErrorChain []APIError `json:"error_chain,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// APIResponse represents a response from CloudFlare API
|
|
||||||
type APIResponse struct {
|
|
||||||
Success bool `json:"success"`
|
|
||||||
Errors []*APIError `json:"errors"`
|
|
||||||
Result json.RawMessage `json:"result"`
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest(method, fmt.Sprintf("%s%s", CloudFlareAPIURL, uri), body)
|
req, err := http.NewRequest(method, fmt.Sprintf("%s%s", CloudFlareAPIURL, uri), body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("X-Auth-Email", c.authEmail)
|
req.Header.Set("X-Auth-Email", d.authEmail)
|
||||||
req.Header.Set("X-Auth-Key", c.authKey)
|
req.Header.Set("X-Auth-Key", d.authKey)
|
||||||
|
|
||||||
client := http.Client{Timeout: 30 * time.Second}
|
resp, err := d.client.Do(req)
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error querying Cloudflare API -> %v", err)
|
return nil, fmt.Errorf("error querying Cloudflare API -> %v", err)
|
||||||
}
|
}
|
||||||
|
@ -212,6 +199,20 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
|
||||||
return r.Result, nil
|
return r.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// APIError contains error details for failed requests
|
||||||
|
type APIError struct {
|
||||||
|
Code int `json:"code,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
ErrorChain []APIError `json:"error_chain,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// APIResponse represents a response from CloudFlare API
|
||||||
|
type APIResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Errors []*APIError `json:"errors"`
|
||||||
|
Result json.RawMessage `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
// cloudFlareRecord represents a CloudFlare DNS record
|
// cloudFlareRecord represents a CloudFlare DNS record
|
||||||
type cloudFlareRecord struct {
|
type cloudFlareRecord struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
|
@ -50,33 +50,33 @@ func NewDNSProviderCredentials(apiKey, secretKey string) (*DNSProvider, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
zoneID, err := c.getHostedZoneID(fqdn)
|
zoneID, err := d.getHostedZoneID(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.addTxtRecord(zoneID, fqdn, value, ttl)
|
return d.addTxtRecord(zoneID, fqdn, value, ttl)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters.
|
// CleanUp removes the TXT record matching the specified parameters.
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
zoneID, err := c.getHostedZoneID(fqdn)
|
zoneID, err := d.getHostedZoneID(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
recordID, err := c.findTxtRecord(zoneID, fqdn)
|
recordID, err := d.findTxtRecord(zoneID, fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.delTxtRecord(recordID, zoneID)
|
return d.delTxtRecord(recordID, zoneID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
func (d *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
||||||
type Data struct {
|
type Data struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
|
@ -87,7 +87,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (string, error) {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := c.makeRequest("GET", "domain", nil)
|
result, err := d.makeRequest(http.MethodGet, "domain", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -107,8 +107,8 @@ 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) {
|
func (d *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) {
|
||||||
result, err := c.makeRequest("GET", fmt.Sprintf("record/%s?host_id=0&offset=0&row_num=2000", zoneID), nil)
|
result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("record/%s?host_id=0&offset=0&row_num=2000", zoneID), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ func (c *DNSProvider) findTxtRecord(zoneID, fqdn string) (string, error) {
|
||||||
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 {
|
func (d *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error {
|
||||||
id, err := strconv.Atoi(zoneID)
|
id, err := strconv.Atoi(zoneID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -148,21 +148,21 @@ func (c *DNSProvider) addTxtRecord(zoneID, fqdn, value string, ttl int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.makeRequest("POST", "record", body)
|
_, err = d.makeRequest(http.MethodPost, "record", body)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) delTxtRecord(recordID, zoneID string) error {
|
func (d *DNSProvider) delTxtRecord(recordID, zoneID string) error {
|
||||||
_, err := c.makeRequest("DELETE", fmt.Sprintf("record/%s/%s", recordID, zoneID), nil)
|
_, err := d.makeRequest(http.MethodDelete, fmt.Sprintf("record/%s/%s", recordID, zoneID), nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) hmac(url, date, body string) string {
|
func (d *DNSProvider) hmac(url, date, body string) string {
|
||||||
sum := md5.Sum([]byte(c.apiKey + url + body + date + c.secretKey))
|
sum := md5.Sum([]byte(d.apiKey + url + body + date + d.secretKey))
|
||||||
return hex.EncodeToString(sum[:])
|
return hex.EncodeToString(sum[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMessage, error) {
|
func (d *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMessage, error) {
|
||||||
type APIResponse struct {
|
type APIResponse struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
|
@ -177,9 +177,9 @@ func (c *DNSProvider) makeRequest(method, uri string, body []byte) (json.RawMess
|
||||||
|
|
||||||
requestDate := time.Now().Format(time.RFC1123Z)
|
requestDate := time.Now().Format(time.RFC1123Z)
|
||||||
|
|
||||||
req.Header.Set("API-KEY", c.apiKey)
|
req.Header.Set("API-KEY", d.apiKey)
|
||||||
req.Header.Set("API-REQUEST-DATE", requestDate)
|
req.Header.Set("API-REQUEST-DATE", requestDate)
|
||||||
req.Header.Set("API-HMAC", c.hmac(url, requestDate, string(body)))
|
req.Header.Set("API-HMAC", d.hmac(url, requestDate, string(body)))
|
||||||
req.Header.Set("API-FORMAT", "json")
|
req.Header.Set("API-FORMAT", "json")
|
||||||
|
|
||||||
resp, err := acme.HTTPClient.Do(req)
|
resp, err := acme.HTTPClient.Do(req)
|
||||||
|
|
|
@ -20,12 +20,7 @@ type DNSProvider struct {
|
||||||
apiAuthToken string
|
apiAuthToken string
|
||||||
recordIDs map[string]int
|
recordIDs map[string]int
|
||||||
recordIDsMu sync.Mutex
|
recordIDsMu sync.Mutex
|
||||||
}
|
client *http.Client
|
||||||
|
|
||||||
// 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
|
// NewDNSProvider returns a DNSProvider instance configured for Digital
|
||||||
|
@ -49,9 +44,16 @@ func NewDNSProviderCredentials(apiAuthToken string) (*DNSProvider, error) {
|
||||||
return &DNSProvider{
|
return &DNSProvider{
|
||||||
apiAuthToken: apiAuthToken,
|
apiAuthToken: apiAuthToken,
|
||||||
recordIDs: make(map[string]int),
|
recordIDs: make(map[string]int),
|
||||||
|
client: &http.Client{Timeout: 30 * time.Second},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
// Present creates a TXT record using the specified parameters
|
// Present creates a TXT record using the specified parameters
|
||||||
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
@ -70,15 +72,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", reqURL, bytes.NewReader(body))
|
req, err := http.NewRequest(http.MethodPost, reqURL, bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken))
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken))
|
||||||
|
|
||||||
client := http.Client{Timeout: 30 * time.Second}
|
resp, err := d.client.Do(req)
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -123,15 +124,15 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
authZone = acme.UnFqdn(authZone)
|
authZone = acme.UnFqdn(authZone)
|
||||||
|
|
||||||
reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", digitalOceanBaseURL, authZone, recordID)
|
reqURL := fmt.Sprintf("%s/v2/domains/%s/records/%d", digitalOceanBaseURL, authZone, recordID)
|
||||||
req, err := http.NewRequest("DELETE", reqURL, nil)
|
req, err := http.NewRequest(http.MethodDelete, reqURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken))
|
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.apiAuthToken))
|
||||||
|
|
||||||
client := http.Client{Timeout: 30 * time.Second}
|
resp, err := d.client.Do(req)
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,75 +39,72 @@ import (
|
||||||
|
|
||||||
// NewDNSChallengeProviderByName Factory for DNS providers
|
// NewDNSChallengeProviderByName Factory for DNS providers
|
||||||
func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) {
|
func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error) {
|
||||||
var err error
|
|
||||||
var provider acme.ChallengeProvider
|
|
||||||
switch name {
|
switch name {
|
||||||
case "azure":
|
case "azure":
|
||||||
provider, err = azure.NewDNSProvider()
|
return azure.NewDNSProvider()
|
||||||
case "auroradns":
|
case "auroradns":
|
||||||
provider, err = auroradns.NewDNSProvider()
|
return auroradns.NewDNSProvider()
|
||||||
case "bluecat":
|
case "bluecat":
|
||||||
provider, err = bluecat.NewDNSProvider()
|
return bluecat.NewDNSProvider()
|
||||||
case "cloudflare":
|
case "cloudflare":
|
||||||
provider, err = cloudflare.NewDNSProvider()
|
return cloudflare.NewDNSProvider()
|
||||||
case "cloudxns":
|
case "cloudxns":
|
||||||
provider, err = cloudxns.NewDNSProvider()
|
return cloudxns.NewDNSProvider()
|
||||||
case "digitalocean":
|
case "digitalocean":
|
||||||
provider, err = digitalocean.NewDNSProvider()
|
return digitalocean.NewDNSProvider()
|
||||||
case "dnsimple":
|
case "dnsimple":
|
||||||
provider, err = dnsimple.NewDNSProvider()
|
return dnsimple.NewDNSProvider()
|
||||||
case "dnsmadeeasy":
|
case "dnsmadeeasy":
|
||||||
provider, err = dnsmadeeasy.NewDNSProvider()
|
return dnsmadeeasy.NewDNSProvider()
|
||||||
case "dnspod":
|
case "dnspod":
|
||||||
provider, err = dnspod.NewDNSProvider()
|
return dnspod.NewDNSProvider()
|
||||||
case "duckdns":
|
case "duckdns":
|
||||||
provider, err = duckdns.NewDNSProvider()
|
return duckdns.NewDNSProvider()
|
||||||
case "dyn":
|
case "dyn":
|
||||||
provider, err = dyn.NewDNSProvider()
|
return dyn.NewDNSProvider()
|
||||||
case "fastdns":
|
case "fastdns":
|
||||||
provider, err = fastdns.NewDNSProvider()
|
return fastdns.NewDNSProvider()
|
||||||
case "exoscale":
|
case "exoscale":
|
||||||
provider, err = exoscale.NewDNSProvider()
|
return exoscale.NewDNSProvider()
|
||||||
case "gandi":
|
case "gandi":
|
||||||
provider, err = gandi.NewDNSProvider()
|
return gandi.NewDNSProvider()
|
||||||
case "gandiv5":
|
case "gandiv5":
|
||||||
provider, err = gandiv5.NewDNSProvider()
|
return gandiv5.NewDNSProvider()
|
||||||
case "glesys":
|
case "glesys":
|
||||||
provider, err = glesys.NewDNSProvider()
|
return glesys.NewDNSProvider()
|
||||||
case "gcloud":
|
case "gcloud":
|
||||||
provider, err = gcloud.NewDNSProvider()
|
return gcloud.NewDNSProvider()
|
||||||
case "godaddy":
|
case "godaddy":
|
||||||
provider, err = godaddy.NewDNSProvider()
|
return godaddy.NewDNSProvider()
|
||||||
case "lightsail":
|
case "lightsail":
|
||||||
provider, err = lightsail.NewDNSProvider()
|
return lightsail.NewDNSProvider()
|
||||||
case "linode":
|
case "linode":
|
||||||
provider, err = linode.NewDNSProvider()
|
return linode.NewDNSProvider()
|
||||||
case "manual":
|
case "manual":
|
||||||
provider, err = acme.NewDNSProviderManual()
|
return acme.NewDNSProviderManual()
|
||||||
case "namecheap":
|
case "namecheap":
|
||||||
provider, err = namecheap.NewDNSProvider()
|
return namecheap.NewDNSProvider()
|
||||||
case "namedotcom":
|
case "namedotcom":
|
||||||
provider, err = namedotcom.NewDNSProvider()
|
return namedotcom.NewDNSProvider()
|
||||||
case "rackspace":
|
case "rackspace":
|
||||||
provider, err = rackspace.NewDNSProvider()
|
return rackspace.NewDNSProvider()
|
||||||
case "route53":
|
case "route53":
|
||||||
provider, err = route53.NewDNSProvider()
|
return route53.NewDNSProvider()
|
||||||
case "rfc2136":
|
case "rfc2136":
|
||||||
provider, err = rfc2136.NewDNSProvider()
|
return rfc2136.NewDNSProvider()
|
||||||
case "vultr":
|
case "vultr":
|
||||||
provider, err = vultr.NewDNSProvider()
|
return vultr.NewDNSProvider()
|
||||||
case "ovh":
|
case "ovh":
|
||||||
provider, err = ovh.NewDNSProvider()
|
return ovh.NewDNSProvider()
|
||||||
case "pdns":
|
case "pdns":
|
||||||
provider, err = pdns.NewDNSProvider()
|
return pdns.NewDNSProvider()
|
||||||
case "ns1":
|
case "ns1":
|
||||||
provider, err = ns1.NewDNSProvider()
|
return ns1.NewDNSProvider()
|
||||||
case "otc":
|
case "otc":
|
||||||
provider, err = otc.NewDNSProvider()
|
return otc.NewDNSProvider()
|
||||||
case "exec":
|
case "exec":
|
||||||
provider, err = exec.NewDNSProvider()
|
return exec.NewDNSProvider()
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unrecognised DNS provider: %s", name)
|
return nil, fmt.Errorf("unrecognised DNS provider: %s", name)
|
||||||
}
|
}
|
||||||
return provider, err
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,22 +46,22 @@ func NewDNSProviderCredentials(accessToken, baseURL string) (*DNSProvider, error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
zoneName, err := c.getHostedZone(domain)
|
zoneName, err := d.getHostedZone(domain)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
accountID, err := c.getAccountID()
|
accountID, err := d.getAccountID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl)
|
recordAttributes := d.newTxtRecord(zoneName, fqdn, value, ttl)
|
||||||
_, err = c.client.Zones.CreateRecord(accountID, zoneName, *recordAttributes)
|
_, err = d.client.Zones.CreateRecord(accountID, zoneName, *recordAttributes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("DNSimple API call failed: %v", err)
|
return fmt.Errorf("DNSimple API call failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -70,21 +70,21 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters.
|
// CleanUp removes the TXT record matching the specified parameters.
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
records, err := c.findTxtRecords(domain, fqdn)
|
records, err := d.findTxtRecords(domain, fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
accountID, err := c.getAccountID()
|
accountID, err := d.getAccountID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rec := range records {
|
for _, rec := range records {
|
||||||
_, err := c.client.Zones.DeleteRecord(accountID, rec.ZoneID, rec.ID)
|
_, err := d.client.Zones.DeleteRecord(accountID, rec.ZoneID, rec.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -93,20 +93,20 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) getHostedZone(domain string) (string, error) {
|
func (d *DNSProvider) getHostedZone(domain string) (string, error) {
|
||||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
accountID, err := c.getAccountID()
|
accountID, err := d.getAccountID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
zoneName := acme.UnFqdn(authZone)
|
zoneName := acme.UnFqdn(authZone)
|
||||||
|
|
||||||
zones, err := c.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName})
|
zones, err := d.client.Zones.ListZones(accountID, &dnsimple.ZoneListOptions{NameLike: zoneName})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("DNSimple API call failed: %v", err)
|
return "", fmt.Errorf("DNSimple API call failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -125,20 +125,20 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) {
|
||||||
return hostedZone.Name, nil
|
return hostedZone.Name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord, error) {
|
func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord, error) {
|
||||||
zoneName, err := c.getHostedZone(domain)
|
zoneName, err := d.getHostedZone(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
accountID, err := c.getAccountID()
|
accountID, err := d.getAccountID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
recordName := c.extractRecordName(fqdn, zoneName)
|
recordName := d.extractRecordName(fqdn, zoneName)
|
||||||
|
|
||||||
result, err := c.client.Zones.ListRecords(accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: recordName, Type: "TXT", ListOptions: dnsimple.ListOptions{}})
|
result, err := d.client.Zones.ListRecords(accountID, zoneName, &dnsimple.ZoneRecordListOptions{Name: recordName, Type: "TXT", ListOptions: dnsimple.ListOptions{}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []dnsimple.ZoneRecord{}, fmt.Errorf("DNSimple API call has failed: %v", err)
|
return []dnsimple.ZoneRecord{}, fmt.Errorf("DNSimple API call has failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -146,8 +146,8 @@ func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnsimple.ZoneRecord
|
||||||
return result.Data, nil
|
return result.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsimple.ZoneRecord {
|
func (d *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsimple.ZoneRecord {
|
||||||
name := c.extractRecordName(fqdn, zoneName)
|
name := d.extractRecordName(fqdn, zoneName)
|
||||||
|
|
||||||
return &dnsimple.ZoneRecord{
|
return &dnsimple.ZoneRecord{
|
||||||
Type: "TXT",
|
Type: "TXT",
|
||||||
|
@ -157,7 +157,7 @@ func (c *DNSProvider) newTxtRecord(zoneName, fqdn, value string, ttl int) *dnsim
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
func (d *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||||
name := acme.UnFqdn(fqdn)
|
name := acme.UnFqdn(fqdn)
|
||||||
if idx := strings.Index(name, "."+domain); idx != -1 {
|
if idx := strings.Index(name, "."+domain); idx != -1 {
|
||||||
return name[:idx]
|
return name[:idx]
|
||||||
|
@ -165,8 +165,8 @@ func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) getAccountID() (string, error) {
|
func (d *DNSProvider) getAccountID() (string, error) {
|
||||||
whoamiResponse, err := c.client.Identity.Whoami()
|
whoamiResponse, err := d.client.Identity.Whoami()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ type DNSProvider struct {
|
||||||
baseURL string
|
baseURL string
|
||||||
apiKey string
|
apiKey string
|
||||||
apiSecret string
|
apiSecret string
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// Domain holds the DNSMadeEasy API representation of a Domain
|
// Domain holds the DNSMadeEasy API representation of a Domain
|
||||||
|
@ -68,10 +69,19 @@ func NewDNSProviderCredentials(baseURL, apiKey, apiSecret string) (*DNSProvider,
|
||||||
return nil, fmt.Errorf("DNS Made Easy credentials missing")
|
return nil, fmt.Errorf("DNS Made Easy credentials missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transport := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: transport,
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
return &DNSProvider{
|
return &DNSProvider{
|
||||||
baseURL: baseURL,
|
baseURL: baseURL,
|
||||||
apiKey: apiKey,
|
apiKey: apiKey,
|
||||||
apiSecret: apiSecret,
|
apiSecret: apiSecret,
|
||||||
|
client: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +145,7 @@ func (d *DNSProvider) getDomain(authZone string) (*Domain, error) {
|
||||||
domainName := authZone[0 : len(authZone)-1]
|
domainName := authZone[0 : len(authZone)-1]
|
||||||
resource := fmt.Sprintf("%s%s", "/dns/managed/name?domainname=", domainName)
|
resource := fmt.Sprintf("%s%s", "/dns/managed/name?domainname=", domainName)
|
||||||
|
|
||||||
resp, err := d.sendRequest("GET", resource, nil)
|
resp, err := d.sendRequest(http.MethodGet, resource, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -153,7 +163,7 @@ func (d *DNSProvider) getDomain(authZone string) (*Domain, error) {
|
||||||
func (d *DNSProvider) getRecords(domain *Domain, recordName, recordType string) (*[]Record, error) {
|
func (d *DNSProvider) getRecords(domain *Domain, recordName, recordType string) (*[]Record, error) {
|
||||||
resource := fmt.Sprintf("%s/%d/%s%s%s%s", "/dns/managed", domain.ID, "records?recordName=", recordName, "&type=", recordType)
|
resource := fmt.Sprintf("%s/%d/%s%s%s%s", "/dns/managed", domain.ID, "records?recordName=", recordName, "&type=", recordType)
|
||||||
|
|
||||||
resp, err := d.sendRequest("GET", resource, nil)
|
resp, err := d.sendRequest(http.MethodGet, resource, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -175,7 +185,7 @@ func (d *DNSProvider) getRecords(domain *Domain, recordName, recordType string)
|
||||||
func (d *DNSProvider) createRecord(domain *Domain, record *Record) error {
|
func (d *DNSProvider) createRecord(domain *Domain, record *Record) error {
|
||||||
url := fmt.Sprintf("%s/%d/%s", "/dns/managed", domain.ID, "records")
|
url := fmt.Sprintf("%s/%d/%s", "/dns/managed", domain.ID, "records")
|
||||||
|
|
||||||
resp, err := d.sendRequest("POST", url, record)
|
resp, err := d.sendRequest(http.MethodPost, url, record)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -187,7 +197,7 @@ func (d *DNSProvider) createRecord(domain *Domain, record *Record) error {
|
||||||
func (d *DNSProvider) deleteRecord(record Record) error {
|
func (d *DNSProvider) deleteRecord(record Record) error {
|
||||||
resource := fmt.Sprintf("%s/%d/%s/%d", "/dns/managed", record.SourceID, "records", record.ID)
|
resource := fmt.Sprintf("%s/%d/%s/%d", "/dns/managed", record.SourceID, "records", record.ID)
|
||||||
|
|
||||||
resp, err := d.sendRequest("DELETE", resource, nil)
|
resp, err := d.sendRequest(http.MethodDelete, resource, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -217,14 +227,7 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{})
|
||||||
req.Header.Set("accept", "application/json")
|
req.Header.Set("accept", "application/json")
|
||||||
req.Header.Set("content-type", "application/json")
|
req.Header.Set("content-type", "application/json")
|
||||||
|
|
||||||
transport := &http.Transport{
|
resp, err := d.client.Do(req)
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
||||||
}
|
|
||||||
client := &http.Client{
|
|
||||||
Transport: transport,
|
|
||||||
Timeout: 10 * time.Second,
|
|
||||||
}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,15 +41,15 @@ func NewDNSProviderCredentials(key string) (*DNSProvider, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
zoneID, zoneName, err := c.getHostedZone(domain)
|
zoneID, zoneName, err := d.getHostedZone(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
recordAttributes := c.newTxtRecord(zoneName, fqdn, value, ttl)
|
recordAttributes := d.newTxtRecord(zoneName, fqdn, value, ttl)
|
||||||
_, _, err = c.client.Domains.CreateRecord(zoneID, *recordAttributes)
|
_, _, err = d.client.Domains.CreateRecord(zoneID, *recordAttributes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("dnspod API call failed: %v", err)
|
return fmt.Errorf("dnspod API call failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -58,21 +58,21 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters.
|
// CleanUp removes the TXT record matching the specified parameters.
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
records, err := c.findTxtRecords(domain, fqdn)
|
records, err := d.findTxtRecords(domain, fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
zoneID, _, err := c.getHostedZone(domain)
|
zoneID, _, err := d.getHostedZone(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rec := range records {
|
for _, rec := range records {
|
||||||
_, err := c.client.Domains.DeleteRecord(zoneID, rec.ID)
|
_, err := d.client.Domains.DeleteRecord(zoneID, rec.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -80,8 +80,8 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) getHostedZone(domain string) (string, string, error) {
|
func (d *DNSProvider) getHostedZone(domain string) (string, string, error) {
|
||||||
zones, _, err := c.client.Domains.List()
|
zones, _, err := d.client.Domains.List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("dnspod API call failed: %v", err)
|
return "", "", fmt.Errorf("dnspod API call failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,8 @@ func (c *DNSProvider) getHostedZone(domain string) (string, string, error) {
|
||||||
return fmt.Sprintf("%v", hostedZone.ID), hostedZone.Name, nil
|
return fmt.Sprintf("%v", hostedZone.ID), hostedZone.Name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Record {
|
func (d *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Record {
|
||||||
name := c.extractRecordName(fqdn, zone)
|
name := d.extractRecordName(fqdn, zone)
|
||||||
|
|
||||||
return &dnspod.Record{
|
return &dnspod.Record{
|
||||||
Type: "TXT",
|
Type: "TXT",
|
||||||
|
@ -118,19 +118,19 @@ func (c *DNSProvider) newTxtRecord(zone, fqdn, value string, ttl int) *dnspod.Re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, error) {
|
func (d *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, error) {
|
||||||
zoneID, zoneName, err := c.getHostedZone(domain)
|
zoneID, zoneName, err := d.getHostedZone(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var records []dnspod.Record
|
var records []dnspod.Record
|
||||||
result, _, err := c.client.Domains.ListRecords(zoneID, "")
|
result, _, err := d.client.Domains.ListRecords(zoneID, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return records, fmt.Errorf("dnspod API call has failed: %v", err)
|
return records, fmt.Errorf("dnspod API call has failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
recordName := c.extractRecordName(fqdn, zoneName)
|
recordName := d.extractRecordName(fqdn, zoneName)
|
||||||
|
|
||||||
for _, record := range result {
|
for _, record := range result {
|
||||||
if record.Name == recordName {
|
if record.Name == recordName {
|
||||||
|
@ -141,7 +141,7 @@ func (c *DNSProvider) findTxtRecords(domain, fqdn string) ([]dnspod.Record, erro
|
||||||
return records, nil
|
return records, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
func (d *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||||
name := acme.UnFqdn(fqdn)
|
name := acme.UnFqdn(fqdn)
|
||||||
if idx := strings.Index(name, "."+domain); idx != -1 {
|
if idx := strings.Index(name, "."+domain); idx != -1 {
|
||||||
return name[:idx]
|
return name[:idx]
|
||||||
|
|
|
@ -37,6 +37,7 @@ type DNSProvider struct {
|
||||||
userName string
|
userName string
|
||||||
password string
|
password string
|
||||||
token string
|
token string
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a DNSProvider instance configured for Dyn DNS.
|
// NewDNSProvider returns a DNSProvider instance configured for Dyn DNS.
|
||||||
|
@ -62,6 +63,7 @@ func NewDNSProviderCredentials(customerName, userName, password string) (*DNSPro
|
||||||
customerName: customerName,
|
customerName: customerName,
|
||||||
userName: userName,
|
userName: userName,
|
||||||
password: password,
|
password: password,
|
||||||
|
client: &http.Client{Timeout: 10 * time.Second},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,8 +84,7 @@ func (d *DNSProvider) sendRequest(method, resource string, payload interface{})
|
||||||
req.Header.Set("Auth-Token", d.token)
|
req.Header.Set("Auth-Token", d.token)
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &http.Client{Timeout: 10 * time.Second}
|
resp, err := d.client.Do(req)
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -129,7 +130,7 @@ func (d *DNSProvider) login() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := &creds{Customer: d.customerName, User: d.userName, Pass: d.password}
|
payload := &creds{Customer: d.customerName, User: d.userName, Pass: d.password}
|
||||||
dynRes, err := d.sendRequest("POST", "Session", payload)
|
dynRes, err := d.sendRequest(http.MethodPost, "Session", payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -153,15 +154,14 @@ func (d *DNSProvider) logout() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
url := fmt.Sprintf("%s/Session", dynBaseURL)
|
url := fmt.Sprintf("%s/Session", dynBaseURL)
|
||||||
req, err := http.NewRequest("DELETE", url, nil)
|
req, err := http.NewRequest(http.MethodDelete, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("Auth-Token", d.token)
|
req.Header.Set("Auth-Token", d.token)
|
||||||
|
|
||||||
client := &http.Client{Timeout: 10 * time.Second}
|
resp, err := d.client.Do(req)
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -198,7 +198,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn)
|
resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn)
|
||||||
_, err = d.sendRequest("POST", resource, data)
|
_, err = d.sendRequest(http.MethodPost, resource, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -220,7 +220,7 @@ func (d *DNSProvider) publish(zone, notes string) error {
|
||||||
pub := &publish{Publish: true, Notes: notes}
|
pub := &publish{Publish: true, Notes: notes}
|
||||||
resource := fmt.Sprintf("Zone/%s/", zone)
|
resource := fmt.Sprintf("Zone/%s/", zone)
|
||||||
|
|
||||||
_, err := d.sendRequest("PUT", resource, pub)
|
_, err := d.sendRequest(http.MethodPut, resource, pub)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,15 +240,16 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
|
||||||
resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn)
|
resource := fmt.Sprintf("TXTRecord/%s/%s/", authZone, fqdn)
|
||||||
url := fmt.Sprintf("%s/%s", dynBaseURL, resource)
|
url := fmt.Sprintf("%s/%s", dynBaseURL, resource)
|
||||||
req, err := http.NewRequest("DELETE", url, nil)
|
|
||||||
|
req, err := http.NewRequest(http.MethodDelete, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("Auth-Token", d.token)
|
req.Header.Set("Auth-Token", d.token)
|
||||||
|
|
||||||
client := &http.Client{Timeout: 10 * time.Second}
|
resp, err := d.client.Do(req)
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,14 +46,14 @@ func NewDNSProviderClient(key, secret, endpoint string) (*DNSProvider, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain)
|
zone, recordName, err := d.FindZoneAndRecordName(fqdn, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
recordID, err := c.FindExistingRecordID(zone, recordName)
|
recordID, err := d.FindExistingRecordID(zone, recordName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -66,13 +66,13 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if recordID == 0 {
|
if recordID == 0 {
|
||||||
_, err := c.client.CreateRecord(zone, record)
|
_, err := d.client.CreateRecord(zone, record)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Error while creating DNS record: " + err.Error())
|
return errors.New("Error while creating DNS record: " + err.Error())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
record.ID = recordID
|
record.ID = recordID
|
||||||
_, err := c.client.UpdateRecord(zone, record)
|
_, err := d.client.UpdateRecord(zone, record)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Error while updating DNS record: " + err.Error())
|
return errors.New("Error while updating DNS record: " + err.Error())
|
||||||
}
|
}
|
||||||
|
@ -82,20 +82,20 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the record matching the specified parameters.
|
// CleanUp removes the record matching the specified parameters.
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
zone, recordName, err := c.FindZoneAndRecordName(fqdn, domain)
|
zone, recordName, err := d.FindZoneAndRecordName(fqdn, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
recordID, err := c.FindExistingRecordID(zone, recordName)
|
recordID, err := d.FindExistingRecordID(zone, recordName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if recordID != 0 {
|
if recordID != 0 {
|
||||||
err = c.client.DeleteRecord(zone, recordID)
|
err = d.client.DeleteRecord(zone, recordID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Error while deleting DNS record: " + err.Error())
|
return errors.New("Error while deleting DNS record: " + err.Error())
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,8 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
|
||||||
// FindExistingRecordID 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
|
// Returns nil if no record could be found
|
||||||
func (c *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, error) {
|
func (d *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, error) {
|
||||||
records, err := c.client.GetRecords(zone)
|
records, err := d.client.GetRecords(zone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, errors.New("Error while retrievening DNS records: " + err.Error())
|
return -1, errors.New("Error while retrievening DNS records: " + err.Error())
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@ func (c *DNSProvider) FindExistingRecordID(zone, recordName string) (int64, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindZoneAndRecordName 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) {
|
func (d *DNSProvider) FindZoneAndRecordName(fqdn, domain string) (string, string, error) {
|
||||||
zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
|
|
|
@ -51,14 +51,14 @@ func NewDNSProviderClient(host, clientToken, clientSecret, accessToken string) (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fullfil the dns-01 challenge.
|
// Present creates a TXT record to fullfil the dns-01 challenge.
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
zoneName, recordName, err := c.findZoneAndRecordName(fqdn, domain)
|
zoneName, recordName, err := d.findZoneAndRecordName(fqdn, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
configdns.Init(c.config)
|
configdns.Init(d.config)
|
||||||
|
|
||||||
zone, err := configdns.GetZone(zoneName)
|
zone, err := configdns.GetZone(zoneName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -71,35 +71,35 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
record.SetField("target", value)
|
record.SetField("target", value)
|
||||||
record.SetField("active", true)
|
record.SetField("active", true)
|
||||||
|
|
||||||
existingRecord := c.findExistingRecord(zone, recordName)
|
existingRecord := d.findExistingRecord(zone, recordName)
|
||||||
|
|
||||||
if existingRecord != nil {
|
if existingRecord != nil {
|
||||||
if reflect.DeepEqual(existingRecord.ToMap(), record.ToMap()) {
|
if reflect.DeepEqual(existingRecord.ToMap(), record.ToMap()) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
zone.RemoveRecord(existingRecord)
|
zone.RemoveRecord(existingRecord)
|
||||||
return c.createRecord(zone, record)
|
return d.createRecord(zone, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.createRecord(zone, record)
|
return d.createRecord(zone, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the record matching the specified parameters.
|
// CleanUp removes the record matching the specified parameters.
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
zoneName, recordName, err := c.findZoneAndRecordName(fqdn, domain)
|
zoneName, recordName, err := d.findZoneAndRecordName(fqdn, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
configdns.Init(c.config)
|
configdns.Init(d.config)
|
||||||
|
|
||||||
zone, err := configdns.GetZone(zoneName)
|
zone, err := configdns.GetZone(zoneName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
existingRecord := c.findExistingRecord(zone, recordName)
|
existingRecord := d.findExistingRecord(zone, recordName)
|
||||||
|
|
||||||
if existingRecord != nil {
|
if existingRecord != nil {
|
||||||
err := zone.RemoveRecord(existingRecord)
|
err := zone.RemoveRecord(existingRecord)
|
||||||
|
@ -112,7 +112,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string, error) {
|
func (d *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string, error) {
|
||||||
zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
zone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
|
@ -124,7 +124,7 @@ func (c *DNSProvider) findZoneAndRecordName(fqdn, domain string) (string, string
|
||||||
return zone, name, nil
|
return zone, name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) findExistingRecord(zone *configdns.Zone, recordName string) *configdns.TxtRecord {
|
func (d *DNSProvider) findExistingRecord(zone *configdns.Zone, recordName string) *configdns.TxtRecord {
|
||||||
for _, r := range zone.Zone.Txt {
|
for _, r := range zone.Zone.Txt {
|
||||||
if r.Name == recordName {
|
if r.Name == recordName {
|
||||||
return r
|
return r
|
||||||
|
@ -134,7 +134,7 @@ func (c *DNSProvider) findExistingRecord(zone *configdns.Zone, recordName string
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) createRecord(zone *configdns.Zone, record *configdns.TxtRecord) error {
|
func (d *DNSProvider) createRecord(zone *configdns.Zone, record *configdns.TxtRecord) error {
|
||||||
err := zone.AddRecord(record)
|
err := zone.AddRecord(record)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -44,6 +44,7 @@ type DNSProvider struct {
|
||||||
inProgressFQDNs map[string]inProgressInfo
|
inProgressFQDNs map[string]inProgressInfo
|
||||||
inProgressAuthZones map[string]struct{}
|
inProgressAuthZones map[string]struct{}
|
||||||
inProgressMu sync.Mutex
|
inProgressMu sync.Mutex
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a DNSProvider instance configured for Gandi.
|
// NewDNSProvider returns a DNSProvider instance configured for Gandi.
|
||||||
|
@ -61,12 +62,13 @@ func NewDNSProvider() (*DNSProvider, error) {
|
||||||
// DNSProvider instance configured for Gandi.
|
// DNSProvider instance configured for Gandi.
|
||||||
func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) {
|
func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) {
|
||||||
if apiKey == "" {
|
if apiKey == "" {
|
||||||
return nil, fmt.Errorf("No Gandi API Key given")
|
return nil, fmt.Errorf("no Gandi API Key given")
|
||||||
}
|
}
|
||||||
return &DNSProvider{
|
return &DNSProvider{
|
||||||
apiKey: apiKey,
|
apiKey: apiKey,
|
||||||
inProgressFQDNs: make(map[string]inProgressInfo),
|
inProgressFQDNs: make(map[string]inProgressInfo),
|
||||||
inProgressAuthZones: make(map[string]struct{}),
|
inProgressAuthZones: make(map[string]struct{}),
|
||||||
|
client: &http.Client{Timeout: 60 * time.Second},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,10 +275,8 @@ func (e rpcError) Error() string {
|
||||||
"Gandi DNS: RPC Error: (%d) %s", e.faultCode, e.faultString)
|
"Gandi DNS: RPC Error: (%d) %s", e.faultCode, e.faultString)
|
||||||
}
|
}
|
||||||
|
|
||||||
func httpPost(url string, bodyType string, body io.Reader) ([]byte, error) {
|
func (d *DNSProvider) httpPost(url string, bodyType string, body io.Reader) ([]byte, error) {
|
||||||
client := http.Client{Timeout: 60 * time.Second}
|
resp, err := d.client.Post(url, bodyType, body)
|
||||||
|
|
||||||
resp, err := client.Post(url, bodyType, body)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Gandi DNS: HTTP Post Error: %v", err)
|
return nil, fmt.Errorf("Gandi DNS: HTTP Post Error: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -294,7 +294,7 @@ func httpPost(url string, bodyType string, body io.Reader) ([]byte, error) {
|
||||||
// marshalling the data given in the call argument to XML and sending
|
// marshalling the data given in the call argument to XML and sending
|
||||||
// that via HTTP Post to Gandi. The response is then unmarshalled into
|
// that via HTTP Post to Gandi. The response is then unmarshalled into
|
||||||
// the resp argument.
|
// the resp argument.
|
||||||
func rpcCall(call *methodCall, resp response) error {
|
func (d *DNSProvider) rpcCall(call *methodCall, resp response) error {
|
||||||
// marshal
|
// marshal
|
||||||
b, err := xml.MarshalIndent(call, "", " ")
|
b, err := xml.MarshalIndent(call, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -303,7 +303,7 @@ func rpcCall(call *methodCall, resp response) error {
|
||||||
|
|
||||||
// post
|
// post
|
||||||
b = append([]byte(`<?xml version="1.0"?>`+"\n"), b...)
|
b = append([]byte(`<?xml version="1.0"?>`+"\n"), b...)
|
||||||
respBody, err := httpPost(endpoint, "text/xml", bytes.NewReader(b))
|
respBody, err := d.httpPost(endpoint, "text/xml", bytes.NewReader(b))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -324,7 +324,7 @@ func rpcCall(call *methodCall, resp response) error {
|
||||||
|
|
||||||
func (d *DNSProvider) getZoneID(domain string) (int, error) {
|
func (d *DNSProvider) getZoneID(domain string) (int, error) {
|
||||||
resp := &responseStruct{}
|
resp := &responseStruct{}
|
||||||
err := rpcCall(&methodCall{
|
err := d.rpcCall(&methodCall{
|
||||||
MethodName: "domain.info",
|
MethodName: "domain.info",
|
||||||
Params: []param{
|
Params: []param{
|
||||||
paramString{Value: d.apiKey},
|
paramString{Value: d.apiKey},
|
||||||
|
@ -351,7 +351,7 @@ func (d *DNSProvider) getZoneID(domain string) (int, error) {
|
||||||
|
|
||||||
func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) {
|
func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) {
|
||||||
resp := &responseStruct{}
|
resp := &responseStruct{}
|
||||||
err := rpcCall(&methodCall{
|
err := d.rpcCall(&methodCall{
|
||||||
MethodName: "domain.zone.clone",
|
MethodName: "domain.zone.clone",
|
||||||
Params: []param{
|
Params: []param{
|
||||||
paramString{Value: d.apiKey},
|
paramString{Value: d.apiKey},
|
||||||
|
@ -385,7 +385,7 @@ func (d *DNSProvider) cloneZone(zoneID int, name string) (int, error) {
|
||||||
|
|
||||||
func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) {
|
func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) {
|
||||||
resp := &responseInt{}
|
resp := &responseInt{}
|
||||||
err := rpcCall(&methodCall{
|
err := d.rpcCall(&methodCall{
|
||||||
MethodName: "domain.zone.version.new",
|
MethodName: "domain.zone.version.new",
|
||||||
Params: []param{
|
Params: []param{
|
||||||
paramString{Value: d.apiKey},
|
paramString{Value: d.apiKey},
|
||||||
|
@ -404,7 +404,7 @@ func (d *DNSProvider) newZoneVersion(zoneID int) (int, error) {
|
||||||
|
|
||||||
func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value string, ttl int) error {
|
func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value string, ttl int) error {
|
||||||
resp := &responseStruct{}
|
resp := &responseStruct{}
|
||||||
err := rpcCall(&methodCall{
|
err := d.rpcCall(&methodCall{
|
||||||
MethodName: "domain.zone.record.add",
|
MethodName: "domain.zone.record.add",
|
||||||
Params: []param{
|
Params: []param{
|
||||||
paramString{Value: d.apiKey},
|
paramString{Value: d.apiKey},
|
||||||
|
@ -433,7 +433,7 @@ func (d *DNSProvider) addTXTRecord(zoneID int, version int, name string, value s
|
||||||
|
|
||||||
func (d *DNSProvider) setZoneVersion(zoneID int, version int) error {
|
func (d *DNSProvider) setZoneVersion(zoneID int, version int) error {
|
||||||
resp := &responseBool{}
|
resp := &responseBool{}
|
||||||
err := rpcCall(&methodCall{
|
err := d.rpcCall(&methodCall{
|
||||||
MethodName: "domain.zone.version.set",
|
MethodName: "domain.zone.version.set",
|
||||||
Params: []param{
|
Params: []param{
|
||||||
paramString{Value: d.apiKey},
|
paramString{Value: d.apiKey},
|
||||||
|
@ -453,7 +453,7 @@ func (d *DNSProvider) setZoneVersion(zoneID int, version int) error {
|
||||||
|
|
||||||
func (d *DNSProvider) setZone(domain string, zoneID int) error {
|
func (d *DNSProvider) setZone(domain string, zoneID int) error {
|
||||||
resp := &responseStruct{}
|
resp := &responseStruct{}
|
||||||
err := rpcCall(&methodCall{
|
err := d.rpcCall(&methodCall{
|
||||||
MethodName: "domain.zone.set",
|
MethodName: "domain.zone.set",
|
||||||
Params: []param{
|
Params: []param{
|
||||||
paramString{Value: d.apiKey},
|
paramString{Value: d.apiKey},
|
||||||
|
@ -481,7 +481,7 @@ func (d *DNSProvider) setZone(domain string, zoneID int) error {
|
||||||
|
|
||||||
func (d *DNSProvider) deleteZone(zoneID int) error {
|
func (d *DNSProvider) deleteZone(zoneID int) error {
|
||||||
resp := &responseBool{}
|
resp := &responseBool{}
|
||||||
err := rpcCall(&methodCall{
|
err := d.rpcCall(&methodCall{
|
||||||
MethodName: "domain.zone.delete",
|
MethodName: "domain.zone.delete",
|
||||||
Params: []param{
|
Params: []param{
|
||||||
paramString{Value: d.apiKey},
|
paramString{Value: d.apiKey},
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/log"
|
||||||
"github.com/xenolf/lego/platform/config/env"
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,6 +41,7 @@ type DNSProvider struct {
|
||||||
apiKey string
|
apiKey string
|
||||||
inProgressFQDNs map[string]inProgressInfo
|
inProgressFQDNs map[string]inProgressInfo
|
||||||
inProgressMu sync.Mutex
|
inProgressMu sync.Mutex
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a DNSProvider instance configured for Gandi.
|
// NewDNSProvider returns a DNSProvider instance configured for Gandi.
|
||||||
|
@ -62,6 +64,7 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) {
|
||||||
return &DNSProvider{
|
return &DNSProvider{
|
||||||
apiKey: apiKey,
|
apiKey: apiKey,
|
||||||
inProgressFQDNs: make(map[string]inProgressInfo),
|
inProgressFQDNs: make(map[string]inProgressInfo),
|
||||||
|
client: &http.Client{Timeout: 10 * time.Second},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,8 +172,7 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf
|
||||||
req.Header.Set("X-Api-Key", d.apiKey)
|
req.Header.Set("X-Api-Key", d.apiKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &http.Client{Timeout: 10 * time.Second}
|
resp, err := d.client.Do(req)
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -181,7 +183,7 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf
|
||||||
}
|
}
|
||||||
var response responseStruct
|
var response responseStruct
|
||||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||||
if err != nil && method != "DELETE" {
|
if err != nil && method != http.MethodDelete {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,23 +194,23 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf
|
||||||
|
|
||||||
func (d *DNSProvider) addTXTRecord(domain string, name string, value string, ttl int) error {
|
func (d *DNSProvider) addTXTRecord(domain string, name string, value string, ttl int) error {
|
||||||
target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name)
|
target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name)
|
||||||
response, err := d.sendRequest("PUT", target, addFieldRequest{
|
response, err := d.sendRequest(http.MethodPut, target, addFieldRequest{
|
||||||
RRSetTTL: ttl,
|
RRSetTTL: ttl,
|
||||||
RRSetValues: []string{value},
|
RRSetValues: []string{value},
|
||||||
})
|
})
|
||||||
if response != nil {
|
if response != nil {
|
||||||
fmt.Printf("Gandi DNS: %s\n", response.Message)
|
log.Infof("Gandi DNS: %s", response.Message)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) deleteTXTRecord(domain string, name string) error {
|
func (d *DNSProvider) deleteTXTRecord(domain string, name string) error {
|
||||||
target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name)
|
target := fmt.Sprintf("domains/%s/records/%s/TXT", domain, name)
|
||||||
response, err := d.sendRequest("DELETE", target, deleteFieldRequest{
|
response, err := d.sendRequest(http.MethodDelete, target, deleteFieldRequest{
|
||||||
Delete: true,
|
Delete: true,
|
||||||
})
|
})
|
||||||
if response != nil && response.Message == "" {
|
if response != nil && response.Message == "" {
|
||||||
fmt.Printf("Gandi DNS: Zone record deleted\n")
|
log.Infof("Gandi DNS: Zone record deleted")
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,10 +96,10 @@ func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
zone, err := c.getHostedZone(domain)
|
zone, err := d.getHostedZone(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -115,7 +115,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look for existing records.
|
// Look for existing records.
|
||||||
list, err := c.client.ResourceRecordSets.List(c.project, zone).Name(fqdn).Type("TXT").Do()
|
list, err := d.client.ResourceRecordSets.List(d.project, zone).Name(fqdn).Type("TXT").Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
change.Deletions = list.Rrsets
|
change.Deletions = list.Rrsets
|
||||||
}
|
}
|
||||||
|
|
||||||
chg, err := c.client.Changes.Create(c.project, zone, change).Do()
|
chg, err := d.client.Changes.Create(d.project, zone, change).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
for chg.Status == "pending" {
|
for chg.Status == "pending" {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
chg, err = c.client.Changes.Get(c.project, zone, chg.Id).Do()
|
chg, err = d.client.Changes.Get(d.project, zone, chg.Id).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -143,15 +143,15 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters.
|
// CleanUp removes the TXT record matching the specified parameters.
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
zone, err := c.getHostedZone(domain)
|
zone, err := d.getHostedZone(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
records, err := c.findTxtRecords(zone, fqdn)
|
records, err := d.findTxtRecords(zone, fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
change := &dns.Change{
|
change := &dns.Change{
|
||||||
Deletions: []*dns.ResourceRecordSet{rec},
|
Deletions: []*dns.ResourceRecordSet{rec},
|
||||||
}
|
}
|
||||||
_, err = c.client.Changes.Create(c.project, zone, change).Do()
|
_, err = d.client.Changes.Create(d.project, zone, change).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -170,19 +170,19 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
|
||||||
// Timeout customizes the timeout values used by the ACME package for checking
|
// Timeout customizes the timeout values used by the ACME package for checking
|
||||||
// DNS record validity.
|
// DNS record validity.
|
||||||
func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
return 180 * time.Second, 5 * time.Second
|
return 180 * time.Second, 5 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
// getHostedZone returns the managed-zone
|
// getHostedZone returns the managed-zone
|
||||||
func (c *DNSProvider) getHostedZone(domain string) (string, error) {
|
func (d *DNSProvider) getHostedZone(domain string) (string, error) {
|
||||||
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
authZone, err := acme.FindZoneByFqdn(acme.ToFqdn(domain), acme.RecursiveNameservers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
zones, err := c.client.ManagedZones.
|
zones, err := d.client.ManagedZones.
|
||||||
List(c.project).
|
List(d.project).
|
||||||
DnsName(authZone).
|
DnsName(authZone).
|
||||||
Do()
|
Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -196,9 +196,9 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) {
|
||||||
return zones.ManagedZones[0].Name, nil
|
return zones.ManagedZones[0].Name, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) {
|
func (d *DNSProvider) findTxtRecords(zone, fqdn string) ([]*dns.ResourceRecordSet, error) {
|
||||||
|
|
||||||
recs, err := c.client.ResourceRecordSets.List(c.project, zone).Do()
|
recs, err := d.client.ResourceRecordSets.List(d.project, zone).Do()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ type DNSProvider struct {
|
||||||
apiKey string
|
apiKey string
|
||||||
activeRecords map[string]int
|
activeRecords map[string]int
|
||||||
inProgressMu sync.Mutex
|
inProgressMu sync.Mutex
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a DNSProvider instance configured for GleSYS.
|
// NewDNSProvider returns a DNSProvider instance configured for GleSYS.
|
||||||
|
@ -49,10 +50,12 @@ func NewDNSProviderCredentials(apiUser string, apiKey string) (*DNSProvider, err
|
||||||
if apiUser == "" || apiKey == "" {
|
if apiUser == "" || apiKey == "" {
|
||||||
return nil, fmt.Errorf("GleSYS DNS: Incomplete credentials provided")
|
return nil, fmt.Errorf("GleSYS DNS: Incomplete credentials provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DNSProvider{
|
return &DNSProvider{
|
||||||
apiUser: apiUser,
|
apiUser: apiUser,
|
||||||
apiKey: apiKey,
|
apiKey: apiKey,
|
||||||
activeRecords: make(map[string]int),
|
activeRecords: make(map[string]int),
|
||||||
|
client: &http.Client{Timeout: 10 * time.Second},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +124,7 @@ func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
// types for JSON method calls, parameters, and responses
|
// types for JSON method calls, parameters, and responses
|
||||||
|
|
||||||
type addRecordRequest struct {
|
type addRecordRequest struct {
|
||||||
Domainname string `json:"domainname"`
|
DomainName string `json:"domainname"`
|
||||||
Host string `json:"host"`
|
Host string `json:"host"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Data string `json:"data"`
|
Data string `json:"data"`
|
||||||
|
@ -129,7 +132,7 @@ type addRecordRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type deleteRecordRequest struct {
|
type deleteRecordRequest struct {
|
||||||
Recordid int `json:"recordid"`
|
RecordID int `json:"recordid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type responseStruct struct {
|
type responseStruct struct {
|
||||||
|
@ -159,8 +162,7 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.SetBasicAuth(d.apiUser, d.apiKey)
|
req.SetBasicAuth(d.apiUser, d.apiKey)
|
||||||
|
|
||||||
client := &http.Client{Timeout: 10 * time.Second}
|
resp, err := d.client.Do(req)
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -179,26 +181,27 @@ func (d *DNSProvider) sendRequest(method string, resource string, payload interf
|
||||||
// functions to perform API actions
|
// functions to perform API actions
|
||||||
|
|
||||||
func (d *DNSProvider) addTXTRecord(fqdn string, domain string, name string, value string, ttl int) (int, error) {
|
func (d *DNSProvider) addTXTRecord(fqdn string, domain string, name string, value string, ttl int) (int, error) {
|
||||||
response, err := d.sendRequest("POST", "addrecord", addRecordRequest{
|
response, err := d.sendRequest(http.MethodPost, "addrecord", addRecordRequest{
|
||||||
Domainname: domain,
|
DomainName: domain,
|
||||||
Host: name,
|
Host: name,
|
||||||
Type: "TXT",
|
Type: "TXT",
|
||||||
Data: value,
|
Data: value,
|
||||||
TTL: ttl,
|
TTL: ttl,
|
||||||
})
|
})
|
||||||
if response != nil && response.Response.Status.Code == 200 {
|
|
||||||
log.Printf("[INFO][%s] GleSYS DNS: Successfully created recordid %d", fqdn, response.Response.Record.Recordid)
|
if response != nil && response.Response.Status.Code == http.StatusOK {
|
||||||
return response.Response.Record.Recordid, nil
|
log.Infof("[%s] GleSYS DNS: Successfully created record id %d", fqdn, response.Response.Record.RecordID)
|
||||||
|
return response.Response.Record.RecordID, nil
|
||||||
}
|
}
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DNSProvider) deleteTXTRecord(fqdn string, recordid int) error {
|
func (d *DNSProvider) deleteTXTRecord(fqdn string, recordid int) error {
|
||||||
response, err := d.sendRequest("POST", "deleterecord", deleteRecordRequest{
|
response, err := d.sendRequest(http.MethodPost, "deleterecord", deleteRecordRequest{
|
||||||
Recordid: recordid,
|
RecordID: recordid,
|
||||||
})
|
})
|
||||||
if response != nil && response.Response.Status.Code == 200 {
|
if response != nil && response.Response.Status.Code == 200 {
|
||||||
log.Printf("[INFO][%s] GleSYS DNS: Successfully deleted recordid %d", fqdn, recordid)
|
log.Infof("[%s] GleSYS DNS: Successfully deleted record id %d", fqdn, recordid)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ const apiURL = "https://api.godaddy.com"
|
||||||
type DNSProvider struct {
|
type DNSProvider struct {
|
||||||
apiKey string
|
apiKey string
|
||||||
apiSecret string
|
apiSecret string
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a DNSProvider instance configured for godaddy.
|
// NewDNSProvider returns a DNSProvider instance configured for godaddy.
|
||||||
|
@ -43,16 +44,20 @@ func NewDNSProviderCredentials(apiKey, apiSecret string) (*DNSProvider, error) {
|
||||||
return nil, fmt.Errorf("GoDaddy credentials missing")
|
return nil, fmt.Errorf("GoDaddy credentials missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DNSProvider{apiKey, apiSecret}, nil
|
return &DNSProvider{
|
||||||
|
apiKey: apiKey,
|
||||||
|
apiSecret: apiSecret,
|
||||||
|
client: &http.Client{Timeout: 30 * time.Second},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timeout returns the timeout and interval to use when checking for DNS
|
// Timeout returns the timeout and interval to use when checking for DNS
|
||||||
// propagation. Adjusting here to cope with spikes in propagation times.
|
// propagation. Adjusting here to cope with spikes in propagation times.
|
||||||
func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
return 120 * time.Second, 2 * time.Second
|
return 120 * time.Second, 2 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
func (d *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||||
name := acme.UnFqdn(fqdn)
|
name := acme.UnFqdn(fqdn)
|
||||||
if idx := strings.Index(name, "."+domain); idx != -1 {
|
if idx := strings.Index(name, "."+domain); idx != -1 {
|
||||||
return name[:idx]
|
return name[:idx]
|
||||||
|
@ -61,9 +66,9 @@ func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge
|
// Present creates a TXT record to fulfil the dns-01 challenge
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
domainZone, err := c.getZone(fqdn)
|
domainZone, err := d.getZone(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -72,7 +77,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
ttl = 600
|
ttl = 600
|
||||||
}
|
}
|
||||||
|
|
||||||
recordName := c.extractRecordName(fqdn, domainZone)
|
recordName := d.extractRecordName(fqdn, domainZone)
|
||||||
rec := []DNSRecord{
|
rec := []DNSRecord{
|
||||||
{
|
{
|
||||||
Type: "TXT",
|
Type: "TXT",
|
||||||
|
@ -82,17 +87,17 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.updateRecords(rec, domainZone, recordName)
|
return d.updateRecords(rec, domainZone, recordName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) updateRecords(records []DNSRecord, domainZone string, recordName string) error {
|
func (d *DNSProvider) updateRecords(records []DNSRecord, domainZone string, recordName string) error {
|
||||||
body, err := json.Marshal(records)
|
body, err := json.Marshal(records)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp *http.Response
|
var resp *http.Response
|
||||||
resp, err = c.makeRequest("PUT", fmt.Sprintf("/v1/domains/%s/records/TXT/%s", domainZone, recordName), bytes.NewReader(body))
|
resp, err = d.makeRequest(http.MethodPut, fmt.Sprintf("/v1/domains/%s/records/TXT/%s", domainZone, recordName), bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -107,14 +112,14 @@ func (c *DNSProvider) updateRecords(records []DNSRecord, domainZone string, reco
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp sets null value in the TXT DNS record as GoDaddy has no proper DELETE record method
|
// 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 {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
domainZone, err := c.getZone(fqdn)
|
domainZone, err := d.getZone(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
recordName := c.extractRecordName(fqdn, domainZone)
|
recordName := d.extractRecordName(fqdn, domainZone)
|
||||||
rec := []DNSRecord{
|
rec := []DNSRecord{
|
||||||
{
|
{
|
||||||
Type: "TXT",
|
Type: "TXT",
|
||||||
|
@ -123,10 +128,10 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.updateRecords(rec, domainZone, recordName)
|
return d.updateRecords(rec, domainZone, recordName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) getZone(fqdn string) (string, error) {
|
func (d *DNSProvider) getZone(fqdn string) (string, error) {
|
||||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -135,7 +140,7 @@ func (c *DNSProvider) getZone(fqdn string) (string, error) {
|
||||||
return acme.UnFqdn(authZone), nil
|
return acme.UnFqdn(authZone), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, error) {
|
func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Response, error) {
|
||||||
req, err := http.NewRequest(method, fmt.Sprintf("%s%s", apiURL, uri), body)
|
req, err := http.NewRequest(method, fmt.Sprintf("%s%s", apiURL, uri), body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -143,10 +148,9 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (*http.Res
|
||||||
|
|
||||||
req.Header.Set("Accept", "application/json")
|
req.Header.Set("Accept", "application/json")
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", c.apiKey, c.apiSecret))
|
req.Header.Set("Authorization", fmt.Sprintf("sso-key %s:%s", d.apiKey, d.apiSecret))
|
||||||
|
|
||||||
client := http.Client{Timeout: 30 * time.Second}
|
return d.client.Do(req)
|
||||||
return client.Do(req)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DNSRecord a DNS record
|
// DNSRecord a DNS record
|
||||||
|
|
|
@ -35,7 +35,7 @@ type customRetryer struct {
|
||||||
// delay of ~400ms with an upper limit of ~30 seconds which should prevent
|
// delay of ~400ms with an upper limit of ~30 seconds which should prevent
|
||||||
// causing a high number of consecutive throttling errors.
|
// causing a high number of consecutive throttling errors.
|
||||||
// For reference: Route 53 enforces an account-wide(!) 5req/s query limit.
|
// For reference: Route 53 enforces an account-wide(!) 5req/s query limit.
|
||||||
func (d customRetryer) RetryRules(r *request.Request) time.Duration {
|
func (c customRetryer) RetryRules(r *request.Request) time.Duration {
|
||||||
retryCount := r.RetryCount
|
retryCount := r.RetryCount
|
||||||
if retryCount > 7 {
|
if retryCount > 7 {
|
||||||
retryCount = 7
|
retryCount = 7
|
||||||
|
@ -70,15 +70,15 @@ func NewDNSProvider() (*DNSProvider, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record using the specified parameters
|
// Present creates a TXT record using the specified parameters
|
||||||
func (r *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
value = `"` + value + `"`
|
value = `"` + value + `"`
|
||||||
err := r.newTxtRecord(domain, fqdn, value)
|
err := d.newTxtRecord(domain, fqdn, value)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
value = `"` + value + `"`
|
value = `"` + value + `"`
|
||||||
params := &lightsail.DeleteDomainEntryInput{
|
params := &lightsail.DeleteDomainEntryInput{
|
||||||
|
@ -89,11 +89,11 @@ func (r *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
Target: aws.String(value),
|
Target: aws.String(value),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, err := r.client.DeleteDomainEntry(params)
|
_, err := d.client.DeleteDomainEntry(params)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DNSProvider) newTxtRecord(domain string, fqdn string, value string) error {
|
func (d *DNSProvider) newTxtRecord(domain string, fqdn string, value string) error {
|
||||||
params := &lightsail.CreateDomainEntryInput{
|
params := &lightsail.CreateDomainEntryInput{
|
||||||
DomainName: aws.String(domain),
|
DomainName: aws.String(domain),
|
||||||
DomainEntry: &lightsail.DomainEntry{
|
DomainEntry: &lightsail.DomainEntry{
|
||||||
|
@ -102,6 +102,6 @@ func (r *DNSProvider) newTxtRecord(domain string, fqdn string, value string) err
|
||||||
Type: aws.String("TXT"),
|
Type: aws.String("TXT"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, err := r.client.CreateDomainEntry(params)
|
_, err := d.client.CreateDomainEntry(params)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ type hostedZoneInfo struct {
|
||||||
|
|
||||||
// DNSProvider implements the acme.ChallengeProvider interface.
|
// DNSProvider implements the acme.ChallengeProvider interface.
|
||||||
type DNSProvider struct {
|
type DNSProvider struct {
|
||||||
linode *dns.DNS
|
client *dns.DNS
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a DNSProvider instance configured for Linode.
|
// NewDNSProvider returns a DNSProvider instance configured for Linode.
|
||||||
|
@ -48,7 +48,7 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DNSProvider{
|
return &DNSProvider{
|
||||||
linode: dns.New(apiKey),
|
client: dns.New(apiKey),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ func (p *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = p.linode.CreateDomainResourceTXT(zone.domainID, acme.UnFqdn(fqdn), value, 60); err != nil {
|
if _, err = p.client.CreateDomainResourceTXT(zone.domainID, acme.UnFqdn(fqdn), value, 60); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all TXT records for the specified domain.
|
// Get all TXT records for the specified domain.
|
||||||
resources, err := p.linode.GetResourcesByType(zone.domainID, "TXT")
|
resources, err := p.client.GetResourcesByType(zone.domainID, "TXT")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ func (p *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
// Remove the specified resource, if it exists.
|
// Remove the specified resource, if it exists.
|
||||||
for _, resource := range resources {
|
for _, resource := range resources {
|
||||||
if resource.Name == zone.resourceName && resource.Target == value {
|
if resource.Name == zone.resourceName && resource.Target == value {
|
||||||
resp, err := p.linode.DeleteDomainResource(resource.DomainID, resource.ResourceID)
|
resp, err := p.client.DeleteDomainResource(resource.DomainID, resource.ResourceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ func (p *DNSProvider) getHostedZoneInfo(fqdn string) (*hostedZoneInfo, error) {
|
||||||
resourceName := strings.TrimSuffix(fqdn, "."+authZone)
|
resourceName := strings.TrimSuffix(fqdn, "."+authZone)
|
||||||
|
|
||||||
// Query the authority zone.
|
// Query the authority zone.
|
||||||
domain, err := p.linode.GetDomain(acme.UnFqdn(authZone))
|
domain, err := p.client.GetDomain(acme.UnFqdn(authZone))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ func TestDNSProvider_Present(t *testing.T) {
|
||||||
mockSrv := newMockServer(t, mockResponses)
|
mockSrv := newMockServer(t, mockResponses)
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
p.linode.ToLinode().SetEndpoint(mockSrv.URL)
|
p.client.ToLinode().SetEndpoint(mockSrv.URL)
|
||||||
|
|
||||||
err = p.Present(domain, "", keyAuth)
|
err = p.Present(domain, "", keyAuth)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -156,7 +156,7 @@ func TestDNSProvider_PresentNoDomain(t *testing.T) {
|
||||||
mockSrv := newMockServer(t, mockResponses)
|
mockSrv := newMockServer(t, mockResponses)
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
p.linode.ToLinode().SetEndpoint(mockSrv.URL)
|
p.client.ToLinode().SetEndpoint(mockSrv.URL)
|
||||||
|
|
||||||
err = p.Present(domain, "", keyAuth)
|
err = p.Present(domain, "", keyAuth)
|
||||||
assert.EqualError(t, err, "dns: requested domain not found")
|
assert.EqualError(t, err, "dns: requested domain not found")
|
||||||
|
@ -193,7 +193,7 @@ func TestDNSProvider_PresentCreateFailed(t *testing.T) {
|
||||||
mockSrv := newMockServer(t, mockResponses)
|
mockSrv := newMockServer(t, mockResponses)
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
p.linode.ToLinode().SetEndpoint(mockSrv.URL)
|
p.client.ToLinode().SetEndpoint(mockSrv.URL)
|
||||||
|
|
||||||
err = p.Present(domain, "", keyAuth)
|
err = p.Present(domain, "", keyAuth)
|
||||||
assert.EqualError(t, err, "Failed to create domain resource")
|
assert.EqualError(t, err, "Failed to create domain resource")
|
||||||
|
@ -244,7 +244,7 @@ func TestDNSProvider_CleanUp(t *testing.T) {
|
||||||
mockSrv := newMockServer(t, mockResponses)
|
mockSrv := newMockServer(t, mockResponses)
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
p.linode.ToLinode().SetEndpoint(mockSrv.URL)
|
p.client.ToLinode().SetEndpoint(mockSrv.URL)
|
||||||
|
|
||||||
err = p.CleanUp(domain, "", keyAuth)
|
err = p.CleanUp(domain, "", keyAuth)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
@ -273,7 +273,7 @@ func TestDNSProvider_CleanUpNoDomain(t *testing.T) {
|
||||||
mockSrv := newMockServer(t, mockResponses)
|
mockSrv := newMockServer(t, mockResponses)
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
p.linode.ToLinode().SetEndpoint(mockSrv.URL)
|
p.client.ToLinode().SetEndpoint(mockSrv.URL)
|
||||||
|
|
||||||
err = p.CleanUp(domain, "", keyAuth)
|
err = p.CleanUp(domain, "", keyAuth)
|
||||||
assert.EqualError(t, err, "dns: requested domain not found")
|
assert.EqualError(t, err, "dns: requested domain not found")
|
||||||
|
@ -322,7 +322,7 @@ func TestDNSProvider_CleanUpDeleteFailed(t *testing.T) {
|
||||||
mockSrv := newMockServer(t, mockResponses)
|
mockSrv := newMockServer(t, mockResponses)
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
p.linode.ToLinode().SetEndpoint(mockSrv.URL)
|
p.client.ToLinode().SetEndpoint(mockSrv.URL)
|
||||||
|
|
||||||
err = p.CleanUp(domain, "", keyAuth)
|
err = p.CleanUp(domain, "", keyAuth)
|
||||||
assert.EqualError(t, err, "Failed to delete domain resource")
|
assert.EqualError(t, err, "Failed to delete domain resource")
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/log"
|
||||||
"github.com/xenolf/lego/platform/config/env"
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -32,7 +33,6 @@ var (
|
||||||
debug = false
|
debug = false
|
||||||
defaultBaseURL = "https://api.namecheap.com/xml.response"
|
defaultBaseURL = "https://api.namecheap.com/xml.response"
|
||||||
getIPURL = "https://dynamicdns.park-your-domain.com/getip"
|
getIPURL = "https://dynamicdns.park-your-domain.com/getip"
|
||||||
httpClient = http.Client{Timeout: 60 * time.Second}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DNSProvider is an implementation of the ChallengeProviderTimeout interface
|
// DNSProvider is an implementation of the ChallengeProviderTimeout interface
|
||||||
|
@ -42,6 +42,7 @@ type DNSProvider struct {
|
||||||
apiUser string
|
apiUser string
|
||||||
apiKey string
|
apiKey string
|
||||||
clientIP string
|
clientIP string
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a DNSProvider instance configured for namecheap.
|
// NewDNSProvider returns a DNSProvider instance configured for namecheap.
|
||||||
|
@ -63,7 +64,9 @@ func NewDNSProviderCredentials(apiUser, apiKey string) (*DNSProvider, error) {
|
||||||
return nil, fmt.Errorf("Namecheap credentials missing")
|
return nil, fmt.Errorf("Namecheap credentials missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
clientIP, err := getClientIP()
|
client := &http.Client{Timeout: 60 * time.Second}
|
||||||
|
|
||||||
|
clientIP, err := getClientIP(client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -73,6 +76,7 @@ func NewDNSProviderCredentials(apiUser, apiKey string) (*DNSProvider, error) {
|
||||||
apiUser: apiUser,
|
apiUser: apiUser,
|
||||||
apiKey: apiKey,
|
apiKey: apiKey,
|
||||||
clientIP: clientIP,
|
clientIP: clientIP,
|
||||||
|
client: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,8 +106,8 @@ type apierror struct {
|
||||||
|
|
||||||
// getClientIP returns the client's public IP address. It uses namecheap's
|
// getClientIP returns the client's public IP address. It uses namecheap's
|
||||||
// IP discovery service to perform the lookup.
|
// IP discovery service to perform the lookup.
|
||||||
func getClientIP() (addr string, err error) {
|
func getClientIP(client *http.Client) (addr string, err error) {
|
||||||
resp, err := httpClient.Get(getIPURL)
|
resp, err := client.Get(getIPURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -115,7 +119,7 @@ func getClientIP() (addr string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
fmt.Println("Client IP:", string(clientIP))
|
log.Println("Client IP:", string(clientIP))
|
||||||
}
|
}
|
||||||
return string(clientIP), nil
|
return string(clientIP), nil
|
||||||
}
|
}
|
||||||
|
@ -189,7 +193,7 @@ func (d *DNSProvider) getTLDs() (tlds map[string]string, err error) {
|
||||||
reqURL, _ := url.Parse(d.baseURL)
|
reqURL, _ := url.Parse(d.baseURL)
|
||||||
reqURL.RawQuery = values.Encode()
|
reqURL.RawQuery = values.Encode()
|
||||||
|
|
||||||
resp, err := httpClient.Get(reqURL.String())
|
resp, err := d.client.Get(reqURL.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -238,7 +242,7 @@ func (d *DNSProvider) getHosts(ch *challenge) (hosts []host, err error) {
|
||||||
reqURL, _ := url.Parse(d.baseURL)
|
reqURL, _ := url.Parse(d.baseURL)
|
||||||
reqURL.RawQuery = values.Encode()
|
reqURL.RawQuery = values.Encode()
|
||||||
|
|
||||||
resp, err := httpClient.Get(reqURL.String())
|
resp, err := d.client.Get(reqURL.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -288,7 +292,7 @@ func (d *DNSProvider) setHosts(ch *challenge, hosts []host) error {
|
||||||
values.Add("TTL"+ind, h.TTL)
|
values.Add("TTL"+ind, h.TTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := httpClient.PostForm(d.baseURL, values)
|
resp, err := d.client.PostForm(d.baseURL, values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -385,7 +389,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
for _, h := range hosts {
|
for _, h := range hosts {
|
||||||
fmt.Printf(
|
log.Printf(
|
||||||
"%-5.5s %-30.30s %-6s %-70.70s\n",
|
"%-5.5s %-30.30s %-6s %-70.70s\n",
|
||||||
h.Type, h.Name, h.TTL, h.Address)
|
h.Type, h.Name, h.TTL, h.Address)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -44,7 +45,7 @@ func assertHdr(tc *testcase, t *testing.T, values *url.Values) {
|
||||||
func mockServer(tc *testcase, t *testing.T, w http.ResponseWriter, r *http.Request) {
|
func mockServer(tc *testcase, t *testing.T, w http.ResponseWriter, r *http.Request) {
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
|
|
||||||
case "GET":
|
case http.MethodGet:
|
||||||
values := r.URL.Query()
|
values := r.URL.Query()
|
||||||
cmd := values.Get("Command")
|
cmd := values.Get("Command")
|
||||||
switch cmd {
|
switch cmd {
|
||||||
|
@ -59,7 +60,7 @@ func mockServer(tc *testcase, t *testing.T, w http.ResponseWriter, r *http.Reque
|
||||||
t.Errorf("Unexpected GET command: %s", cmd)
|
t.Errorf("Unexpected GET command: %s", cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "POST":
|
case http.MethodPost:
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
values := r.Form
|
values := r.Form
|
||||||
cmd := values.Get("Command")
|
cmd := values.Get("Command")
|
||||||
|
@ -90,6 +91,7 @@ func testGetHosts(tc *testcase, t *testing.T) {
|
||||||
apiUser: fakeUser,
|
apiUser: fakeUser,
|
||||||
apiKey: fakeKey,
|
apiKey: fakeKey,
|
||||||
clientIP: fakeClientIP,
|
clientIP: fakeClientIP,
|
||||||
|
client: &http.Client{Timeout: 60 * time.Second},
|
||||||
}
|
}
|
||||||
|
|
||||||
ch, _ := newChallenge(tc.domain, "", tlds)
|
ch, _ := newChallenge(tc.domain, "", tlds)
|
||||||
|
@ -133,6 +135,7 @@ func mockDNSProvider(url string) *DNSProvider {
|
||||||
apiUser: fakeUser,
|
apiUser: fakeUser,
|
||||||
apiKey: fakeKey,
|
apiKey: fakeKey,
|
||||||
clientIP: fakeClientIP,
|
clientIP: fakeClientIP,
|
||||||
|
client: &http.Client{Timeout: 60 * time.Second},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,18 +49,18 @@ func NewDNSProviderCredentials(username, apiToken, server string) (*DNSProvider,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
request := &namecom.Record{
|
request := &namecom.Record{
|
||||||
DomainName: domain,
|
DomainName: domain,
|
||||||
Host: c.extractRecordName(fqdn, domain),
|
Host: d.extractRecordName(fqdn, domain),
|
||||||
Type: "TXT",
|
Type: "TXT",
|
||||||
TTL: uint32(ttl),
|
TTL: uint32(ttl),
|
||||||
Answer: value,
|
Answer: value,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := c.client.CreateRecord(request)
|
_, err := d.client.CreateRecord(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Name.com API call failed: %v", err)
|
return fmt.Errorf("Name.com API call failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -69,10 +69,10 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters.
|
// CleanUp removes the TXT record matching the specified parameters.
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
records, err := c.getRecords(domain)
|
records, err := d.getRecords(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
DomainName: domain,
|
DomainName: domain,
|
||||||
ID: rec.ID,
|
ID: rec.ID,
|
||||||
}
|
}
|
||||||
_, err := c.client.DeleteRecord(request)
|
_, err := d.client.DeleteRecord(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) {
|
func (d *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
records []*namecom.Record
|
records []*namecom.Record
|
||||||
|
@ -106,7 +106,7 @@ func (c *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for request.Page > 0 {
|
for request.Page > 0 {
|
||||||
response, err = c.client.ListRecords(request)
|
response, err = d.client.ListRecords(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ func (c *DNSProvider) getRecords(domain string) ([]*namecom.Record, error) {
|
||||||
return records, nil
|
return records, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
func (d *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||||
name := acme.UnFqdn(fqdn)
|
name := acme.UnFqdn(fqdn)
|
||||||
if idx := strings.Index(name, "."+domain); idx != -1 {
|
if idx := strings.Index(name, "."+domain); idx != -1 {
|
||||||
return name[:idx]
|
return name[:idx]
|
||||||
|
|
|
@ -43,16 +43,16 @@ func NewDNSProviderCredentials(key string) (*DNSProvider, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge.
|
// Present creates a TXT record to fulfil the dns-01 challenge.
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
zone, err := c.getHostedZone(domain)
|
zone, err := d.getHostedZone(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
record := c.newTxtRecord(zone, fqdn, value, ttl)
|
record := d.newTxtRecord(zone, fqdn, value, ttl)
|
||||||
_, err = c.client.Records.Create(record)
|
_, err = d.client.Records.Create(record)
|
||||||
if err != nil && err != rest.ErrRecordExists {
|
if err != nil && err != rest.ErrRecordExists {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -61,21 +61,21 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters.
|
// CleanUp removes the TXT record matching the specified parameters.
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
zone, err := c.getHostedZone(domain)
|
zone, err := d.getHostedZone(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
name := acme.UnFqdn(fqdn)
|
name := acme.UnFqdn(fqdn)
|
||||||
_, err = c.client.Records.Delete(zone.Zone, name, "TXT")
|
_, err = d.client.Records.Delete(zone.Zone, name, "TXT")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) {
|
func (d *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) {
|
||||||
zone, _, err := c.client.Zones.Get(domain)
|
zone, _, err := d.client.Zones.Get(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ func (c *DNSProvider) getHostedZone(domain string) (*dns.Zone, error) {
|
||||||
return zone, nil
|
return zone, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record {
|
func (d *DNSProvider) newTxtRecord(zone *dns.Zone, fqdn, value string, ttl int) *dns.Record {
|
||||||
name := acme.UnFqdn(fqdn)
|
name := acme.UnFqdn(fqdn)
|
||||||
|
|
||||||
return &dns.Record{
|
return &dns.Record{
|
||||||
|
|
|
@ -77,7 +77,7 @@ func (m *DNSMock) HandleListZonesSuccessfully() {
|
||||||
}]}
|
}]}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
assert.Equal(m.t, r.Method, "GET")
|
assert.Equal(m.t, r.Method, http.MethodGet)
|
||||||
assert.Equal(m.t, r.URL.Path, "/v2/zones")
|
assert.Equal(m.t, r.URL.Path, "/v2/zones")
|
||||||
assert.Equal(m.t, r.URL.RawQuery, "name=example.com.")
|
assert.Equal(m.t, r.URL.RawQuery, "name=example.com.")
|
||||||
assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
|
assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
|
||||||
|
@ -92,7 +92,7 @@ func (m *DNSMock) HandleListZonesEmpty() {
|
||||||
]}
|
]}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
assert.Equal(m.t, r.Method, "GET")
|
assert.Equal(m.t, r.Method, http.MethodGet)
|
||||||
assert.Equal(m.t, r.URL.Path, "/v2/zones")
|
assert.Equal(m.t, r.URL.Path, "/v2/zones")
|
||||||
assert.Equal(m.t, r.URL.RawQuery, "name=example.com.")
|
assert.Equal(m.t, r.URL.RawQuery, "name=example.com.")
|
||||||
assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
|
assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
|
||||||
|
@ -108,7 +108,7 @@ func (m *DNSMock) HandleDeleteRecordsetsSuccessfully() {
|
||||||
}]}
|
}]}
|
||||||
`)
|
`)
|
||||||
|
|
||||||
assert.Equal(m.t, r.Method, "DELETE")
|
assert.Equal(m.t, r.Method, http.MethodDelete)
|
||||||
assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets/321321")
|
assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets/321321")
|
||||||
assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
|
assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
|
||||||
})
|
})
|
||||||
|
@ -130,7 +130,7 @@ func (m *DNSMock) HandleListRecordsetsEmpty() {
|
||||||
// HandleListRecordsetsSuccessfully Handle list recordsets successfully
|
// HandleListRecordsetsSuccessfully Handle list recordsets successfully
|
||||||
func (m *DNSMock) HandleListRecordsetsSuccessfully() {
|
func (m *DNSMock) HandleListRecordsetsSuccessfully() {
|
||||||
m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) {
|
m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method == "GET" {
|
if r.Method == http.MethodGet {
|
||||||
fmt.Fprintf(w, `{
|
fmt.Fprintf(w, `{
|
||||||
"recordsets":[{
|
"recordsets":[{
|
||||||
"id":"321321"
|
"id":"321321"
|
||||||
|
@ -140,7 +140,7 @@ func (m *DNSMock) HandleListRecordsetsSuccessfully() {
|
||||||
assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets")
|
assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets")
|
||||||
assert.Equal(m.t, r.URL.RawQuery, "type=TXT&name=_acme-challenge.example.com.")
|
assert.Equal(m.t, r.URL.RawQuery, "type=TXT&name=_acme-challenge.example.com.")
|
||||||
|
|
||||||
} else if r.Method == "POST" {
|
} else if r.Method == http.MethodPost {
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
|
|
||||||
assert.Nil(m.t, err)
|
assert.Nil(m.t, err)
|
||||||
|
|
|
@ -170,7 +170,7 @@ func (d *DNSProvider) loginRequest() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest("POST", d.identityEndpoint, bytes.NewReader(body))
|
req, err := http.NewRequest(http.MethodPost, d.identityEndpoint, bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -242,7 +242,7 @@ func (d *DNSProvider) getZoneID(zone string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
resource := fmt.Sprintf("zones?name=%s", zone)
|
resource := fmt.Sprintf("zones?name=%s", zone)
|
||||||
resp, err := d.SendRequest("GET", resource, nil)
|
resp, err := d.SendRequest(http.MethodGet, resource, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -278,7 +278,7 @@ func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
resource := fmt.Sprintf("zones/%s/recordsets?type=TXT&name=%s", zoneID, fqdn)
|
resource := fmt.Sprintf("zones/%s/recordsets?type=TXT&name=%s", zoneID, fqdn)
|
||||||
resp, err := d.SendRequest("GET", resource, nil)
|
resp, err := d.SendRequest(http.MethodGet, resource, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -307,7 +307,7 @@ func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error)
|
||||||
func (d *DNSProvider) deleteRecordSet(zoneID, recordID string) error {
|
func (d *DNSProvider) deleteRecordSet(zoneID, recordID string) error {
|
||||||
resource := fmt.Sprintf("zones/%s/recordsets/%s", zoneID, recordID)
|
resource := fmt.Sprintf("zones/%s/recordsets/%s", zoneID, recordID)
|
||||||
|
|
||||||
_, err := d.SendRequest("DELETE", resource, nil)
|
_, err := d.SendRequest(http.MethodDelete, resource, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,7 +351,7 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
TTL: ttl,
|
TTL: ttl,
|
||||||
Records: []string{fmt.Sprintf("\"%s\"", value)},
|
Records: []string{fmt.Sprintf("\"%s\"", value)},
|
||||||
}
|
}
|
||||||
_, err = d.SendRequest("POST", resource, r1)
|
_, err = d.SendRequest(http.MethodPost, resource, r1)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,16 +102,14 @@ func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
// Create TXT record
|
// Create TXT record
|
||||||
err = d.client.Post(reqURL, reqData, &respData)
|
err = d.client.Post(reqURL, reqData, &respData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error when call OVH api to add record : %q \n", err)
|
return fmt.Errorf("error when call OVH api to add record: %v", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the change
|
// Apply the change
|
||||||
reqURL = fmt.Sprintf("/domain/zone/%s/refresh", authZone)
|
reqURL = fmt.Sprintf("/domain/zone/%s/refresh", authZone)
|
||||||
err = d.client.Post(reqURL, nil, nil)
|
err = d.client.Post(reqURL, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error when call OVH api to refresh zone : %q \n", err)
|
return fmt.Errorf("error when call OVH api to refresh zone: %v", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d.recordIDsMu.Lock()
|
d.recordIDsMu.Lock()
|
||||||
|
@ -144,8 +142,7 @@ func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
|
|
||||||
err = d.client.Delete(reqURL, nil)
|
err = d.client.Delete(reqURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error when call OVH api to delete challenge record : %q \n", err)
|
return fmt.Errorf("error when call OVH api to delete challenge record: %v", err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete record ID from map
|
// Delete record ID from map
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
|
"github.com/xenolf/lego/log"
|
||||||
"github.com/xenolf/lego/platform/config/env"
|
"github.com/xenolf/lego/platform/config/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,6 +23,7 @@ type DNSProvider struct {
|
||||||
apiKey string
|
apiKey string
|
||||||
host *url.URL
|
host *url.URL
|
||||||
apiVersion int
|
apiVersion int
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a DNSProvider instance configured for pdns.
|
// NewDNSProvider returns a DNSProvider instance configured for pdns.
|
||||||
|
@ -52,25 +54,31 @@ func NewDNSProviderCredentials(host *url.URL, key string) (*DNSProvider, error)
|
||||||
return nil, fmt.Errorf("PDNS API URL missing")
|
return nil, fmt.Errorf("PDNS API URL missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
provider := &DNSProvider{
|
d := &DNSProvider{
|
||||||
host: host,
|
host: host,
|
||||||
apiKey: key,
|
apiKey: key,
|
||||||
|
client: &http.Client{Timeout: 30 * time.Second},
|
||||||
}
|
}
|
||||||
provider.getAPIVersion()
|
|
||||||
|
|
||||||
return provider, nil
|
apiVersion, err := d.getAPIVersion()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("PDNS: failed to get API version %v", err)
|
||||||
|
}
|
||||||
|
d.apiVersion = apiVersion
|
||||||
|
|
||||||
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timeout returns the timeout and interval to use when checking for DNS
|
// Timeout returns the timeout and interval to use when checking for DNS
|
||||||
// propagation. Adjusting here to cope with spikes in propagation times.
|
// propagation. Adjusting here to cope with spikes in propagation times.
|
||||||
func (c *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
func (d *DNSProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
return 120 * time.Second, 2 * time.Second
|
return 120 * time.Second, 2 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge
|
// Present creates a TXT record to fulfil the dns-01 challenge
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
zone, err := c.getHostedZone(fqdn)
|
zone, err := d.getHostedZone(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -78,7 +86,7 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
name := fqdn
|
name := fqdn
|
||||||
|
|
||||||
// pre-v1 API wants non-fqdn
|
// pre-v1 API wants non-fqdn
|
||||||
if c.apiVersion == 0 {
|
if d.apiVersion == 0 {
|
||||||
name = acme.UnFqdn(fqdn)
|
name = acme.UnFqdn(fqdn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,20 +118,20 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body))
|
_, err = d.makeRequest(http.MethodPatch, zone.URL, bytes.NewReader(body))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
zone, err := c.getHostedZone(fqdn)
|
zone, err := d.getHostedZone(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
set, err := c.findTxtRecord(fqdn)
|
set, err := d.findTxtRecord(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -142,11 +150,11 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.makeRequest("PATCH", zone.URL, bytes.NewReader(body))
|
_, err = d.makeRequest(http.MethodPatch, zone.URL, bytes.NewReader(body))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) {
|
func (d *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) {
|
||||||
var zone hostedZone
|
var zone hostedZone
|
||||||
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -154,12 +162,12 @@ func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
url := "/servers/localhost/zones"
|
url := "/servers/localhost/zones"
|
||||||
result, err := c.makeRequest("GET", url, nil)
|
result, err := d.makeRequest(http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
zones := []hostedZone{}
|
var zones []hostedZone
|
||||||
err = json.Unmarshal(result, &zones)
|
err = json.Unmarshal(result, &zones)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -172,7 +180,7 @@ func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err = c.makeRequest("GET", url, nil)
|
result, err = d.makeRequest(http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -198,13 +206,13 @@ func (c *DNSProvider) getHostedZone(fqdn string) (*hostedZone, error) {
|
||||||
return &zone, nil
|
return &zone, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) {
|
func (d *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) {
|
||||||
zone, err := c.getHostedZone(fqdn)
|
zone, err := d.getHostedZone(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.makeRequest("GET", zone.URL, nil)
|
_, err = d.makeRequest(http.MethodGet, zone.URL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -218,21 +226,21 @@ func (c *DNSProvider) findTxtRecord(fqdn string) (*rrSet, error) {
|
||||||
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() {
|
func (d *DNSProvider) getAPIVersion() (int, error) {
|
||||||
type APIVersion struct {
|
type APIVersion struct {
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := c.makeRequest("GET", "/api", nil)
|
result, err := d.makeRequest(http.MethodGet, "/api", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var versions []APIVersion
|
var versions []APIVersion
|
||||||
err = json.Unmarshal(result, &versions)
|
err = json.Unmarshal(result, &versions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
latestVersion := 0
|
latestVersion := 0
|
||||||
|
@ -241,41 +249,45 @@ func (c *DNSProvider) getAPIVersion() {
|
||||||
latestVersion = v.Version
|
latestVersion = v.Version
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.apiVersion = latestVersion
|
|
||||||
|
return latestVersion, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
|
func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
|
||||||
type APIError struct {
|
type APIError struct {
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = ""
|
var path = ""
|
||||||
if c.host.Path != "/" {
|
if d.host.Path != "/" {
|
||||||
path = c.host.Path
|
path = d.host.Path
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(uri, "/") {
|
if !strings.HasPrefix(uri, "/") {
|
||||||
uri = "/" + uri
|
uri = "/" + uri
|
||||||
}
|
}
|
||||||
if c.apiVersion > 0 && !strings.HasPrefix(uri, "/api/v") {
|
|
||||||
uri = "/api/v" + strconv.Itoa(c.apiVersion) + uri
|
if d.apiVersion > 0 && !strings.HasPrefix(uri, "/api/v") {
|
||||||
|
uri = "/api/v" + strconv.Itoa(d.apiVersion) + uri
|
||||||
}
|
}
|
||||||
url := c.host.Scheme + "://" + c.host.Host + path + uri
|
|
||||||
|
url := d.host.Scheme + "://" + d.host.Host + path + uri
|
||||||
req, err := http.NewRequest(method, url, body)
|
req, err := http.NewRequest(method, url, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("X-API-Key", c.apiKey)
|
req.Header.Set("X-API-Key", d.apiKey)
|
||||||
|
|
||||||
client := http.Client{Timeout: 30 * time.Second}
|
resp, err := d.client.Do(req)
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Error talking to PDNS API -> %v", err)
|
return nil, fmt.Errorf("error talking to PDNS API -> %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != 422 && (resp.StatusCode < 200 || resp.StatusCode >= 300) {
|
if resp.StatusCode != http.StatusUnprocessableEntity && (resp.StatusCode < 200 || resp.StatusCode >= 300) {
|
||||||
return nil, fmt.Errorf("Unexpected HTTP status code %d when fetching '%s'", resp.StatusCode, url)
|
return nil, fmt.Errorf("unexpected HTTP status code %d when fetching '%s'", resp.StatusCode, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg json.RawMessage
|
var msg json.RawMessage
|
||||||
|
@ -297,7 +309,7 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if apiError.Error != "" {
|
if apiError.Error != "" {
|
||||||
return nil, fmt.Errorf("Error talking to PDNS API -> %v", apiError.Error)
|
return nil, fmt.Errorf("error talking to PDNS API -> %v", apiError.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return msg, nil
|
return msg, nil
|
||||||
|
|
|
@ -22,6 +22,7 @@ var rackspaceAPIURL = "https://identity.api.rackspacecloud.com/v2.0/tokens"
|
||||||
type DNSProvider struct {
|
type DNSProvider struct {
|
||||||
token string
|
token string
|
||||||
cloudDNSEndpoint string
|
cloudDNSEndpoint string
|
||||||
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDNSProvider returns a DNSProvider instance configured for Rackspace.
|
// NewDNSProvider returns a DNSProvider instance configured for Rackspace.
|
||||||
|
@ -44,35 +45,7 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) {
|
||||||
return nil, fmt.Errorf("Rackspace credentials missing")
|
return nil, fmt.Errorf("Rackspace credentials missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
type APIKeyCredentials struct {
|
authData := AuthData{
|
||||||
Username string `json:"username"`
|
|
||||||
APIKey string `json:"apiKey"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Auth struct {
|
|
||||||
APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RackspaceAuthData struct {
|
|
||||||
Auth `json:"auth"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RackspaceIdentity struct {
|
|
||||||
Access struct {
|
|
||||||
ServiceCatalog []struct {
|
|
||||||
Endpoints []struct {
|
|
||||||
PublicURL string `json:"publicURL"`
|
|
||||||
TenantID string `json:"tenantId"`
|
|
||||||
} `json:"endpoints"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
} `json:"serviceCatalog"`
|
|
||||||
Token struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
} `json:"token"`
|
|
||||||
} `json:"access"`
|
|
||||||
}
|
|
||||||
|
|
||||||
authData := RackspaceAuthData{
|
|
||||||
Auth: Auth{
|
Auth: Auth{
|
||||||
APIKeyCredentials: APIKeyCredentials{
|
APIKeyCredentials: APIKeyCredentials{
|
||||||
Username: user,
|
Username: user,
|
||||||
|
@ -86,13 +59,13 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", rackspaceAPIURL, bytes.NewReader(body))
|
req, err := http.NewRequest(http.MethodPost, rackspaceAPIURL, bytes.NewReader(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
client := http.Client{Timeout: 30 * time.Second}
|
client := &http.Client{Timeout: 30 * time.Second}
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
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)
|
||||||
|
@ -103,7 +76,7 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) {
|
||||||
return nil, fmt.Errorf("Rackspace Authentication failed. Response code: %d", resp.StatusCode)
|
return nil, fmt.Errorf("Rackspace Authentication failed. Response code: %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
var rackspaceIdentity RackspaceIdentity
|
var rackspaceIdentity Identity
|
||||||
err = json.NewDecoder(resp.Body).Decode(&rackspaceIdentity)
|
err = json.NewDecoder(resp.Body).Decode(&rackspaceIdentity)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -124,13 +97,14 @@ func NewDNSProviderCredentials(user, key string) (*DNSProvider, error) {
|
||||||
return &DNSProvider{
|
return &DNSProvider{
|
||||||
token: rackspaceIdentity.Access.Token.ID,
|
token: rackspaceIdentity.Access.Token.ID,
|
||||||
cloudDNSEndpoint: dnsEndpoint,
|
cloudDNSEndpoint: dnsEndpoint,
|
||||||
|
client: client,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the dns-01 challenge
|
// Present creates a TXT record to fulfil the dns-01 challenge
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
zoneID, err := c.getHostedZoneID(fqdn)
|
zoneID, err := d.getHostedZoneID(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -149,30 +123,30 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.makeRequest("POST", fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body))
|
_, err = d.makeRequest(http.MethodPost, fmt.Sprintf("/domains/%d/records", zoneID), bytes.NewReader(body))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters
|
// CleanUp removes the TXT record matching the specified parameters
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
zoneID, err := c.getHostedZoneID(fqdn)
|
zoneID, err := d.getHostedZoneID(fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
record, err := c.findTxtRecord(fqdn, zoneID)
|
record, err := d.findTxtRecord(fqdn, zoneID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.makeRequest("DELETE", fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil)
|
_, err = d.makeRequest(http.MethodDelete, fmt.Sprintf("/domains/%d/records?id=%s", zoneID, record.ID), nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// getHostedZoneID performs a lookup to get the DNS zone which needs
|
// getHostedZoneID performs a lookup to get the DNS zone which needs
|
||||||
// modifying for a given FQDN
|
// modifying for a given FQDN
|
||||||
func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
|
func (d *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
|
||||||
// HostedZones represents the response when querying Rackspace DNS zones
|
// HostedZones represents the response when querying Rackspace DNS zones
|
||||||
type ZoneSearchResponse struct {
|
type ZoneSearchResponse struct {
|
||||||
TotalEntries int `json:"totalEntries"`
|
TotalEntries int `json:"totalEntries"`
|
||||||
|
@ -187,7 +161,7 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := c.makeRequest("GET", fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil)
|
result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("/domains?name=%s", acme.UnFqdn(authZone)), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -207,8 +181,8 @@ func (c *DNSProvider) getHostedZoneID(fqdn string) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// findTxtRecord searches a DNS zone for a TXT record with a specific name
|
// findTxtRecord searches a DNS zone for a TXT record with a specific name
|
||||||
func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*Record, error) {
|
func (d *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)
|
result, err := d.makeRequest(http.MethodGet, fmt.Sprintf("/domains/%d/records?type=TXT&name=%s", zoneID, acme.UnFqdn(fqdn)), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -232,14 +206,14 @@ func (c *DNSProvider) findTxtRecord(fqdn string, zoneID int) (*Record, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeRequest is a wrapper function used for making DNS API requests
|
// makeRequest is a wrapper function used for making DNS API requests
|
||||||
func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
|
func (d *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawMessage, error) {
|
||||||
url := c.cloudDNSEndpoint + uri
|
url := d.cloudDNSEndpoint + uri
|
||||||
req, err := http.NewRequest(method, url, body)
|
req, err := http.NewRequest(method, url, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Set("X-Auth-Token", c.token)
|
req.Header.Set("X-Auth-Token", d.token)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
client := http.Client{Timeout: 30 * time.Second}
|
client := http.Client{Timeout: 30 * time.Second}
|
||||||
|
@ -263,6 +237,38 @@ func (c *DNSProvider) makeRequest(method, uri string, body io.Reader) (json.RawM
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// APIKeyCredentials API credential
|
||||||
|
type APIKeyCredentials struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
APIKey string `json:"apiKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth auth credentials
|
||||||
|
type Auth struct {
|
||||||
|
APIKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthData Auth data
|
||||||
|
type AuthData struct {
|
||||||
|
Auth `json:"auth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identity Identity
|
||||||
|
type Identity struct {
|
||||||
|
Access struct {
|
||||||
|
ServiceCatalog []struct {
|
||||||
|
Endpoints []struct {
|
||||||
|
PublicURL string `json:"publicURL"`
|
||||||
|
TenantID string `json:"tenantId"`
|
||||||
|
} `json:"endpoints"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
} `json:"serviceCatalog"`
|
||||||
|
Token struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
} `json:"token"`
|
||||||
|
} `json:"access"`
|
||||||
|
}
|
||||||
|
|
||||||
// Records is the list of records sent/received from the DNS API
|
// Records is the list of records sent/received from the DNS API
|
||||||
type Records struct {
|
type Records struct {
|
||||||
Record []Record `json:"records"`
|
Record []Record `json:"records"`
|
||||||
|
|
|
@ -35,25 +35,21 @@ func NewDNSProviderCredentials(apiKey string) (*DNSProvider, error) {
|
||||||
return nil, fmt.Errorf("Vultr credentials missing")
|
return nil, fmt.Errorf("Vultr credentials missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
c := &DNSProvider{
|
return &DNSProvider{client: vultr.NewClient(apiKey, nil)}, nil
|
||||||
client: vultr.NewClient(apiKey, nil),
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present creates a TXT record to fulfil the DNS-01 challenge.
|
// Present creates a TXT record to fulfil the DNS-01 challenge.
|
||||||
func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
func (d *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
zoneDomain, err := c.getHostedZone(domain)
|
zoneDomain, err := d.getHostedZone(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
name := c.extractRecordName(fqdn, zoneDomain)
|
name := d.extractRecordName(fqdn, zoneDomain)
|
||||||
|
|
||||||
err = c.client.CreateDNSRecord(zoneDomain, name, "TXT", `"`+value+`"`, 0, ttl)
|
err = d.client.CreateDNSRecord(zoneDomain, name, "TXT", `"`+value+`"`, 0, ttl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Vultr API call failed: %v", err)
|
return fmt.Errorf("Vultr API call failed: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -62,16 +58,16 @@ func (c *DNSProvider) Present(domain, token, keyAuth string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp removes the TXT record matching the specified parameters.
|
// CleanUp removes the TXT record matching the specified parameters.
|
||||||
func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
|
||||||
|
|
||||||
zoneDomain, records, err := c.findTxtRecords(domain, fqdn)
|
zoneDomain, records, err := d.findTxtRecords(domain, fqdn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rec := range records {
|
for _, rec := range records {
|
||||||
err := c.client.DeleteDNSRecord(zoneDomain, rec.RecordID)
|
err := d.client.DeleteDNSRecord(zoneDomain, rec.RecordID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -79,17 +75,17 @@ func (c *DNSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) getHostedZone(domain string) (string, error) {
|
func (d *DNSProvider) getHostedZone(domain string) (string, error) {
|
||||||
domains, err := c.client.GetDNSDomains()
|
domains, err := d.client.GetDNSDomains()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("Vultr API call failed: %v", err)
|
return "", fmt.Errorf("Vultr API call failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostedDomain vultr.DNSDomain
|
var hostedDomain vultr.DNSDomain
|
||||||
for _, d := range domains {
|
for _, dom := range domains {
|
||||||
if strings.HasSuffix(domain, d.Domain) {
|
if strings.HasSuffix(domain, dom.Domain) {
|
||||||
if len(d.Domain) > len(hostedDomain.Domain) {
|
if len(dom.Domain) > len(hostedDomain.Domain) {
|
||||||
hostedDomain = d
|
hostedDomain = dom
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,19 +96,19 @@ func (c *DNSProvider) getHostedZone(domain string) (string, error) {
|
||||||
return hostedDomain.Domain, nil
|
return hostedDomain.Domain, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRecord, error) {
|
func (d *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRecord, error) {
|
||||||
zoneDomain, err := c.getHostedZone(domain)
|
zoneDomain, err := d.getHostedZone(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var records []vultr.DNSRecord
|
var records []vultr.DNSRecord
|
||||||
result, err := c.client.GetDNSRecords(zoneDomain)
|
result, err := d.client.GetDNSRecords(zoneDomain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", records, fmt.Errorf("Vultr API call has failed: %v", err)
|
return "", records, fmt.Errorf("Vultr API call has failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
recordName := c.extractRecordName(fqdn, zoneDomain)
|
recordName := d.extractRecordName(fqdn, zoneDomain)
|
||||||
for _, record := range result {
|
for _, record := range result {
|
||||||
if record.Type == "TXT" && record.Name == recordName {
|
if record.Type == "TXT" && record.Name == recordName {
|
||||||
records = append(records, record)
|
records = append(records, record)
|
||||||
|
@ -122,7 +118,7 @@ func (c *DNSProvider) findTxtRecords(domain, fqdn string) (string, []vultr.DNSRe
|
||||||
return zoneDomain, records, nil
|
return zoneDomain, records, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *DNSProvider) extractRecordName(fqdn, domain string) string {
|
func (d *DNSProvider) extractRecordName(fqdn, domain string) string {
|
||||||
name := acme.UnFqdn(fqdn)
|
name := acme.UnFqdn(fqdn)
|
||||||
if idx := strings.Index(name, "."+domain); idx != -1 {
|
if idx := strings.Index(name, "."+domain); idx != -1 {
|
||||||
return name[:idx]
|
return name[:idx]
|
||||||
|
|
Loading…
Reference in a new issue