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 }