github.com/cilium/cilium@v1.16.2/pkg/hubble/relay/server/server.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package server
     5  
     6  import (
     7  	"context"
     8  	"crypto/tls"
     9  	"errors"
    10  	"fmt"
    11  	"net"
    12  	"net/http"
    13  	"strings"
    14  
    15  	"github.com/prometheus/client_golang/prometheus"
    16  	"github.com/prometheus/client_golang/prometheus/collectors"
    17  	"github.com/prometheus/client_golang/prometheus/promhttp"
    18  	"github.com/sirupsen/logrus"
    19  	"golang.org/x/sync/errgroup"
    20  	"google.golang.org/grpc"
    21  	"google.golang.org/grpc/credentials"
    22  	healthpb "google.golang.org/grpc/health/grpc_health_v1"
    23  	"google.golang.org/grpc/reflection"
    24  
    25  	observerpb "github.com/cilium/cilium/api/v1/observer"
    26  	"github.com/cilium/cilium/pkg/hubble/peer"
    27  	peerTypes "github.com/cilium/cilium/pkg/hubble/peer/types"
    28  	"github.com/cilium/cilium/pkg/hubble/relay/defaults"
    29  	"github.com/cilium/cilium/pkg/hubble/relay/observer"
    30  	"github.com/cilium/cilium/pkg/hubble/relay/pool"
    31  )
    32  
    33  var (
    34  	// ErrNoClientTLSConfig is returned when no client TLS config is set unless
    35  	// WithInsecureClient() is provided.
    36  	ErrNoClientTLSConfig = errors.New("no client TLS config is set")
    37  	// ErrNoServerTLSConfig is returned when no server TLS config is set unless
    38  	// WithInsecureServer() is provided.
    39  	ErrNoServerTLSConfig = errors.New("no server TLS config is set")
    40  
    41  	registry = prometheus.NewPedanticRegistry()
    42  )
    43  
    44  func init() {
    45  	registry.MustRegister(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}))
    46  	registry.MustRegister(collectors.NewGoCollector())
    47  }
    48  
    49  // Server is a proxy that connects to a running instance of hubble gRPC server
    50  // via unix domain socket.
    51  type Server struct {
    52  	server           *grpc.Server
    53  	grpcHealthServer *grpc.Server
    54  	pm               *pool.PeerManager
    55  	healthServer     *healthServer
    56  	metricsServer    *http.Server
    57  	opts             options
    58  }
    59  
    60  // New creates a new Server.
    61  func New(options ...Option) (*Server, error) {
    62  	opts := defaultOptions // start with defaults
    63  	options = append(options, DefaultOptions...)
    64  	for _, opt := range options {
    65  		if err := opt(&opts); err != nil {
    66  			return nil, fmt.Errorf("failed to apply option: %w", err)
    67  		}
    68  	}
    69  	if opts.clientTLSConfig == nil && !opts.insecureClient {
    70  		return nil, ErrNoClientTLSConfig
    71  	}
    72  	if opts.serverTLSConfig == nil && !opts.insecureServer {
    73  		return nil, ErrNoServerTLSConfig
    74  	}
    75  
    76  	var peerClientBuilder peerTypes.ClientBuilder = &peerTypes.LocalClientBuilder{
    77  		DialTimeout: opts.dialTimeout,
    78  	}
    79  	if !strings.HasPrefix(opts.peerTarget, "unix://") {
    80  		peerClientBuilder = &peerTypes.RemoteClientBuilder{
    81  			DialTimeout:   opts.dialTimeout,
    82  			TLSConfig:     opts.clientTLSConfig,
    83  			TLSServerName: peer.TLSServerName(defaults.PeerServiceName, opts.clusterName),
    84  		}
    85  	}
    86  
    87  	pm, err := pool.NewPeerManager(
    88  		registry,
    89  		pool.WithPeerServiceAddress(opts.peerTarget),
    90  		pool.WithPeerClientBuilder(peerClientBuilder),
    91  		pool.WithClientConnBuilder(pool.GRPCClientConnBuilder{
    92  			DialTimeout: opts.dialTimeout,
    93  			Options: []grpc.DialOption{
    94  				grpc.WithBlock(),
    95  				grpc.FailOnNonTempDialError(true),
    96  				grpc.WithReturnConnectionError(),
    97  			},
    98  			TLSConfig: opts.clientTLSConfig,
    99  		}),
   100  		pool.WithRetryTimeout(opts.retryTimeout),
   101  		pool.WithLogger(opts.log),
   102  	)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	var serverOpts []grpc.ServerOption
   108  
   109  	for _, interceptor := range opts.grpcUnaryInterceptors {
   110  		serverOpts = append(serverOpts, grpc.UnaryInterceptor(interceptor))
   111  	}
   112  	for _, interceptor := range opts.grpcStreamInterceptors {
   113  		serverOpts = append(serverOpts, grpc.StreamInterceptor(interceptor))
   114  	}
   115  
   116  	if opts.serverTLSConfig != nil {
   117  		tlsConfig := opts.serverTLSConfig.ServerConfig(&tls.Config{
   118  			MinVersion: MinTLSVersion,
   119  		})
   120  		serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(tlsConfig)))
   121  	}
   122  	grpcServer := grpc.NewServer(serverOpts...)
   123  	grpcHealthServer := grpc.NewServer()
   124  
   125  	observerOptions := copyObserverOptionsWithLogger(opts.log, opts.observerOptions)
   126  	observerSrv, err := observer.NewServer(pm, observerOptions...)
   127  	if err != nil {
   128  		return nil, fmt.Errorf("failed to create observer server: %w", err)
   129  	}
   130  	healthSrv := newHealthServer(pm, defaults.HealthCheckInterval)
   131  
   132  	observerpb.RegisterObserverServer(grpcServer, observerSrv)
   133  	healthpb.RegisterHealthServer(grpcServer, healthSrv.svc)
   134  	healthpb.RegisterHealthServer(grpcHealthServer, healthSrv.svc)
   135  	reflection.Register(grpcServer)
   136  
   137  	if opts.grpcMetrics != nil {
   138  		registry.MustRegister(opts.grpcMetrics)
   139  		opts.grpcMetrics.InitializeMetrics(grpcServer)
   140  	}
   141  
   142  	var metricsServer *http.Server
   143  	if opts.metricsListenAddress != "" {
   144  		mux := http.NewServeMux()
   145  		mux.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{}))
   146  		metricsServer = &http.Server{
   147  			Addr:    opts.metricsListenAddress,
   148  			Handler: mux,
   149  		}
   150  	}
   151  
   152  	return &Server{
   153  		pm:               pm,
   154  		server:           grpcServer,
   155  		grpcHealthServer: grpcHealthServer,
   156  		metricsServer:    metricsServer,
   157  		healthServer:     healthSrv,
   158  		opts:             opts,
   159  	}, nil
   160  }
   161  
   162  // Serve starts the hubble-relay server. Serve does not return unless a
   163  // listening fails with fatal errors. Serve will return a non-nil error if
   164  // Stop() is not called.
   165  func (s *Server) Serve() error {
   166  	var eg errgroup.Group
   167  	if s.metricsServer != nil {
   168  		eg.Go(func() error {
   169  			s.opts.log.WithField("address", s.opts.metricsListenAddress).Info("Starting metrics server...")
   170  			return s.metricsServer.ListenAndServe()
   171  		})
   172  	}
   173  
   174  	eg.Go(func() error {
   175  		s.opts.log.WithField("options", fmt.Sprintf("%+v", s.opts)).Info("Starting gRPC server...")
   176  		s.pm.Start()
   177  		s.healthServer.start()
   178  		socket, err := net.Listen("tcp", s.opts.listenAddress)
   179  		if err != nil {
   180  			return fmt.Errorf("failed to listen on tcp socket %s: %w", s.opts.listenAddress, err)
   181  		}
   182  		return s.server.Serve(socket)
   183  	})
   184  
   185  	eg.Go(func() error {
   186  		s.opts.log.WithField("addr", s.opts.healthListenAddress).Info("Starting gRPC health server...")
   187  		socket, err := net.Listen("tcp", s.opts.healthListenAddress)
   188  		if err != nil {
   189  			return fmt.Errorf("failed to listen on %s: %w", s.opts.healthListenAddress, err)
   190  		}
   191  		return s.grpcHealthServer.Serve(socket)
   192  	})
   193  
   194  	return eg.Wait()
   195  }
   196  
   197  // Stop terminates the hubble-relay server.
   198  func (s *Server) Stop() {
   199  	s.opts.log.Info("Stopping server...")
   200  	s.server.Stop()
   201  	if s.metricsServer != nil {
   202  		if err := s.metricsServer.Shutdown(context.Background()); err != nil {
   203  			s.opts.log.WithError(err).Info("Failed to gracefully stop metrics server")
   204  		}
   205  	}
   206  	s.pm.Stop()
   207  	s.healthServer.stop()
   208  	s.opts.log.Info("Server stopped")
   209  }
   210  
   211  // observerOptions returns the configured hubble-relay observer options along
   212  // with the hubble-relay logger.
   213  func copyObserverOptionsWithLogger(log logrus.FieldLogger, options []observer.Option) []observer.Option {
   214  	newOptions := make([]observer.Option, len(options), len(options)+1)
   215  	copy(newOptions, options)
   216  	newOptions = append(newOptions, observer.WithLogger(log))
   217  	return newOptions
   218  }