Skip to content

Commit

Permalink
RVA: Recheck CAA records (#7221)
Browse files Browse the repository at this point in the history
Previously, `va.IsCAAValid` would only check CAA records from the
primary VA during initial domain control validation, completely ignoring
any configured RVAs. The upcoming
[MPIC](ryancdickson/staging#8) ballot will
require that it be done from multiple perspectives. With the currently
deployed [Multi-Perspective
Validation](https://letsencrypt.org/2020/02/19/multi-perspective-validation.html)
in staging and production, this change brings us in line with the
[proposed phase
3](https://github.com/ryancdickson/staging/pull/8/files#r1368708684).
This change reuses the existing
[MaxRemoteValidationFailures](https://github.com/letsencrypt/boulder/blob/21fc1912732274d3fc92f3660a8f45d25b2a702c/cmd/boulder-va/main.go#L35)
variable for the required non-corroboration quorum.
> Phase 3: June 15, 2025 - December 14, 2025 ("CAs MUST implement MPIC
in blocking mode*"):
>
>    MUST implement MPIC? Yes
> Required quorum?: Minimally, 2 remote perspectives must be used. If
using less than 6 remote perspectives, 1 non-corroboration is allowed.
If using 6 or more remote perspectives, 2 non-corroborations are
allowed.
>    MUST block issuance if quorum is not met: Yes.
> Geographic diversity requirements?: Perspectives must be 500km from 1)
the primary perspective and 2) all other perspectives used in the
quorum.
>
> * Note: "Blocking Mode" is a nickname. As opposed to "monitoring mode"
(described in the last milestone), CAs MUST NOT issue a certificate if
quorum requirements are not met from this point forward.

Adds new VA feature flags: 
* `EnforceMultiCAA` instructs a primary VA to command each of its
configured RVAs to perform a CAA recheck.
* `MultiCAAFullResults` causes the primary VA to block waiting for all
RVA CAA recheck results to arrive.


Renamed `va.logRemoteValidationDifferentials` to
`va.logRemoteDifferentials` because it can handle initial domain control
validations and CAA rechecking with minimal editing.

Part of #7061
  • Loading branch information
pgporada authored Jan 25, 2024
1 parent f8a6d3b commit 03152aa
Show file tree
Hide file tree
Showing 12 changed files with 892 additions and 197 deletions.
7 changes: 5 additions & 2 deletions cmd/boulder-va/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,11 @@ func main() {
remotes = append(
remotes,
va.RemoteVA{
VAClient: vapb.NewVAClient(vaConn),
Address: rva.ServerAddress,
RemoteClients: va.RemoteClients{
VAClient: vapb.NewVAClient(vaConn),
CAAClient: vapb.NewCAAClient(vaConn),
},
Address: rva.ServerAddress,
},
)
}
Expand Down
12 changes: 12 additions & 0 deletions features/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ 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. The primary VA will
// return an early decision if MultiCAAFullResults is false.
EnforceMultiCAA bool

// MultiCAAFullResults will cause the main VA to block and wait for all of
// the remote VA CAA recheck results instead of returning early if the
// number of failures is greater than the configured
// maxRemoteValidationFailures. Only used when EnforceMultiCAA is true.
MultiCAAFullResults bool
}

var fMu = new(sync.RWMutex)
Expand Down
2 changes: 1 addition & 1 deletion ra/ra.go
Original file line number Diff line number Diff line change
Expand Up @@ -888,7 +888,7 @@ func (ra *RegistrationAuthorityImpl) checkAuthorizationsCAA(
return nil
}

// recheckCAA accepts a list of of names that need to have their CAA records
// recheckCAA accepts a list of names that need to have their CAA records
// rechecked because their associated authorizations are sufficiently old and
// performs the CAA checks required for each. If any of the rechecks fail an
// error is returned.
Expand Down
24 changes: 13 additions & 11 deletions ra/ra_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,14 @@ func numAuthorizations(o *corepb.Order) int {
}

type DummyValidationAuthority struct {
request chan *vapb.PerformValidationRequest
ResultError error
ResultReturn *vapb.ValidationResult
performValidationRequest chan *vapb.PerformValidationRequest
PerformValidationRequestResultError error
PerformValidationRequestResultReturn *vapb.ValidationResult
}

func (dva *DummyValidationAuthority) PerformValidation(ctx context.Context, req *vapb.PerformValidationRequest, _ ...grpc.CallOption) (*vapb.ValidationResult, error) {
dva.request <- req
return dva.ResultReturn, dva.ResultError
dva.performValidationRequest <- req
return dva.PerformValidationRequestResultReturn, dva.PerformValidationRequestResultError
}

var (
Expand Down Expand Up @@ -324,7 +324,9 @@ func initAuthorities(t *testing.T) (*DummyValidationAuthority, sapb.StorageAutho

saDBCleanUp := test.ResetBoulderTestDatabase(t)

va := &DummyValidationAuthority{request: make(chan *vapb.PerformValidationRequest, 1)}
va := &DummyValidationAuthority{
performValidationRequest: make(chan *vapb.PerformValidationRequest, 1),
}

pa, err := policy.New(map[core.AcmeChallenge]bool{
core.ChallengeTypeHTTP01: true,
Expand Down Expand Up @@ -813,7 +815,7 @@ func TestPerformValidationAlreadyValid(t *testing.T) {
authzPB, err := bgrpc.AuthzToPB(authz)
test.AssertNotError(t, err, "bgrpc.AuthzToPB failed")

va.ResultReturn = &vapb.ValidationResult{
va.PerformValidationRequestResultReturn = &vapb.ValidationResult{
Records: []*corepb.ValidationRecord{
{
AddressUsed: []byte("192.168.0.1"),
Expand Down Expand Up @@ -842,7 +844,7 @@ func TestPerformValidationSuccess(t *testing.T) {
// We know this is OK because of TestNewAuthorization
authzPB := createPendingAuthorization(t, sa, Identifier, fc.Now().Add(12*time.Hour))

va.ResultReturn = &vapb.ValidationResult{
va.PerformValidationRequestResultReturn = &vapb.ValidationResult{
Records: []*corepb.ValidationRecord{
{
AddressUsed: []byte("192.168.0.1"),
Expand All @@ -864,7 +866,7 @@ func TestPerformValidationSuccess(t *testing.T) {

var vaRequest *vapb.PerformValidationRequest
select {
case r := <-va.request:
case r := <-va.performValidationRequest:
vaRequest = r
case <-time.After(time.Second):
t.Fatal("Timed out waiting for DummyValidationAuthority.PerformValidation to complete")
Expand Down Expand Up @@ -903,7 +905,7 @@ func TestPerformValidationVAError(t *testing.T) {

authzPB := createPendingAuthorization(t, sa, Identifier, fc.Now().Add(12*time.Hour))

va.ResultError = fmt.Errorf("Something went wrong")
va.PerformValidationRequestResultError = fmt.Errorf("Something went wrong")

challIdx := dnsChallIdx(t, authzPB.Challenges)
authzPB, err := ra.PerformValidation(ctx, &rapb.PerformValidationRequest{
Expand All @@ -915,7 +917,7 @@ func TestPerformValidationVAError(t *testing.T) {

var vaRequest *vapb.PerformValidationRequest
select {
case r := <-va.request:
case r := <-va.performValidationRequest:
vaRequest = r
case <-time.After(time.Second):
t.Fatal("Timed out waiting for DummyValidationAuthority.PerformValidation to complete")
Expand Down
2 changes: 2 additions & 0 deletions test/config-next/va.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
"EnforceMultiVA": true,
"MultiVAFullResults": true,
"CAAAfterValidation": true,
"EnforceMultiCAA": true,
"MultiCAAFullResults": true,
"DOH": true
},
"remoteVAs": [
Expand Down
Loading

0 comments on commit 03152aa

Please sign in to comment.