Deprecate EnforceMultiVA and MultiVAFullResults feature flags (#7520)
These flags have been true and false, respectively, for years. We do not expect to change them at any time in the future, and their continued existence makes certain parts of the VA code significantly more complex. Remove all references to them, preserving behavior in the "enforce, but not full results" configuration. IN-10358 tracks the corresponding config changes
This commit is contained in:
parent
f84050a20e
commit
c3c278a1a2
|
@ -37,19 +37,12 @@ and
|
||||||
[`test/config-next/remoteva-b.json`](https://github.com/letsencrypt/boulder/blob/5c27eadb1db0605f380e41c8bd444a7f4ffe3c08/test/config-next/remoteva-b.json)
|
[`test/config-next/remoteva-b.json`](https://github.com/letsencrypt/boulder/blob/5c27eadb1db0605f380e41c8bd444a7f4ffe3c08/test/config-next/remoteva-b.json)
|
||||||
as their config files.
|
as their config files.
|
||||||
|
|
||||||
There are two feature flags that control whether multi-VA takes effect:
|
We require that almost all remote validation requests succeed; the exact number
|
||||||
MultiVAFullResults and EnforceMultiVA. If MultiVAFullResults is enabled
|
is controlled by the VA's `maxRemoteFailures` config variable. If the number of
|
||||||
then each primary validation will also send out remote validation requests, and
|
failing remote VAs exceeds that threshold, validation is terminated. If the
|
||||||
wait for all the results to come in, so we can log the results for analysis. If
|
number of successful remote VAs is high enough that it would be impossible for
|
||||||
EnforceMultiVA is enabled, we require that almost all remote validation requests
|
the outstanding remote VAs to exceed that threshold, validation immediately
|
||||||
succeed. The primary VA's "maxRemoteValidationFailures" config field specifies
|
succeeds.
|
||||||
how many remote VAs can fail before the primary VA considers overall validation
|
|
||||||
a failure. It should be strictly less than the number of remote VAs.
|
|
||||||
|
|
||||||
Validation is also controlled by the "multiVAPolicyFile" config field on the
|
|
||||||
primary VA. This specifies a file that can contain temporary overrides for
|
|
||||||
domains or accounts that fail under multi-va. Over time those temporary
|
|
||||||
overrides will be removed.
|
|
||||||
|
|
||||||
There are some integration tests that test this end to end. The most relevant is
|
There are some integration tests that test this end to end. The most relevant is
|
||||||
probably
|
probably
|
||||||
|
|
|
@ -20,13 +20,8 @@ type Config struct {
|
||||||
CAAAfterValidation bool
|
CAAAfterValidation bool
|
||||||
AllowNoCommonName bool
|
AllowNoCommonName bool
|
||||||
SHA256SubjectKeyIdentifier bool
|
SHA256SubjectKeyIdentifier bool
|
||||||
|
EnforceMultiVA bool
|
||||||
// EnforceMultiVA causes the VA to block on remote VA PerformValidation
|
MultiVAFullResults bool
|
||||||
// requests in order to make a valid/invalid decision with the results.
|
|
||||||
EnforceMultiVA bool
|
|
||||||
// MultiVAFullResults will cause the main VA to wait for all of the remote VA
|
|
||||||
// results, not just the threshold required to make a decision.
|
|
||||||
MultiVAFullResults bool
|
|
||||||
|
|
||||||
// ECDSAForAll enables all accounts, regardless of their presence in the CA's
|
// ECDSAForAll enables all accounts, regardless of their presence in the CA's
|
||||||
// ecdsaAllowedAccounts config value, to get issuance from ECDSA issuers.
|
// ecdsaAllowedAccounts config value, to get issuance from ECDSA issuers.
|
||||||
|
|
|
@ -38,8 +38,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {
|
||||||
"EnforceMultiVA": true,
|
|
||||||
"MultiVAFullResults": true,
|
|
||||||
"EnforceMultiCAA": true,
|
"EnforceMultiCAA": true,
|
||||||
"MultiCAAFullResults": true,
|
"MultiCAAFullResults": true,
|
||||||
"DOH": true
|
"DOH": true
|
||||||
|
|
|
@ -38,10 +38,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"features": {
|
"features": {},
|
||||||
"EnforceMultiVA": true,
|
|
||||||
"MultiVAFullResults": true
|
|
||||||
},
|
|
||||||
"remoteVAs": [
|
"remoteVAs": [
|
||||||
{
|
{
|
||||||
"serverAddress": "rva1.service.consul:9397",
|
"serverAddress": "rva1.service.consul:9397",
|
||||||
|
|
|
@ -212,7 +212,7 @@ func (va *ValidationAuthorityImpl) processRemoteCAAResults(
|
||||||
// If we are using `features.MultiCAAFullResults` then we haven't returned
|
// If we are using `features.MultiCAAFullResults` then we haven't returned
|
||||||
// early and can now log the differential between what the primary VA saw and
|
// early and can now log the differential between what the primary VA saw and
|
||||||
// what all of the remote VAs saw.
|
// what all of the remote VAs saw.
|
||||||
va.logRemoteDifferentials(
|
va.logRemoteResults(
|
||||||
domain,
|
domain,
|
||||||
acctID,
|
acctID,
|
||||||
challengeType,
|
challengeType,
|
||||||
|
|
186
va/va.go
186
va/va.go
|
@ -23,7 +23,6 @@ import (
|
||||||
"github.com/letsencrypt/boulder/canceled"
|
"github.com/letsencrypt/boulder/canceled"
|
||||||
"github.com/letsencrypt/boulder/core"
|
"github.com/letsencrypt/boulder/core"
|
||||||
berrors "github.com/letsencrypt/boulder/errors"
|
berrors "github.com/letsencrypt/boulder/errors"
|
||||||
"github.com/letsencrypt/boulder/features"
|
|
||||||
bgrpc "github.com/letsencrypt/boulder/grpc"
|
bgrpc "github.com/letsencrypt/boulder/grpc"
|
||||||
"github.com/letsencrypt/boulder/identifier"
|
"github.com/letsencrypt/boulder/identifier"
|
||||||
blog "github.com/letsencrypt/boulder/log"
|
blog "github.com/letsencrypt/boulder/log"
|
||||||
|
@ -82,21 +81,20 @@ type RemoteVA struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type vaMetrics struct {
|
type vaMetrics struct {
|
||||||
validationTime *prometheus.HistogramVec
|
validationTime *prometheus.HistogramVec
|
||||||
localValidationTime *prometheus.HistogramVec
|
localValidationTime *prometheus.HistogramVec
|
||||||
remoteValidationTime *prometheus.HistogramVec
|
remoteValidationTime *prometheus.HistogramVec
|
||||||
remoteValidationFailures prometheus.Counter
|
remoteValidationFailures prometheus.Counter
|
||||||
prospectiveRemoteValidationFailures prometheus.Counter
|
caaCheckTime *prometheus.HistogramVec
|
||||||
caaCheckTime *prometheus.HistogramVec
|
localCAACheckTime *prometheus.HistogramVec
|
||||||
localCAACheckTime *prometheus.HistogramVec
|
remoteCAACheckTime *prometheus.HistogramVec
|
||||||
remoteCAACheckTime *prometheus.HistogramVec
|
remoteCAACheckFailures prometheus.Counter
|
||||||
remoteCAACheckFailures prometheus.Counter
|
prospectiveRemoteCAACheckFailures prometheus.Counter
|
||||||
prospectiveRemoteCAACheckFailures prometheus.Counter
|
tlsALPNOIDCounter *prometheus.CounterVec
|
||||||
tlsALPNOIDCounter *prometheus.CounterVec
|
http01Fallbacks prometheus.Counter
|
||||||
http01Fallbacks prometheus.Counter
|
http01Redirects prometheus.Counter
|
||||||
http01Redirects prometheus.Counter
|
caaCounter *prometheus.CounterVec
|
||||||
caaCounter *prometheus.CounterVec
|
ipv4FallbackCounter prometheus.Counter
|
||||||
ipv4FallbackCounter prometheus.Counter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initMetrics(stats prometheus.Registerer) *vaMetrics {
|
func initMetrics(stats prometheus.Registerer) *vaMetrics {
|
||||||
|
@ -130,12 +128,6 @@ func initMetrics(stats prometheus.Registerer) *vaMetrics {
|
||||||
Help: "Number of validations failed due to remote VAs returning failure when consensus is enforced",
|
Help: "Number of validations failed due to remote VAs returning failure when consensus is enforced",
|
||||||
})
|
})
|
||||||
stats.MustRegister(remoteValidationFailures)
|
stats.MustRegister(remoteValidationFailures)
|
||||||
prospectiveRemoteValidationFailures := prometheus.NewCounter(
|
|
||||||
prometheus.CounterOpts{
|
|
||||||
Name: "prospective_remote_validation_failures",
|
|
||||||
Help: "Number of validations that would have failed due to remote VAs returning failure if consesus were enforced",
|
|
||||||
})
|
|
||||||
stats.MustRegister(prospectiveRemoteValidationFailures)
|
|
||||||
caaCheckTime := prometheus.NewHistogramVec(
|
caaCheckTime := prometheus.NewHistogramVec(
|
||||||
prometheus.HistogramOpts{
|
prometheus.HistogramOpts{
|
||||||
Name: "caa_check_time",
|
Name: "caa_check_time",
|
||||||
|
@ -204,21 +196,20 @@ func initMetrics(stats prometheus.Registerer) *vaMetrics {
|
||||||
stats.MustRegister(ipv4FallbackCounter)
|
stats.MustRegister(ipv4FallbackCounter)
|
||||||
|
|
||||||
return &vaMetrics{
|
return &vaMetrics{
|
||||||
validationTime: validationTime,
|
validationTime: validationTime,
|
||||||
remoteValidationTime: remoteValidationTime,
|
remoteValidationTime: remoteValidationTime,
|
||||||
localValidationTime: localValidationTime,
|
localValidationTime: localValidationTime,
|
||||||
remoteValidationFailures: remoteValidationFailures,
|
remoteValidationFailures: remoteValidationFailures,
|
||||||
prospectiveRemoteValidationFailures: prospectiveRemoteValidationFailures,
|
caaCheckTime: caaCheckTime,
|
||||||
caaCheckTime: caaCheckTime,
|
localCAACheckTime: localCAACheckTime,
|
||||||
localCAACheckTime: localCAACheckTime,
|
remoteCAACheckTime: remoteCAACheckTime,
|
||||||
remoteCAACheckTime: remoteCAACheckTime,
|
remoteCAACheckFailures: remoteCAACheckFailures,
|
||||||
remoteCAACheckFailures: remoteCAACheckFailures,
|
prospectiveRemoteCAACheckFailures: prospectiveRemoteCAACheckFailures,
|
||||||
prospectiveRemoteCAACheckFailures: prospectiveRemoteCAACheckFailures,
|
tlsALPNOIDCounter: tlsALPNOIDCounter,
|
||||||
tlsALPNOIDCounter: tlsALPNOIDCounter,
|
http01Fallbacks: http01Fallbacks,
|
||||||
http01Fallbacks: http01Fallbacks,
|
http01Redirects: http01Redirects,
|
||||||
http01Redirects: http01Redirects,
|
caaCounter: caaCounter,
|
||||||
caaCounter: caaCounter,
|
ipv4FallbackCounter: ipv4FallbackCounter,
|
||||||
ipv4FallbackCounter: ipv4FallbackCounter,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -528,23 +519,9 @@ func (va *ValidationAuthorityImpl) performRemoteValidation(
|
||||||
// processRemoteValidationResults evaluates a primary VA result, and a channel
|
// processRemoteValidationResults evaluates a primary VA result, and a channel
|
||||||
// of remote VA problems to produce a single overall validation result based on
|
// of remote VA problems to produce a single overall validation result based on
|
||||||
// configured feature flags. The overall result is calculated based on the VA's
|
// configured feature flags. The overall result is calculated based on the VA's
|
||||||
// configured `maxRemoteFailures` value.
|
// configured `maxRemoteFailures` value, and the function returns as soon as
|
||||||
//
|
// that threshold has been exceeded or cannot possibly be exceeded.
|
||||||
// If the `MultiVAFullResults` feature is enabled then
|
|
||||||
// `processRemoteValidationResults` will expect to read a result from the
|
|
||||||
// `remoteErrors` channel for each VA and will not produce an overall result
|
|
||||||
// until all remote VAs have responded. In this case `logRemoteDifferentials`
|
|
||||||
// will also be called to describe the differential between the primary and all
|
|
||||||
// of the remote VAs.
|
|
||||||
//
|
|
||||||
// If the `MultiVAFullResults` feature flag is not enabled then
|
|
||||||
// `processRemoteValidationResults` will potentially return before all remote
|
|
||||||
// VAs have had a chance to respond. This happens if the success or failure
|
|
||||||
// threshold is met. This doesn't allow for logging the differential between the
|
|
||||||
// primary and remote VAs but is more performant.
|
|
||||||
func (va *ValidationAuthorityImpl) processRemoteValidationResults(
|
func (va *ValidationAuthorityImpl) processRemoteValidationResults(
|
||||||
domain string,
|
|
||||||
acctID int64,
|
|
||||||
challengeType string,
|
challengeType string,
|
||||||
remoteResultsChan <-chan *remoteVAResult) *probs.ProblemDetails {
|
remoteResultsChan <-chan *remoteVAResult) *probs.ProblemDetails {
|
||||||
|
|
||||||
|
@ -575,60 +552,36 @@ func (va *ValidationAuthorityImpl) processRemoteValidationResults(
|
||||||
} else {
|
} else {
|
||||||
bad++
|
bad++
|
||||||
}
|
}
|
||||||
// Store the first non-nil problem to return later (if `MultiVAFullResults`
|
// Store the first non-nil problem to return later.
|
||||||
// is enabled).
|
|
||||||
if firstProb == nil && result.Problem != nil {
|
if firstProb == nil && result.Problem != nil {
|
||||||
firstProb = result.Problem
|
firstProb = result.Problem
|
||||||
}
|
}
|
||||||
// If MultiVAFullResults isn't enabled then return early whenever the
|
// Return as soon as we have enough successes or failures for a definitive result.
|
||||||
// success or failure threshold is met.
|
if good >= required {
|
||||||
if !features.Get().MultiVAFullResults {
|
state = "success"
|
||||||
if good >= required {
|
return nil
|
||||||
state = "success"
|
} else if bad > va.maxRemoteFailures {
|
||||||
return nil
|
modifiedProblem := *result.Problem
|
||||||
} else if bad > va.maxRemoteFailures {
|
modifiedProblem.Detail = "During secondary validation: " + firstProb.Detail
|
||||||
modifiedProblem := *result.Problem
|
return &modifiedProblem
|
||||||
modifiedProblem.Detail = "During secondary validation: " + firstProb.Detail
|
|
||||||
return &modifiedProblem
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we haven't returned early because of MultiVAFullResults being enabled
|
// If we somehow haven't returned early, we need to break the loop once all
|
||||||
// we need to break the loop once all of the VAs have returned a result.
|
// of the VAs have returned a result.
|
||||||
if len(remoteResults) == len(va.remoteVAs) {
|
if len(remoteResults) == len(va.remoteVAs) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If we are using `features.MultiVAFullResults` then we haven't returned
|
|
||||||
// early and can now log the differential between what the primary VA saw and
|
|
||||||
// what all of the remote VAs saw.
|
|
||||||
va.logRemoteDifferentials(
|
|
||||||
domain,
|
|
||||||
acctID,
|
|
||||||
challengeType,
|
|
||||||
remoteResults)
|
|
||||||
|
|
||||||
// Based on the threshold of good/bad return nil or a problem.
|
|
||||||
if good >= required {
|
|
||||||
state = "success"
|
|
||||||
return nil
|
|
||||||
} else if bad > va.maxRemoteFailures {
|
|
||||||
modifiedProblem := *firstProb
|
|
||||||
modifiedProblem.Detail = "During secondary validation: " + firstProb.Detail
|
|
||||||
va.metrics.prospectiveRemoteValidationFailures.Inc()
|
|
||||||
return &modifiedProblem
|
|
||||||
}
|
|
||||||
|
|
||||||
// This condition should not occur - it indicates the good/bad counts didn't
|
// This condition should not occur - it indicates the good/bad counts didn't
|
||||||
// meet either the required threshold or the maxRemoteFailures threshold.
|
// meet either the required threshold or the maxRemoteFailures threshold.
|
||||||
return probs.ServerInternal("Too few remote PerformValidation RPC results")
|
return probs.ServerInternal("Too few remote PerformValidation RPC results")
|
||||||
}
|
}
|
||||||
|
|
||||||
// logRemoteDifferentials is called by `processRemoteValidationResults` when the
|
// logRemoteResults is called by `processRemoteCAAResults` when the
|
||||||
// `MultiVAFullResults` feature flag is enabled and `processRemoteCAAResults`
|
|
||||||
// `MultiCAAFullResults` feature flag is enabled. It produces a JSON log line
|
// `MultiCAAFullResults` feature flag is enabled. It produces a JSON log line
|
||||||
// that contains the primary VA result and the results each remote VA returned.
|
// that contains the results each remote VA returned.
|
||||||
func (va *ValidationAuthorityImpl) logRemoteDifferentials(
|
func (va *ValidationAuthorityImpl) logRemoteResults(
|
||||||
domain string,
|
domain string,
|
||||||
acctID int64,
|
acctID int64,
|
||||||
challengeType string,
|
challengeType string,
|
||||||
|
@ -726,41 +679,24 @@ func (va *ValidationAuthorityImpl) PerformValidation(ctx context.Context, req *v
|
||||||
logEvent.Error = prob.Error()
|
logEvent.Error = prob.Error()
|
||||||
logEvent.InternalError = err.Error()
|
logEvent.InternalError = err.Error()
|
||||||
} else if remoteResults != nil {
|
} else if remoteResults != nil {
|
||||||
if !features.Get().EnforceMultiVA && features.Get().MultiVAFullResults {
|
remoteProb := va.processRemoteValidationResults(
|
||||||
go func() {
|
string(challenge.Type),
|
||||||
_ = va.processRemoteValidationResults(
|
remoteResults)
|
||||||
req.Domain,
|
|
||||||
req.Authz.RegID,
|
|
||||||
string(challenge.Type),
|
|
||||||
remoteResults)
|
|
||||||
}()
|
|
||||||
// Since prob was nil and we're not enforcing the results from
|
|
||||||
// `processRemoteValidationResults` set the challenge status to
|
|
||||||
// valid so the validationTime metrics increment has the correct
|
|
||||||
// result label.
|
|
||||||
challenge.Status = core.StatusValid
|
|
||||||
} else if features.Get().EnforceMultiVA {
|
|
||||||
remoteProb := va.processRemoteValidationResults(
|
|
||||||
req.Domain,
|
|
||||||
req.Authz.RegID,
|
|
||||||
string(challenge.Type),
|
|
||||||
remoteResults)
|
|
||||||
|
|
||||||
// If the remote result was a non-nil problem then fail the validation
|
// If the remote result was a non-nil problem then fail the validation
|
||||||
if remoteProb != nil {
|
if remoteProb != nil {
|
||||||
prob = remoteProb
|
prob = remoteProb
|
||||||
challenge.Status = core.StatusInvalid
|
challenge.Status = core.StatusInvalid
|
||||||
challenge.Error = remoteProb
|
challenge.Error = remoteProb
|
||||||
// We only set .Error here, not .InternalError, because the
|
// We only set .Error here, not .InternalError, because the
|
||||||
// remote VA doesn't send us the internal error. But that's ok,
|
// remote VA doesn't send us the internal error. But that's ok,
|
||||||
// it got logged at the remote VA.
|
// it got logged at the remote VA.
|
||||||
logEvent.Error = remoteProb.Error()
|
logEvent.Error = remoteProb.Error()
|
||||||
va.log.Infof("Validation failed due to remote failures: identifier=%v err=%s",
|
va.log.Infof("Validation failed due to remote failures: identifier=%v err=%s",
|
||||||
req.Domain, remoteProb)
|
req.Domain, remoteProb)
|
||||||
va.metrics.remoteValidationFailures.Inc()
|
va.metrics.remoteValidationFailures.Inc()
|
||||||
} else {
|
} else {
|
||||||
challenge.Status = core.StatusValid
|
challenge.Status = core.StatusValid
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
challenge.Status = core.StatusValid
|
challenge.Status = core.StatusValid
|
||||||
|
|
175
va/va_test.go
175
va/va_test.go
|
@ -395,20 +395,6 @@ func TestMultiVA(t *testing.T) {
|
||||||
VAClient: cancelledVA{},
|
VAClient: cancelledVA{},
|
||||||
CAAClient: cancelledVA{},
|
CAAClient: cancelledVA{},
|
||||||
}
|
}
|
||||||
enforceMultiVA := features.Config{
|
|
||||||
EnforceMultiVA: true,
|
|
||||||
}
|
|
||||||
enforceMultiVAFullResults := features.Config{
|
|
||||||
EnforceMultiVA: true,
|
|
||||||
MultiVAFullResults: true,
|
|
||||||
}
|
|
||||||
noEnforceMultiVA := features.Config{
|
|
||||||
EnforceMultiVA: false,
|
|
||||||
}
|
|
||||||
noEnforceMultiVAFullResults := features.Config{
|
|
||||||
EnforceMultiVA: false,
|
|
||||||
MultiVAFullResults: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
unauthorized := probs.Unauthorized(fmt.Sprintf(
|
unauthorized := probs.Unauthorized(fmt.Sprintf(
|
||||||
`The key authorization file from the server did not match this challenge. Expected %q (got "???")`,
|
`The key authorization file from the server did not match this challenge. Expected %q (got "???")`,
|
||||||
|
@ -420,124 +406,68 @@ func TestMultiVA(t *testing.T) {
|
||||||
Name string
|
Name string
|
||||||
RemoteVAs []RemoteVA
|
RemoteVAs []RemoteVA
|
||||||
AllowedUAs map[string]bool
|
AllowedUAs map[string]bool
|
||||||
Features features.Config
|
|
||||||
ExpectedProb *probs.ProblemDetails
|
ExpectedProb *probs.ProblemDetails
|
||||||
ExpectedLog string
|
ExpectedLog string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
// With local and both remote VAs working there should be no problem.
|
// With local and both remote VAs working there should be no problem.
|
||||||
Name: "Local and remote VAs OK, enforce multi VA",
|
Name: "Local and remote VAs OK",
|
||||||
RemoteVAs: remoteVAs,
|
RemoteVAs: remoteVAs,
|
||||||
AllowedUAs: allowedUAs,
|
AllowedUAs: allowedUAs,
|
||||||
Features: enforceMultiVA,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// Ditto if multi VA enforcement is disabled
|
|
||||||
Name: "Local and remote VAs OK, no enforce multi VA",
|
|
||||||
RemoteVAs: remoteVAs,
|
|
||||||
AllowedUAs: allowedUAs,
|
|
||||||
Features: noEnforceMultiVA,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// If the local VA fails everything should fail
|
// If the local VA fails everything should fail
|
||||||
Name: "Local VA bad, remote VAs OK, no enforce multi VA",
|
Name: "Local VA bad, remote VAs OK",
|
||||||
RemoteVAs: remoteVAs,
|
RemoteVAs: remoteVAs,
|
||||||
AllowedUAs: map[string]bool{remoteUA1: true, remoteUA2: true},
|
AllowedUAs: map[string]bool{remoteUA1: true, remoteUA2: true},
|
||||||
Features: noEnforceMultiVA,
|
|
||||||
ExpectedProb: unauthorized,
|
ExpectedProb: unauthorized,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Ditto when enforcing remote VA
|
// If a remote VA fails with an internal err it should fail
|
||||||
Name: "Local VA bad, remote VAs OK, enforce multi VA",
|
Name: "Local VA ok, remote VA internal err",
|
||||||
RemoteVAs: remoteVAs,
|
|
||||||
AllowedUAs: map[string]bool{remoteUA1: true, remoteUA2: true},
|
|
||||||
Features: enforceMultiVA,
|
|
||||||
ExpectedProb: unauthorized,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// If a remote VA fails with an internal err it should fail when enforcing multi VA
|
|
||||||
Name: "Local VA ok, remote VA internal err, enforce multi VA",
|
|
||||||
RemoteVAs: []RemoteVA{
|
RemoteVAs: []RemoteVA{
|
||||||
{remoteVA1, remoteUA1},
|
{remoteVA1, remoteUA1},
|
||||||
{brokenVA, "broken"},
|
{brokenVA, "broken"},
|
||||||
},
|
},
|
||||||
AllowedUAs: allowedUAs,
|
AllowedUAs: allowedUAs,
|
||||||
Features: enforceMultiVA,
|
|
||||||
ExpectedProb: probs.ServerInternal("During secondary validation: Remote PerformValidation RPC failed"),
|
ExpectedProb: probs.ServerInternal("During secondary validation: Remote PerformValidation RPC failed"),
|
||||||
// The real failure cause should be logged
|
// The real failure cause should be logged
|
||||||
ExpectedLog: expectedInternalErrLine,
|
ExpectedLog: expectedInternalErrLine,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
// If a remote VA fails with an internal err it should not fail when not
|
|
||||||
// enforcing multi VA
|
|
||||||
Name: "Local VA ok, remote VA internal err, no enforce multi VA",
|
|
||||||
RemoteVAs: []RemoteVA{
|
|
||||||
{remoteVA1, remoteUA1},
|
|
||||||
{brokenVA, "broken"},
|
|
||||||
},
|
|
||||||
AllowedUAs: allowedUAs,
|
|
||||||
Features: noEnforceMultiVA,
|
|
||||||
// Like above, the real failure cause will be logged eventually, but that
|
|
||||||
// will happen asynchronously. It's not guaranteed to happen before the
|
|
||||||
// test case exits, so we don't check for it here.
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// With only one working remote VA there should *not* be a validation
|
|
||||||
// failure when not enforcing multi VA.
|
|
||||||
Name: "Local VA and one remote VA OK, no enforce multi VA",
|
|
||||||
RemoteVAs: remoteVAs,
|
|
||||||
AllowedUAs: map[string]bool{localUA: true, remoteUA2: true},
|
|
||||||
Features: noEnforceMultiVA,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
// With only one working remote VA there should be a validation failure
|
// With only one working remote VA there should be a validation failure
|
||||||
// when enforcing multi VA.
|
Name: "Local VA and one remote VA OK",
|
||||||
Name: "Local VA and one remote VA OK, enforce multi VA",
|
|
||||||
RemoteVAs: remoteVAs,
|
RemoteVAs: remoteVAs,
|
||||||
AllowedUAs: map[string]bool{localUA: true, remoteUA2: true},
|
AllowedUAs: map[string]bool{localUA: true, remoteUA2: true},
|
||||||
Features: enforceMultiVA,
|
|
||||||
ExpectedProb: probs.Unauthorized(fmt.Sprintf(
|
ExpectedProb: probs.Unauthorized(fmt.Sprintf(
|
||||||
`During secondary validation: The key authorization file from the server did not match this challenge. Expected %q (got "???")`,
|
`During secondary validation: The key authorization file from the server did not match this challenge. Expected %q (got "???")`,
|
||||||
expectedKeyAuthorization)),
|
expectedKeyAuthorization)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// When enforcing multi-VA, any cancellations are a problem.
|
// Any remote VA cancellations are a problem.
|
||||||
Name: "Local VA and one remote VA OK, one cancelled VA, enforce multi VA",
|
Name: "Local VA and one remote VA OK, one cancelled VA",
|
||||||
RemoteVAs: []RemoteVA{
|
RemoteVAs: []RemoteVA{
|
||||||
{remoteVA1, remoteUA1},
|
{remoteVA1, remoteUA1},
|
||||||
{cancelledVA, remoteUA2},
|
{cancelledVA, remoteUA2},
|
||||||
},
|
},
|
||||||
AllowedUAs: allowedUAs,
|
AllowedUAs: allowedUAs,
|
||||||
Features: enforceMultiVA,
|
|
||||||
ExpectedProb: probs.ServerInternal("During secondary validation: Remote PerformValidation RPC canceled"),
|
ExpectedProb: probs.ServerInternal("During secondary validation: Remote PerformValidation RPC canceled"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// When enforcing multi-VA, any cancellations are a problem.
|
// Any remote VA cancellations are a problem.
|
||||||
Name: "Local VA OK, two cancelled remote VAs, enforce multi VA",
|
Name: "Local VA OK, two cancelled remote VAs",
|
||||||
RemoteVAs: []RemoteVA{
|
RemoteVAs: []RemoteVA{
|
||||||
{cancelledVA, remoteUA1},
|
{cancelledVA, remoteUA1},
|
||||||
{cancelledVA, remoteUA2},
|
{cancelledVA, remoteUA2},
|
||||||
},
|
},
|
||||||
AllowedUAs: allowedUAs,
|
AllowedUAs: allowedUAs,
|
||||||
Features: enforceMultiVA,
|
|
||||||
ExpectedProb: probs.ServerInternal("During secondary validation: Remote PerformValidation RPC canceled"),
|
ExpectedProb: probs.ServerInternal("During secondary validation: Remote PerformValidation RPC canceled"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// With the local and remote VAs seeing diff problems and the full results
|
// With the local and remote VAs seeing diff problems, we expect a problem.
|
||||||
// feature flag on but multi VA enforcement off we expect
|
|
||||||
// no problem.
|
|
||||||
Name: "Local and remote VA differential, full results, no enforce multi VA",
|
|
||||||
RemoteVAs: remoteVAs,
|
|
||||||
AllowedUAs: map[string]bool{localUA: true},
|
|
||||||
Features: noEnforceMultiVAFullResults,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// With the local and remote VAs seeing diff problems and the full results
|
|
||||||
// feature flag on and multi VA enforcement on we expect a problem.
|
|
||||||
Name: "Local and remote VA differential, full results, enforce multi VA",
|
Name: "Local and remote VA differential, full results, enforce multi VA",
|
||||||
RemoteVAs: remoteVAs,
|
RemoteVAs: remoteVAs,
|
||||||
AllowedUAs: map[string]bool{localUA: true},
|
AllowedUAs: map[string]bool{localUA: true},
|
||||||
Features: enforceMultiVAFullResults,
|
|
||||||
ExpectedProb: probs.Unauthorized(fmt.Sprintf(
|
ExpectedProb: probs.Unauthorized(fmt.Sprintf(
|
||||||
`During secondary validation: The key authorization file from the server did not match this challenge. Expected %q (got "???")`,
|
`During secondary validation: The key authorization file from the server did not match this challenge. Expected %q (got "???")`,
|
||||||
expectedKeyAuthorization)),
|
expectedKeyAuthorization)),
|
||||||
|
@ -552,9 +482,6 @@ func TestMultiVA(t *testing.T) {
|
||||||
// Configure a primary VA with testcase remote VAs.
|
// Configure a primary VA with testcase remote VAs.
|
||||||
localVA, mockLog := setup(ms.Server, 0, localUA, tc.RemoteVAs, nil)
|
localVA, mockLog := setup(ms.Server, 0, localUA, tc.RemoteVAs, nil)
|
||||||
|
|
||||||
features.Set(tc.Features)
|
|
||||||
defer features.Reset()
|
|
||||||
|
|
||||||
// Perform all validations
|
// Perform all validations
|
||||||
res, _ := localVA.PerformValidation(ctx, req)
|
res, _ := localVA.PerformValidation(ctx, req)
|
||||||
if res.Problems == nil && tc.ExpectedProb != nil {
|
if res.Problems == nil && tc.ExpectedProb != nil {
|
||||||
|
@ -601,66 +528,28 @@ func TestMultiVAEarlyReturn(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a local test VA with the two remote VAs
|
// Create a local test VA with the two remote VAs
|
||||||
localVA, mockLog := setup(ms.Server, 0, localUA, remoteVAs, nil)
|
localVA, _ := setup(ms.Server, 0, localUA, remoteVAs, nil)
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
Name string
|
|
||||||
EarlyReturn bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
Name: "One slow remote VA, no early return",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "One slow remote VA, early return",
|
|
||||||
EarlyReturn: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
earlyReturnFeatures := features.Config{
|
|
||||||
EnforceMultiVA: true,
|
|
||||||
MultiVAFullResults: false,
|
|
||||||
}
|
|
||||||
noEarlyReturnFeatures := features.Config{
|
|
||||||
EnforceMultiVA: true,
|
|
||||||
MultiVAFullResults: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Perform all validations
|
||||||
|
start := time.Now()
|
||||||
req := createValidationRequest("localhost", core.ChallengeTypeHTTP01)
|
req := createValidationRequest("localhost", core.ChallengeTypeHTTP01)
|
||||||
for _, tc := range testCases {
|
res, _ := localVA.PerformValidation(ctx, req)
|
||||||
t.Run(tc.Name, func(t *testing.T) {
|
|
||||||
mockLog.Clear()
|
|
||||||
|
|
||||||
var err error
|
// It should always fail
|
||||||
if tc.EarlyReturn {
|
if res.Problems == nil {
|
||||||
features.Set(earlyReturnFeatures)
|
t.Error("expected prob from PerformValidation, got nil")
|
||||||
} else {
|
}
|
||||||
features.Set(noEarlyReturnFeatures)
|
|
||||||
}
|
|
||||||
test.AssertNotError(t, err, "Failed to set MultiVAFullResults feature flag")
|
|
||||||
defer features.Reset()
|
|
||||||
|
|
||||||
start := time.Now()
|
elapsed := time.Since(start).Round(time.Millisecond).Milliseconds()
|
||||||
|
|
||||||
// Perform all validations
|
// The slow UA should sleep for `slowRemoteSleepMillis`. But the first remote
|
||||||
res, _ := localVA.PerformValidation(ctx, req)
|
// VA should fail quickly and the early-return code should cause the overall
|
||||||
// It should always fail
|
// overall validation to return a prob quickly (i.e. in less than half of
|
||||||
if res.Problems == nil {
|
// `slowRemoteSleepMillis`).
|
||||||
t.Error("expected prob from PerformValidation, got nil")
|
if elapsed > slowRemoteSleepMillis/2 {
|
||||||
}
|
t.Errorf(
|
||||||
|
"Expected an early return from PerformValidation in < %d ms, took %d ms",
|
||||||
elapsed := time.Since(start).Round(time.Millisecond).Milliseconds()
|
slowRemoteSleepMillis/2, elapsed)
|
||||||
|
|
||||||
// The slow UA should sleep for `slowRemoteSleepMillis`. In the early return
|
|
||||||
// case the first remote VA should fail the overall validation and a prob
|
|
||||||
// should be returned quickly (i.e. in less than half of `slowRemoteSleepMillis`).
|
|
||||||
// In the non-early return case we don't expect a problem until
|
|
||||||
// `slowRemoteSleepMillis`.
|
|
||||||
if tc.EarlyReturn && elapsed > slowRemoteSleepMillis/2 {
|
|
||||||
t.Errorf(
|
|
||||||
"Expected an early return from PerformValidation in < %d ms, took %d ms",
|
|
||||||
slowRemoteSleepMillis/2, elapsed)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -691,14 +580,6 @@ func TestMultiVAPolicy(t *testing.T) {
|
||||||
// Create a local test VA with the two remote VAs
|
// Create a local test VA with the two remote VAs
|
||||||
localVA, _ := setup(ms.Server, 0, localUA, remoteVAs, nil)
|
localVA, _ := setup(ms.Server, 0, localUA, remoteVAs, nil)
|
||||||
|
|
||||||
// Ensure multi VA enforcement is enabled, don't wait for full multi VA
|
|
||||||
// results.
|
|
||||||
features.Set(features.Config{
|
|
||||||
EnforceMultiVA: true,
|
|
||||||
MultiVAFullResults: false,
|
|
||||||
})
|
|
||||||
defer features.Reset()
|
|
||||||
|
|
||||||
// Perform validation for a domain not in the disabledDomains list
|
// Perform validation for a domain not in the disabledDomains list
|
||||||
req := createValidationRequest("letsencrypt.org", core.ChallengeTypeHTTP01)
|
req := createValidationRequest("letsencrypt.org", core.ChallengeTypeHTTP01)
|
||||||
res, _ := localVA.PerformValidation(ctx, req)
|
res, _ := localVA.PerformValidation(ctx, req)
|
||||||
|
@ -814,7 +695,7 @@ func TestLogRemoteDifferentials(t *testing.T) {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
mockLog.Clear()
|
mockLog.Clear()
|
||||||
|
|
||||||
localVA.logRemoteDifferentials(
|
localVA.logRemoteResults(
|
||||||
"example.com", 1999, "blorpus-01", tc.remoteProbs)
|
"example.com", 1999, "blorpus-01", tc.remoteProbs)
|
||||||
|
|
||||||
lines := mockLog.GetAllMatching("remoteVADifferentials JSON=.*")
|
lines := mockLog.GetAllMatching("remoteVADifferentials JSON=.*")
|
||||||
|
|
Loading…
Reference in New Issue