Skip to content

Commit

Permalink
VA/RA: Remove non-MPIC DCV and CAA checks
Browse files Browse the repository at this point in the history
  • Loading branch information
beautifulentropy committed Feb 26, 2025
1 parent d27f0c8 commit c4f390d
Show file tree
Hide file tree
Showing 16 changed files with 437 additions and 1,527 deletions.
25 changes: 2 additions & 23 deletions features/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type Config struct {
DisableLegacyLimitWrites bool
MultipleCertificateProfiles bool
InsertAuthzsIndividually bool
EnforceMultiCAA bool
EnforceMPIC bool

// ServeRenewalInfo exposes the renewalInfo endpoint in the directory and for
// GET requests. WARNING: This feature is a draft and highly unstable.
Expand Down Expand Up @@ -52,11 +54,6 @@ type Config struct {
// DOH enables DNS-over-HTTPS queries for validation
DOH bool

// EnforceMultiCAA causes the VA to kick off remote CAA rechecks when true.
// When false, no remote CAA rechecks will be performed. The primary VA will
// make a valid/invalid decision with the results.
EnforceMultiCAA bool

// CheckIdentifiersPaused checks if any of the identifiers in the order are
// currently paused at NewOrder time. If any are paused, an error is
// returned to the Subscriber indicating that the order cannot be processed
Expand All @@ -83,24 +80,6 @@ type Config struct {
// removing pending authz reuse.
NoPendingAuthzReuse bool

// EnforceMPIC enforces SC-067 V3: Require Multi-Perspective Issuance
// Corroboration by:
// - Requiring at least three distinct perspectives, as outlined in the
// "Phased Implementation Timeline" in BRs section 3.2.2.9 ("Effective
// March 15, 2025").
// - Ensuring that corroborating (passing) perspectives reside in at least
// 2 distinct Regional Internet Registries (RIRs) per the "Phased
// Implementation Timeline" in BRs section 3.2.2.9 ("Effective March 15,
// 2026").
// - Including an MPIC summary consisting of: passing perspectives, failing
// perspectives, passing RIRs, and a quorum met for issuance (e.g., 2/3
// or 3/3) in each validation audit log event, per BRs Section 5.4.1,
// Requirement 2.8.
//
// This feature flag also causes CAA checks to happen after all remote VAs
// have passed DCV.
EnforceMPIC bool

// UnsplitIssuance causes the RA to make a single call to the CA for issuance,
// calling the new `IssueCertificate` instead of the old `IssuePrecertficate` /
// `IssueCertificateForPrecertificate` pair.
Expand Down
56 changes: 19 additions & 37 deletions ra/ra.go
Original file line number Diff line number Diff line change
Expand Up @@ -855,19 +855,11 @@ func (ra *RegistrationAuthorityImpl) recheckCAA(ctx context.Context, authzs []*c
}
var resp *vapb.IsCAAValidResponse
var err error
if !features.Get().EnforceMPIC {
resp, err = ra.VA.IsCAAValid(ctx, &vapb.IsCAAValidRequest{
Domain: name,
ValidationMethod: method,
AccountURIID: authz.RegistrationID,
})
} else {
resp, err = ra.VA.DoCAA(ctx, &vapb.IsCAAValidRequest{
Domain: name,
ValidationMethod: method,
AccountURIID: authz.RegistrationID,
})
}
resp, err = ra.VA.DoCAA(ctx, &vapb.IsCAAValidRequest{
Domain: name,
ValidationMethod: method,
AccountURIID: authz.RegistrationID,
})
if err != nil {
ra.log.AuditErrf("Rechecking CAA: %s", err)
err = berrors.InternalServerError(
Expand Down Expand Up @@ -1535,33 +1527,23 @@ func (ra *RegistrationAuthorityImpl) resetAccountPausingLimit(ctx context.Contex
}
}

// doDCVAndCAA performs DCV and CAA checks. When EnforceMPIC is enabled, the
// checks are executed sequentially: DCV is performed first and CAA is only
// checked if DCV is successful. Validation records from the DCV check are
// returned even if the CAA check fails. When EnforceMPIC is disabled, DCV and
// CAA checks are performed in the same request.
// doDCVAndCAA performs DCV and CAA checks sequentially: DCV is performed first
// and CAA is only checked if DCV is successful. Validation records from the DCV
// check are returned even if the CAA check fails.
func (ra *RegistrationAuthorityImpl) checkDCVAndCAA(ctx context.Context, dcvReq *vapb.PerformValidationRequest, caaReq *vapb.IsCAAValidRequest) (*corepb.ProblemDetails, []*corepb.ValidationRecord, error) {
if !features.Get().EnforceMPIC {
performValidationRes, err := ra.VA.PerformValidation(ctx, dcvReq)
if err != nil {
return nil, nil, err
}
return performValidationRes.Problem, performValidationRes.Records, nil
} else {
doDCVRes, err := ra.VA.DoDCV(ctx, dcvReq)
if err != nil {
return nil, nil, err
}
if doDCVRes.Problem != nil {
return doDCVRes.Problem, doDCVRes.Records, nil
}
doDCVRes, err := ra.VA.DoDCV(ctx, dcvReq)
if err != nil {
return nil, nil, err
}
if doDCVRes.Problem != nil {
return doDCVRes.Problem, doDCVRes.Records, nil
}

doCAAResp, err := ra.VA.DoCAA(ctx, caaReq)
if err != nil {
return nil, nil, err
}
return doCAAResp.Problem, doDCVRes.Records, nil
doCAAResp, err := ra.VA.DoCAA(ctx, caaReq)
if err != nil {
return nil, nil, err
}
return doCAAResp.Problem, doDCVRes.Records, nil
}

// PerformValidation initiates validation for a specific challenge associated
Expand Down
3 changes: 1 addition & 2 deletions test/config-next/ra.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,7 @@
"AsyncFinalize": true,
"AutomaticallyPauseZombieClients": true,
"NoPendingAuthzReuse": true,
"UnsplitIssuance": true,
"EnforceMPIC": true
"UnsplitIssuance": true
},
"ctLogs": {
"stagger": "500ms",
Expand Down
1 change: 0 additions & 1 deletion test/config-next/va.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
}
},
"features": {
"EnforceMultiCAA": true,
"DOH": true
},
"remoteVAs": [
Expand Down
5 changes: 5 additions & 0 deletions test/config/remoteva-a.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
"va.boulder"
]
},
"va.CAA": {
"clientNames": [
"va.boulder"
]
},
"grpc.health.v1.Health": {
"clientNames": [
"health-checker.boulder"
Expand Down
5 changes: 5 additions & 0 deletions test/config/remoteva-b.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
"va.boulder"
]
},
"va.CAA": {
"clientNames": [
"va.boulder"
]
},
"grpc.health.v1.Health": {
"clientNames": [
"health-checker.boulder"
Expand Down
5 changes: 5 additions & 0 deletions test/config/remoteva-c.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
"va.boulder"
]
},
"va.CAA": {
"clientNames": [
"va.boulder"
]
},
"grpc.health.v1.Health": {
"clientNames": [
"health-checker.boulder"
Expand Down
30 changes: 20 additions & 10 deletions va/caa.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/letsencrypt/boulder/core"
corepb "github.com/letsencrypt/boulder/core/proto"
berrors "github.com/letsencrypt/boulder/errors"
"github.com/letsencrypt/boulder/features"
"github.com/letsencrypt/boulder/identifier"
"github.com/letsencrypt/boulder/probs"
vapb "github.com/letsencrypt/boulder/va/proto"
Expand All @@ -27,15 +26,20 @@ type caaParams struct {
validationMethod core.AcmeChallenge
}

// IsCAAValid checks requested CAA records from a VA, and recursively any RVAs
// configured in the VA. It returns a response or an error.
func (va *ValidationAuthorityImpl) IsCAAValid(ctx context.Context, req *vapb.IsCAAValidRequest) (*vapb.IsCAAValidResponse, error) {
// DoCAA conducts a CAA check for the specified dnsName. When invoked on the
// primary Validation Authority (VA) and the local check succeeds, it also
// performs CAA checks using the configured remote VAs. Failed checks are
// indicated by a non-nil Problems in the returned ValidationResult. DoCAA
// returns error only for internal logic errors (and the client may receive
// errors from gRPC in the event of a communication problem). This method
// implements the CAA portion of Multi-Perspective Issuance Corroboration as
// defined in BRs Sections 3.2.2.9 and 5.4.1.
func (va *ValidationAuthorityImpl) DoCAA(ctx context.Context, req *vapb.IsCAAValidRequest) (*vapb.IsCAAValidResponse, error) {
if core.IsAnyNilOrZero(req.Domain, req.ValidationMethod, req.AccountURIID) {
return nil, berrors.InternalServerError("incomplete IsCAAValid request")
}
logEvent := verificationRequestEvent{
// TODO(#7061) Plumb req.Authz.Id as "AuthzID:" through from the RA to
// correlate which authz triggered this request.
logEvent := validationLogEvent{
AuthzID: req.AuthzID,
Requester: req.AccountURIID,
Identifier: req.Domain,
}
Expand All @@ -51,7 +55,11 @@ func (va *ValidationAuthorityImpl) IsCAAValid(ctx context.Context, req *vapb.IsC
validationMethod: challType,
}

// Initialize variables and a deferred function to handle check latency
// metrics, log check errors, and log an MPIC summary. Avoid using := to
// redeclare `prob`, `localLatency`, or `summary` below this point.
var prob *probs.ProblemDetails
var summary *mpicSummary
var internalErr error
var localLatency time.Duration
start := va.clk.Now()
Expand All @@ -72,6 +80,7 @@ func (va *ValidationAuthorityImpl) IsCAAValid(ctx context.Context, req *vapb.IsC
if va.isPrimaryVA() {
// Observe total check latency (primary+remote).
va.observeLatency(opCAA, allPerspectives, string(challType), probType, outcome, va.clk.Since(start))
logEvent.Summary = summary
}
// Log the total check latency.
logEvent.Latency = va.clk.Since(start).Round(time.Millisecond).Seconds()
Expand All @@ -90,15 +99,16 @@ func (va *ValidationAuthorityImpl) IsCAAValid(ctx context.Context, req *vapb.IsC
prob.Detail = fmt.Sprintf("While processing CAA for %s: %s", req.Domain, prob.Detail)
}

if features.Get().EnforceMultiCAA {
if va.isPrimaryVA() {
op := func(ctx context.Context, remoteva RemoteVA, req proto.Message) (remoteResult, error) {
checkRequest, ok := req.(*vapb.IsCAAValidRequest)
if !ok {
return nil, fmt.Errorf("got type %T, want *vapb.IsCAAValidRequest", req)
}
return remoteva.IsCAAValid(ctx, checkRequest)
return remoteva.DoCAA(ctx, checkRequest)
}
remoteProb := va.performRemoteOperation(ctx, op, req)
var remoteProb *probs.ProblemDetails
summary, remoteProb = va.doRemoteOperation(ctx, op, req)
// If the remote result was a non-nil problem then fail the CAA check
if remoteProb != nil {
prob = remoteProb
Expand Down
Loading

0 comments on commit c4f390d

Please sign in to comment.