Skip to content

Commit

Permalink
FAB-1260 Add support for mutual TLS to GRPCServer
Browse files Browse the repository at this point in the history
Adds initial support for authentication using client certificates.

* Regenerate test certificates as the CA did not include the
clientauth extended usage attribute which is enforced by Go's
x509 library

* Added support for requiring and enforcing client certificates

* Add a SecureServerConfig struct to collect TLS-related parameters
in order to improve readiability / usability of the NewServer*
functions

* Refactor code and tests to use SecureServerConfig

* Added utility methods to server_test.go to reduce duplicate code
to make new tests more readable via table structure

* Latest commit add additional tests and makes tests run in
parallel

* DOES NOT support dynamically adding client certificate
authorities to a running server instance.  Will be addressed in
the next change set

Change-Id: Ibeca9c2ac4412735ca7b482d4157dae3edc4ea6f
Signed-off-by: Gari Singh <gari.r.singh@gmail.com>
  • Loading branch information
mastersingh24 committed Dec 30, 2016
1 parent dcaa22b commit d1ea412
Show file tree
Hide file tree
Showing 63 changed files with 863 additions and 422 deletions.
110 changes: 56 additions & 54 deletions core/comm/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@ import (
"google.golang.org/grpc/credentials"
)

//A SecureServerConfig structure is used to configure security (e.g. TLS) for a
//GRPCServer instance
type SecureServerConfig struct {
//Whether or not to use TLS for communication
UseTLS bool
//PEM-encoded X509 public key to be used by the server for TLS communication
ServerCertificate []byte
//PEM-encoded private key to be used by the server for TLS communication
ServerKey []byte
//Set of PEM-encoded X509 certificate authorities to optionally send
//as part of the server handshake
ServerRootCAs [][]byte
//Whether or not TLS client must present certificates for authentication
RequireClientCert bool
//Set of PEM-encoded X509 certificate authorities to use when verifying
//client certificates
ClientRootCAs [][]byte
}

