99 lines
3.1 KiB
Go
99 lines
3.1 KiB
Go
package grpc
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"net"
|
|
|
|
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
|
"github.com/honeycombio/beeline-go/wrappers/hnygrpc"
|
|
"github.com/jmhodges/clock"
|
|
"github.com/letsencrypt/boulder/cmd"
|
|
bcreds "github.com/letsencrypt/boulder/grpc/creds"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/keepalive"
|
|
"google.golang.org/grpc/status"
|
|
)
|
|
|
|
// CodedError is a alias required to appease go vet
|
|
var CodedError = status.Errorf
|
|
|
|
var errNilTLS = errors.New("boulder/grpc: received nil tls.Config")
|
|
|
|
// NewServer creates a gRPC server that uses the provided *tls.Config, and
|
|
// verifies that clients present a certificate that (a) is signed by one of
|
|
// the configured ClientCAs, and (b) contains at least one
|
|
// subjectAlternativeName matching the accepted list from GRPCServerConfig.
|
|
func NewServer(c *cmd.GRPCServerConfig, tlsConfig *tls.Config, metrics serverMetrics, clk clock.Clock, interceptors ...grpc.UnaryServerInterceptor) (*grpc.Server, net.Listener, error) {
|
|
if tlsConfig == nil {
|
|
return nil, nil, errNilTLS
|
|
}
|
|
acceptedSANs := make(map[string]struct{})
|
|
for _, name := range c.ClientNames {
|
|
acceptedSANs[name] = struct{}{}
|
|
}
|
|
|
|
creds, err := bcreds.NewServerCredentials(tlsConfig, acceptedSANs)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
l, err := net.Listen("tcp", c.Address)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
si := newServerInterceptor(metrics, clk)
|
|
allInterceptors := []grpc.UnaryServerInterceptor{
|
|
si.intercept,
|
|
si.metrics.grpcMetrics.UnaryServerInterceptor(),
|
|
hnygrpc.UnaryServerInterceptor(),
|
|
}
|
|
allInterceptors = append(allInterceptors, interceptors...)
|
|
options := []grpc.ServerOption{
|
|
grpc.Creds(creds),
|
|
grpc.ChainUnaryInterceptor(allInterceptors...),
|
|
}
|
|
if c.MaxConnectionAge.Duration > 0 {
|
|
options = append(options,
|
|
grpc.KeepaliveParams(keepalive.ServerParameters{
|
|
MaxConnectionAge: c.MaxConnectionAge.Duration,
|
|
}))
|
|
}
|
|
return grpc.NewServer(options...), l, nil
|
|
}
|
|
|
|
// serverMetrics is a struct type used to return a few registered metrics from
|
|
// `NewServerMetrics`
|
|
type serverMetrics struct {
|
|
grpcMetrics *grpc_prometheus.ServerMetrics
|
|
rpcLag prometheus.Histogram
|
|
}
|
|
|
|
// NewServerMetrics registers metrics with a registry. It must be called a
|
|
// maximum of once per registry, or there will be conflicting names.
|
|
// It constructs and registers a *grpc_prometheus.ServerMetrics with timing
|
|
// histogram enabled as well as a prometheus Histogram for RPC latency.
|
|
func NewServerMetrics(stats registry) serverMetrics {
|
|
// Create the grpc prometheus server metrics instance and register it
|
|
grpcMetrics := grpc_prometheus.NewServerMetrics()
|
|
grpcMetrics.EnableHandlingTimeHistogram()
|
|
stats.MustRegister(grpcMetrics)
|
|
|
|
// rpcLag is a prometheus histogram tracking the difference between the time
|
|
// the client sent an RPC and the time the server received it. Create and
|
|
// register it.
|
|
rpcLag := prometheus.NewHistogram(
|
|
prometheus.HistogramOpts{
|
|
Name: "grpc_lag",
|
|
Help: "Delta between client RPC send time and server RPC receipt time",
|
|
})
|
|
stats.MustRegister(rpcLag)
|
|
|
|
return serverMetrics{
|
|
grpcMetrics: grpcMetrics,
|
|
rpcLag: rpcLag,
|
|
}
|
|
}
|