diff --git a/dnsutil.go b/dnsutil.go index 2573cb96..fa49be6c 100644 --- a/dnsutil.go +++ b/dnsutil.go @@ -328,6 +328,33 @@ func recursiveNameservers(custom []string) []string { return servers } +// Follows CNAME records and returns the canonical name. The function +// supports CNAME pointing to another CNAME. +func followCName(fqdn string, nameservers []string) (string, error) { + if !strings.HasSuffix(fqdn, ".") { + fqdn += "." + } + cname, err := dnsQuery(fqdn, dns.TypeCNAME, nameservers, true) + if err != nil { + return "", err + } + switch cname.Rcode { + case dns.RcodeSuccess: + canoncialFqdn := updateDomainWithCName(cname, fqdn) + if canoncialFqdn == fqdn { + return canoncialFqdn, nil + } + return followCName(canoncialFqdn, nameservers) + case dns.RcodeNameError: + // It's OK if the FQDN doesn't exist. This is possible during the + // DNS-01 chanllenge. The solver will create a TXT record for the + // FQDN. + return fqdn, nil + default: + return "", fmt.Errorf("got error when querying CNAME for domain %q: %v", fqdn, cname.Rcode) + } +} + var defaultNameservers = []string{ "8.8.8.8:53", "8.8.4.4:53", diff --git a/solvers.go b/solvers.go index 8cdaeaf8..b500cab4 100644 --- a/solvers.go +++ b/solvers.go @@ -268,6 +268,12 @@ func (s *DNS01Solver) Present(ctx context.Context, challenge acme.Challenge) err // https://github.com/caddyserver/caddy/issues/3474 activeDNSChallenges.Lock(dnsName) + resolvers := recursiveNameservers(s.Resolvers) + + dnsName, err := followCName(dnsName, resolvers) + if err != nil { + return fmt.Errorf("could not query CNAME for domain %q: %v", dnsName, err) + } zone, err := findZoneByFQDN(dnsName, recursiveNameservers(s.Resolvers)) if err != nil { return fmt.Errorf("could not determine zone for domain %q: %v", dnsName, err)