Skip to content

Commit 088b872

Browse files
Roland Bracewell Shoemakerjsha
authored andcommitted
Implement multi VA validation (#2802)
Adds basic multi-path validation functionality. A new method `performRemoteValidation` is added to `boulder-va` which is called if it is configured with a list of remote VA gRPC addresses. In this initial implementation the remote VAs are only used to check the validation result of the main VA, if all of the remote validations succeed but the local validation failed, the overall validation will still fail. Remote VAs use the exact same code as the local VA to perform validation. If the local validation succeeds then a configured quorum of the remote VA successes must be met in order to fully complete the validation. This implementation assumes that metrics are collected from the remote VAs in order to have visibility into their individual validation latencies etc. Fixes #2621.
1 parent 7cdab9c commit 088b872

File tree

8 files changed

+469
-63
lines changed

8 files changed

+469
-63
lines changed

cmd/boulder-va/main.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"flag"
55
"os"
6+
"strings"
67
"time"
78

89
"github.com/jmhodges/clock"
@@ -38,6 +39,9 @@ type config struct {
3839
// Feature flag to enable enforcement of CAA SERVFAILs.
3940
CAASERVFAILExceptions string
4041

42+
RemoteVAs []cmd.GRPCClientConfig
43+
MaxRemoteValidationFailures int
44+
4145
Features map[string]bool
4246
}
4347

@@ -119,18 +123,36 @@ func main() {
119123
resolver = r
120124
}
121125

126+
tls, err := c.VA.TLS.Load()
127+
cmd.FailOnError(err, "TLS config")
128+
129+
var remotes []va.RemoteVA
130+
if len(c.VA.RemoteVAs) > 0 {
131+
for _, rva := range c.VA.RemoteVAs {
132+
vaConn, err := bgrpc.ClientSetup(&rva, tls, scope)
133+
cmd.FailOnError(err, "Unable to create remote VA client")
134+
remotes = append(
135+
remotes,
136+
va.RemoteVA{
137+
bgrpc.NewValidationAuthorityGRPCClient(vaConn),
138+
strings.Join(rva.ServerAddresses, ","),
139+
},
140+
)
141+
}
142+
}
143+
122144
vai := va.NewValidationAuthorityImpl(
123145
pc,
124146
sbc,
125147
resolver,
148+
remotes,
149+
c.VA.MaxRemoteValidationFailures,
126150
c.VA.UserAgent,
127151
c.VA.IssuerDomain,
128152
scope,
129153
clk,
130154
logger)
131155

132-
tls, err := c.VA.TLS.Load()
133-
cmd.FailOnError(err, "TLS config")
134156
grpcSrv, l, err := bgrpc.NewServer(c.VA.GRPC, tls, scope)
135157
cmd.FailOnError(err, "Unable to setup VA gRPC server")
136158
err = bgrpc.RegisterValidationAuthorityGRPCServer(grpcSrv, vai)

test/config-next/va-remote-a.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"va": {
3+
"CAASERVFAILExceptions": "test/caa-servfail-exceptions.txt",
4+
"userAgent": "boulder",
5+
"debugAddr": ":8011",
6+
"portConfig": {
7+
"httpPort": 5002,
8+
"httpsPort": 5001,
9+
"tlsPort": 5001
10+
},
11+
"dnsTries": 3,
12+
"issuerDomain": "happy-hacker-ca.invalid",
13+
"tls": {
14+
"caCertfile": "test/grpc-creds/minica.pem",
15+
"certFile": "test/grpc-creds/va.boulder/cert.pem",
16+
"keyFile": "test/grpc-creds/va.boulder/key.pem"
17+
},
18+
"grpc": {
19+
"address": ":9097",
20+
"clientNames": [
21+
"va.boulder"
22+
]
23+
},
24+
"features": {
25+
"IPv6First": true
26+
}
27+
},
28+
29+
"syslog": {
30+
"stdoutlevel": 6,
31+
"sysloglevel": 4
32+
},
33+
34+
"common": {
35+
"dnsResolver": "127.0.0.1:8053",
36+
"dnsTimeout": "1s",
37+
"dnsAllowLoopbackAddresses": true
38+
}
39+
}

