Skip to content

Commit

Permalink
Make the DNS-Over-HTTPS Json endpoint configurable
Browse files Browse the repository at this point in the history
Signed-off-by: Florian Liebhart <flo.liebhart@gmail.com>
  • Loading branch information
FlorianLiebhart committed Apr 4, 2022
1 parent fe5b45e commit 53c2177
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 12 deletions.
9 changes: 9 additions & 0 deletions cmd/controller/app/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ type ControllerOptions struct {

ACMEDNS01CheckMethod string

DnsOverHttpsJsonEndpoint string

ClusterIssuerAmbientCredentials bool
IssuerAmbientCredentials bool

Expand Down Expand Up @@ -131,6 +133,8 @@ const (

defaultACMEDNS01CheckMethod = dnsutil.ACMEDNS01CheckViaDNSLookup

defaultDnsOverHttpsJsonEndpoint = "https://8.8.8.8/resolve"

defaultClusterResourceNamespace = "kube-system"
defaultNamespace = ""

Expand Down Expand Up @@ -303,6 +307,11 @@ func (s *ControllerOptions) AddFlags(fs *pflag.FlagSet) {
"than the rest of the world (aka DNS split horizon).",
dnsutil.ACMEDNS01CheckViaDNSLookup, dnsutil.ACMEDNS01CheckViaHTTPS))

fs.StringVar(&s.DnsOverHttpsJsonEndpoint, "dns-over-https-json-endpoint", defaultDnsOverHttpsJsonEndpoint, fmt.Sprintf(
"[%s, %s] Only used when specifying \"dns-over-https\" for the \"acme-dns01-check-method\" option. "+
"This allows specifying what JSON endpoint to use for doing the DNS-over-HTTPS verification."+
"Examples: 'https://1.1.1.1/dns-query', 'https://8.8.8.8/resolve', ''https://8.8.4.4/resolve'. or 'https://9.9.9.9:5053/dns-query'"))

fs.StringVar(&s.ACMEHTTP01SolverResourceRequestCPU, "acme-http01-solver-resource-request-cpu", defaultACMEHTTP01SolverResourceRequestCPU, ""+
"Defines the resource request CPU size when spawning new ACME HTTP01 challenge solver pods.")

Expand Down
8 changes: 8 additions & 0 deletions pkg/controller/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,14 @@ type ACMEOptions struct {
// ACMEDNS01CheckMethod specifies how to check for DNS propagation for DNS01 challenges
ACMEDNS01CheckMethod string

// DnsOverHttpsJsonEndpoint allows specifying what Json endpoint to use for doing the DNS-over-HTTPS verification.
// Examples:
// - "https://1.1.1.1/dns-query"
// - "https://8.8.8.8/resolve"
// - "https://8.8.4.4/resolve"
// - "https://9.9.9.9:5053/dns-query"
DnsOverHttpsJsonEndpoint string

// ACMEHTTP01SolverImage is the image to use for solving ACME HTTP01
// challenges
HTTP01SolverImage string
Expand Down
2 changes: 1 addition & 1 deletion pkg/issuer/acme/dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (s *Solver) Check(ctx context.Context, issuer v1.GenericIssuer, ch *cmacme.
log.V(logf.DebugLevel).Info("checking DNS propagation", "nameservers", s.Context.DNS01Nameservers)

ok, err := util.PreCheckDNS(fqdn, ch.Spec.Key, s.Context.DNS01Nameservers,
s.Context.DNS01CheckAuthoritative, s.Context.ACMEDNS01CheckMethod)
s.Context.DNS01CheckAuthoritative, s.Context.ACMEDNS01CheckMethod, s.Context.DnsOverHttpsJsonEndpoint)
if err != nil {
return err
}
Expand Down
31 changes: 23 additions & 8 deletions pkg/issuer/acme/dns/util/wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
)

type preCheckDNSFunc func(fqdn, value string, nameservers []string,
useAuthoritative bool, acmeDNS01CheckMethod string) (bool, error)
useAuthoritative bool, acmeDNS01CheckMethod string, dnsOverHttpsJsonEndpoint string) (bool, error)
type dnsQueryFunc func(fqdn string, rtype uint16, nameservers []string, recursive bool) (in *dns.Msg, err error)

