Skip to content

Commit

Permalink
RA/VA: Add MPIC compliant DCV and CAA checks
Browse files Browse the repository at this point in the history
  • Loading branch information
beautifulentropy committed Dec 5, 2024
1 parent c448c12 commit 4934d07
Show file tree
Hide file tree
Showing 16 changed files with 1,056 additions and 405 deletions.
6 changes: 5 additions & 1 deletion cmd/boulder-ra/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/letsencrypt/boulder/ratelimits"
bredis "github.com/letsencrypt/boulder/redis"
sapb "github.com/letsencrypt/boulder/sa/proto"
"github.com/letsencrypt/boulder/va"
vapb "github.com/letsencrypt/boulder/va/proto"
)

Expand Down Expand Up @@ -301,7 +302,10 @@ func main() {
cmd.FailOnError(policyErr, "Couldn't load rate limit policies file")
rai.PA = pa

rai.VA = vac
rai.VA = va.RemoteClients{
VAClient: vac,
CAAClient: caaClient,
}
rai.CA = cac
rai.OCSP = ocspc
rai.SA = sac
Expand Down
4 changes: 4 additions & 0 deletions features/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ type Config struct {
// and pause issuance for each (account, hostname) pair that repeatedly
// fails validation.
AutomaticallyPauseZombieClients bool

// SeparateDCVAndCAAChecks causes the VA to perform DCV checks and CAA checks
// in separate steps, using separate VA methods DoDCV and DoCAA.
SeparateDCVAndCAAChecks bool
}

var fMu = new(sync.RWMutex)
Expand Down
84 changes: 64 additions & 20 deletions ra/ra.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import (
"github.com/letsencrypt/boulder/ratelimits"
"github.com/letsencrypt/boulder/revocation"
sapb "github.com/letsencrypt/boulder/sa/proto"
"github.com/letsencrypt/boulder/va"
vapb "github.com/letsencrypt/boulder/va/proto"

"github.com/letsencrypt/boulder/web"
Expand All @@ -74,6 +75,11 @@ type caaChecker interface {
in *vapb.IsCAAValidRequest,
opts ...grpc.CallOption,
) (*vapb.IsCAAValidResponse, error)
DoCAA(
ctx context.Context,
in *vapb.IsCAAValidRequest,
opts ...grpc.CallOption,
) (*vapb.IsCAAValidResponse, error)
}

// RegistrationAuthorityImpl defines an RA.
Expand All @@ -84,7 +90,7 @@ type RegistrationAuthorityImpl struct {
rapb.UnsafeRegistrationAuthorityServer
CA capb.CertificateAuthorityClient
OCSP capb.OCSPGeneratorClient
VA vapb.VAClient
VA va.RemoteClients
SA sapb.StorageAuthorityClient
PA core.PolicyAuthority
publisher pubpb.PublisherClient
Expand Down Expand Up @@ -849,12 +855,21 @@ func (ra *RegistrationAuthorityImpl) recheckCAA(ctx context.Context, authzs []*c
}
return
}

resp, err := ra.caa.IsCAAValid(ctx, &vapb.IsCAAValidRequest{
Domain: name,
ValidationMethod: method,
AccountURIID: authz.RegistrationID,
})
var resp *vapb.IsCAAValidResponse
var err error
if !features.Get().SeparateDCVAndCAAChecks {
resp, err = ra.caa.IsCAAValid(ctx, &vapb.IsCAAValidRequest{
Domain: name,
ValidationMethod: method,
AccountURIID: authz.RegistrationID,
})
} else {
resp, err = ra.caa.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 @@ -1832,6 +1847,30 @@ func (ra *RegistrationAuthorityImpl) resetAccountPausingLimit(ctx context.Contex
}
}

func (ra *RegistrationAuthorityImpl) checkDCVAndCAA(ctx context.Context, dcvReq *vapb.PerformValidationRequest, caaReq *vapb.IsCAAValidRequest) (*corepb.ProblemDetails, []*corepb.ValidationRecord, error) {
if !features.Get().SeparateDCVAndCAAChecks {
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
}

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

// PerformValidation initiates validation for a specific challenge associated
// with the given base authorization. The authorization and challenge are
// updated based on the results.
Expand Down Expand Up @@ -1916,32 +1955,37 @@ func (ra *RegistrationAuthorityImpl) PerformValidation(
copy(challenges, authz.Challenges)
authz.Challenges = challenges
chall, _ := bgrpc.ChallengeToPB(authz.Challenges[challIndex])
req := vapb.PerformValidationRequest{
DnsName: authz.Identifier.Value,
Challenge: chall,
Authz: &vapb.AuthzMeta{
Id: authz.ID,
RegID: authz.RegistrationID,
checkProb, checkRecords, err := ra.checkDCVAndCAA(
vaCtx,
&vapb.PerformValidationRequest{
DnsName: authz.Identifier.Value,
Challenge: chall,
Authz: &vapb.AuthzMeta{Id: authz.ID, RegID: authz.RegistrationID},
ExpectedKeyAuthorization: expectedKeyAuthorization,
},
ExpectedKeyAuthorization: expectedKeyAuthorization,
}
res, err := ra.VA.PerformValidation(vaCtx, &req)
&vapb.IsCAAValidRequest{
Domain: authz.Identifier.Value,
ValidationMethod: chall.Type,
AccountURIID: authz.RegistrationID,
AuthzID: authz.ID,
},
)
challenge := &authz.Challenges[challIndex]
var prob *probs.ProblemDetails
if err != nil {
prob = probs.ServerInternal("Could not communicate with VA")
ra.log.AuditErrf("Could not communicate with VA: %s", err)
} else {
if res.Problem != nil {
prob, err = bgrpc.PBToProblemDetails(res.Problem)
if checkProb != nil {
prob, err = bgrpc.PBToProblemDetails(checkProb)
if err != nil {
prob = probs.ServerInternal("Could not communicate with VA")
ra.log.AuditErrf("Could not communicate with VA: %s", err)
}
}
// Save the updated records
records := make([]core.ValidationRecord, len(res.Records))
for i, r := range res.Records {
records := make([]core.ValidationRecord, len(checkRecords))
for i, r := range checkRecords {
records[i], err = bgrpc.PBToValidationRecord(r)
if err != nil {
prob = probs.ServerInternal("Records for validation corrupt")
Expand Down
Loading

0 comments on commit 4934d07

Please sign in to comment.