test/config-next/va-remote-b.json

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"va": {
3+
"CAASERVFAILExceptions": "test/caa-servfail-exceptions.txt",
4+
"userAgent": "boulder",
5+
"debugAddr": ":8012",
6+
"portConfig": {
7+
"httpPort": 5002,
8+
"httpsPort": 5001,
9+
"tlsPort": 5001
10+
},
11+
"dnsTries": 3,
12+
"issuerDomain": "happy-hacker-ca.invalid",
13+
"tls": {
14+
"caCertfile": "test/grpc-creds/minica.pem",
15+
"certFile": "test/grpc-creds/va.boulder/cert.pem",
16+
"keyFile": "test/grpc-creds/va.boulder/key.pem"
17+
},
18+
"grpc": {
19+
"address": ":9098",
20+
"clientNames": [
21+
"va.boulder"
22+
]
23+
},
24+
"features": {
25+
"IPv6First": true
26+
}
27+
},
28+
29+
"syslog": {
30+
"stdoutlevel": 6,
31+
"sysloglevel": 4
32+
},
33+
34+
"common": {
35+
"dnsResolver": "127.0.0.1:8053",
36+
"dnsTimeout": "1s",
37+
"dnsAllowLoopbackAddresses": true
38+
}
39+
}

test/config-next/va.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,17 @@
3030
"features": {
3131
"GoogleSafeBrowsingV4": true,
3232
"IPv6First": true
33-
}
33+
},
34+
"remoteVAs": [
35+
{
36+
"serverAddresses": ["va.boulder:19097"],
37+
"timeout": "15s"
38+
},
39+
{
40+
"serverAddresses": ["va.boulder:19098"],
41+
"timeout": "15s"
42+
}
43+
]
3444
},
3545

3646
"syslog": {

test/startservers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ def start(race_detection):
5454
[":19094", "ra.boulder:9094"],
5555
[":19095", "sa.boulder:9095"],
5656
[":19096", "ca.boulder:9096"],
57+
[":19097", "va.boulder:9097"],
58+
[":19098", "va.boulder:9098"]
5759
]:
5860
forward(srv[0], srv[1])
5961
progs = [
@@ -72,6 +74,12 @@ def start(race_detection):
7274
'dns-test-srv',
7375
'mail-test-srv --closeFirst 5'
7476
]
77+
if default_config_dir.startswith("test/config-next"):
78+
# Run the two 'remote' VAs
79+
progs.extend([
80+
'boulder-va --config %s' % os.path.join(default_config_dir, "va-remote-a.json"),
81+
'boulder-va --config %s' % os.path.join(default_config_dir, "va-remote-b.json")
82+
])
7583
if not install(race_detection):
7684
return False
7785
for prog in progs:
@@ -93,6 +101,9 @@ def start(race_detection):
93101
if not check():
94102
return False
95103
ports = range(8000, 8005) + [4000, 4430]
104+
if default_config_dir.startswith("test/config-next"):
105+
# Add the two 'remote' VA debug ports
106+
ports.extend([8011, 8012])
96107
for debug_port in ports:
97108
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
98109
s.connect(('localhost', debug_port))

va/gsb_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ func TestIsSafeDomain(t *testing.T) {
3434
&cmd.PortConfig{},
3535
sbc,
3636
nil,
37+
nil,
38+
0,
3739
"user agent 1.0",
3840
"letsencrypt.org",
3941
stats,
@@ -83,6 +85,8 @@ func TestAllowNilInIsSafeDomain(t *testing.T) {
8385
&cmd.PortConfig{},
8486
nil,
8587
nil,
88+
nil,
89+
0,
8690
"user agent 1.0",
8791
"letsencrypt.org",
8892
stats,

0 commit comments

Comments
 (0)