var (
Expand All @@ -48,6 +48,8 @@ const (
ACMEDNS01CheckViaHTTPS = "dns-over-https"
)

const DefaultDnsOverHttpsJsonEndpoint = "https://8.8.8.8/resolve"

var defaultNameservers = []string{
"8.8.8.8:53",
"8.8.4.4:53",
Expand Down Expand Up @@ -132,14 +134,27 @@ func checkDNSPropagationWithDNSLookup(fqdn, value string, nameservers []string,
return checkAuthoritativeNss(fqdn, value, authoritativeNss)
}

func checkDNSPropagationWithHTTPS(fqdn, value string, nameservers []string, useAuthoritative bool) (bool, error) {
// The dnsOverHttpsJsonEndpoint has to be a JSON GET endpoint and NOT an RFC 8484 GET endpoint.
// This decision was taken because the JSON format is much easier to parse, test and debug, and is a standard
// for the big DNS-over-HTTPS DNS providers such as Google, Cloudflare, or Quad9.
// Examples:
// - "https://1.1.1.1/dns-query"
// - "https://8.8.8.8/resolve"
// - "https://8.8.4.4/resolve"
// - "https://9.9.9.9:5053/dns-query"
func checkDNSPropagationWithHTTPS(fqdn, value string, dnsOverHttpsJsonEndpoint string) (bool, error) {
logf.V(logf.InfoLevel).Infof("Checking DNS propagation for FQDN %s using Google's API for DNS over HTTPS", fqdn)

req, err := http.NewRequest("GET", "https://8.8.8.8/resolve?name="+fqdn+"&type=TXT", nil)
if dnsOverHttpsJsonEndpoint == "" {
dnsOverHttpsJsonEndpoint = DefaultDnsOverHttpsJsonEndpoint
}

req, err := http.NewRequest("GET", dnsOverHttpsJsonEndpoint+"?name="+fqdn+"&type=TXT", nil)
if err != nil {
return false, err
}
req.Header.Add("Cache-Control", "no-cache")
req.Header.Add("accept", "application/dns-json")
r, err := http.DefaultClient.Do(req)
if err != nil {
return false, fmt.Errorf("Unable to lookup DNS via HTTPS: %s", err)
Expand All @@ -158,7 +173,7 @@ func checkDNSPropagationWithHTTPS(fqdn, value string, nameservers []string, useA
if resp.Status == 0 && len(resp.Answer) >= 1 {
for _, answer := range resp.Answer {
if txt := strings.Trim(answer.Data, "\""); txt == value {
logf.V(logf.DebugLevel).Infof("Selfchecking using the DNS-over-HTTPS Lookup method was successful")
logf.V(logf.DebugLevel).Infof("Self-checking using the DNS-over-HTTPS Lookup method was successful")
return true, nil
}
}
Expand All @@ -169,14 +184,14 @@ func checkDNSPropagationWithHTTPS(fqdn, value string, nameservers []string, useA
}

func checkDNSPropagation(fqdn, value string, nameservers []string,
useAuthoritative bool, acmeDNS01CheckMethod string) (bool, error) {
useAuthoritative bool, acmeDNS01CheckMethod string, dnsOverHttpsJsonEndpoint string) (bool, error) {
switch acmeDNS01CheckMethod {
case ACMEDNS01CheckViaDNSLookup:
logf.V(logf.DebugLevel).Infof("Selfchecking using the DNS Lookup method")
logf.V(logf.DebugLevel).Infof("Self-checking using the DNS Lookup method")
return checkDNSPropagationWithDNSLookup(fqdn, value, nameservers, useAuthoritative)
case ACMEDNS01CheckViaHTTPS:
logf.V(logf.DebugLevel).Infof("Selfchecking using the DNS over HTTPS Lookup method")
return checkDNSPropagationWithHTTPS(fqdn, value, nameservers, useAuthoritative)
logf.V(logf.DebugLevel).Infof("Self-checking using the DNS-over-HTTPS Lookup method")
return checkDNSPropagationWithHTTPS(fqdn, value, dnsOverHttpsJsonEndpoint)
default:
return false, fmt.Errorf("Unknown DNS propagation method")
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/issuer/acme/dns/util/wait_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,23 +166,23 @@ func TestMatchCAA(t *testing.T) {
}

func TestPreCheckDNSOverHTTPS(t *testing.T) {
ok, err := PreCheckDNS("google.com.", "v=spf1 include:_spf.google.com ~all", []string{"8.8.8.8:53"}, true, "dns-over-https")
ok, err := PreCheckDNS("google.com.", "v=spf1 include:_spf.google.com ~all", []string{"8.8.8.8:53"}, true, "dns-over-https", "https://8.8.8.8/resolve")
if err != nil || !ok {
t.Errorf("preCheckDNS failed for acme-staging.api.letsencrypt.org: %s", err.Error())
}
}

func TestPreCheckDNS(t *testing.T) {
// TODO: find a better TXT record to use in tests
ok, err := PreCheckDNS("google.com.", "v=spf1 include:_spf.google.com ~all", []string{"8.8.8.8:53"}, true, "dnslookup")
ok, err := PreCheckDNS("google.com.", "v=spf1 include:_spf.google.com ~all", []string{"8.8.8.8:53"}, true, "dnslookup", "")
if err != nil || !ok {
t.Errorf("preCheckDNS failed for acme-staging.api.letsencrypt.org: %s", err.Error())
}
}

func TestPreCheckDNSNonAuthoritative(t *testing.T) {
// TODO: find a better TXT record to use in tests
ok, err := PreCheckDNS("google.com.", "v=spf1 include:_spf.google.com ~all", []string{"1.1.1.1:53"}, false, "dnslookup")
ok, err := PreCheckDNS("google.com.", "v=spf1 include:_spf.google.com ~all", []string{"1.1.1.1:53"}, false, "dnslookup", "")
if err != nil || !ok {
t.Errorf("preCheckDNS failed for acme-staging.api.letsencrypt.org: %s", err.Error())
}
Expand Down

0 comments on commit 53c2177

Please sign in to comment.