Skip to content

Commit 2c0e72a

Browse files
committed
[FAB-1799] Add function to replace client root CAs
The GRPCServer had methods for appending and removing client root certificates but did not have a method to set/update the list in its entirety. * Add SetClientRootCAs function to GRPCServer * Add new tests and update existing tests Change-Id: I8159ae9a0cea663a882b5bcaecc184b4934ab8eb Signed-off-by: Gari Singh <[email protected]>
1 parent 5c171cc commit 2c0e72a

File tree

2 files changed

+188
-2
lines changed

2 files changed

+188
-2
lines changed

core/comm/server.go

+38
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ type GRPCServer interface {
7171
//RemoveClientRootCAs removes PEM-encoded X509 certificate authorities from
7272
//the list of authorities used to verify client certificates
7373
RemoveClientRootCAs(clientRoots [][]byte) error
74+
//SetClientRootCAs sets the list of authorities used to verify client
75+
//certificates based on a list of PEM-encoded X509 certificate authorities
76+
SetClientRootCAs(clientRoots [][]byte) error
7477
}
7578

7679
type grpcServerImpl struct {
@@ -302,6 +305,41 @@ func (gServer *grpcServerImpl) removeClientRootCA(clientRoot []byte) error {
302305
return nil
303306
}
304307

308+
//SetClientRootCAs sets the list of authorities used to verify client
309+
//certificates based on a list of PEM-encoded X509 certificate authorities
310+
func (gServer *grpcServerImpl) SetClientRootCAs(clientRoots [][]byte) error {
311+
gServer.lock.Lock()
312+
defer gServer.lock.Unlock()
313+
314+
errMsg := "Failed to set client root certificate(s): %s"
315+
316+
//create a new map and CertPool
317+
clientRootCAs := make(map[string]*x509.Certificate)
318+
for _, clientRoot := range clientRoots {
319+
certs, subjects, err := pemToX509Certs(clientRoot)
320+
if err != nil {
321+
return fmt.Errorf(errMsg, err.Error())
322+
}
323+
if len(certs) >= 1 {
324+
for i, cert := range certs {
325+
//add it to our clientRootCAs map using subject as key
326+
clientRootCAs[subjects[i]] = cert
327+
}
328+
}
329+
}
330+
331+
//create a new CertPool and populate with the new clientRootCAs
332+
certPool := x509.NewCertPool()
333+
for _, clientRoot := range clientRootCAs {
334+
certPool.AddCert(clientRoot)
335+
}
336+
//replace the internal map
337+
gServer.clientRootCAs = clientRootCAs
338+
//replace the current ClientCAs pool
339+
gServer.tlsConfig.ClientCAs = certPool
340+
return nil
341+
}
342+
305343
//utility function to parse PEM-encoded certs
306344
func pemToX509Certs(pemCerts []byte) ([]*x509.Certificate, []string, error) {
307345

core/comm/server_test.go

+150-2
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,22 @@ func TestNewGRPCServerInvalidParameters(t *testing.T) {
461461
if err != nil {
462462
t.Log(err.Error())
463463
}
464+
465+
srv, err := comm.NewGRPCServer(":9046",
466+
comm.SecureServerConfig{
467+
UseTLS: true,
468+
ServerCertificate: []byte(selfSignedCertPEM),
469+
ServerKey: []byte(selfSignedKeyPEM),
470+
RequireClientCert: true})
471+
badRootCAs := [][]byte{[]byte(badPEM)}
472+
err = srv.SetClientRootCAs(badRootCAs)
473+
//check for error
474+
msg = "Failed to set client root certificate(s): " +
475+
"asn1: syntax error: data truncated"
476+
assert.EqualError(t, err, msg)
477+
if err != nil {
478+
t.Log(err.Error())
479+
}
464480
}
465481

466482
func TestNewGRPCServer(t *testing.T) {
@@ -1146,8 +1162,8 @@ func TestRemoveClientRootCAs(t *testing.T) {
11461162

11471163
}
11481164

1149-
//test for race conditions - test locally using "go test -race -run TestConcurrentAppendRemove"
1150-
func TestConcurrentAppendRemove(t *testing.T) {
1165+
//test for race conditions - test locally using "go test -race -run TestConcurrentAppendRemoveSet"
1166+
func TestConcurrentAppendRemoveSet(t *testing.T) {
11511167

11521168
t.Parallel()
11531169
//get the config for one of our Org1 test servers and include client CAs from
@@ -1183,6 +1199,18 @@ func TestConcurrentAppendRemove(t *testing.T) {
11831199

11841200
}()
11851201

1202+
wg.Add(1)
1203+
go func() {
1204+
defer wg.Done()
1205+
//set client root CAs
1206+
err := srv.SetClientRootCAs([][]byte{testOrgs[1].childOrgs[0].rootCA,
1207+
testOrgs[1].childOrgs[1].rootCA})
1208+
if err != nil {
1209+
t.Fatal("Failed to set client root CAs")
1210+
}
1211+
1212+
}()
1213+
11861214
//TODO: enable this after creating a custom type for grpc.TransportCredentials
11871215
/*
11881216
clientConfig := testOrgs[1].childOrgs[0].trustedClients([][]byte{testOrgs[0].rootCA})[0]
@@ -1204,6 +1232,126 @@ func TestConcurrentAppendRemove(t *testing.T) {
12041232
}
12051233
}()
12061234

1235+
wg.Add(1)
1236+
go func() {
1237+
defer wg.Done()
1238+
//set client root CAs
1239+
err := srv.SetClientRootCAs([][]byte{testOrgs[1].childOrgs[0].rootCA,
1240+
testOrgs[1].childOrgs[1].rootCA})
1241+
if err != nil {
1242+
t.Fatal("Failed to set client root CAs")
1243+
}
1244+
1245+
}()
1246+
12071247
wg.Wait()
12081248

12091249
}
1250+
1251+
func TestSetClientRootCAs(t *testing.T) {
1252+
1253+
t.Parallel()
1254+
1255+
//get the config for one of our Org1 test servers
1256+
serverConfig := testOrgs[0].testServers(9303, [][]byte{})[0].config
1257+
address := testOrgs[0].testServers(9303, [][]byte{})[0].address
1258+
1259+
//create a GRPCServer
1260+
srv, err := comm.NewGRPCServer(address, serverConfig)
1261+
if err != nil {
1262+
t.Fatalf("Failed to create GRPCServer due to: %s", err.Error())
1263+
}
1264+
1265+
//register the GRPC test server and start the GRPCServer
1266+
testpb.RegisterTestServiceServer(srv.Server(), &testServiceServer{})
1267+
go srv.Start()
1268+
defer srv.Stop()
1269+
//should not be needed but just in case
1270+
time.Sleep(10 * time.Millisecond)
1271+
1272+
//set up out test clients
1273+
//Org1
1274+
clientConfigOrg1Child1 := testOrgs[0].childOrgs[0].trustedClients([][]byte{testOrgs[0].rootCA})[0]
1275+
clientConfigOrg1Child2 := testOrgs[0].childOrgs[1].trustedClients([][]byte{testOrgs[0].rootCA})[0]
1276+
clientConfigsOrg1Children := []*tls.Config{clientConfigOrg1Child1, clientConfigOrg1Child2}
1277+
org1ChildRootCAs := [][]byte{testOrgs[0].childOrgs[0].rootCA,
1278+
testOrgs[0].childOrgs[1].rootCA}
1279+
//Org2
1280+
clientConfigOrg2Child1 := testOrgs[1].childOrgs[0].trustedClients([][]byte{testOrgs[0].rootCA})[0]
1281+
clientConfigOrg2Child2 := testOrgs[1].childOrgs[1].trustedClients([][]byte{testOrgs[0].rootCA})[0]
1282+
clientConfigsOrg2Children := []*tls.Config{clientConfigOrg2Child1, clientConfigOrg2Child2}
1283+
org2ChildRootCAs := [][]byte{testOrgs[1].childOrgs[0].rootCA,
1284+
testOrgs[1].childOrgs[1].rootCA}
1285+
1286+
//initially set client CAs to Org1 children
1287+
err = srv.SetClientRootCAs(org1ChildRootCAs)
1288+
if err != nil {
1289+
t.Fatalf("SetClientRootCAs failed due to: %s", err.Error())
1290+
}
1291+
1292+
//clientConfigsOrg1Children are currently trusted
1293+
for i, clientConfig := range clientConfigsOrg1Children {
1294+
//invoke the EmptyCall service
1295+
_, err = invokeEmptyCall(address, []grpc.DialOption{
1296+
grpc.WithTransportCredentials(credentials.NewTLS(clientConfig))})
1297+
1298+
//we expect success as these are trusted clients
1299+
if err != nil {
1300+
t.Fatalf("Trusted client%d failed to connect to %s with error: %s",
1301+
i, address, err.Error())
1302+
} else {
1303+
t.Logf("Trusted client%d successfully connected to %s", i, address)
1304+
}
1305+
}
1306+
1307+
//clientConfigsOrg2Children are currently not trusted
1308+
for j, clientConfig := range clientConfigsOrg2Children {
1309+
//invoke the EmptyCall service
1310+
_, err = invokeEmptyCall(address, []grpc.DialOption{
1311+
grpc.WithTransportCredentials(credentials.NewTLS(clientConfig))})
1312+
//we expect failure as these are now untrusted clients
1313+
if err != nil {
1314+
t.Logf("Untrusted client%d was correctly rejected by %s", j, address)
1315+
} else {
1316+
t.Fatalf("Untrusted client %d should not have been able to connect to %s", j,
1317+
address)
1318+
}
1319+
}
1320+
1321+
//now set client CAs to Org2 children
1322+
err = srv.SetClientRootCAs(org2ChildRootCAs)
1323+
if err != nil {
1324+
t.Fatalf("SetClientRootCAs failed due to: %s", err.Error())
1325+
}
1326+
1327+
//now reverse trusted and not trusted
1328+
//clientConfigsOrg1Children are currently trusted
1329+
for i, clientConfig := range clientConfigsOrg2Children {
1330+
//invoke the EmptyCall service
1331+
_, err = invokeEmptyCall(address, []grpc.DialOption{
1332+
grpc.WithTransportCredentials(credentials.NewTLS(clientConfig))})
1333+
1334+
//we expect success as these are trusted clients
1335+
if err != nil {
1336+
t.Fatalf("Trusted client%d failed to connect to %s with error: %s",
1337+
i, address, err.Error())
1338+
} else {
1339+
t.Logf("Trusted client%d successfully connected to %s", i, address)
1340+
}
1341+
}
1342+
1343+
//clientConfigsOrg2Children are currently not trusted
1344+
for j, clientConfig := range clientConfigsOrg1Children {
1345+
//invoke the EmptyCall service
1346+
_, err = invokeEmptyCall(address, []grpc.DialOption{
1347+
grpc.WithTransportCredentials(credentials.NewTLS(clientConfig))})
1348+
//we expect failure as these are now untrusted clients
1349+
if err != nil {
1350+
t.Logf("Untrusted client%d was correctly rejected by %s", j, address)
1351+
} else {
1352+
t.Fatalf("Untrusted client %d should not have been able to connect to %s", j,
1353+
address)
1354+
}
1355+
}
1356+
1357+
}

0 commit comments

Comments
 (0)