Skip to content

Commit 05bb7d6

Browse files
committed
Dynamically add and remove client CAs to GRPCServer
This completes the work for handling mutual TLS by adding two new methods to the GRPCServer object: - AppendClientRootCAs - RemoveClientRootCAs These functions allow you to add/remove root certificates used to verify client certificates to a running instance of a GRPCServer Had to refactor the internal structure in order to deal with the way pointers work in Go in order for this to work. * patchset 2 added the JIRA entry to commit * patchset 3 adds locking to make Append and/or Remove mutually exclusive. Given that these should not be called too often, just went with a stright mutex lock. Added test which passes the race detector. NOTE: there is still a possible race condition (which will not crash the process) between Append/Remove and the the TLS server handling handshakes. Will address in future changeset if people think its needed Fixes FAB-1492 Change-Id: I600bff8a5aa448a1fe301c9397daca5bd31d367a Signed-off-by: Gari Singh <[email protected]>
1 parent af3a722 commit 05bb7d6

File tree

2 files changed

+441
-24
lines changed

2 files changed

+441
-24
lines changed

core/comm/server.go

+140-11
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ package comm
1919
import (
2020
"crypto/tls"
2121
"crypto/x509"
22+
"encoding/pem"
2223
"errors"
24+
"fmt"
2325
"net"
26+
"sync"
2427

2528
"google.golang.org/grpc"
2629
"google.golang.org/grpc/credentials"
@@ -62,6 +65,12 @@ type GRPCServer interface {
6265
//TLSEnabled is a flag indicating whether or not TLS is enabled for this
6366
//GRPCServer instance
6467
TLSEnabled() bool
68+
//AppendClientRootCAs appends PEM-encoded X509 certificate authorities to
69+
//the list of authorities used to verify client certificates
70+
AppendClientRootCAs(clientRoots [][]byte) error
71+
//RemoveClientRootCAs removes PEM-encoded X509 certificate authorities from
72+
//the list of authorities used to verify client certificates
73+
RemoveClientRootCAs(clientRoots [][]byte) error
6574
}
6675

6776
type grpcServerImpl struct {
@@ -78,9 +87,13 @@ type grpcServerImpl struct {
7887
//List of certificate authorities to optionally pass to the client during
7988
//the TLS handshake
8089
serverRootCAs []tls.Certificate
81-
//List of certificate authorities to be used to authenticate clients if
82-
//client authentication is required
83-
clientRootCAs *x509.CertPool
90+
//lock to protect concurrent access to append / remove
91+
lock *sync.Mutex
92+
//Set of PEM-encoded X509 certificate authorities used to populate
93+
//the tlsConfig.ClientCAs indexed by subject
94+
clientRootCAs map[string]*x509.Certificate
95+
//TLS configuration used by the grpc server
96+
tlsConfig *tls.Config
8497
//Is TLS enabled?
8598
tlsEnabled bool
8699
}
@@ -110,6 +123,7 @@ func NewGRPCServerFromListener(listener net.Listener, secureConfig SecureServerC
110123
grpcServer := &grpcServerImpl{
111124
address: listener.Addr().String(),
112125
listener: listener,
126+
lock: &sync.Mutex{},
113127
}
114128

115129
//set up our server options
@@ -130,27 +144,29 @@ func NewGRPCServerFromListener(listener net.Listener, secureConfig SecureServerC
130144

131145
//base server certificate
132146
certificates := []tls.Certificate{grpcServer.serverCertificate}
133-
tlsConfig := &tls.Config{
134-
Certificates: certificates,
147+
grpcServer.tlsConfig = &tls.Config{
148+
Certificates: certificates,
149+
SessionTicketsDisabled: true,
135150
}
136151
//checkif client authentication is required
137152
if secureConfig.RequireClientCert {
138153
//require TLS client auth
139-
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
154+
grpcServer.tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
140155
//if we have client root CAs, create a certPool
141156
if len(secureConfig.ClientRootCAs) > 0 {
142-
grpcServer.clientRootCAs = x509.NewCertPool()
157+
grpcServer.clientRootCAs = make(map[string]*x509.Certificate)
158+
grpcServer.tlsConfig.ClientCAs = x509.NewCertPool()
143159
for _, clientRootCA := range secureConfig.ClientRootCAs {
144-
if !grpcServer.clientRootCAs.AppendCertsFromPEM(clientRootCA) {
145-
return nil, errors.New("Failed to load client root certificates")
160+
err = grpcServer.appendClientRootCA(clientRootCA)
161+
if err != nil {
162+
return nil, err
146163
}
147164
}
148-
tlsConfig.ClientCAs = grpcServer.clientRootCAs
149165
}
150166
}
151167

152168
//create credentials
153-
creds := credentials.NewTLS(tlsConfig)
169+
creds := credentials.NewTLS(grpcServer.tlsConfig)
154170

155171
//add to server options
156172
serverOpts = append(serverOpts, grpc.Creds(creds))
@@ -200,3 +216,116 @@ func (gServer *grpcServerImpl) Start() error {
200216
func (gServer *grpcServerImpl) Stop() {
201217
gServer.server.Stop()
202218
}
219+
220+
//AppendClientRootCAs appends PEM-encoded X509 certificate authorities to
221+
//the list of authorities used to verify client certificates
222+
func (gServer *grpcServerImpl) AppendClientRootCAs(clientRoots [][]byte) error {
223+
gServer.lock.Lock()
224+
defer gServer.lock.Unlock()
225+
for _, clientRoot := range clientRoots {
226+
err := gServer.appendClientRootCA(clientRoot)
227+
if err != nil {
228+
return err
229+
}
230+
}
231+
return nil
232+
}
233+
234+
//internal function to add a PEM-encoded clientRootCA
235+
func (gServer *grpcServerImpl) appendClientRootCA(clientRoot []byte) error {
236+
237+
errMsg := "Failed to append client root certificate(s): %s"
238+
//convert to x509
239+
certs, subjects, err := pemToX509Certs(clientRoot)
240+
if err != nil {
241+
return fmt.Errorf(errMsg, err.Error())
242+
}
243+
244+
if len(certs) < 1 {
245+
return fmt.Errorf(errMsg, "No client root certificates found")
246+
}
247+
248+
for i, cert := range certs {
249+
//first add to the ClientCAs
250+
gServer.tlsConfig.ClientCAs.AddCert(cert)
251+
//add it to our clientRootCAs map using subject as key
252+
gServer.clientRootCAs[subjects[i]] = cert
253+
}
254+
return nil
255+
}
256+
257+
//RemoveClientRootCAs removes PEM-encoded X509 certificate authorities from
258+
//the list of authorities used to verify client certificates
259+
func (gServer *grpcServerImpl) RemoveClientRootCAs(clientRoots [][]byte) error {
260+
gServer.lock.Lock()
261+
defer gServer.lock.Unlock()
262+
//remove from internal map
263+
for _, clientRoot := range clientRoots {
264+
err := gServer.removeClientRootCA(clientRoot)
265+
if err != nil {
266+
return err
267+
}
268+
}
269+
270+
//create a new CertPool and populate with current clientRootCAs
271+
certPool := x509.NewCertPool()
272+
for _, clientRoot := range gServer.clientRootCAs {
273+
certPool.AddCert(clientRoot)
274+
}
275+
276+
//replace the current ClientCAs pool
277+
gServer.tlsConfig.ClientCAs = certPool
278+
return nil
279+
}
280+
281+
//internal function to remove a PEM-encoded clientRootCA
282+
func (gServer *grpcServerImpl) removeClientRootCA(clientRoot []byte) error {
283+
284+
errMsg := "Failed to remove client root certificate(s): %s"
285+
//convert to x509
286+
certs, subjects, err := pemToX509Certs(clientRoot)
287+
if err != nil {
288+
return fmt.Errorf(errMsg, err.Error())
289+
}
290+
291+
if len(certs) < 1 {
292+
return fmt.Errorf(errMsg, "No client root certificates found")
293+
}
294+
295+
for i, subject := range subjects {
296+
//remove it from our clientRootCAs map using subject as key
297+
//check to see if we have match
298+
if certs[i].Equal(gServer.clientRootCAs[subject]) {
299+
delete(gServer.clientRootCAs, subject)
300+
}
301+
}
302+
return nil
303+
}
304+
305+
//utility function to parse PEM-encoded certs
306+
func pemToX509Certs(pemCerts []byte) ([]*x509.Certificate, []string, error) {
307+
308+
//it's possible that multiple certs are encoded
309+
certs := []*x509.Certificate{}
310+
subjects := []string{}
311+
for len(pemCerts) > 0 {
312+
var block *pem.Block
313+
block, pemCerts = pem.Decode(pemCerts)
314+
if block == nil {
315+
break
316+
}
317+
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
318+
continue
319+
}
320+
321+
cert, err := x509.ParseCertificate(block.Bytes)
322+
if err != nil {
323+
return nil, subjects, err
324+
} else {
325+
certs = append(certs, cert)
326+
//extract and append the subject
327+
subjects = append(subjects, string(cert.RawSubject))
328+
}
329+
}
330+
return certs, subjects, nil
331+
}

0 commit comments

Comments
 (0)