From a995cca418e8357440f9e62d3627c9a9fab81780 Mon Sep 17 00:00:00 2001 From: Oleksandr Kovalchuk Date: Fri, 20 Dec 2019 19:17:53 +0200 Subject: [PATCH 1/3] Perform domain normalization for wildcard domains Perform domain normalization for wildcard domains, so we do query TXT records for _acme-challenge.example.domain instead of _acme-challenge.*.example.domain when performing DNS-01 challenge. In this way the behavior is consistent with letsencrypt and records queried are in sync with the ones that are shown in certbot manual mode. --- acme/challenge.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/acme/challenge.go b/acme/challenge.go index 4fa39668..687f3f3c 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -385,11 +385,21 @@ func (dc *dns01Challenge) validate(db nosql.DB, jwk *jose.JSONWebKey, vo validat return dc, nil } - txtRecords, err := vo.lookupTxt("_acme-challenge." + dc.Value) + // Normalize domain for wildcard DNS names + // This is done to avoid making TXT lookups for domains like + // _acme-challenge.*.example.com + // Instead perform txt lookup for _acme-challenge.example.com + domain := dc.Value + if strings.HasPrefix(domain, "*") { + domain = strings.TrimPrefix(domain, "*.") + } + + txtRecords, err := vo.lookupTxt("_acme-challenge." + domain) + fmt.Printf("Lookup TXT for _acme-challenge." + domain) if err != nil { if err = dc.storeError(db, DNSErr(errors.Wrapf(err, "error looking up TXT "+ - "records for domain %s", dc.Value))); err != nil { + "records for domain %s", domain))); err != nil { return nil, err } return dc, nil From 46832bb9b3d15c39187b60ca0d6ec0832d90bec3 Mon Sep 17 00:00:00 2001 From: Oleksandr Kovalchuk Date: Fri, 20 Dec 2019 22:22:12 +0200 Subject: [PATCH 2/3] Remove superflurous Printf statement The statement was used for debug purposes and should not be included in the final build --- acme/challenge.go | 1 - 1 file changed, 1 deletion(-) diff --git a/acme/challenge.go b/acme/challenge.go index 687f3f3c..1c040f5d 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -395,7 +395,6 @@ func (dc *dns01Challenge) validate(db nosql.DB, jwk *jose.JSONWebKey, vo validat } txtRecords, err := vo.lookupTxt("_acme-challenge." + domain) - fmt.Printf("Lookup TXT for _acme-challenge." + domain) if err != nil { if err = dc.storeError(db, DNSErr(errors.Wrapf(err, "error looking up TXT "+ From ec8ff0bcedc34c1c0457da7f60b067abef54896e Mon Sep 17 00:00:00 2001 From: Oleksandr Kovalchuk Date: Fri, 20 Dec 2019 22:54:41 +0200 Subject: [PATCH 3/3] Add testcase which ensures we pass correct domain to lookupTxt Make sure we do not pass domains with asterisk (wildcard) in the middle, like _acme-challenge.*.example.com to lookupTxt function, but preprocess domain and remove leading wildcard so we lookup for _acme-challenge.example.com. --- acme/challenge_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/acme/challenge_test.go b/acme/challenge_test.go index 6291803d..720321e5 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -930,6 +930,47 @@ func TestDNS01Validate(t *testing.T) { res: ch, } }, + "ok/lookup-txt-wildcard": func(t *testing.T) test { + ch, err := newDNSCh() + assert.FatalError(t, err) + _ch, ok := ch.(*dns01Challenge) + assert.Fatal(t, ok) + _ch.baseChallenge.Value = "*.zap.internal" + + jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) + assert.FatalError(t, err) + + expKeyAuth, err := KeyAuthorization(ch.getToken(), jwk) + assert.FatalError(t, err) + h := sha256.Sum256([]byte(expKeyAuth)) + expected := base64.RawURLEncoding.EncodeToString(h[:]) + + baseClone := ch.clone() + baseClone.Status = StatusValid + baseClone.Error = nil + newCh := &dns01Challenge{baseClone} + + return test{ + ch: ch, + res: newCh, + vo: validateOptions{ + lookupTxt: func(url string) ([]string, error) { + assert.Equals(t, url, "_acme-challenge.zap.internal") + return []string{"foo", expected}, nil + }, + }, + jwk: jwk, + db: &db.MockNoSQLDB{ + MCmpAndSwap: func(bucket, key, old, newval []byte) ([]byte, bool, error) { + dnsCh, err := unmarshalChallenge(newval) + assert.FatalError(t, err) + assert.Equals(t, dnsCh.getStatus(), StatusValid) + baseClone.Validated = dnsCh.getValidated() + return nil, true, nil + }, + }, + } + }, "fail/key-authorization-gen-error": func(t *testing.T) test { ch, err := newDNSCh() assert.FatalError(t, err)