Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

va/ra: Deprecate EnforceMultiCAA and EnforceMPIC #8025

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions cmd/boulder-va/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ type RemoteVAGRPCClientConfig struct {
// Requirement 2.7 ("Multi-Perspective Issuance Corroboration attempts
// from each Network Perspective"). It should uniquely identify a group
// of RVAs deployed in the same datacenter.
//
// TODO(#7615): Make mandatory.
Perspective string `validate:"omitempty"`
Perspective string `validate:"required"`

// RIR indicates the Regional Internet Registry where this RVA is
// located. This field is used to identify the RIR region from which a
Expand All @@ -44,9 +42,7 @@ type RemoteVAGRPCClientConfig struct {
// - APNIC
// - LACNIC
// - AFRINIC
//
// TODO(#7615): Make mandatory.
RIR string `validate:"omitempty,oneof=ARIN RIPE APNIC LACNIC AFRINIC"`
RIR string `validate:"required,oneof=ARIN RIPE APNIC LACNIC AFRINIC"`
}

type Config struct {
Expand Down
8 changes: 2 additions & 6 deletions cmd/remoteva/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ type Config struct {
// Requirement 2.7 ("Multi-Perspective Issuance Corroboration attempts
// from each Network Perspective"). It should uniquely identify a group
// of RVAs deployed in the same datacenter.
//
// TODO(#7615): Make mandatory.
Perspective string `omitempty:"omitempty"`
Perspective string `omitempty:"required"`

// RIR indicates the Regional Internet Registry where this RVA is
// located. This field is used to identify the RIR region from which a
Expand All @@ -39,9 +37,7 @@ type Config struct {
// - APNIC
// - LACNIC
// - AFRINIC
//
// TODO(#7615): Make mandatory.
RIR string `validate:"omitempty,oneof=ARIN RIPE APNIC LACNIC AFRINIC"`
RIR string `validate:"required,oneof=ARIN RIPE APNIC LACNIC AFRINIC"`

// SkipGRPCClientCertVerification, when disabled as it should typically
// be, will cause the remoteva server (which receives gRPCs from a
Expand Down
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
Loading