Skip to content

Commit f24a979

Browse files
authored
VA: put most recent, not original, IP in error messages (#7468)
When we present error messages containing IP addresses to end users, sometimes we're presenting the IP at which we began a chain of redirects, but not presenting the IP at which the final redirect was located. This can make it difficult for client operators to identify exactly which server in their infrastructure is misbehaving. Change our IP error messages to reference the most-recently-tried address from the set of all validation records, rather than the address which started the current (potential) redirect chain. Fixes #7347
1 parent d219948 commit f24a979

File tree

3 files changed

+21
-28
lines changed

3 files changed

+21
-28
lines changed

va/http.go

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,6 @@ func (vt *httpValidationTarget) nextIP() error {
197197
return nil
198198
}
199199

200-
// ip returns the current *net.IP for the validation target. It may return nil
201-
// if all possible IPs have been expended by calls to nextIP.
202-
func (vt *httpValidationTarget) ip() net.IP {
203-
return vt.cur
204-
}
205-
206200
// newHTTPValidationTarget creates a httpValidationTarget for the given host,
207201
// port, and path. This involves querying DNS for the IP addresses for the host.
208202
// An error is returned if there are no usable IP addresses or if the DNS
@@ -369,7 +363,7 @@ func (va *ValidationAuthorityImpl) setupHTTPValidation(
369363
}
370364

371365
// Get the target IP to build a preresolved dialer with
372-
targetIP := target.ip()
366+
targetIP := target.cur
373367
if targetIP == nil {
374368
return nil,
375369
record,
@@ -425,20 +419,12 @@ func (va *ValidationAuthorityImpl) processHTTPValidation(
425419
ctx context.Context,
426420
host string,
427421
path string) ([]byte, []core.ValidationRecord, error) {
428-
429422
// Create a target for the host, port and path with no query parameters
430423
target, err := va.newHTTPValidationTarget(ctx, host, va.httpPort, path, "")
431424
if err != nil {
432425
return nil, nil, err
433426
}
434427

435-
// newIPError implements the error interface. It wraps an error and the IP
436-
// of the remote host in an IPError so we can display the IP in the problem
437-
// details returned to the client.
438-
newIPError := func(target *httpValidationTarget, err error) error {
439-
return ipError{ip: target.cur, err: err}
440-
}
441-
442428
// Create an initial GET Request
443429
initialURL := url.URL{
444430
Scheme: "http",
@@ -447,7 +433,7 @@ func (va *ValidationAuthorityImpl) processHTTPValidation(
447433
}
448434
initialReq, err := http.NewRequest("GET", initialURL.String(), nil)
449435
if err != nil {
450-
return nil, nil, newIPError(target, err)
436+
return nil, nil, newIPError(target.cur, err)
451437
}
452438

453439
// Add a context to the request. Shave some time from the
@@ -484,7 +470,7 @@ func (va *ValidationAuthorityImpl) processHTTPValidation(
484470
// Set up the initial validation request and a base validation record
485471
dialer, baseRecord, err := va.setupHTTPValidation(initialReq.URL.String(), target)
486472
if err != nil {
487-
return nil, []core.ValidationRecord{}, newIPError(target, err)
473+
return nil, []core.ValidationRecord{}, newIPError(target.cur, err)
488474
}
489475

490476
// Build a transport for this validation that will use the preresolvedDialer's
@@ -606,16 +592,17 @@ func (va *ValidationAuthorityImpl) processHTTPValidation(
606592
// have a fallback address to use and must return the original error.
607593
advanceTargetIPErr := target.nextIP()
608594
if advanceTargetIPErr != nil {
609-
return nil, records, newIPError(target, err)
595+
return nil, records, newIPError(records[len(records)-1].AddressUsed, err)
610596
}
611597

612598
// setup another validation to retry the target with the new IP and append
613599
// the retry record.
614600
retryDialer, retryRecord, err := va.setupHTTPValidation(initialReq.URL.String(), target)
615-
records = append(records, retryRecord)
616601
if err != nil {
617-
return nil, records, newIPError(target, err)
602+
return nil, records, newIPError(records[len(records)-1].AddressUsed, err)
618603
}
604+
605+
records = append(records, retryRecord)
619606
va.metrics.http01Fallbacks.Inc()
620607
// Replace the transport's dialer with the preresolvedDialer for the retry
621608
// host.
@@ -626,15 +613,15 @@ func (va *ValidationAuthorityImpl) processHTTPValidation(
626613
// If the retry still failed there isn't anything more to do, return the
627614
// error immediately.
628615
if err != nil {
629-
return nil, records, newIPError(target, err)
616+
return nil, records, newIPError(retryRecord.AddressUsed, err)
630617
}
631618
} else if err != nil {
632619
// if the error was not a fallbackErr then return immediately.
633-
return nil, records, newIPError(target, err)
620+
return nil, records, newIPError(records[len(records)-1].AddressUsed, err)
634621
}
635622

636623
if httpResponse.StatusCode != 200 {
637-
return nil, records, newIPError(target, berrors.UnauthorizedError("Invalid response from %s: %d",
624+
return nil, records, newIPError(records[len(records)-1].AddressUsed, berrors.UnauthorizedError("Invalid response from %s: %d",
638625
records[len(records)-1].URL, httpResponse.StatusCode))
639626
}
640627

@@ -646,13 +633,13 @@ func (va *ValidationAuthorityImpl) processHTTPValidation(
646633
err = closeErr
647634
}
648635
if err != nil {
649-
return nil, records, newIPError(target, berrors.UnauthorizedError("Error reading HTTP response body: %v", err))
636+
return nil, records, newIPError(records[len(records)-1].AddressUsed, berrors.UnauthorizedError("Error reading HTTP response body: %v", err))
650637
}
651638

652639
// io.LimitedReader will silently truncate a Reader so if the
653640
// resulting payload is the same size as maxResponseSize fail
654641
if len(body) >= maxResponseSize {
655-
return nil, records, newIPError(target, berrors.UnauthorizedError("Invalid response from %s: %q",
642+
return nil, records, newIPError(records[len(records)-1].AddressUsed, berrors.UnauthorizedError("Invalid response from %s: %q",
656643
records[len(records)-1].URL, body))
657644
}
658645

va/http_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func TestPreresolvedDialerTimeout(t *testing.T) {
9494
// Check that the HTTP connection doesn't return too fast, and times
9595
// out after the expected time
9696
if took < va.singleDialTimeout {
97-
t.Fatalf("fetch returned before %s (took: %s) with %#v", va.singleDialTimeout, took, err)
97+
t.Fatalf("fetch returned before %s (took: %s) with %q", va.singleDialTimeout, took, err.Error())
9898
}
9999
if took > 2*va.singleDialTimeout {
100100
t.Fatalf("fetch didn't timeout after %s (took: %s)", va.singleDialTimeout, took)
@@ -184,7 +184,7 @@ func TestHTTPValidationTarget(t *testing.T) {
184184
// Calling ip() on the target should give the expected IPs in the right
185185
// order.
186186
for i, expectedIP := range tc.ExpectedIPs {
187-
gotIP := target.ip()
187+
gotIP := target.cur
188188
if gotIP == nil {
189189
t.Errorf("Expected IP %d to be %s got nil", i, expectedIP)
190190
} else {
@@ -1404,7 +1404,7 @@ func TestHTTPDialTimeout(t *testing.T) {
14041404
// Check that the HTTP connection doesn't return too fast, and times
14051405
// out after the expected time
14061406
if took < (timeout-200*time.Millisecond)/2 {
1407-
t.Fatalf("HTTP returned before %s (%s) with %#v", timeout, took, err)
1407+
t.Fatalf("HTTP returned before %s (%s) with %q", timeout, took, err.Error())
14081408
}
14091409
if took > 2*timeout {
14101410
t.Fatalf("HTTP connection didn't timeout after %s seconds", timeout)

va/va.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,12 @@ type ipError struct {
330330
err error
331331
}
332332

333+
// newIPError wraps an error and the IP of the remote host in an ipError so we
334+
// can display the IP in the problem details returned to the client.
335+
func newIPError(ip net.IP, err error) error {
336+
return ipError{ip: ip, err: err}
337+
}
338+
333339
// Unwrap returns the underlying error.
334340
func (i ipError) Unwrap() error {
335341
return i.err

0 commit comments

Comments
 (0)