Skip to content

Commit

Permalink
[FAB-1799] Add function to replace client root CAs
Browse files Browse the repository at this point in the history
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 <gari.r.singh@gmail.com>
  • Loading branch information
mastersingh24 committed Jan 22, 2017
1 parent 5c171cc commit 2c0e72a
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 2 deletions.
38 changes: 38 additions & 0 deletions core/comm/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ type GRPCServer interface {
//RemoveClientRootCAs removes PEM-encoded X509 certificate authorities from
//the list of authorities used to verify client certificates
RemoveClientRootCAs(clientRoots [][]byte) error
//SetClientRootCAs sets the list of authorities used to verify client
//certificates based on a list of PEM-encoded X509 certificate authorities
SetClientRootCAs(clientRoots [][]byte) error
}

type grpcServerImpl struct {
Expand Down Expand Up @@ -302,6 +305,41 @@ func (gServer *grpcServerImpl) removeClientRootCA(clientRoot []byte) error {
return nil
}

//SetClientRootCAs sets the list of authorities used to verify client
//certificates based on a list of PEM-encoded X509 certificate authorities
func (gServer *grpcServerImpl) SetClientRootCAs(clientRoots [][]byte) error {
gServer.lock.Lock()
defer gServer.lock.Unlock()

errMsg := "Failed to set client root certificate(s): %s"

//create a new map and CertPool
clientRootCAs := make(map[string]*x509.Certificate)
for _, clientRoot := range clientRoots {
certs, subjects, err := pemToX509Certs(clientRoot)
if err != nil {
return fmt.Errorf(errMsg, err.Error())
}
if len(certs) >= 1 {
for i, cert := range certs {
//add it to our clientRootCAs map using subject as key
clientRootCAs[subjects[i]] = cert
}
}
}

//create a new CertPool and populate with the new clientRootCAs
certPool := x509.NewCertPool()
for _, clientRoot := range clientRootCAs {
certPool.AddCert(clientRoot)
}
//replace the internal map
gServer.clientRootCAs = clientRootCAs
//replace the current ClientCAs pool
gServer.tlsConfig.ClientCAs = certPool
return nil
}

//utility function to parse PEM-encoded certs
func pemToX509Certs(pemCerts []byte) ([]*x509.Certificate, []string, error) {

Expand Down
152 changes: 150 additions & 2 deletions core/comm/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,22 @@ func TestNewGRPCServerInvalidParameters(t *testing.T) {
if err != nil {
t.Log(err.Error())
}

srv, err := comm.NewGRPCServer(":9046",
comm.SecureServerConfig{
UseTLS: true,
ServerCertificate: []byte(selfSignedCertPEM),
ServerKey: []byte(selfSignedKeyPEM),
RequireClientCert: true})
badRootCAs := [][]byte{[]byte(badPEM)}
err = srv.SetClientRootCAs(badRootCAs)
//check for error
msg = "Failed to set client root certificate(s): " +
"asn1: syntax error: data truncated"
assert.EqualError(t, err, msg)
if err != nil {
t.Log(err.Error())
}
}

func TestNewGRPCServer(t *testing.T) {
Expand Down Expand Up @@ -1146,8 +1162,8 @@ func TestRemoveClientRootCAs(t *testing.T) {

}

//test for race conditions - test locally using "go test -race -run TestConcurrentAppendRemove"
func TestConcurrentAppendRemove(t *testing.T) {
//test for race conditions - test locally using "go test -race -run TestConcurrentAppendRemoveSet"
func TestConcurrentAppendRemoveSet(t *testing.T) {

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

}()

wg.Add(1)
go func() {
defer wg.Done()
//set client root CAs
err := srv.SetClientRootCAs([][]byte{testOrgs[1].childOrgs[0].rootCA,
testOrgs[1].childOrgs[1].rootCA})
if err != nil {
t.Fatal("Failed to set client root CAs")
}

}()

//TODO: enable this after creating a custom type for grpc.TransportCredentials
/*
clientConfig := testOrgs[1].childOrgs[0].trustedClients([][]byte{testOrgs[0].rootCA})[0]
Expand All @@ -1204,6 +1232,126 @@ func TestConcurrentAppendRemove(t *testing.T) {
}
}()

wg.Add(1)
go func() {
defer wg.Done()
//set client root CAs
err := srv.SetClientRootCAs([][]byte{testOrgs[1].childOrgs[0].rootCA,
testOrgs[1].childOrgs[1].rootCA})
if err != nil {
t.Fatal("Failed to set client root CAs")
}

}()

wg.Wait()

}

func TestSetClientRootCAs(t *testing.T) {

t.Parallel()

//get the config for one of our Org1 test servers
serverConfig := testOrgs[0].testServers(9303, [][]byte{})[0].config
address := testOrgs[0].testServers(9303, [][]byte{})[0].address

//create a GRPCServer
srv, err := comm.NewGRPCServer(address, serverConfig)
if err != nil {
t.Fatalf("Failed to create GRPCServer due to: %s", err.Error())
}

//register the GRPC test server and start the GRPCServer
testpb.RegisterTestServiceServer(srv.Server(), &testServiceServer{})
go srv.Start()
defer srv.Stop()
//should not be needed but just in case
time.Sleep(10 * time.Millisecond)

//set up out test clients
//Org1
clientConfigOrg1Child1 := testOrgs[0].childOrgs[0].trustedClients([][]byte{testOrgs[0].rootCA})[0]
clientConfigOrg1Child2 := testOrgs[0].childOrgs[1].trustedClients([][]byte{testOrgs[0].rootCA})[0]
clientConfigsOrg1Children := []*tls.Config{clientConfigOrg1Child1, clientConfigOrg1Child2}
org1ChildRootCAs := [][]byte{testOrgs[0].childOrgs[0].rootCA,
testOrgs[0].childOrgs[1].rootCA}
//Org2
clientConfigOrg2Child1 := testOrgs[1].childOrgs[0].trustedClients([][]byte{testOrgs[0].rootCA})[0]
clientConfigOrg2Child2 := testOrgs[1].childOrgs[1].trustedClients([][]byte{testOrgs[0].rootCA})[0]
clientConfigsOrg2Children := []*tls.Config{clientConfigOrg2Child1, clientConfigOrg2Child2}
org2ChildRootCAs := [][]byte{testOrgs[1].childOrgs[0].rootCA,
testOrgs[1].childOrgs[1].rootCA}

//initially set client CAs to Org1 children
err = srv.SetClientRootCAs(org1ChildRootCAs)
if err != nil {
t.Fatalf("SetClientRootCAs failed due to: %s", err.Error())
}

//clientConfigsOrg1Children are currently trusted
for i, clientConfig := range clientConfigsOrg1Children {
//invoke the EmptyCall service
_, err = invokeEmptyCall(address, []grpc.DialOption{
grpc.WithTransportCredentials(credentials.NewTLS(clientConfig))})

//we expect success as these are trusted clients
if err != nil {
t.Fatalf("Trusted client%d failed to connect to %s with error: %s",
i, address, err.Error())
} else {
t.Logf("Trusted client%d successfully connected to %s", i, address)
}
}

//clientConfigsOrg2Children are currently not trusted
for j, clientConfig := range clientConfigsOrg2Children {
//invoke the EmptyCall service
_, err = invokeEmptyCall(address, []grpc.DialOption{
grpc.WithTransportCredentials(credentials.NewTLS(clientConfig))})
//we expect failure as these are now untrusted clients
if err != nil {
t.Logf("Untrusted client%d was correctly rejected by %s", j, address)
} else {
t.Fatalf("Untrusted client %d should not have been able to connect to %s", j,
address)
}
}

//now set client CAs to Org2 children
err = srv.SetClientRootCAs(org2ChildRootCAs)
if err != nil {
t.Fatalf("SetClientRootCAs failed due to: %s", err.Error())
}

//now reverse trusted and not trusted
//clientConfigsOrg1Children are currently trusted
for i, clientConfig := range clientConfigsOrg2Children {
//invoke the EmptyCall service
_, err = invokeEmptyCall(address, []grpc.DialOption{
grpc.WithTransportCredentials(credentials.NewTLS(clientConfig))})

//we expect success as these are trusted clients
if err != nil {
t.Fatalf("Trusted client%d failed to connect to %s with error: %s",
i, address, err.Error())
} else {
t.Logf("Trusted client%d successfully connected to %s", i, address)
}
}

//clientConfigsOrg2Children are currently not trusted
for j, clientConfig := range clientConfigsOrg1Children {
//invoke the EmptyCall service
_, err = invokeEmptyCall(address, []grpc.DialOption{
grpc.WithTransportCredentials(credentials.NewTLS(clientConfig))})
//we expect failure as these are now untrusted clients
if err != nil {
t.Logf("Untrusted client%d was correctly rejected by %s", j, address)
} else {
t.Fatalf("Untrusted client %d should not have been able to connect to %s", j,
address)
}
}

}

0 comments on commit 2c0e72a

Please sign in to comment.