func TestDNSLookupHost(t *testing.T) { obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr}, testStats, clock.NewFake(), 1) ip, err := obj.LookupHost(context.Background(), "servfail.com") t.Logf("servfail.com - IP: %s, Err: %s", ip, err) test.AssertError(t, err, "Server failure") test.Assert(t, len(ip) == 0, "Should not have IPs") ip, err = obj.LookupHost(context.Background(), "nonexistent.letsencrypt.org") t.Logf("nonexistent.letsencrypt.org - IP: %s, Err: %s", ip, err) test.AssertNotError(t, err, "Not an error to not exist") test.Assert(t, len(ip) == 0, "Should not have IPs") // Single IPv4 address ip, err = obj.LookupHost(context.Background(), "cps.letsencrypt.org") t.Logf("cps.letsencrypt.org - IP: %s, Err: %s", ip, err) test.AssertNotError(t, err, "Not an error to exist") test.Assert(t, len(ip) == 1, "Should have IP") ip, err = obj.LookupHost(context.Background(), "cps.letsencrypt.org") t.Logf("cps.letsencrypt.org - IP: %s, Err: %s", ip, err) test.AssertNotError(t, err, "Not an error to exist") test.Assert(t, len(ip) == 1, "Should have IP") // No IPv6 ip, err = obj.LookupHost(context.Background(), "v6.letsencrypt.org") t.Logf("v6.letsencrypt.org - IP: %s, Err: %s", ip, err) test.AssertNotError(t, err, "Not an error to exist") test.Assert(t, len(ip) == 0, "Should not have IPs") }
func TestValidateEmail(t *testing.T) { testFailures := []struct { input string expected string }{ {"an email`", unparseableEmailDetail}, {"*****@*****.**", emptyDNSResponseDetail}, {"*****@*****.**", "DNS problem: query timed out looking up A for always.timeout"}, {"*****@*****.**", "DNS problem: networking error looking up A for always.error"}, } testSuccesses := []string{ "*****@*****.**", "*****@*****.**", } for _, tc := range testFailures { problem := validateEmail(context.Background(), tc.input, &bdns.MockDNSResolver{}) if problem.Type != probs.InvalidEmailProblem { t.Errorf("validateEmail(%q): got problem type %#v, expected %#v", tc.input, problem.Type, probs.InvalidEmailProblem) } if problem.Detail != tc.expected { t.Errorf("validateEmail(%q): got %#v, expected %#v", tc.input, problem.Detail, tc.expected) } } for _, addr := range testSuccesses { if prob := validateEmail(context.Background(), addr, &bdns.MockDNSResolver{}); prob != nil { t.Errorf("validateEmail(%q): expected success, but it failed: %s", addr, prob) } } }
func TestHTTPRedirectUserAgent(t *testing.T) { chall := core.HTTPChallenge01(accountKey) err := setChallengeToken(&chall, expectedToken) test.AssertNotError(t, err, "Failed to complete HTTP challenge") hs := httpSrv(t, expectedToken) defer hs.Close() port, err := getPort(hs) test.AssertNotError(t, err, "failed to get test server port") stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{HTTPPort: port}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} va.UserAgent = rejectUserAgent setChallengeToken(&chall, pathMoved) _, prob := va.validateHTTP01(context.Background(), ident, chall) if prob == nil { t.Fatalf("Challenge with rejectUserAgent should have failed (%s).", pathMoved) } setChallengeToken(&chall, pathFound) _, prob = va.validateHTTP01(context.Background(), ident, chall) if prob == nil { t.Fatalf("Challenge with rejectUserAgent should have failed (%s).", pathFound) } }
func TestHTTPRedirectLookup(t *testing.T) { chall := core.HTTPChallenge01(accountKey) err := setChallengeToken(&chall, expectedToken) test.AssertNotError(t, err, "Failed to complete HTTP challenge") hs := httpSrv(t, expectedToken) defer hs.Close() port, err := getPort(hs) test.AssertNotError(t, err, "failed to get test server port") stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{HTTPPort: port}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} log.Clear() setChallengeToken(&chall, pathMoved) _, prob := va.validateHTTP01(context.Background(), ident, chall) if prob != nil { t.Fatalf("Unexpected failure in redirect (%s): %s", pathMoved, prob) } test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/`+pathMoved+`" to ".*/`+pathValid+`"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 2) log.Clear() setChallengeToken(&chall, pathFound) _, prob = va.validateHTTP01(context.Background(), ident, chall) if prob != nil { t.Fatalf("Unexpected failure in redirect (%s): %s", pathFound, prob) } test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/`+pathFound+`" to ".*/`+pathMoved+`"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/`+pathMoved+`" to ".*/`+pathValid+`"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 3) log.Clear() setChallengeToken(&chall, pathReLookupInvalid) _, err = va.validateHTTP01(context.Background(), ident, chall) test.AssertError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`No IPv4 addresses found for invalid.invalid`)), 1) log.Clear() setChallengeToken(&chall, pathReLookup) _, prob = va.validateHTTP01(context.Background(), ident, chall) if prob != nil { t.Fatalf("Unexpected error in redirect (%s): %s", pathReLookup, prob) } test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/`+pathReLookup+`" to ".*other.valid:\d+/path"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for other.valid \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) log.Clear() setChallengeToken(&chall, pathRedirectPort) _, err = va.validateHTTP01(context.Background(), ident, chall) test.AssertError(t, err, chall.Token) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/port-redirect" to ".*other.valid:8080/path"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for other.valid \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) }
func TestDNSLookupsNoServer(t *testing.T) { obj := NewTestDNSResolverImpl(time.Second*10, []string{}, testStats, clock.NewFake(), 1) _, _, err := obj.LookupTXT(context.Background(), "letsencrypt.org") test.AssertError(t, err, "No servers") _, err = obj.LookupHost(context.Background(), "letsencrypt.org") test.AssertError(t, err, "No servers") _, err = obj.LookupCAA(context.Background(), "letsencrypt.org") test.AssertError(t, err, "No servers") }
func TestDNSLookupTXT(t *testing.T) { obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr}, testStats, clock.NewFake(), 1) a, _, err := obj.LookupTXT(context.Background(), "letsencrypt.org") t.Logf("A: %v", a) test.AssertNotError(t, err, "No message") a, _, err = obj.LookupTXT(context.Background(), "split-txt.letsencrypt.org") t.Logf("A: %v ", a) test.AssertNotError(t, err, "No message") test.AssertEquals(t, len(a), 1) test.AssertEquals(t, a[0], "abc") }
// TestDNSValidationLive is an integration test, depending on // the existence of some Internet resources. Because of that, // it asserts nothing; it is intended for coverage. func TestDNSValidationLive(t *testing.T) { stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} mockRA := &MockRegistrationAuthority{} va.RA = mockRA goodChalDNS := core.DNSChallenge01(accountKey) // The matching value LPsIwTo7o8BoG0-vjCyGQGBWSVIPxI-i_X336eUOQZo // is set at _acme-challenge.good.bin.coffee goodChalDNS.Token = expectedToken var goodIdent = core.AcmeIdentifier{ Type: core.IdentifierDNS, Value: "good.bin.coffee", } var badIdent = core.AcmeIdentifier{ Type: core.IdentifierType("dns"), Value: "bad.bin.coffee", } var authzGood = core.Authorization{ ID: core.NewToken(), RegistrationID: 1, Identifier: goodIdent, Challenges: []core.Challenge{goodChalDNS}, } va.validate(context.Background(), authzGood, 0) if authzGood.Challenges[0].Status != core.StatusValid { t.Logf("TestDNSValidationLive on Good did not succeed.") } badChalDNS := core.DNSChallenge01(accountKey) // The matching value is NOT set at _acme-challenge.bad.bin.coffee badChalDNS.Token = "yfCBb-bRTLz8Wd1C0lTUQK3qlKj3-t2tYGwx5Hj7r_w" var authzBad = core.Authorization{ ID: core.NewToken(), RegistrationID: 1, Identifier: badIdent, Challenges: []core.Challenge{badChalDNS}, } va.validate(context.Background(), authzBad, 0) if authzBad.Challenges[0].Status != core.StatusInvalid { t.Logf("TestDNSValidationLive on Bad did succeed inappropriately.") } }
func TestDNSLookupCAA(t *testing.T) { obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr}, testStats, clock.NewFake(), 1) caas, err := obj.LookupCAA(context.Background(), "bracewel.net") test.AssertNotError(t, err, "CAA lookup failed") test.Assert(t, len(caas) > 0, "Should have CAA records") caas, err = obj.LookupCAA(context.Background(), "nonexistent.letsencrypt.org") test.AssertNotError(t, err, "CAA lookup failed") test.Assert(t, len(caas) == 0, "Shouldn't have CAA records") caas, err = obj.LookupCAA(context.Background(), "cname.example.com") test.AssertNotError(t, err, "CAA lookup failed") test.Assert(t, len(caas) > 0, "Should follow CNAME to find CAA") }
func TestDNSNXDOMAIN(t *testing.T) { obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr}, testStats, clock.NewFake(), 1) hostname := "nxdomain.letsencrypt.org" _, err := obj.LookupHost(context.Background(), hostname) expected := dnsError{dns.TypeA, hostname, nil, dns.RcodeNameError} if err, ok := err.(*dnsError); !ok || *err != expected { t.Errorf("Looking up %s, got %#v, expected %#v", hostname, err, expected) } _, _, err = obj.LookupTXT(context.Background(), hostname) expected.recordType = dns.TypeTXT if err, ok := err.(*dnsError); !ok || *err != expected { t.Errorf("Looking up %s, got %#v, expected %#v", hostname, err, expected) } }
func TestDNSServFail(t *testing.T) { obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr}, testStats, clock.NewFake(), 1) bad := "servfail.com" _, _, err := obj.LookupTXT(context.Background(), bad) test.AssertError(t, err, "LookupTXT didn't return an error") _, err = obj.LookupHost(context.Background(), bad) test.AssertError(t, err, "LookupHost didn't return an error") // CAA lookup ignores validation failures from the resolver for now // and returns an empty list of CAA records. emptyCaa, err := obj.LookupCAA(context.Background(), bad) test.Assert(t, len(emptyCaa) == 0, "Query returned non-empty list of CAA records") test.AssertNotError(t, err, "LookupCAA returned an error") }
func TestDNSDuplicateServers(t *testing.T) { obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr, dnsLoopbackAddr}, testStats, clock.NewFake(), 1) _, err := obj.LookupHost(context.Background(), "letsencrypt.org") test.AssertNotError(t, err, "No message") }
func TestCAAFailure(t *testing.T) { chall := createChallenge(core.ChallengeTypeTLSSNI01) hs := tlssniSrv(t, chall) defer hs.Close() port, err := getPort(hs) test.AssertNotError(t, err, "failed to get test server port") stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{TLSPort: port}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} mockRA := &MockRegistrationAuthority{} va.RA = mockRA ident.Value = "reserved.com" var authz = core.Authorization{ ID: core.NewToken(), RegistrationID: 1, Identifier: ident, Challenges: []core.Challenge{chall}, } va.validate(context.Background(), authz, 0) test.AssertEquals(t, core.StatusInvalid, mockRA.lastAuthz.Challenges[0].Status) }
func TestDNSValidationNoAuthorityOK(t *testing.T) { stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} mockRA := &MockRegistrationAuthority{} va.RA = mockRA // create a challenge with well known token chalDNS := core.DNSChallenge01(accountKey) chalDNS.Token = expectedToken keyAuthorization, _ := core.NewKeyAuthorization(chalDNS.Token, accountKey) chalDNS.KeyAuthorization = &keyAuthorization goodIdent := core.AcmeIdentifier{ Type: core.IdentifierDNS, Value: "no-authority-dns01.com", } var authz = core.Authorization{ ID: core.NewToken(), RegistrationID: 1, Identifier: goodIdent, Challenges: []core.Challenge{chalDNS}, } va.validate(context.Background(), authz, 0) test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization") test.Assert(t, authz.Challenges[0].Status == core.StatusValid, "Should be valid.") }
func TestDNSValidationServFail(t *testing.T) { stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} mockRA := &MockRegistrationAuthority{} va.RA = mockRA chalDNS := createChallenge(core.ChallengeTypeDNS01) badIdent := core.AcmeIdentifier{ Type: core.IdentifierDNS, Value: "servfail.com", } var authz = core.Authorization{ ID: core.NewToken(), RegistrationID: 1, Identifier: badIdent, Challenges: []core.Challenge{chalDNS}, } va.validate(context.Background(), authz, 0) test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization") test.Assert(t, authz.Challenges[0].Status == core.StatusInvalid, "Should be invalid.") test.AssertEquals(t, authz.Challenges[0].Error.Type, probs.ConnectionProblem) }
func TestDNSValidationInvalid(t *testing.T) { var notDNS = core.AcmeIdentifier{ Type: core.IdentifierType("iris"), Value: "790DB180-A274-47A4-855F-31C428CB1072", } chalDNS := core.DNSChallenge01(accountKey) var authz = core.Authorization{ ID: core.NewToken(), RegistrationID: 1, Identifier: notDNS, Challenges: []core.Challenge{chalDNS}, } stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} mockRA := &MockRegistrationAuthority{} va.RA = mockRA va.validate(context.Background(), authz, 0) test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization") test.Assert(t, authz.Challenges[0].Status == core.StatusInvalid, "Should be invalid.") test.AssertEquals(t, authz.Challenges[0].Error.Type, probs.MalformedProblem) }
func TestDNSValidationNotSane(t *testing.T) { stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} mockRA := &MockRegistrationAuthority{} va.RA = mockRA chal0 := core.DNSChallenge01(accountKey) chal0.Token = "" chal1 := core.DNSChallenge01(accountKey) chal1.Token = "yfCBb-bRTLz8Wd1C0lTUQK3qlKj3-t2tYGwx5Hj7r_" var authz = core.Authorization{ ID: core.NewToken(), RegistrationID: 1, Identifier: ident, Challenges: []core.Challenge{chal0, chal1}, } for i := 0; i < len(authz.Challenges); i++ { va.validate(context.Background(), authz, i) test.AssertEquals(t, authz.Challenges[i].Status, core.StatusInvalid) test.AssertEquals(t, authz.Challenges[i].Error.Type, probs.MalformedProblem) } }
func TestValidateHTTP(t *testing.T) { chall := core.HTTPChallenge01(accountKey) err := setChallengeToken(&chall, core.NewToken()) test.AssertNotError(t, err, "Failed to complete HTTP challenge") hs := httpSrv(t, chall.Token) port, err := getPort(hs) test.AssertNotError(t, err, "failed to get test server port") stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{HTTPPort: port}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} mockRA := &MockRegistrationAuthority{} va.RA = mockRA defer hs.Close() var authz = core.Authorization{ ID: core.NewToken(), RegistrationID: 1, Identifier: ident, Challenges: []core.Challenge{chall}, } va.validate(context.Background(), authz, 0) test.AssertEquals(t, core.StatusValid, mockRA.lastAuthz.Challenges[0].Status) }
func TestDNSTXTAuthorities(t *testing.T) { obj := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr}, testStats, clock.NewFake(), 1) _, auths, err := obj.LookupTXT(context.Background(), "letsencrypt.org") test.AssertNotError(t, err, "TXT lookup failed") test.AssertEquals(t, len(auths), 1) test.AssertEquals(t, auths[0], "letsencrypt.org. 0 IN SOA ns.letsencrypt.org. master.letsencrypt.org. 1 1 1 1 1") }
func TestValidateContacts(t *testing.T) { _, _, ra, _, cleanUp := initAuthorities(t) defer cleanUp() tel, _ := core.ParseAcmeURL("tel:") ansible, _ := core.ParseAcmeURL("ansible:earth.sol.milkyway.laniakea/letsencrypt") validEmail, _ := core.ParseAcmeURL("mailto:[email protected]") malformedEmail, _ := core.ParseAcmeURL("mailto:admin.com") err := ra.validateContacts(context.Background(), []*core.AcmeURL{}) test.AssertNotError(t, err, "No Contacts") err = ra.validateContacts(context.Background(), []*core.AcmeURL{tel, validEmail}) test.AssertError(t, err, "Too Many Contacts") err = ra.validateContacts(context.Background(), []*core.AcmeURL{tel}) test.AssertNotError(t, err, "Simple Telephone") err = ra.validateContacts(context.Background(), []*core.AcmeURL{validEmail}) test.AssertNotError(t, err, "Valid Email") err = ra.validateContacts(context.Background(), []*core.AcmeURL{malformedEmail}) test.AssertError(t, err, "Malformed Email") err = ra.validateContacts(context.Background(), []*core.AcmeURL{ansible}) test.AssertError(t, err, "Unknown scheme") err = ra.validateContacts(context.Background(), []*core.AcmeURL{nil}) test.AssertError(t, err, "Nil AcmeURL") }
func TestCAATimeout(t *testing.T) { stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} va.IssuerDomain = "letsencrypt.org" err := va.checkCAA(context.Background(), core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "caa-timeout.com"}, 101) if err.Type != probs.ConnectionProblem { t.Errorf("Expected timeout error type %s, got %s", probs.ConnectionProblem, err.Type) } expected := "DNS problem: query timed out looking up CAA for always.timeout" if err.Detail != expected { t.Errorf("checkCAA: got %#v, expected %#v", err.Detail, expected) } }
func TestTLSError(t *testing.T) { chall := createChallenge(core.ChallengeTypeTLSSNI01) hs := brokenTLSSrv() port, err := getPort(hs) test.AssertNotError(t, err, "failed to get test server port") stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{TLSPort: port}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} _, prob := va.validateTLSSNI01(context.Background(), ident, chall) if prob == nil { t.Fatalf("TLS validation should have failed: What cert was used?") } test.AssertEquals(t, prob.Type, probs.TLSProblem) }
func TestValidateTLSSNINotSane(t *testing.T) { stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{}, nil, stats, clock.Default()) // no calls made va.DNSResolver = &bdns.MockDNSResolver{} mockRA := &MockRegistrationAuthority{} va.RA = mockRA chall := createChallenge(core.ChallengeTypeTLSSNI01) chall.Token = "not sane" var authz = core.Authorization{ ID: core.NewToken(), RegistrationID: 1, Identifier: ident, Challenges: []core.Challenge{chall}, } va.validate(context.Background(), authz, 0) test.AssertEquals(t, core.StatusInvalid, mockRA.lastAuthz.Challenges[0].Status) }
func TestDNSValidationNoServer(t *testing.T) { c, _ := statsd.NewNoopClient() stats := metrics.NewNoopScope() va := NewValidationAuthorityImpl(&PortConfig{}, nil, c, clock.Default()) va.DNSResolver = bdns.NewTestDNSResolverImpl(time.Second*5, []string{}, stats, clock.Default(), 1) mockRA := &MockRegistrationAuthority{} va.RA = mockRA chalDNS := createChallenge(core.ChallengeTypeDNS01) var authz = core.Authorization{ ID: core.NewToken(), RegistrationID: 1, Identifier: ident, Challenges: []core.Challenge{chalDNS}, } va.validate(context.Background(), authz, 0) test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization") test.Assert(t, authz.Challenges[0].Status == core.StatusInvalid, "Should be invalid.") test.AssertEquals(t, authz.Challenges[0].Error.Type, probs.ConnectionProblem) }
func TestRetry(t *testing.T) { isTempErr := &net.OpError{Op: "read", Err: tempError(true)} nonTempErr := &net.OpError{Op: "read", Err: tempError(false)} servFailError := errors.New("DNS problem: server failure at resolver looking up TXT for example.com") netError := errors.New("DNS problem: networking error looking up TXT for example.com") type testCase struct { maxTries int te *testExchanger expected error expectedCount int } tests := []*testCase{ // The success on first try case { maxTries: 3, te: &testExchanger{ errs: []error{nil}, }, expected: nil, expectedCount: 1, }, // Immediate non-OpError, error returns immediately { maxTries: 3, te: &testExchanger{ errs: []error{errors.New("nope")}, }, expected: servFailError, expectedCount: 1, }, // Temporary err, then non-OpError stops at two tries { maxTries: 3, te: &testExchanger{ errs: []error{isTempErr, errors.New("nope")}, }, expected: servFailError, expectedCount: 2, }, // Temporary error given always { maxTries: 3, te: &testExchanger{ errs: []error{ isTempErr, isTempErr, isTempErr, }, }, expected: netError, expectedCount: 3, }, // Even with maxTries at 0, we should still let a single request go // through { maxTries: 0, te: &testExchanger{ errs: []error{nil}, }, expected: nil, expectedCount: 1, }, // Temporary error given just once causes two tries { maxTries: 3, te: &testExchanger{ errs: []error{ isTempErr, nil, }, }, expected: nil, expectedCount: 2, }, // Temporary error given twice causes three tries { maxTries: 3, te: &testExchanger{ errs: []error{ isTempErr, isTempErr, nil, }, }, expected: nil, expectedCount: 3, }, // Temporary error given thrice causes three tries and fails { maxTries: 3, te: &testExchanger{ errs: []error{ isTempErr, isTempErr, isTempErr, }, }, expected: netError, expectedCount: 3, }, // temporary then non-Temporary error causes two retries { maxTries: 3, te: &testExchanger{ errs: []error{ isTempErr, nonTempErr, }, }, expected: netError, expectedCount: 2, }, } for i, tc := range tests { dr := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr}, testStats, clock.NewFake(), tc.maxTries) dr.DNSClient = tc.te _, _, err := dr.LookupTXT(context.Background(), "example.com") if err == errTooManyRequests { t.Errorf("#%d, sent more requests than the test case handles", i) } expectedErr := tc.expected if (expectedErr == nil && err != nil) || (expectedErr != nil && err == nil) || (expectedErr != nil && expectedErr.Error() != err.Error()) { t.Errorf("#%d, error, expected %v, got %v", i, expectedErr, err) } if tc.expectedCount != tc.te.count { t.Errorf("#%d, error, expectedCount %v, got %v", i, tc.expectedCount, tc.te.count) } } dr := NewTestDNSResolverImpl(time.Second*10, []string{dnsLoopbackAddr}, testStats, clock.NewFake(), 3) dr.DNSClient = &testExchanger{errs: []error{isTempErr, isTempErr, nil}} ctx, cancel := context.WithCancel(context.Background()) cancel() _, _, err := dr.LookupTXT(ctx, "example.com") if err == nil || err.Error() != "DNS problem: query timed out looking up TXT for example.com" { t.Errorf("expected %s, got %s", context.Canceled, err) } dr.DNSClient = &testExchanger{errs: []error{isTempErr, isTempErr, nil}} ctx, _ = context.WithTimeout(context.Background(), -10*time.Hour) _, _, err = dr.LookupTXT(ctx, "example.com") if err == nil || err.Error() != "DNS problem: query timed out looking up TXT for example.com" { t.Errorf("expected %s, got %s", context.DeadlineExceeded, err) } dr.DNSClient = &testExchanger{errs: []error{isTempErr, isTempErr, nil}} ctx, deadlineCancel := context.WithTimeout(context.Background(), -10*time.Hour) deadlineCancel() _, _, err = dr.LookupTXT(ctx, "example.com") if err == nil || err.Error() != "DNS problem: query timed out looking up TXT for example.com" { t.Errorf("expected %s, got %s", context.DeadlineExceeded, err) } }
func TestTLSSNI(t *testing.T) { chall := createChallenge(core.ChallengeTypeTLSSNI01) hs := tlssniSrv(t, chall) port, err := getPort(hs) test.AssertNotError(t, err, "failed to get test server port") stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{TLSPort: port}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} log.Clear() _, prob := va.validateTLSSNI01(context.Background(), ident, chall) if prob != nil { t.Fatalf("Unexpected failre in validateTLSSNI01: %s", prob) } test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) log.Clear() _, prob = va.validateTLSSNI01(context.Background(), core.AcmeIdentifier{ Type: core.IdentifierType("ip"), Value: net.JoinHostPort("127.0.0.1", fmt.Sprintf("%d", port)), }, chall) if prob == nil { t.Fatalf("IdentifierType IP shouldn't have worked.") } test.AssertEquals(t, prob.Type, probs.MalformedProblem) log.Clear() _, prob = va.validateTLSSNI01(context.Background(), core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall) if prob == nil { t.Fatalf("Domain name was supposed to be invalid.") } test.AssertEquals(t, prob.Type, probs.UnknownHostProblem) // Need to create a new authorized keys object to get an unknown SNI (from the signature value) chall.Token = core.NewToken() keyAuthorization, _ := core.NewKeyAuthorization(chall.Token, accountKey) chall.KeyAuthorization = &keyAuthorization log.Clear() started := time.Now() _, prob = va.validateTLSSNI01(context.Background(), ident, chall) took := time.Since(started) // Check that the HTTP connection times out after 5 seconds and doesn't block for 10 seconds test.Assert(t, (took > (time.Second * 5)), "HTTP timed out before 5 seconds") test.Assert(t, (took < (time.Second * 10)), "HTTP connection didn't timeout after 5 seconds") if prob == nil { t.Fatalf("Connection should've timed out") } test.AssertEquals(t, prob.Type, probs.ConnectionProblem) test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1) // Take down validation server and check that validation fails. hs.Close() _, err = va.validateTLSSNI01(context.Background(), ident, chall) if err == nil { t.Fatalf("Server's down; expected refusal. Where did we connect?") } test.AssertEquals(t, prob.Type, probs.ConnectionProblem) }
func TestHTTP(t *testing.T) { chall := core.HTTPChallenge01(accountKey) err := setChallengeToken(&chall, expectedToken) test.AssertNotError(t, err, "Failed to complete HTTP challenge") // NOTE: We do not attempt to shut down the server. The problem is that the // "wait-long" handler sleeps for ten seconds, but this test finishes in less // than that. So if we try to call hs.Close() at the end of the test, we'll be // closing the test server while a request is still pending. Unfortunately, // there appears to be an issue in httptest that trips Go's race detector when // that happens, failing the test. So instead, we live with leaving the server // around till the process exits. // TODO(#661): add hs.Close back, see ticket for blocker hs := httpSrv(t, chall.Token) goodPort, err := getPort(hs) test.AssertNotError(t, err, "failed to get test server port") // Attempt to fail a challenge by telling the VA to connect to a port we are // not listening on. badPort := goodPort + 1 if badPort == 65536 { badPort = goodPort - 1 } stats, _ := statsd.NewNoopClient() va := NewValidationAuthorityImpl(&PortConfig{HTTPPort: badPort}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} _, prob := va.validateHTTP01(context.Background(), ident, chall) if prob == nil { t.Fatalf("Server's down; expected refusal. Where did we connect?") } test.AssertEquals(t, prob.Type, probs.ConnectionProblem) va = NewValidationAuthorityImpl(&PortConfig{HTTPPort: goodPort}, nil, stats, clock.Default()) va.DNSResolver = &bdns.MockDNSResolver{} log.Clear() t.Logf("Trying to validate: %+v\n", chall) _, prob = va.validateHTTP01(context.Background(), ident, chall) if prob != nil { t.Errorf("Unexpected failure in HTTP validation: %s", prob) } test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1) log.Clear() setChallengeToken(&chall, path404) _, prob = va.validateHTTP01(context.Background(), ident, chall) if prob == nil { t.Fatalf("Should have found a 404 for the challenge.") } test.AssertEquals(t, prob.Type, probs.UnauthorizedProblem) test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1) log.Clear() setChallengeToken(&chall, pathWrongToken) // The "wrong token" will actually be the expectedToken. It's wrong // because it doesn't match pathWrongToken. _, prob = va.validateHTTP01(context.Background(), ident, chall) if prob == nil { t.Fatalf("Should have found the wrong token value.") } test.AssertEquals(t, prob.Type, probs.UnauthorizedProblem) test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1) log.Clear() setChallengeToken(&chall, pathMoved) _, prob = va.validateHTTP01(context.Background(), ident, chall) if prob != nil { t.Fatalf("Failed to follow 301 redirect") } test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/`+pathMoved+`" to ".*/`+pathValid+`"`)), 1) log.Clear() setChallengeToken(&chall, pathFound) _, prob = va.validateHTTP01(context.Background(), ident, chall) if prob != nil { t.Fatalf("Failed to follow 302 redirect") } test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/`+pathFound+`" to ".*/`+pathMoved+`"`)), 1) test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/`+pathMoved+`" to ".*/`+pathValid+`"`)), 1) ipIdentifier := core.AcmeIdentifier{Type: core.IdentifierType("ip"), Value: "127.0.0.1"} _, prob = va.validateHTTP01(context.Background(), ipIdentifier, chall) if prob == nil { t.Fatalf("IdentifierType IP shouldn't have worked.") } test.AssertEquals(t, prob.Type, probs.MalformedProblem) _, prob = va.validateHTTP01(context.Background(), core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall) if prob == nil { t.Fatalf("Domain name is invalid.") } test.AssertEquals(t, prob.Type, probs.UnknownHostProblem) setChallengeToken(&chall, pathWaitLong) started := time.Now() _, prob = va.validateHTTP01(context.Background(), ident, chall) took := time.Since(started) // Check that the HTTP connection times out after 5 seconds and doesn't block for 10 seconds test.Assert(t, (took > (time.Second * 5)), "HTTP timed out before 5 seconds") test.Assert(t, (took < (time.Second * 10)), "HTTP connection didn't timeout after 5 seconds") if prob == nil { t.Fatalf("Connection should've timed out") } test.AssertEquals(t, prob.Type, probs.ConnectionProblem) }