Resolve CNAME when creating dns-01 challenge (#791)

* Resolve CNAME when creating dns-01 challenge

It may be desirable to host the dns-01 challenge in a zone other than
the one where the challenge is presented. For example, when validating
a.example.com, the challenge may need to live on example.org.

This change resolves CNAMEs encountered when determining the FQDN of the
challenge, and replaces them with the alias.

This PR is based on the original work in #584.

Co-authored-by: Gurvinder Singh <gurvinder.singh@uninett.no>

* review: feature-flip.

* review: restore acmedns test.
This commit is contained in:
Matthew Horan 2019-02-08 23:02:58 -05:00 committed by Ludovic Fernandez
parent 9409b92ed5
commit 348b6f3721
3 changed files with 29 additions and 9 deletions

16
challenge/dns01/cname.go Normal file
View file

@ -0,0 +1,16 @@
package dns01
import "github.com/miekg/dns"
// Update FQDN with CNAME if any
func updateDomainWithCName(r *dns.Msg, fqdn string) string {
for _, rr := range r.Answer {
if cn, ok := rr.(*dns.CNAME); ok {
if cn.Hdr.Name == fqdn {
return cn.Target
}
}
}
return fqdn
}

View file

@ -4,8 +4,11 @@ import (
"crypto/sha256"
"encoding/base64"
"fmt"
"os"
"strconv"
"time"
"github.com/miekg/dns"
"github.com/xenolf/lego/acme"
"github.com/xenolf/lego/acme/api"
"github.com/xenolf/lego/challenge"
@ -172,5 +175,14 @@ func GetRecord(domain, keyAuth string) (fqdn string, value string) {
// base64URL encoding without padding
value = base64.RawURLEncoding.EncodeToString(keyAuthShaBytes[:sha256.Size])
fqdn = fmt.Sprintf("_acme-challenge.%s.", domain)
if ok, _ := strconv.ParseBool(os.Getenv("LEGO_EXPERIMENTAL_CNAME_SUPPORT")); ok {
r, err := dnsQuery(fqdn, dns.TypeCNAME, recursiveNameservers, true)
// Check if the domain has CNAME then return that
if err == nil && r.Rcode == dns.RcodeSuccess {
fqdn = updateDomainWithCName(r, fqdn)
}
}
return
}

View file

@ -60,15 +60,7 @@ func (p preCheck) checkDNSPropagation(fqdn, value string) (bool, error) {
}
if r.Rcode == dns.RcodeSuccess {
// If we see a CNAME here then use the alias
for _, rr := range r.Answer {
if cn, ok := rr.(*dns.CNAME); ok {
if cn.Hdr.Name == fqdn {
fqdn = cn.Target
break
}
}
}
fqdn = updateDomainWithCName(r, fqdn)
}
authoritativeNss, err := lookupNameservers(fqdn)