Skip to content

Commit d64132e

Browse files
VA: Use performValidation for IsCAAValid remote checks (#7850)
- Remove undeployed feature flag MultiCAAFullResults - Perform local CAA checks prior to initiating remote checks, instead of starting remote checks and proceeding to perform local checks. - Remove VA.IsCAAValid specific remote validation logic, use VA.performRemoteOperation instead - Refactor va.logRemoteResults to be easier to test and omit the RVA problem - Drive-by fix: Calculate logEvent.Latency with va.clk.Since() instead of time.Since() like everything else in VA.performRemoteOperation
1 parent 27a7714 commit d64132e

File tree

6 files changed

+91
-272
lines changed

6 files changed

+91
-272
lines changed

features/features.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,9 @@ type Config struct {
4747

4848
// EnforceMultiCAA causes the VA to kick off remote CAA rechecks when true.
4949
// When false, no remote CAA rechecks will be performed. The primary VA will
50-
// make a valid/invalid decision with the results. The primary VA will
51-
// return an early decision if MultiCAAFullResults is false.
50+
// make a valid/invalid decision with the results.
5251
EnforceMultiCAA bool
5352

54-
// MultiCAAFullResults will cause the main VA to block and wait for all of
55-
// the remote VA CAA recheck results instead of returning early if the
56-
// number of failures is greater than the number allowed by MPIC.
57-
// Only used when EnforceMultiCAA is true.
58-
MultiCAAFullResults bool
59-
6053
// MultipleCertificateProfiles, when enabled, triggers the following
6154
// behavior:
6255
// - SA.NewOrderAndAuthzs: upon receiving a NewOrderRequest with a

test/config-next/va.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
},
4040
"features": {
4141
"EnforceMultiCAA": true,
42-
"MultiCAAFullResults": true,
4342
"DOH": true
4443
},
4544
"remoteVAs": [

va/caa.go

Lines changed: 16 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,20 @@ package va
33
import (
44
"context"
55
"fmt"
6-
"math/rand/v2"
76
"net/url"
87
"regexp"
98
"strings"
109
"sync"
1110
"time"
1211

1312
"github.com/miekg/dns"
13+
"google.golang.org/protobuf/proto"
1414

1515
"github.com/letsencrypt/boulder/bdns"
1616
"github.com/letsencrypt/boulder/core"
1717
corepb "github.com/letsencrypt/boulder/core/proto"
1818
berrors "github.com/letsencrypt/boulder/errors"
1919
"github.com/letsencrypt/boulder/features"
20-
bgrpc "github.com/letsencrypt/boulder/grpc"
2120
"github.com/letsencrypt/boulder/identifier"
2221
"github.com/letsencrypt/boulder/probs"
2322
vapb "github.com/letsencrypt/boulder/va/proto"
@@ -80,14 +79,6 @@ func (va *ValidationAuthorityImpl) IsCAAValid(ctx context.Context, req *vapb.IsC
8079
va.log.AuditObject("CAA check result", logEvent)
8180
}()
8281

83-
var remoteCAAResults chan *remoteVAResult
84-
if features.Get().EnforceMultiCAA {
85-
if remoteVACount := len(va.remoteVAs); remoteVACount > 0 {
86-
remoteCAAResults = make(chan *remoteVAResult, remoteVACount)
87-
go va.performRemoteCAACheck(ctx, req, remoteCAAResults)
88-
}
89-
}
90-
9182
internalErr = va.checkCAA(ctx, acmeID, params)
9283

9384
// Stop the clock for local check latency.
@@ -97,31 +88,22 @@ func (va *ValidationAuthorityImpl) IsCAAValid(ctx context.Context, req *vapb.IsC
9788
logEvent.InternalError = internalErr.Error()
9889
prob = detailedError(internalErr)
9990
prob.Detail = fmt.Sprintf("While processing CAA for %s: %s", req.Domain, prob.Detail)
100-
} else if remoteCAAResults != nil {
101-
if !features.Get().EnforceMultiCAA && features.Get().MultiCAAFullResults {
102-
// If we're not going to enforce multi CAA but we are logging the
103-
// differentials then collect and log the remote results in a separate go
104-
// routine to avoid blocking the primary VA.
105-
go func() {
106-
_ = va.processRemoteCAAResults(
107-
req.Domain,
108-
req.AccountURIID,
109-
string(challType),
110-
remoteCAAResults)
111-
}()
112-
} else if features.Get().EnforceMultiCAA {
113-
remoteProb := va.processRemoteCAAResults(
114-
req.Domain,
115-
req.AccountURIID,
116-
string(challType),
117-
remoteCAAResults)
118-
119-
// If the remote result was a non-nil problem then fail the CAA check
120-
if remoteProb != nil {
121-
prob = remoteProb
122-
va.log.Infof("CAA check failed due to remote failures: identifier=%v err=%s",
123-
req.Domain, remoteProb)
91+
}
92+
93+
if features.Get().EnforceMultiCAA {
94+
op := func(ctx context.Context, remoteva RemoteVA, req proto.Message) (remoteResult, error) {
95+
checkRequest, ok := req.(*vapb.IsCAAValidRequest)
96+
if !ok {
97+
return nil, fmt.Errorf("got type %T, want *vapb.IsCAAValidRequest", req)
12498
}
99+
return remoteva.IsCAAValid(ctx, checkRequest)
100+
}
101+
remoteProb := va.performRemoteOperation(ctx, op, req)
102+
// If the remote result was a non-nil problem then fail the CAA check
103+
if remoteProb != nil {
104+
prob = remoteProb
105+
va.log.Infof("CAA check failed due to remote failures: identifier=%v err=%s",
106+
req.Domain, remoteProb)
125107
}
126108
}
127109

@@ -139,138 +121,6 @@ func (va *ValidationAuthorityImpl) IsCAAValid(ctx context.Context, req *vapb.IsC
139121
}
140122
}
141123

142-
// processRemoteCAAResults evaluates a primary VA result, and a channel of
143-
// remote VA problems to produce a single overall validation result based on
144-
// configured feature flags. The overall result is calculated based on the VA's
145-
// configured `maxRemoteFailures` value.
146-
//
147-
// If the `MultiCAAFullResults` feature is enabled then
148-
// `processRemoteCAAResults` will expect to read a result from the
149-
// `remoteResultsChan` channel for each VA and will not produce an overall
150-
// result until all remote VAs have responded. In this case
151-
// `logRemoteDifferentials` will also be called to describe the differential
152-
// between the primary and all of the remote VAs.
153-
//
154-
// If the `MultiCAAFullResults` feature flag is not enabled then
155-
// `processRemoteCAAResults` will potentially return before all remote VAs have
156-
// had a chance to respond. This happens if the success or failure threshold is
157-
// met. This doesn't allow for logging the differential between the primary and
158-
// remote VAs but is more performant.
159-
func (va *ValidationAuthorityImpl) processRemoteCAAResults(
160-
domain string,
161-
acctID int64,
162-
challengeType string,
163-
remoteResultsChan <-chan *remoteVAResult) *probs.ProblemDetails {
164-
165-
required := len(va.remoteVAs) - va.maxRemoteFailures
166-
good := 0
167-
bad := 0
168-
169-
var remoteResults []*remoteVAResult
170-
var firstProb *probs.ProblemDetails
171-
// Due to channel behavior this could block indefinitely and we rely on gRPC
172-
// honoring the context deadline used in client calls to prevent that from
173-
// happening.
174-
for result := range remoteResultsChan {
175-
// Add the result to the slice
176-
remoteResults = append(remoteResults, result)
177-
if result.Problem == nil {
178-
good++
179-
} else {
180-
bad++
181-
// Store the first non-nil problem to return later (if `MultiCAAFullResults`
182-
// is enabled).
183-
if firstProb == nil {
184-
firstProb = result.Problem
185-
}
186-
}
187-
188-
// If MultiCAAFullResults isn't enabled then return early whenever the
189-
// success or failure threshold is met.
190-
if !features.Get().MultiCAAFullResults {
191-
if good >= required {
192-
return nil
193-
} else if bad > va.maxRemoteFailures {
194-
modifiedProblem := *result.Problem
195-
modifiedProblem.Detail = "During secondary CAA checking: " + firstProb.Detail
196-
return &modifiedProblem
197-
}
198-
}
199-
200-
// If we haven't returned early because of MultiCAAFullResults being
201-
// enabled we need to break the loop once all of the VAs have returned a
202-
// result.
203-
if len(remoteResults) == len(va.remoteVAs) {
204-
break
205-
}
206-
}
207-
// If we are using `features.MultiCAAFullResults` then we haven't returned
208-
// early and can now log the differential between what the primary VA saw and
209-
// what all of the remote VAs saw.
210-
va.logRemoteResults(
211-
domain,
212-
acctID,
213-
challengeType,
214-
remoteResults)
215-
216-
// Based on the threshold of good/bad return nil or a problem.
217-
if good >= required {
218-
return nil
219-
} else if bad > va.maxRemoteFailures {
220-
modifiedProblem := *firstProb
221-
modifiedProblem.Detail = "During secondary CAA checking: " + firstProb.Detail
222-
va.metrics.prospectiveRemoteCAACheckFailures.Inc()
223-
return &modifiedProblem
224-
}
225-
226-
// This condition should not occur - it indicates the good/bad counts didn't
227-
// meet either the required threshold or the maxRemoteFailures threshold.
228-
return probs.ServerInternal("Too few remote IsCAAValid RPC results")
229-
}
230-
231-
// performRemoteCAACheck calls `isCAAValid` for each of the configured remoteVAs
232-
// in a random order. The provided `results` chan should have an equal size to
233-
// the number of remote VAs. The CAA checks will be performed in separate
234-
// go-routines. If the result `error` from a remote `isCAAValid` RPC is nil or a
235-
// nil `ProblemDetails` instance it is written directly to the `results` chan.
236-
// If the err is a canceled error it is treated as a nil error. Otherwise the
237-
// error/problem is written to the results channel as-is.
238-
func (va *ValidationAuthorityImpl) performRemoteCAACheck(
239-
ctx context.Context,
240-
req *vapb.IsCAAValidRequest,
241-
results chan<- *remoteVAResult) {
242-
for _, i := range rand.Perm(len(va.remoteVAs)) {
243-
remoteVA := va.remoteVAs[i]
244-
go func(rva RemoteVA) {
245-
result := &remoteVAResult{
246-
VAHostname: rva.Address,
247-
}
248-
res, err := rva.IsCAAValid(ctx, req)
249-
if err != nil {
250-
if core.IsCanceled(err) {
251-
// Handle the cancellation error.
252-
result.Problem = probs.ServerInternal("Remote VA IsCAAValid RPC canceled")
253-
} else {
254-
// Handle validation error.
255-
va.log.Errf("Remote VA %q.IsCAAValid failed: %s", rva.Address, err)
256-
result.Problem = probs.ServerInternal("Remote VA IsCAAValid RPC failed")
257-
}
258-
} else if res.Problem != nil {
259-
prob, err := bgrpc.PBToProblemDetails(res.Problem)
260-
if err != nil {
261-
va.log.Infof("Remote VA %q.IsCAAValid returned malformed problem: %s", rva.Address, err)
262-
result.Problem = probs.ServerInternal(
263-
fmt.Sprintf("Remote VA IsCAAValid RPC returned malformed result: %s", err))
264-
} else {
265-
va.log.Infof("Remote VA %q.IsCAAValid returned problem: %s", rva.Address, prob)
266-
result.Problem = prob
267-
}
268-
}
269-
results <- result
270-
}(remoteVA)
271-
}
272-
}
273-
274124
// checkCAA performs a CAA lookup & validation for the provided identifier. If
275125
// the CAA lookup & validation fail a problem is returned.
276126
func (va *ValidationAuthorityImpl) checkCAA(

0 commit comments

Comments
 (0)