Refactor validation return types
Instead of returning a modified challenge, the validate methods now return a slice of ValidationRecords and a ProblemDetails. These can then be assigned to the relevant part of the challenge, and the challenge's status updated, in a single place. This should help avoid errors where local modifications are made to a challenge and then not returned.
This commit is contained in:
parent
a63a88f7a7
commit
dc9eb37534
|
@ -140,8 +140,7 @@ func (va *ValidationAuthorityImpl) resolveAndConstructDialer(name string, port i
|
|||
|
||||
// Validation methods
|
||||
|
||||
func (va *ValidationAuthorityImpl) fetchHTTP(identifier core.AcmeIdentifier, path string, useTLS bool, input core.Challenge) ([]byte, core.Challenge, error) {
|
||||
emptyBody := []byte{}
|
||||
func (va *ValidationAuthorityImpl) fetchHTTP(identifier core.AcmeIdentifier, path string, useTLS bool, input core.Challenge) ([]byte, []core.ValidationRecord, *probs.ProblemDetails) {
|
||||
challenge := input
|
||||
|
||||
host := identifier.Value
|
||||
|
@ -168,13 +167,11 @@ func (va *ValidationAuthorityImpl) fetchHTTP(identifier core.AcmeIdentifier, pat
|
|||
va.log.Audit(fmt.Sprintf("Attempting to validate %s for %s", challenge.Type, url))
|
||||
httpRequest, err := http.NewRequest("GET", url.String(), nil)
|
||||
if err != nil {
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
va.log.Debug(fmt.Sprintf("%s [%s] HTTP failure: %s", challenge.Type, identifier, err))
|
||||
return nil, nil, &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: "URL provided for HTTP was invalid",
|
||||
}
|
||||
va.log.Debug(fmt.Sprintf("%s [%s] HTTP failure: %s", challenge.Type, identifier, err))
|
||||
challenge.Status = core.StatusInvalid
|
||||
return emptyBody, challenge, err
|
||||
}
|
||||
|
||||
if va.UserAgent != "" {
|
||||
|
@ -183,11 +180,9 @@ func (va *ValidationAuthorityImpl) fetchHTTP(identifier core.AcmeIdentifier, pat
|
|||
|
||||
dialer, prob := va.resolveAndConstructDialer(host, port)
|
||||
dialer.record.URL = url.String()
|
||||
challenge.ValidationRecord = append(challenge.ValidationRecord, dialer.record)
|
||||
validationRecords := []core.ValidationRecord{dialer.record}
|
||||
if prob != nil {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = prob
|
||||
return emptyBody, challenge, prob
|
||||
return nil, validationRecords, prob
|
||||
}
|
||||
|
||||
tr := &http.Transport{
|
||||
|
@ -214,7 +209,7 @@ func (va *ValidationAuthorityImpl) fetchHTTP(identifier core.AcmeIdentifier, pat
|
|||
httpRequest.Header.Set("Accept", "*/*")
|
||||
|
||||
logRedirect := func(req *http.Request, via []*http.Request) error {
|
||||
if len(challenge.ValidationRecord) >= maxRedirect {
|
||||
if len(validationRecords) >= maxRedirect {
|
||||
return fmt.Errorf("Too many redirects")
|
||||
}
|
||||
|
||||
|
@ -244,7 +239,7 @@ func (va *ValidationAuthorityImpl) fetchHTTP(identifier core.AcmeIdentifier, pat
|
|||
|
||||
dialer, err := va.resolveAndConstructDialer(reqHost, reqPort)
|
||||
dialer.record.URL = req.URL.String()
|
||||
challenge.ValidationRecord = append(challenge.ValidationRecord, dialer.record)
|
||||
validationRecords = append(validationRecords, dialer.record)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -259,44 +254,35 @@ func (va *ValidationAuthorityImpl) fetchHTTP(identifier core.AcmeIdentifier, pat
|
|||
}
|
||||
httpResponse, err := client.Do(httpRequest)
|
||||
if err != nil {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
va.log.Debug(err.Error())
|
||||
return nil, validationRecords, &probs.ProblemDetails{
|
||||
Type: parseHTTPConnError(err),
|
||||
Detail: fmt.Sprintf("Could not connect to %s", url),
|
||||
}
|
||||
va.log.Debug(strings.Join([]string{challenge.Error.Error(), err.Error()}, ": "))
|
||||
return emptyBody, challenge, err
|
||||
}
|
||||
defer httpResponse.Body.Close()
|
||||
|
||||
if httpResponse.StatusCode != 200 {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
return nil, validationRecords, &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: fmt.Sprintf("Invalid response from %s [%s]: %d",
|
||||
url.String(), dialer.record.AddressUsed, httpResponse.StatusCode),
|
||||
}
|
||||
err = challenge.Error
|
||||
return emptyBody, challenge, err
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(httpResponse.Body)
|
||||
if err != nil {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
return nil, validationRecords, &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: fmt.Sprintf("Error reading HTTP response body: %v", err),
|
||||
}
|
||||
return emptyBody, challenge, err
|
||||
}
|
||||
return body, challenge, nil
|
||||
return body, validationRecords, nil
|
||||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateTLSWithZName(identifier core.AcmeIdentifier, input core.Challenge, zName string) (core.Challenge, error) {
|
||||
challenge := input
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateTLSWithZName(identifier core.AcmeIdentifier, challenge core.Challenge, zName string) ([]core.ValidationRecord, *probs.ProblemDetails) {
|
||||
addr, allAddrs, problem := va.getAddr(identifier.Value)
|
||||
challenge.ValidationRecord = []core.ValidationRecord{
|
||||
validationRecords := []core.ValidationRecord{
|
||||
core.ValidationRecord{
|
||||
Hostname: identifier.Value,
|
||||
AddressesResolved: allAddrs,
|
||||
|
@ -304,15 +290,13 @@ func (va *ValidationAuthorityImpl) validateTLSWithZName(identifier core.AcmeIden
|
|||
},
|
||||
}
|
||||
if problem != nil {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = problem
|
||||
return challenge, challenge.Error
|
||||
return validationRecords, problem
|
||||
}
|
||||
|
||||
// Make a connection with SNI = nonceName
|
||||
portString := strconv.Itoa(va.tlsPort)
|
||||
hostPort := net.JoinHostPort(addr.String(), portString)
|
||||
challenge.ValidationRecord[0].Port = portString
|
||||
validationRecords[0].Port = portString
|
||||
va.log.Notice(fmt.Sprintf("%s [%s] Attempting to validate for %s %s", challenge.Type, identifier, hostPort, zName))
|
||||
conn, err := tls.DialWithDialer(&net.Dialer{Timeout: validationTimeout}, "tcp", hostPort, &tls.Config{
|
||||
ServerName: zName,
|
||||
|
@ -320,106 +304,84 @@ func (va *ValidationAuthorityImpl) validateTLSWithZName(identifier core.AcmeIden
|
|||
})
|
||||
|
||||
if err != nil {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
va.log.Debug(fmt.Sprintf("%s [%s] TLS Connection failure: %s", challenge.Type, identifier, err))
|
||||
return validationRecords, &probs.ProblemDetails{
|
||||
Type: parseHTTPConnError(err),
|
||||
Detail: "Failed to connect to host for DVSNI challenge",
|
||||
}
|
||||
va.log.Debug(fmt.Sprintf("%s [%s] TLS Connection failure: %s", challenge.Type, identifier, err))
|
||||
return challenge, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Check that zName is a dNSName SAN in the server's certificate
|
||||
certs := conn.ConnectionState().PeerCertificates
|
||||
if len(certs) == 0 {
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
return validationRecords, &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: "No certs presented for TLS SNI challenge",
|
||||
}
|
||||
challenge.Status = core.StatusInvalid
|
||||
return challenge, challenge.Error
|
||||
}
|
||||
for _, name := range certs[0].DNSNames {
|
||||
if subtle.ConstantTimeCompare([]byte(name), []byte(zName)) == 1 {
|
||||
challenge.Status = core.StatusValid
|
||||
return challenge, nil
|
||||
return validationRecords, nil
|
||||
}
|
||||
}
|
||||
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
return validationRecords, &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: fmt.Sprintf("Correct zName not found for TLS SNI challenge. Found %s",
|
||||
strings.Join(certs[0].DNSNames, ", ")),
|
||||
}
|
||||
challenge.Status = core.StatusInvalid
|
||||
return challenge, challenge.Error
|
||||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateHTTP01(identifier core.AcmeIdentifier, input core.Challenge) (core.Challenge, error) {
|
||||
challenge := input
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateHTTP01(identifier core.AcmeIdentifier, challenge core.Challenge) ([]core.ValidationRecord, *probs.ProblemDetails) {
|
||||
if identifier.Type != core.IdentifierDNS {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
va.log.Debug(fmt.Sprintf("%s [%s] Identifier failure", challenge.Type, identifier))
|
||||
return nil, &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: "Identifier type for HTTP validation was not DNS",
|
||||
}
|
||||
|
||||
va.log.Debug(fmt.Sprintf("%s [%s] Identifier failure", challenge.Type, identifier))
|
||||
return challenge, challenge.Error
|
||||
}
|
||||
|
||||
// Perform the fetch
|
||||
path := fmt.Sprintf(".well-known/acme-challenge/%s", challenge.Token)
|
||||
body, challenge, err := va.fetchHTTP(identifier, path, false, challenge)
|
||||
body, validationRecords, err := va.fetchHTTP(identifier, path, false, challenge)
|
||||
if err != nil {
|
||||
return challenge, err
|
||||
return validationRecords, err
|
||||
}
|
||||
|
||||
payload := strings.TrimRight(string(body), whitespaceCutset)
|
||||
|
||||
// Parse body as a key authorization object
|
||||
serverKeyAuthorization, err := core.NewKeyAuthorizationFromString(payload)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error parsing key authorization file: %s", err.Error())
|
||||
va.log.Debug(err.Error())
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
serverKeyAuthorization, authErr := core.NewKeyAuthorizationFromString(payload)
|
||||
if authErr != nil {
|
||||
va.log.Debug(authErr.Error())
|
||||
return validationRecords, &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: err.Error(),
|
||||
Detail: fmt.Sprintf("Error parsing key authorization file: %s", authErr.Error()),
|
||||
}
|
||||
return challenge, err
|
||||
}
|
||||
|
||||
// Check that the account key for this challenge is authorized by this object
|
||||
if !serverKeyAuthorization.Match(challenge.Token, challenge.AccountKey) {
|
||||
err = fmt.Errorf("The key authorization file from the server did not match this challenge [%v] != [%v]",
|
||||
errString := fmt.Sprintf("The key authorization file from the server did not match this challenge [%v] != [%v]",
|
||||
challenge.KeyAuthorization.String(), string(body))
|
||||
va.log.Debug(err.Error())
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
va.log.Debug(errString)
|
||||
return validationRecords, &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: err.Error(),
|
||||
Detail: errString,
|
||||
}
|
||||
return challenge, err
|
||||
}
|
||||
|
||||
challenge.Status = core.StatusValid
|
||||
return challenge, nil
|
||||
return validationRecords, nil
|
||||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateTLSSNI01(identifier core.AcmeIdentifier, input core.Challenge) (core.Challenge, error) {
|
||||
challenge := input
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateTLSSNI01(identifier core.AcmeIdentifier, challenge core.Challenge) ([]core.ValidationRecord, *probs.ProblemDetails) {
|
||||
if identifier.Type != "dns" {
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
va.log.Debug(fmt.Sprintf("TLS-SNI [%s] Identifier failure", identifier))
|
||||
return nil, &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: "Identifier type for TLS-SNI was not DNS",
|
||||
}
|
||||
challenge.Status = core.StatusInvalid
|
||||
va.log.Debug(fmt.Sprintf("TLS-SNI [%s] Identifier failure", identifier))
|
||||
return challenge, challenge.Error
|
||||
}
|
||||
|
||||
// Compute the digest that will appear in the certificate
|
||||
|
@ -453,17 +415,13 @@ func parseHTTPConnError(err error) probs.ProblemType {
|
|||
return probs.ConnectionProblem
|
||||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateDNS01(identifier core.AcmeIdentifier, input core.Challenge) (core.Challenge, error) {
|
||||
challenge := input
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateDNS01(identifier core.AcmeIdentifier, challenge core.Challenge) ([]core.ValidationRecord, *probs.ProblemDetails) {
|
||||
if identifier.Type != core.IdentifierDNS {
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
va.log.Debug(fmt.Sprintf("DNS [%s] Identifier failure", identifier))
|
||||
return nil, &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: "Identifier type for DNS was not itself DNS",
|
||||
}
|
||||
va.log.Debug(fmt.Sprintf("DNS [%s] Identifier failure", identifier))
|
||||
challenge.Status = core.StatusInvalid
|
||||
return challenge, challenge.Error
|
||||
}
|
||||
|
||||
// Compute the digest of the key authorization file
|
||||
|
@ -478,25 +436,21 @@ func (va *ValidationAuthorityImpl) validateDNS01(identifier core.AcmeIdentifier,
|
|||
va.stats.Inc("VA.DNS.Rate", 1, 1.0)
|
||||
|
||||
if err != nil {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = bdns.ProblemDetailsFromDNSError(err)
|
||||
va.log.Debug(fmt.Sprintf("%s [%s] DNS failure: %s", challenge.Type, identifier, err))
|
||||
return challenge, challenge.Error
|
||||
return nil, bdns.ProblemDetailsFromDNSError(err)
|
||||
}
|
||||
|
||||
for _, element := range txts {
|
||||
if subtle.ConstantTimeCompare([]byte(element), []byte(authorizedKeysDigest)) == 1 {
|
||||
challenge.Status = core.StatusValid
|
||||
return challenge, nil
|
||||
// Successful challenge validation
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
challenge.Error = &probs.ProblemDetails{
|
||||
return nil, &probs.ProblemDetails{
|
||||
Type: probs.UnauthorizedProblem,
|
||||
Detail: "Correct value not found for DNS challenge",
|
||||
}
|
||||
challenge.Status = core.StatusInvalid
|
||||
return challenge, challenge.Error
|
||||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) checkCAA(identifier core.AcmeIdentifier, regID int64) *probs.ProblemDetails {
|
||||
|
@ -525,31 +479,25 @@ func (va *ValidationAuthorityImpl) validate(authz core.Authorization, challengeI
|
|||
Requester: authz.RegistrationID,
|
||||
RequestTime: va.clk.Now(),
|
||||
}
|
||||
if !authz.Challenges[challengeIndex].IsSane(true) {
|
||||
chall := &authz.Challenges[challengeIndex]
|
||||
chall.Status = core.StatusInvalid
|
||||
chall.Error = &probs.ProblemDetails{Type: probs.MalformedProblem,
|
||||
Detail: fmt.Sprintf("Challenge failed sanity check.")}
|
||||
logEvent.Challenge = *chall
|
||||
logEvent.Error = chall.Error.Detail
|
||||
challenge := &authz.Challenges[challengeIndex]
|
||||
vStart := va.clk.Now()
|
||||
validationRecords, prob := va.validateChallengeAndCAA(authz.Identifier, *challenge, authz.RegistrationID)
|
||||
va.stats.TimingDuration(fmt.Sprintf("VA.Validations.%s.%s", challenge.Type, challenge.Status), time.Since(vStart), 1.0)
|
||||
|
||||
challenge.ValidationRecord = validationRecords
|
||||
if prob != nil {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = prob
|
||||
logEvent.Error = prob.Error()
|
||||
} else if !authz.Challenges[challengeIndex].RecordsSane() {
|
||||
challenge.Status = core.StatusInvalid
|
||||
challenge.Error = &probs.ProblemDetails{Type: probs.ServerInternalProblem,
|
||||
Detail: "Records for validation failed sanity check"}
|
||||
logEvent.Error = challenge.Error.Error()
|
||||
} else {
|
||||
var err error
|
||||
|
||||
vStart := va.clk.Now()
|
||||
authz.Challenges[challengeIndex], err = va.validateChallengeAndCAA(authz.Identifier, authz.Challenges[challengeIndex], authz.RegistrationID)
|
||||
va.stats.TimingDuration(fmt.Sprintf("VA.Validations.%s.%s", authz.Challenges[challengeIndex].Type, authz.Challenges[challengeIndex].Status), time.Since(vStart), 1.0)
|
||||
|
||||
if err != nil {
|
||||
logEvent.Error = err.Error()
|
||||
} else if !authz.Challenges[challengeIndex].RecordsSane() {
|
||||
chall := &authz.Challenges[challengeIndex]
|
||||
chall.Status = core.StatusInvalid
|
||||
chall.Error = &probs.ProblemDetails{Type: probs.ServerInternalProblem,
|
||||
Detail: "Records for validation failed sanity check"}
|
||||
logEvent.Error = chall.Error.Detail
|
||||
}
|
||||
logEvent.Challenge = authz.Challenges[challengeIndex]
|
||||
challenge.Status = core.StatusValid
|
||||
}
|
||||
logEvent.Challenge = *challenge
|
||||
|
||||
// AUDIT[ Certificate Requests ] 11917fa4-10ef-4e0d-9105-bacbe7836a3c
|
||||
va.log.AuditObject("Validation result", logEvent)
|
||||
|
@ -559,27 +507,31 @@ func (va *ValidationAuthorityImpl) validate(authz core.Authorization, challengeI
|
|||
va.RA.OnValidationUpdate(authz)
|
||||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateChallengeAndCAA(identifier core.AcmeIdentifier, challenge core.Challenge, regID int64) (core.Challenge, error) {
|
||||
func (va *ValidationAuthorityImpl) validateChallengeAndCAA(identifier core.AcmeIdentifier, challenge core.Challenge, regID int64) ([]core.ValidationRecord, *probs.ProblemDetails) {
|
||||
ch := make(chan *probs.ProblemDetails, 1)
|
||||
go func() {
|
||||
ch <- va.checkCAA(identifier, regID)
|
||||
}()
|
||||
|
||||
result, err := va.validateChallenge(identifier, challenge)
|
||||
validationRecords, err := va.validateChallenge(identifier, challenge)
|
||||
if err != nil {
|
||||
return result, err
|
||||
return validationRecords, err
|
||||
}
|
||||
|
||||
problemDetails := <-ch
|
||||
if problemDetails != nil {
|
||||
result.Error = problemDetails
|
||||
result.Status = core.StatusInvalid
|
||||
return result, problemDetails
|
||||
caaProblem := <-ch
|
||||
if caaProblem != nil {
|
||||
return validationRecords, caaProblem
|
||||
}
|
||||
return result, nil
|
||||
return validationRecords, nil
|
||||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateChallenge(identifier core.AcmeIdentifier, challenge core.Challenge) (core.Challenge, error) {
|
||||
func (va *ValidationAuthorityImpl) validateChallenge(identifier core.AcmeIdentifier, challenge core.Challenge) ([]core.ValidationRecord, *probs.ProblemDetails) {
|
||||
if !challenge.IsSane(true) {
|
||||
return nil, &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: fmt.Sprintf("Challenge failed sanity check."),
|
||||
}
|
||||
}
|
||||
switch challenge.Type {
|
||||
case core.ChallengeTypeHTTP01:
|
||||
return va.validateHTTP01(identifier, challenge)
|
||||
|
@ -588,7 +540,10 @@ func (va *ValidationAuthorityImpl) validateChallenge(identifier core.AcmeIdentif
|
|||
case core.ChallengeTypeDNS01:
|
||||
return va.validateDNS01(identifier, challenge)
|
||||
}
|
||||
return core.Challenge{}, fmt.Errorf("invalid challenge type %s", challenge.Type)
|
||||
return nil, &probs.ProblemDetails{
|
||||
Type: probs.MalformedProblem,
|
||||
Detail: fmt.Sprintf("invalid challenge type %s", challenge.Type),
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateValidations runs the validate() method asynchronously using goroutines.
|
||||
|
|
|
@ -194,7 +194,7 @@ func tlssniSrv(t *testing.T, chall core.Challenge) *httptest.Server {
|
|||
return hs
|
||||
}
|
||||
|
||||
func TestHttp(t *testing.T) {
|
||||
func TestHTTP(t *testing.T) {
|
||||
chall := core.HTTPChallenge01(accountKey)
|
||||
err := setChallengeToken(&chall, expectedToken)
|
||||
test.AssertNotError(t, err, "Failed to complete HTTP challenge")
|
||||
|
@ -222,75 +222,84 @@ func TestHttp(t *testing.T) {
|
|||
va := NewValidationAuthorityImpl(&PortConfig{HTTPPort: badPort}, nil, stats, clock.Default())
|
||||
va.DNSResolver = &mocks.DNSResolver{}
|
||||
|
||||
invalidChall, err := va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Server's down; expected refusal. Where did we connect?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
_, prob := va.validateHTTP01(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 = &mocks.DNSResolver{}
|
||||
|
||||
log.Clear()
|
||||
t.Logf("Trying to validate: %+v\n", chall)
|
||||
finChall, err := va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, "Error validating http")
|
||||
_, prob = va.validateHTTP01(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)
|
||||
invalidChall, err = va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Should have found a 404 for the challenge.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnauthorizedProblem)
|
||||
_, prob = va.validateHTTP01(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.
|
||||
invalidChall, err = va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Should have found the wrong token value.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnauthorizedProblem)
|
||||
_, prob = va.validateHTTP01(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)
|
||||
finChall, err = va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, "Failed to follow 301 redirect")
|
||||
_, prob = va.validateHTTP01(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)
|
||||
finChall, err = va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, "Failed to follow 302 redirect")
|
||||
_, prob = va.validateHTTP01(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"}
|
||||
invalidChall, err = va.validateHTTP01(ipIdentifier, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "IdentifierType IP shouldn't have worked.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.MalformedProblem)
|
||||
_, prob = va.validateHTTP01(ipIdentifier, chall)
|
||||
if prob == nil {
|
||||
t.Fatalf("IdentifierType IP shouldn't have worked.")
|
||||
}
|
||||
test.AssertEquals(t, prob.Type, probs.MalformedProblem)
|
||||
|
||||
invalidChall, err = va.validateHTTP01(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Domain name is invalid.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnknownHostProblem)
|
||||
_, prob = va.validateHTTP01(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()
|
||||
invalidChall, err = va.validateHTTP01(ident, chall)
|
||||
_, prob = va.validateHTTP01(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")
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Connection should've timed out")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
if prob == nil {
|
||||
t.Fatalf("Connection should've timed out")
|
||||
}
|
||||
test.AssertEquals(t, prob.Type, probs.ConnectionProblem)
|
||||
}
|
||||
|
||||
func TestHTTPRedirectLookup(t *testing.T) {
|
||||
|
@ -308,43 +317,43 @@ func TestHTTPRedirectLookup(t *testing.T) {
|
|||
|
||||
log.Clear()
|
||||
setChallengeToken(&chall, pathMoved)
|
||||
finChall, err := va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, chall.Token)
|
||||
_, prob := va.validateHTTP01(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)
|
||||
finChall, err = va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, chall.Token)
|
||||
_, prob = va.validateHTTP01(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)
|
||||
finChall, err = va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusInvalid)
|
||||
_, err = va.validateHTTP01(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)
|
||||
finChall, err = va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, chall.Token)
|
||||
_, prob = va.validateHTTP01(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)
|
||||
finChall, err = va.validateHTTP01(ident, chall)
|
||||
fmt.Println(finChall.ValidationRecord)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusInvalid)
|
||||
_, err = va.validateHTTP01(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)
|
||||
|
@ -365,10 +374,10 @@ func TestHTTPRedirectLoop(t *testing.T) {
|
|||
va.DNSResolver = &mocks.DNSResolver{}
|
||||
|
||||
log.Clear()
|
||||
finChall, err := va.validateHTTP01(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, chall.Token)
|
||||
fmt.Println(finChall)
|
||||
_, prob := va.validateHTTP01(ident, chall)
|
||||
if prob == nil {
|
||||
t.Fatalf("Challenge should have failed for %s", chall.Token)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPRedirectUserAgent(t *testing.T) {
|
||||
|
@ -386,12 +395,16 @@ func TestHTTPRedirectUserAgent(t *testing.T) {
|
|||
va.UserAgent = rejectUserAgent
|
||||
|
||||
setChallengeToken(&chall, pathMoved)
|
||||
finChall, _ := va.validateHTTP01(ident, chall)
|
||||
test.AssertNotEquals(t, finChall.Status, core.StatusValid)
|
||||
_, prob := va.validateHTTP01(ident, chall)
|
||||
if prob == nil {
|
||||
t.Fatalf("Challenge with rejectUserAgent should have failed (%s).", pathMoved)
|
||||
}
|
||||
|
||||
setChallengeToken(&chall, pathFound)
|
||||
finChall, _ = va.validateHTTP01(ident, chall)
|
||||
test.AssertNotEquals(t, finChall.Status, core.StatusValid)
|
||||
_, prob = va.validateHTTP01(ident, chall)
|
||||
if prob == nil {
|
||||
t.Fatalf("Challenge with rejectUserAgent should have failed (%s).", pathFound)
|
||||
}
|
||||
}
|
||||
|
||||
func getPort(hs *httptest.Server) (int, error) {
|
||||
|
@ -423,25 +436,28 @@ func TestTLSSNI(t *testing.T) {
|
|||
va.DNSResolver = &mocks.DNSResolver{}
|
||||
|
||||
log.Clear()
|
||||
finChall, err := va.validateTLSSNI01(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, "")
|
||||
_, prob := va.validateTLSSNI01(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()
|
||||
invalidChall, err := va.validateTLSSNI01(core.AcmeIdentifier{
|
||||
_, prob = va.validateTLSSNI01(core.AcmeIdentifier{
|
||||
Type: core.IdentifierType("ip"),
|
||||
Value: net.JoinHostPort("127.0.0.1", fmt.Sprintf("%d", port)),
|
||||
}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "IdentifierType IP shouldn't have worked.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.MalformedProblem)
|
||||
if prob == nil {
|
||||
t.Fatalf("IdentifierType IP shouldn't have worked.")
|
||||
}
|
||||
test.AssertEquals(t, prob.Type, probs.MalformedProblem)
|
||||
|
||||
log.Clear()
|
||||
invalidChall, err = va.validateTLSSNI01(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Domain name was supposed to be invalid.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.UnknownHostProblem)
|
||||
_, prob = va.validateTLSSNI01(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()
|
||||
|
@ -450,22 +466,24 @@ func TestTLSSNI(t *testing.T) {
|
|||
|
||||
log.Clear()
|
||||
started := time.Now()
|
||||
invalidChall, err = va.validateTLSSNI01(ident, chall)
|
||||
_, prob = va.validateTLSSNI01(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")
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Connection should've timed out")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
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()
|
||||
invalidChall, err = va.validateTLSSNI01(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Server's down; expected refusal. Where did we connect?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.ConnectionProblem)
|
||||
_, err = va.validateTLSSNI01(ident, chall)
|
||||
if err == nil {
|
||||
t.Fatalf("Server's down; expected refusal. Where did we connect?")
|
||||
}
|
||||
test.AssertEquals(t, prob.Type, probs.ConnectionProblem)
|
||||
}
|
||||
|
||||
func brokenTLSSrv() *httptest.Server {
|
||||
|
@ -489,10 +507,11 @@ func TestTLSError(t *testing.T) {
|
|||
va := NewValidationAuthorityImpl(&PortConfig{TLSPort: port}, nil, stats, clock.Default())
|
||||
va.DNSResolver = &mocks.DNSResolver{}
|
||||
|
||||
invalidChall, err := va.validateTLSSNI01(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "What cert was used?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, probs.TLSProblem)
|
||||
_, prob := va.validateTLSSNI01(ident, chall)
|
||||
if prob == nil {
|
||||
t.Fatalf("TLS validation should have failed: What cert was used?")
|
||||
}
|
||||
test.AssertEquals(t, prob.Type, probs.TLSProblem)
|
||||
}
|
||||
|
||||
func TestValidateHTTP(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue