github.com/cilium/cilium@v1.16.2/api/v1/operator/server/server.go (about) 1 // Code generated by go-swagger; DO NOT EDIT. 2 3 // Copyright Authors of Cilium 4 // SPDX-License-Identifier: Apache-2.0 5 6 package server 7 8 import ( 9 "context" 10 "crypto/tls" 11 "crypto/x509" 12 "errors" 13 "fmt" 14 "log" 15 "net" 16 "net/http" 17 "os" 18 "strconv" 19 "sync" 20 "time" 21 22 "github.com/go-openapi/loads" 23 "github.com/go-openapi/runtime/middleware" 24 "github.com/go-openapi/swag" 25 "github.com/sirupsen/logrus" 26 "github.com/spf13/pflag" 27 "golang.org/x/net/netutil" 28 29 "github.com/cilium/cilium/api/v1/operator/server/restapi" 30 "github.com/cilium/cilium/api/v1/operator/server/restapi/cluster" 31 "github.com/cilium/cilium/api/v1/operator/server/restapi/metrics" 32 "github.com/cilium/cilium/api/v1/operator/server/restapi/operator" 33 "github.com/cilium/hive/cell" 34 35 "github.com/cilium/cilium/pkg/api" 36 "github.com/cilium/cilium/pkg/hive" 37 ) 38 39 // Cell implements the cilium operator REST API server when provided 40 // the required request handlers. 41 var Cell = cell.Module( 42 "cilium-operator-server", 43 "cilium operator server", 44 45 cell.Provide(newForCell), 46 APICell, 47 ) 48 49 // APICell provides the restapi.CiliumOperatorAPI type, populated 50 // with the request handlers. This cell is an alternative to 'Cell' when only 51 // the API type is required and not the full server implementation. 52 var APICell = cell.Provide(newAPI) 53 54 type apiParams struct { 55 cell.In 56 57 Spec *Spec 58 59 Middleware middleware.Builder `name:"cilium-operator-middleware" optional:"true"` 60 61 ClusterGetClusterHandler cluster.GetClusterHandler 62 OperatorGetHealthzHandler operator.GetHealthzHandler 63 MetricsGetMetricsHandler metrics.GetMetricsHandler 64 } 65 66 func newAPI(p apiParams) *restapi.CiliumOperatorAPI { 67 api := restapi.NewCiliumOperatorAPI(p.Spec.Document) 68 69 // Construct the API from the provided handlers 70 71 api.ClusterGetClusterHandler = p.ClusterGetClusterHandler 72 api.OperatorGetHealthzHandler = p.OperatorGetHealthzHandler 73 api.MetricsGetMetricsHandler = p.MetricsGetMetricsHandler 74 75 // Inject custom middleware if provided by Hive 76 if p.Middleware != nil { 77 api.Middleware = func(builder middleware.Builder) http.Handler { 78 return p.Middleware(api.Context().APIHandler(builder)) 79 } 80 } 81 82 return api 83 } 84 85 type serverParams struct { 86 cell.In 87 88 Lifecycle cell.Lifecycle 89 Shutdowner hive.Shutdowner 90 Logger logrus.FieldLogger 91 Spec *Spec 92 API *restapi.CiliumOperatorAPI 93 } 94 95 func newForCell(p serverParams) (*Server, error) { 96 s := NewServer(p.API) 97 s.shutdowner = p.Shutdowner 98 s.logger = p.Logger 99 p.Lifecycle.Append(s) 100 return s, nil 101 } 102 103 const ( 104 schemeHTTP = "http" 105 schemeHTTPS = "https" 106 schemeUnix = "unix" 107 ) 108 109 var defaultSchemes []string 110 111 func init() { 112 defaultSchemes = []string{ 113 schemeHTTP, 114 schemeUnix, 115 } 116 } 117 118 var ( 119 enabledListeners []string 120 gracefulTimeout time.Duration 121 maxHeaderSize int 122 123 socketPath string 124 125 host string 126 port int 127 listenLimit int 128 keepAlive time.Duration 129 readTimeout time.Duration 130 writeTimeout time.Duration 131 132 tlsHost string 133 tlsPort int 134 tlsListenLimit int 135 tlsKeepAlive time.Duration 136 tlsReadTimeout time.Duration 137 tlsWriteTimeout time.Duration 138 tlsCertificate string 139 tlsCertificateKey string 140 tlsCACertificate string 141 ) 142 143 type ServerConfig struct { 144 EnableCiliumOperatorServerAccess []string 145 } 146 147 var ( 148 defaultServerConfig = ServerConfig{ 149 EnableCiliumOperatorServerAccess: []string{"*"}, 150 } 151 AdminEnableFlag = "enable-cilium-operator-server-access" 152 ) 153 154 func (cfg ServerConfig) Flags(flags *pflag.FlagSet) { 155 flags.StringSlice(AdminEnableFlag, cfg.EnableCiliumOperatorServerAccess, 156 "List of cilium operator APIs which are administratively enabled. Supports '*'.") 157 } 158 159 var SpecCell = cell.Module( 160 "cilium-operator-spec", 161 "cilium operator Specification", 162 163 cell.Config(defaultServerConfig), 164 cell.Provide(newSpec), 165 ) 166 167 type Spec struct { 168 *loads.Document 169 170 // DeniedAPIs is a set of APIs that are administratively disabled. 171 DeniedAPIs api.PathSet 172 } 173 174 func newSpec(cfg ServerConfig) (*Spec, error) { 175 swaggerSpec, err := loads.Analyzed(SwaggerJSON, "") 176 if err != nil { 177 return nil, fmt.Errorf("failed to load swagger spec: %w", err) 178 } 179 180 deniedAPIs, err := api.AllowedFlagsToDeniedPaths(swaggerSpec, cfg.EnableCiliumOperatorServerAccess) 181 if err != nil { 182 return nil, fmt.Errorf("failed to parse %q flag: %w", 183 AdminEnableFlag, err) 184 } 185 186 return &Spec{ 187 Document: swaggerSpec, 188 DeniedAPIs: deniedAPIs, 189 }, nil 190 } 191 192 // NewServer creates a new api cilium operator server but does not configure it 193 func NewServer(api *restapi.CiliumOperatorAPI) *Server { 194 s := new(Server) 195 s.api = api 196 return s 197 } 198 199 // ConfigureAPI configures the API and handlers. 200 func (s *Server) ConfigureAPI() { 201 if s.api != nil { 202 s.handler = configureAPI(s.api) 203 } 204 } 205 206 // ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse 207 func (s *Server) ConfigureFlags() { 208 if s.api != nil { 209 configureFlags(s.api) 210 } 211 } 212 213 // Server for the cilium operator API 214 type Server struct { 215 EnabledListeners []string 216 CleanupTimeout time.Duration 217 GracefulTimeout time.Duration 218 MaxHeaderSize int 219 220 SocketPath string 221 domainSocketL *net.UnixListener 222 223 Host string 224 Port int 225 ListenLimit int 226 KeepAlive time.Duration 227 ReadTimeout time.Duration 228 WriteTimeout time.Duration 229 httpServerL net.Listener 230 231 TLSHost string 232 TLSPort int 233 TLSCertificate string 234 TLSCertificateKey string 235 TLSCACertificate string 236 TLSListenLimit int 237 TLSKeepAlive time.Duration 238 TLSReadTimeout time.Duration 239 TLSWriteTimeout time.Duration 240 httpsServerL net.Listener 241 242 api *restapi.CiliumOperatorAPI 243 handler http.Handler 244 hasListeners bool 245 servers []*http.Server 246 247 wg sync.WaitGroup 248 shutdowner hive.Shutdowner 249 logger logrus.FieldLogger 250 } 251 252 // Logf logs message either via defined user logger or via system one if no user logger is defined. 253 func (s *Server) Logf(f string, args ...interface{}) { 254 if s.logger != nil { 255 s.logger.Infof(f, args...) 256 } else if s.api != nil && s.api.Logger != nil { 257 s.api.Logger(f, args...) 258 } else { 259 log.Printf(f, args...) 260 } 261 } 262 263 // Fatalf logs message either via defined user logger or via system one if no user logger is defined. 264 // Exits with non-zero status after printing 265 func (s *Server) Fatalf(f string, args ...interface{}) { 266 if s.shutdowner != nil { 267 s.shutdowner.Shutdown(hive.ShutdownWithError(fmt.Errorf(f, args...))) 268 } else if s.api != nil && s.api.Logger != nil { 269 s.api.Logger(f, args...) 270 os.Exit(1) 271 } else { 272 log.Fatalf(f, args...) 273 } 274 } 275 276 // SetAPI configures the server with the specified API. Needs to be called before Serve 277 func (s *Server) SetAPI(api *restapi.CiliumOperatorAPI) { 278 if api == nil { 279 s.api = nil 280 s.handler = nil 281 return 282 } 283 284 s.api = api 285 s.handler = configureAPI(api) 286 } 287 288 // GetAPI returns the configured API. Modifications on the API must be performed 289 // before server is started. 290 func (s *Server) GetAPI() *restapi.CiliumOperatorAPI { 291 return s.api 292 } 293 294 func (s *Server) hasScheme(scheme string) bool { 295 schemes := s.EnabledListeners 296 if len(schemes) == 0 { 297 schemes = defaultSchemes 298 } 299 300 for _, v := range schemes { 301 if v == scheme { 302 return true 303 } 304 } 305 return false 306 } 307 308 func (s *Server) Serve() error { 309 // TODO remove when this is not needed for compatibility anymore 310 if err := s.Start(context.TODO()); err != nil { 311 return err 312 } 313 s.wg.Wait() 314 return nil 315 } 316 317 // Start the server 318 func (s *Server) Start(cell.HookContext) (err error) { 319 if !s.hasListeners { 320 if err = s.Listen(); err != nil { 321 return err 322 } 323 } 324 325 if len(s.servers) != 0 { 326 return errors.New("already started") 327 } 328 329 // set default handler, if none is set 330 if s.handler == nil { 331 if s.api == nil { 332 return errors.New("can't create the default handler, as no api is set") 333 } 334 335 s.ConfigureAPI() 336 s.SetHandler(s.api.Serve(nil)) 337 } 338 339 if s.hasScheme(schemeUnix) { 340 domainSocket := new(http.Server) 341 domainSocket.MaxHeaderBytes = s.MaxHeaderSize 342 domainSocket.Handler = s.handler 343 if int64(s.CleanupTimeout) > 0 { 344 domainSocket.IdleTimeout = s.CleanupTimeout 345 } 346 347 configureServer(domainSocket, "unix", s.SocketPath) 348 349 if os.Getuid() == 0 { 350 err := api.SetDefaultPermissions(s.SocketPath) 351 if err != nil { 352 return err 353 } 354 } 355 s.servers = append(s.servers, domainSocket) 356 s.wg.Add(1) 357 s.Logf("Serving cilium operator at unix://%s", s.SocketPath) 358 go func(l net.Listener) { 359 defer s.wg.Done() 360 if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed { 361 s.Fatalf("%v", err) 362 } 363 s.Logf("Stopped serving cilium operator at unix://%s", s.SocketPath) 364 }(s.domainSocketL) 365 } 366 367 if s.hasScheme(schemeHTTP) { 368 httpServer := new(http.Server) 369 httpServer.MaxHeaderBytes = s.MaxHeaderSize 370 httpServer.ReadTimeout = s.ReadTimeout 371 httpServer.WriteTimeout = s.WriteTimeout 372 httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0) 373 if s.ListenLimit > 0 { 374 s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit) 375 } 376 377 if int64(s.CleanupTimeout) > 0 { 378 httpServer.IdleTimeout = s.CleanupTimeout 379 } 380 381 httpServer.Handler = s.handler 382 383 configureServer(httpServer, "http", s.httpServerL.Addr().String()) 384 385 s.servers = append(s.servers, httpServer) 386 s.wg.Add(1) 387 s.Logf("Serving cilium operator at http://%s", s.httpServerL.Addr()) 388 go func(l net.Listener) { 389 defer s.wg.Done() 390 if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed { 391 s.Fatalf("%v", err) 392 } 393 s.Logf("Stopped serving cilium operator at http://%s", l.Addr()) 394 }(s.httpServerL) 395 } 396 397 if s.hasScheme(schemeHTTPS) { 398 httpsServer := new(http.Server) 399 httpsServer.MaxHeaderBytes = s.MaxHeaderSize 400 httpsServer.ReadTimeout = s.TLSReadTimeout 401 httpsServer.WriteTimeout = s.TLSWriteTimeout 402 httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0) 403 if s.TLSListenLimit > 0 { 404 s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit) 405 } 406 if int64(s.CleanupTimeout) > 0 { 407 httpsServer.IdleTimeout = s.CleanupTimeout 408 } 409 httpsServer.Handler = s.handler 410 411 // Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go 412 httpsServer.TLSConfig = &tls.Config{ 413 // Causes servers to use Go's default ciphersuite preferences, 414 // which are tuned to avoid attacks. Does nothing on clients. 415 PreferServerCipherSuites: true, 416 // Only use curves which have assembly implementations 417 // https://github.com/golang/go/tree/master/src/crypto/elliptic 418 CurvePreferences: []tls.CurveID{tls.CurveP256}, 419 // Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility 420 NextProtos: []string{"h2", "http/1.1"}, 421 // https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols 422 MinVersion: tls.VersionTLS12, 423 // These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy 424 CipherSuites: []uint16{ 425 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 426 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 427 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 428 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 429 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 430 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 431 }, 432 } 433 434 // build standard config from server options 435 if s.TLSCertificate != "" && s.TLSCertificateKey != "" { 436 httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1) 437 httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(s.TLSCertificate, s.TLSCertificateKey) 438 if err != nil { 439 return err 440 } 441 } 442 443 if s.TLSCACertificate != "" { 444 // include specified CA certificate 445 caCert, caCertErr := os.ReadFile(s.TLSCACertificate) 446 if caCertErr != nil { 447 return caCertErr 448 } 449 caCertPool := x509.NewCertPool() 450 ok := caCertPool.AppendCertsFromPEM(caCert) 451 if !ok { 452 return fmt.Errorf("cannot parse CA certificate") 453 } 454 httpsServer.TLSConfig.ClientCAs = caCertPool 455 httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert 456 } 457 458 // call custom TLS configurator 459 configureTLS(httpsServer.TLSConfig) 460 461 if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil { 462 // after standard and custom config are passed, this ends up with no certificate 463 if s.TLSCertificate == "" { 464 if s.TLSCertificateKey == "" { 465 s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified") 466 } 467 s.Fatalf("the required flag `--tls-certificate` was not specified") 468 } 469 if s.TLSCertificateKey == "" { 470 s.Fatalf("the required flag `--tls-key` was not specified") 471 } 472 // this happens with a wrong custom TLS configurator 473 s.Fatalf("no certificate was configured for TLS") 474 } 475 476 // must have at least one certificate or panics 477 httpsServer.TLSConfig.BuildNameToCertificate() 478 479 configureServer(httpsServer, "https", s.httpsServerL.Addr().String()) 480 481 s.servers = append(s.servers, httpsServer) 482 s.wg.Add(1) 483 s.Logf("Serving cilium operator at https://%s", s.httpsServerL.Addr()) 484 go func(l net.Listener) { 485 defer s.wg.Done() 486 if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed { 487 s.Fatalf("%v", err) 488 } 489 s.Logf("Stopped serving cilium operator at https://%s", l.Addr()) 490 }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig)) 491 } 492 493 return nil 494 } 495 496 // Listen creates the listeners for the server 497 func (s *Server) Listen() error { 498 if s.hasListeners { // already done this 499 return nil 500 } 501 502 if s.hasScheme(schemeHTTPS) { 503 // Use http host if https host wasn't defined 504 if s.TLSHost == "" { 505 s.TLSHost = s.Host 506 } 507 // Use http listen limit if https listen limit wasn't defined 508 if s.TLSListenLimit == 0 { 509 s.TLSListenLimit = s.ListenLimit 510 } 511 // Use http tcp keep alive if https tcp keep alive wasn't defined 512 if int64(s.TLSKeepAlive) == 0 { 513 s.TLSKeepAlive = s.KeepAlive 514 } 515 // Use http read timeout if https read timeout wasn't defined 516 if int64(s.TLSReadTimeout) == 0 { 517 s.TLSReadTimeout = s.ReadTimeout 518 } 519 // Use http write timeout if https write timeout wasn't defined 520 if int64(s.TLSWriteTimeout) == 0 { 521 s.TLSWriteTimeout = s.WriteTimeout 522 } 523 } 524 525 if s.hasScheme(schemeUnix) { 526 addr, err := net.ResolveUnixAddr("unix", s.SocketPath) 527 if err != nil { 528 return err 529 } 530 domSockListener, err := net.ListenUnix("unix", addr) 531 if err != nil { 532 return err 533 } 534 s.domainSocketL = domSockListener 535 } 536 537 if s.hasScheme(schemeHTTP) { 538 listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port))) 539 if err != nil { 540 return err 541 } 542 543 h, p, err := swag.SplitHostPort(listener.Addr().String()) 544 if err != nil { 545 return err 546 } 547 s.Host = h 548 s.Port = p 549 s.httpServerL = listener 550 } 551 552 if s.hasScheme(schemeHTTPS) { 553 tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort))) 554 if err != nil { 555 return err 556 } 557 558 sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String()) 559 if err != nil { 560 return err 561 } 562 s.TLSHost = sh 563 s.TLSPort = sp 564 s.httpsServerL = tlsListener 565 } 566 567 s.hasListeners = true 568 return nil 569 } 570 571 // Shutdown server and clean up resources 572 func (s *Server) Shutdown() error { 573 ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout) 574 defer cancel() 575 return s.Stop(ctx) 576 } 577 578 func (s *Server) Stop(ctx cell.HookContext) error { 579 // first execute the pre-shutdown hook 580 s.api.PreServerShutdown() 581 582 shutdownChan := make(chan bool) 583 for i := range s.servers { 584 server := s.servers[i] 585 go func() { 586 var success bool 587 defer func() { 588 shutdownChan <- success 589 }() 590 if err := server.Shutdown(ctx); err != nil { 591 s.Logf("HTTP server Shutdown: %v", err) 592 593 // Forcefully close open connections. 594 server.Close() 595 } else { 596 success = true 597 } 598 }() 599 } 600 601 // Wait until all listeners have successfully shut down before calling ServerShutdown 602 success := true 603 for range s.servers { 604 success = success && <-shutdownChan 605 } 606 if success { 607 s.api.ServerShutdown() 608 } 609 610 s.wg.Wait() 611 s.servers = nil 612 613 return nil 614 } 615 616 // GetHandler returns a handler useful for testing 617 func (s *Server) GetHandler() http.Handler { 618 return s.handler 619 } 620 621 // SetHandler allows for setting a http handler on this server 622 func (s *Server) SetHandler(handler http.Handler) { 623 s.handler = handler 624 } 625 626 // UnixListener returns the domain socket listener 627 func (s *Server) UnixListener() (*net.UnixListener, error) { 628 if !s.hasListeners { 629 if err := s.Listen(); err != nil { 630 return nil, err 631 } 632 } 633 return s.domainSocketL, nil 634 } 635 636 // HTTPListener returns the http listener 637 func (s *Server) HTTPListener() (net.Listener, error) { 638 if !s.hasListeners { 639 if err := s.Listen(); err != nil { 640 return nil, err 641 } 642 } 643 return s.httpServerL, nil 644 } 645 646 // TLSListener returns the https listener 647 func (s *Server) TLSListener() (net.Listener, error) { 648 if !s.hasListeners { 649 if err := s.Listen(); err != nil { 650 return nil, err 651 } 652 } 653 return s.httpsServerL, nil 654 }