github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/internal/pkg/comm/server.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package comm
     8  
     9  import (
    10  	"crypto/tls"
    11  	"crypto/x509"
    12  	"net"
    13  	"sync"
    14  	"sync/atomic"
    15  	"time"
    16  
    17  	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
    18  	"github.com/pkg/errors"
    19  	"google.golang.org/grpc"
    20  	"google.golang.org/grpc/health"
    21  	healthpb "google.golang.org/grpc/health/grpc_health_v1"
    22  )
    23  
    24  type GRPCServer struct {
    25  	// Listen address for the server specified as hostname:port
    26  	address string
    27  	// Listener for handling network requests
    28  	listener net.Listener
    29  	// GRPC server
    30  	server *grpc.Server
    31  	// Certificate presented by the server for TLS communication
    32  	// stored as an atomic reference
    33  	serverCertificate atomic.Value
    34  	// lock to protect concurrent access to append / remove
    35  	lock *sync.Mutex
    36  	// TLS configuration used by the grpc server
    37  	tls *TLSConfig
    38  	// Server for gRPC Health Check Protocol.
    39  	healthServer *health.Server
    40  }
    41  
    42  // NewGRPCServer creates a new implementation of a GRPCServer given a
    43  // listen address
    44  func NewGRPCServer(address string, serverConfig ServerConfig) (*GRPCServer, error) {
    45  	if address == "" {
    46  		return nil, errors.New("missing address parameter")
    47  	}
    48  	//create our listener
    49  	lis, err := net.Listen("tcp", address)
    50  
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	return NewGRPCServerFromListener(lis, serverConfig)
    55  }
    56  
    57  // NewGRPCServerFromListener creates a new implementation of a GRPCServer given
    58  // an existing net.Listener instance using default keepalive
    59  func NewGRPCServerFromListener(listener net.Listener, serverConfig ServerConfig) (*GRPCServer, error) {
    60  	grpcServer := &GRPCServer{
    61  		address:  listener.Addr().String(),
    62  		listener: listener,
    63  		lock:     &sync.Mutex{},
    64  	}
    65  
    66  	//set up our server options
    67  	var serverOpts []grpc.ServerOption
    68  
    69  	secureConfig := serverConfig.SecOpts
    70  	if secureConfig.UseTLS {
    71  		//both key and cert are required
    72  		if secureConfig.Key != nil && secureConfig.Certificate != nil {
    73  			//load server public and private keys
    74  			cert, err := tls.X509KeyPair(secureConfig.Certificate, secureConfig.Key)
    75  			if err != nil {
    76  				return nil, err
    77  			}
    78  
    79  			grpcServer.serverCertificate.Store(cert)
    80  
    81  			//set up our TLS config
    82  			if len(secureConfig.CipherSuites) == 0 {
    83  				secureConfig.CipherSuites = DefaultTLSCipherSuites
    84  			}
    85  			getCert := func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
    86  				cert := grpcServer.serverCertificate.Load().(tls.Certificate)
    87  				return &cert, nil
    88  			}
    89  
    90  			grpcServer.tls = NewTLSConfig(&tls.Config{
    91  				VerifyPeerCertificate:  secureConfig.VerifyCertificate,
    92  				GetCertificate:         getCert,
    93  				SessionTicketsDisabled: true,
    94  				CipherSuites:           secureConfig.CipherSuites,
    95  			})
    96  
    97  			if serverConfig.SecOpts.TimeShift > 0 {
    98  				timeShift := serverConfig.SecOpts.TimeShift
    99  				grpcServer.tls.config.Time = func() time.Time {
   100  					return time.Now().Add((-1) * timeShift)
   101  				}
   102  			}
   103  			grpcServer.tls.config.ClientAuth = tls.RequestClientCert
   104  			//check if client authentication is required
   105  			if secureConfig.RequireClientCert {
   106  				//require TLS client auth
   107  				grpcServer.tls.config.ClientAuth = tls.RequireAndVerifyClientCert
   108  				//if we have client root CAs, create a certPool
   109  				if len(secureConfig.ClientRootCAs) > 0 {
   110  					grpcServer.tls.config.ClientCAs = x509.NewCertPool()
   111  					for _, clientRootCA := range secureConfig.ClientRootCAs {
   112  						err = grpcServer.appendClientRootCA(clientRootCA)
   113  						if err != nil {
   114  							return nil, err
   115  						}
   116  					}
   117  				}
   118  			}
   119  
   120  			// create credentials and add to server options
   121  			creds := NewServerTransportCredentials(grpcServer.tls, serverConfig.Logger)
   122  			serverOpts = append(serverOpts, grpc.Creds(creds))
   123  		} else {
   124  			return nil, errors.New("serverConfig.SecOpts must contain both Key and Certificate when UseTLS is true")
   125  		}
   126  	}
   127  	// set max send and recv msg sizes
   128  	serverOpts = append(serverOpts, grpc.MaxSendMsgSize(MaxSendMsgSize))
   129  	serverOpts = append(serverOpts, grpc.MaxRecvMsgSize(MaxRecvMsgSize))
   130  	// set the keepalive options
   131  	serverOpts = append(serverOpts, ServerKeepaliveOptions(serverConfig.KaOpts)...)
   132  	// set connection timeout
   133  	if serverConfig.ConnectionTimeout <= 0 {
   134  		serverConfig.ConnectionTimeout = DefaultConnectionTimeout
   135  	}
   136  	serverOpts = append(
   137  		serverOpts,
   138  		grpc.ConnectionTimeout(serverConfig.ConnectionTimeout))
   139  	// set the interceptors
   140  	if len(serverConfig.StreamInterceptors) > 0 {
   141  		serverOpts = append(
   142  			serverOpts,
   143  			grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(serverConfig.StreamInterceptors...)),
   144  		)
   145  	}
   146  
   147  	if len(serverConfig.UnaryInterceptors) > 0 {
   148  		serverOpts = append(
   149  			serverOpts,
   150  			grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(serverConfig.UnaryInterceptors...)),
   151  		)
   152  	}
   153  
   154  	if serverConfig.ServerStatsHandler != nil {
   155  		serverOpts = append(serverOpts, grpc.StatsHandler(serverConfig.ServerStatsHandler))
   156  	}
   157  
   158  	grpcServer.server = grpc.NewServer(serverOpts...)
   159  
   160  	if serverConfig.HealthCheckEnabled {
   161  		grpcServer.healthServer = health.NewServer()
   162  		healthpb.RegisterHealthServer(grpcServer.server, grpcServer.healthServer)
   163  	}
   164  
   165  	return grpcServer, nil
   166  }
   167  
   168  // SetServerCertificate assigns the current TLS certificate to be the peer's server certificate
   169  func (gServer *GRPCServer) SetServerCertificate(cert tls.Certificate) {
   170  	gServer.serverCertificate.Store(cert)
   171  }
   172  
   173  // Address returns the listen address for this GRPCServer instance
   174  func (gServer *GRPCServer) Address() string {
   175  	return gServer.address
   176  }
   177  
   178  // Listener returns the net.Listener for the GRPCServer instance
   179  func (gServer *GRPCServer) Listener() net.Listener {
   180  	return gServer.listener
   181  }
   182  
   183  // Server returns the grpc.Server for the GRPCServer instance
   184  func (gServer *GRPCServer) Server() *grpc.Server {
   185  	return gServer.server
   186  }
   187  
   188  // ServerCertificate returns the tls.Certificate used by the grpc.Server
   189  func (gServer *GRPCServer) ServerCertificate() tls.Certificate {
   190  	return gServer.serverCertificate.Load().(tls.Certificate)
   191  }
   192  
   193  // TLSEnabled is a flag indicating whether or not TLS is enabled for the
   194  // GRPCServer instance
   195  func (gServer *GRPCServer) TLSEnabled() bool {
   196  	return gServer.tls != nil
   197  }
   198  
   199  // MutualTLSRequired is a flag indicating whether or not client certificates
   200  // are required for this GRPCServer instance
   201  func (gServer *GRPCServer) MutualTLSRequired() bool {
   202  	return gServer.TLSEnabled() &&
   203  		gServer.tls.Config().ClientAuth == tls.RequireAndVerifyClientCert
   204  }
   205  
   206  // Start starts the underlying grpc.Server
   207  func (gServer *GRPCServer) Start() error {
   208  	// if health check is enabled, set the health status for all registered services
   209  	if gServer.healthServer != nil {
   210  		for name := range gServer.server.GetServiceInfo() {
   211  			gServer.healthServer.SetServingStatus(
   212  				name,
   213  				healthpb.HealthCheckResponse_SERVING,
   214  			)
   215  		}
   216  
   217  		gServer.healthServer.SetServingStatus(
   218  			"",
   219  			healthpb.HealthCheckResponse_SERVING,
   220  		)
   221  	}
   222  	return gServer.server.Serve(gServer.listener)
   223  }
   224  
   225  // Stop stops the underlying grpc.Server
   226  func (gServer *GRPCServer) Stop() {
   227  	gServer.server.Stop()
   228  }
   229  
   230  // internal function to add a PEM-encoded clientRootCA
   231  func (gServer *GRPCServer) appendClientRootCA(clientRoot []byte) error {
   232  	certs, err := pemToX509Certs(clientRoot)
   233  	if err != nil {
   234  		return errors.WithMessage(err, "failed to append client root certificate(s)")
   235  	}
   236  
   237  	if len(certs) < 1 {
   238  		return errors.New("no client root certificates found")
   239  	}
   240  
   241  	for _, cert := range certs {
   242  		gServer.tls.AddClientRootCA(cert)
   243  	}
   244  
   245  	return nil
   246  }
   247  
   248  // SetClientRootCAs sets the list of authorities used to verify client
   249  // certificates based on a list of PEM-encoded X509 certificate authorities
   250  func (gServer *GRPCServer) SetClientRootCAs(clientRoots [][]byte) error {
   251  	gServer.lock.Lock()
   252  	defer gServer.lock.Unlock()
   253  
   254  	certPool := x509.NewCertPool()
   255  
   256  	for _, clientRoot := range clientRoots {
   257  		certs, err := pemToX509Certs(clientRoot)
   258  		if err != nil {
   259  			return errors.WithMessage(err, "failed to set client root certificate(s)")
   260  		}
   261  
   262  		for _, cert := range certs {
   263  			certPool.AddCert(cert)
   264  		}
   265  	}
   266  	gServer.tls.SetClientCAs(certPool)
   267  	return nil
   268  }