//GRPCServer defines an interface representing a GRPC-based server
type GRPCServer interface {
//Address returns the listen address for the GRPCServer
Expand All @@ -40,7 +59,8 @@ type GRPCServer interface {
Listener() net.Listener
//ServerCertificate returns the tls.Certificate used by the grpc.Server
ServerCertificate() tls.Certificate
//TLSEnabled is a flag indicating whether or not TLS is enabled for this GRPCServer instance
//TLSEnabled is a flag indicating whether or not TLS is enabled for this
//GRPCServer instance
TLSEnabled() bool
}

Expand All @@ -55,29 +75,19 @@ type grpcServerImpl struct {
serverCertificate tls.Certificate
//Key used by the server for TLS communication
serverKeyPEM []byte
//List of certificate authorities to optionally pass to the client during the TLS handshake
//List of certificate authorities to optionally pass to the client during
//the TLS handshake
serverRootCAs []tls.Certificate
//List of certificate authorities to be used to authenticate clients if client authentication is required
//List of certificate authorities to be used to authenticate clients if
//client authentication is required
clientRootCAs *x509.CertPool
//Is TLS enabled?
tlsEnabled bool
}

/*
NewGRPCServer creates a new implementation of a GRPCServer given a listen address.
In order to enable TLS, serverKey and ServerCertificate are required.
Parameters:
address: Listen address to use formatted as hostname:port
serverKey: PEM-encoded private key to be used by the server for TLS communication
serverCertificate: PEM-encoded X509 public key to be used by the server for TLS communication
serverRootCAs: (optional) Set of PEM-encoded X509 certificate authorities to optionally send as part of
the server handshake
clientRootCAs: (optional) Set of PEM-encoded X509 certificate authorities to use when verifying client
certificates
*/
func NewGRPCServer(address string, serverKey []byte, serverCertificate []byte, serverRootCAs [][]byte,
clientRootCAs [][]byte) (GRPCServer, error) {
//NewGRPCServer creates a new implementation of a GRPCServer given a
//listen address.
func NewGRPCServer(address string, secureConfig SecureServerConfig) (GRPCServer, error) {

if address == "" {
return nil, errors.New("Missing address parameter")
Expand All @@ -89,25 +99,13 @@ func NewGRPCServer(address string, serverKey []byte, serverCertificate []byte, s
return nil, err
}

return NewGRPCServerFromListener(lis, serverKey, serverCertificate, serverRootCAs, clientRootCAs)
return NewGRPCServerFromListener(lis, secureConfig)

}

/*
NewGRPCServerFromListener creates a new implementation of a GRPCServer given an existing Listener instance.
In order to enable TLS, serverKey and ServerCertificate are required.
Parameters:
listener: Listener to use
serverKey: PEM-encoded private key to be used by the server for TLS communication
serverCertificate: PEM-encoded X509 public key to be used by the server for TLS communication
serverRootCAs: (optional) Set of PEM-encoded X509 certificate authorities to optionally send as part of
the server handshake
clientRootCAs: (optional) Set of PEM-encoded X509 certificate authorities to use when verifying client
certificates
*/
func NewGRPCServerFromListener(listener net.Listener, serverKey []byte, serverCertificate []byte,
serverRootCAs [][]byte, clientRootCAs [][]byte) (GRPCServer, error) {
//NewGRPCServerFromListener creates a new implementation of a GRPCServer given
//an existing net.Listener instance.
func NewGRPCServerFromListener(listener net.Listener, secureConfig SecureServerConfig) (GRPCServer, error) {

grpcServer := &grpcServerImpl{
address: listener.Addr().String(),
Expand All @@ -116,13 +114,13 @@ func NewGRPCServerFromListener(listener net.Listener, serverKey []byte, serverCe

//set up our server options
var serverOpts []grpc.ServerOption
//check for TLS parameters
if serverKey != nil || serverCertificate != nil {
//both are required
if serverKey != nil && serverCertificate != nil {
//check secureConfig
if secureConfig.UseTLS {
//both key and cert are required
if secureConfig.ServerKey != nil && secureConfig.ServerCertificate != nil {
grpcServer.tlsEnabled = true
//load server public and private keys
cert, err := tls.X509KeyPair(serverCertificate, serverKey)
cert, err := tls.X509KeyPair(secureConfig.ServerCertificate, secureConfig.ServerKey)
if err != nil {
return nil, err
}
Expand All @@ -132,21 +130,24 @@ func NewGRPCServerFromListener(listener net.Listener, serverKey []byte, serverCe

//base server certificate
certificates := []tls.Certificate{grpcServer.serverCertificate}

/**
//if we have server root CAs append them
if len(serverRootCAs) > 0 {
//certificates = append(certificates, serverRootCAs...)
}
//if we have client root CAs, create a certPool
if len(clientRootCAs) > 0 {
grpcServer.clientRootCAs = x509.NewCertPool()
}
*/
tlsConfig := &tls.Config{
Certificates: certificates,
}
//checkif client authentication is required
if secureConfig.RequireClientCert {
//require TLS client auth
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
//if we have client root CAs, create a certPool
if len(secureConfig.ClientRootCAs) > 0 {
grpcServer.clientRootCAs = x509.NewCertPool()
for _, clientRootCA := range secureConfig.ClientRootCAs {
if !grpcServer.clientRootCAs.AppendCertsFromPEM(clientRootCA) {
return nil, errors.New("Failed to load client root certificates")
}
}
tlsConfig.ClientCAs = grpcServer.clientRootCAs
}
}

//create credentials
creds := credentials.NewTLS(tlsConfig)
Expand All @@ -155,10 +156,10 @@ func NewGRPCServerFromListener(listener net.Listener, serverKey []byte, serverCe
serverOpts = append(serverOpts, grpc.Creds(creds))

} else {
return nil, errors.New("Both serverKey and serverCertificate are required in order to enable TLS")
return nil, errors.New("secureConfig must contain both ServerKey and " +
"ServerCertificate when UseTLS is true")
}
}

grpcServer.server = grpc.NewServer(serverOpts...)

return grpcServer, nil
Expand All @@ -184,7 +185,8 @@ func (gServer *grpcServerImpl) ServerCertificate() tls.Certificate {
return gServer.serverCertificate
}

//TLSEnabled is a flag indicating whether or not TLS is enabled for the GRPCServer instance
//TLSEnabled is a flag indicating whether or not TLS is enabled for the
//GRPCServer instance
func (gServer *grpcServerImpl) TLSEnabled() bool {
return gServer.tlsEnabled
}
Expand Down

0 comments on commit d1ea412

Please sign in to comment.