Skip to content

Commit 4850cf2

Browse files
authored
Add an extra timeout waiting for RVAs (#8434)
Once we have enough RVA results, set a tighter timeout on the remaining ones so we don't hold up issuance on a single slow perspective. This strikes a balance between our previous strategy (cancelling all dangling remotes immediately upon getting enough results) and our current strategy (don't cancel any remotes unless we hit our global timeout). Add a new VA config field to control how long we wait for the lagging perspectives. If this config field is not set, default to not cancelling early, which is our current behavior.
1 parent b17ccf0 commit 4850cf2

File tree

5 files changed

+28
-5
lines changed

5 files changed

+28
-5
lines changed

cmd/boulder-va/main.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/letsencrypt/boulder/bdns"
1212
"github.com/letsencrypt/boulder/cmd"
13+
"github.com/letsencrypt/boulder/config"
1314
"github.com/letsencrypt/boulder/features"
1415
bgrpc "github.com/letsencrypt/boulder/grpc"
1516
"github.com/letsencrypt/boulder/iana"
@@ -51,9 +52,12 @@ type Config struct {
5152
VA struct {
5253
vaConfig.Common
5354
RemoteVAs []RemoteVAGRPCClientConfig `validate:"omitempty,dive"`
54-
// Deprecated and ignored
55-
MaxRemoteValidationFailures int `validate:"omitempty,min=0,required_with=RemoteVAs"`
56-
Features features.Config
55+
// SlowRemoteTimeout sets how long the VA is willing to wait for slow
56+
// RemoteVA instances to finish their work. It starts counting from
57+
// when the VA first gets a quorum of (un)successful remote results.
58+
// Leaving this value zero means the VA won't early-cancel slow remotes.
59+
SlowRemoteTimeout config.Duration
60+
Features features.Config
5761
}
5862

5963
Syslog cmd.SyslogConfig
@@ -149,7 +153,9 @@ func main() {
149153
c.VA.AccountURIPrefixes,
150154
va.PrimaryPerspective,
151155
"",
152-
iana.IsReservedAddr)
156+
iana.IsReservedAddr,
157+
c.VA.SlowRemoteTimeout.Duration,
158+
)
153159
cmd.FailOnError(err, "Unable to create VA server")
154160

155161
start, err := bgrpc.NewServer(c.VA.GRPC, logger).Add(

cmd/remoteva/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,9 @@ func main() {
140140
c.RVA.AccountURIPrefixes,
141141
c.RVA.Perspective,
142142
c.RVA.RIR,
143-
iana.IsReservedAddr)
143+
iana.IsReservedAddr,
144+
0,
145+
)
144146
cmd.FailOnError(err, "Unable to create Remote-VA server")
145147

146148
start, err := bgrpc.NewServer(c.RVA.GRPC, logger).Add(

test/config-next/va.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"http://boulder.service.consul:4001/acme/acct/",
6464
"http://boulder.service.consul:4000/acme/reg/"
6565
],
66+
"slowRemoteTimeout": "2s",
6667
"features": {
6768
"DNSAccount01Enabled": true
6869
}

va/va.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ type ValidationAuthorityImpl struct {
216216
maxRemoteFailures int
217217
accountURIPrefixes []string
218218
singleDialTimeout time.Duration
219+
slowRemoteTimeout time.Duration
219220
perspective string
220221
rir string
221222
isReservedIPFunc func(netip.Addr) error
@@ -239,6 +240,7 @@ func NewValidationAuthorityImpl(
239240
perspective string,
240241
rir string,
241242
reservedIPChecker func(netip.Addr) error,
243+
slowRemoteTimeout time.Duration,
242244
) (*ValidationAuthorityImpl, error) {
243245

244246
if len(accountURIPrefixes) == 0 {
@@ -620,6 +622,16 @@ func (va *ValidationAuthorityImpl) doRemoteOperation(ctx context.Context, op rem
620622
firstProb = currProb
621623
}
622624

625+
if va.slowRemoteTimeout != 0 {
626+
// If enough perspectives have passed, or enough perspectives have
627+
// failed, set a tighter deadline for the remaining perspectives.
628+
if (len(passed) >= required && len(passedRIRs) >= requiredRIRs) ||
629+
(len(failed) > remoteVACount-required) {
630+
timer := time.AfterFunc(va.slowRemoteTimeout, cancel)
631+
defer timer.Stop()
632+
}
633+
}
634+
623635
// Once all the VAs have returned a result, break the loop.
624636
if len(passed)+len(failed) >= remoteVACount {
625637
break

va/va_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ func setup(srv *httptest.Server, userAgent string, remoteVAs []RemoteVA, mockDNS
145145
perspective,
146146
"",
147147
isNonLoopbackReservedIP,
148+
time.Second,
148149
)
149150
if err != nil {
150151
panic(fmt.Sprintf("Failed to create validation authority: %v", err))
@@ -323,6 +324,7 @@ func TestNewValidationAuthorityImplWithDuplicateRemotes(t *testing.T) {
323324
"example perspective",
324325
"",
325326
isNonLoopbackReservedIP,
327+
time.Second,
326328
)
327329
test.AssertError(t, err, "NewValidationAuthorityImpl allowed duplicate remote perspectives")
328330
test.AssertContains(t, err.Error(), "duplicate remote VA perspective \"dadaist\"")

0 commit comments

Comments
 (0)