github.com/kubearmor/cilium@v1.6.12/api/v1/health/server/server.go (about) 1 // Code generated by go-swagger; DO NOT EDIT. 2 3 package server 4 5 import ( 6 "context" 7 "crypto/tls" 8 "crypto/x509" 9 "errors" 10 "fmt" 11 "io/ioutil" 12 "log" 13 "net" 14 "net/http" 15 "os" 16 "os/signal" 17 "strconv" 18 "sync" 19 "sync/atomic" 20 "syscall" 21 "time" 22 23 "github.com/go-openapi/runtime/flagext" 24 "github.com/go-openapi/swag" 25 flags "github.com/jessevdk/go-flags" 26 "golang.org/x/net/netutil" 27 28 "github.com/cilium/cilium/api/v1/health/server/restapi" 29 "github.com/cilium/cilium/pkg/api" 30 ) 31 32 const ( 33 schemeHTTP = "http" 34 schemeHTTPS = "https" 35 schemeUnix = "unix" 36 ) 37 38 var defaultSchemes []string 39 40 func init() { 41 defaultSchemes = []string{ 42 schemeUnix, 43 } 44 } 45 46 // NewServer creates a new api cilium health server but does not configure it 47 func NewServer(api *restapi.CiliumHealthAPI) *Server { 48 s := new(Server) 49 50 s.shutdown = make(chan struct{}) 51 s.api = api 52 s.interrupt = make(chan os.Signal, 1) 53 return s 54 } 55 56 // ConfigureAPI configures the API and handlers. 57 func (s *Server) ConfigureAPI() { 58 if s.api != nil { 59 s.handler = configureAPI(s.api) 60 } 61 } 62 63 // ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse 64 func (s *Server) ConfigureFlags() { 65 if s.api != nil { 66 configureFlags(s.api) 67 } 68 } 69 70 // Server for the cilium health API 71 type Server struct { 72 EnabledListeners []string `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"` 73 CleanupTimeout time.Duration `long:"cleanup-timeout" description:"grace period for which to wait before killing idle connections" default:"10s"` 74 GracefulTimeout time.Duration `long:"graceful-timeout" description:"grace period for which to wait before shutting down the server" default:"15s"` 75 MaxHeaderSize flagext.ByteSize `long:"max-header-size" description:"controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body." default:"1MiB"` 76 77 SocketPath flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/cilium-health.sock"` 78 domainSocketL net.Listener 79 80 Host string `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"` 81 Port int `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"` 82 ListenLimit int `long:"listen-limit" description:"limit the number of outstanding requests"` 83 KeepAlive time.Duration `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"` 84 ReadTimeout time.Duration `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"` 85 WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"` 86 httpServerL net.Listener 87 88 TLSHost string `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"` 89 TLSPort int `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"` 90 TLSCertificate flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"` 91 TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure conections" env:"TLS_PRIVATE_KEY"` 92 TLSCACertificate flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"` 93 TLSListenLimit int `long:"tls-listen-limit" description:"limit the number of outstanding requests"` 94 TLSKeepAlive time.Duration `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"` 95 TLSReadTimeout time.Duration `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"` 96 TLSWriteTimeout time.Duration `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"` 97 httpsServerL net.Listener 98 99 api *restapi.CiliumHealthAPI 100 handler http.Handler 101 hasListeners bool 102 shutdown chan struct{} 103 shuttingDown int32 104 interrupted bool 105 interrupt chan os.Signal 106 } 107 108 // Logf logs message either via defined user logger or via system one if no user logger is defined. 109 func (s *Server) Logf(f string, args ...interface{}) { 110 if s.api != nil && s.api.Logger != nil { 111 s.api.Logger(f, args...) 112 } else { 113 log.Printf(f, args...) 114 } 115 } 116 117 // Fatalf logs message either via defined user logger or via system one if no user logger is defined. 118 // Exits with non-zero status after printing 119 func (s *Server) Fatalf(f string, args ...interface{}) { 120 if s.api != nil && s.api.Logger != nil { 121 s.api.Logger(f, args...) 122 os.Exit(1) 123 } else { 124 log.Fatalf(f, args...) 125 } 126 } 127 128 // SetAPI configures the server with the specified API. Needs to be called before Serve 129 func (s *Server) SetAPI(api *restapi.CiliumHealthAPI) { 130 if api == nil { 131 s.api = nil 132 s.handler = nil 133 return 134 } 135 136 s.api = api 137 s.api.Logger = log.Printf 138 s.handler = configureAPI(api) 139 } 140 141 func (s *Server) hasScheme(scheme string) bool { 142 schemes := s.EnabledListeners 143 if len(schemes) == 0 { 144 schemes = defaultSchemes 145 } 146 147 for _, v := range schemes { 148 if v == scheme { 149 return true 150 } 151 } 152 return false 153 } 154 155 // Serve the api 156 func (s *Server) Serve() (err error) { 157 if !s.hasListeners { 158 if err = s.Listen(); err != nil { 159 return err 160 } 161 } 162 163 // set default handler, if none is set 164 if s.handler == nil { 165 if s.api == nil { 166 return errors.New("can't create the default handler, as no api is set") 167 } 168 169 s.SetHandler(s.api.Serve(nil)) 170 } 171 172 wg := new(sync.WaitGroup) 173 once := new(sync.Once) 174 signalNotify(s.interrupt) 175 go handleInterrupt(once, s) 176 177 servers := []*http.Server{} 178 wg.Add(1) 179 go s.handleShutdown(wg, &servers) 180 181 if s.hasScheme(schemeUnix) { 182 domainSocket := new(http.Server) 183 domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize) 184 domainSocket.Handler = s.handler 185 if int64(s.CleanupTimeout) > 0 { 186 domainSocket.IdleTimeout = s.CleanupTimeout 187 } 188 189 configureServer(domainSocket, "unix", string(s.SocketPath)) 190 191 if os.Getuid() == 0 { 192 err := api.SetDefaultPermissions(string(s.SocketPath)) 193 if err != nil { 194 return err 195 } 196 } 197 servers = append(servers, domainSocket) 198 wg.Add(1) 199 s.Logf("Serving cilium health at unix://%s", s.SocketPath) 200 go func(l net.Listener) { 201 defer wg.Done() 202 if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed { 203 s.Fatalf("%v", err) 204 } 205 s.Logf("Stopped serving cilium health at unix://%s", s.SocketPath) 206 }(s.domainSocketL) 207 } 208 209 if s.hasScheme(schemeHTTP) { 210 httpServer := new(http.Server) 211 httpServer.MaxHeaderBytes = int(s.MaxHeaderSize) 212 httpServer.ReadTimeout = s.ReadTimeout 213 httpServer.WriteTimeout = s.WriteTimeout 214 httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0) 215 if s.ListenLimit > 0 { 216 s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit) 217 } 218 219 if int64(s.CleanupTimeout) > 0 { 220 httpServer.IdleTimeout = s.CleanupTimeout 221 } 222 223 httpServer.Handler = s.handler 224 225 configureServer(httpServer, "http", s.httpServerL.Addr().String()) 226 227 servers = append(servers, httpServer) 228 wg.Add(1) 229 s.Logf("Serving cilium health at http://%s", s.httpServerL.Addr()) 230 go func(l net.Listener) { 231 defer wg.Done() 232 if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed { 233 s.Fatalf("%v", err) 234 } 235 s.Logf("Stopped serving cilium health at http://%s", l.Addr()) 236 }(s.httpServerL) 237 } 238 239 if s.hasScheme(schemeHTTPS) { 240 httpsServer := new(http.Server) 241 httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize) 242 httpsServer.ReadTimeout = s.TLSReadTimeout 243 httpsServer.WriteTimeout = s.TLSWriteTimeout 244 httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0) 245 if s.TLSListenLimit > 0 { 246 s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit) 247 } 248 if int64(s.CleanupTimeout) > 0 { 249 httpsServer.IdleTimeout = s.CleanupTimeout 250 } 251 httpsServer.Handler = s.handler 252 253 // Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go 254 httpsServer.TLSConfig = &tls.Config{ 255 // Causes servers to use Go's default ciphersuite preferences, 256 // which are tuned to avoid attacks. Does nothing on clients. 257 PreferServerCipherSuites: true, 258 // Only use curves which have assembly implementations 259 // https://github.com/golang/go/tree/master/src/crypto/elliptic 260 CurvePreferences: []tls.CurveID{tls.CurveP256}, 261 // Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility 262 NextProtos: []string{"http/1.1", "h2"}, 263 // https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols 264 MinVersion: tls.VersionTLS12, 265 // These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy 266 CipherSuites: []uint16{ 267 tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 268 tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 269 tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 270 tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 271 tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 272 tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 273 }, 274 } 275 276 // build standard config from server options 277 if s.TLSCertificate != "" && s.TLSCertificateKey != "" { 278 httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1) 279 httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(string(s.TLSCertificate), string(s.TLSCertificateKey)) 280 if err != nil { 281 return err 282 } 283 } 284 285 if s.TLSCACertificate != "" { 286 // include specified CA certificate 287 caCert, caCertErr := ioutil.ReadFile(string(s.TLSCACertificate)) 288 if caCertErr != nil { 289 return caCertErr 290 } 291 caCertPool := x509.NewCertPool() 292 ok := caCertPool.AppendCertsFromPEM(caCert) 293 if !ok { 294 return fmt.Errorf("cannot parse CA certificate") 295 } 296 httpsServer.TLSConfig.ClientCAs = caCertPool 297 httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert 298 } 299 300 // call custom TLS configurator 301 configureTLS(httpsServer.TLSConfig) 302 303 if len(httpsServer.TLSConfig.Certificates) == 0 { 304 // after standard and custom config are passed, this ends up with no certificate 305 if s.TLSCertificate == "" { 306 if s.TLSCertificateKey == "" { 307 s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified") 308 } 309 s.Fatalf("the required flag `--tls-certificate` was not specified") 310 } 311 if s.TLSCertificateKey == "" { 312 s.Fatalf("the required flag `--tls-key` was not specified") 313 } 314 // this happens with a wrong custom TLS configurator 315 s.Fatalf("no certificate was configured for TLS") 316 } 317 318 // must have at least one certificate or panics 319 httpsServer.TLSConfig.BuildNameToCertificate() 320 321 configureServer(httpsServer, "https", s.httpsServerL.Addr().String()) 322 323 servers = append(servers, httpsServer) 324 wg.Add(1) 325 s.Logf("Serving cilium health at https://%s", s.httpsServerL.Addr()) 326 go func(l net.Listener) { 327 defer wg.Done() 328 if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed { 329 s.Fatalf("%v", err) 330 } 331 s.Logf("Stopped serving cilium health at https://%s", l.Addr()) 332 }(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig)) 333 } 334 335 wg.Wait() 336 return nil 337 } 338 339 // Listen creates the listeners for the server 340 func (s *Server) Listen() error { 341 if s.hasListeners { // already done this 342 return nil 343 } 344 345 if s.hasScheme(schemeHTTPS) { 346 // Use http host if https host wasn't defined 347 if s.TLSHost == "" { 348 s.TLSHost = s.Host 349 } 350 // Use http listen limit if https listen limit wasn't defined 351 if s.TLSListenLimit == 0 { 352 s.TLSListenLimit = s.ListenLimit 353 } 354 // Use http tcp keep alive if https tcp keep alive wasn't defined 355 if int64(s.TLSKeepAlive) == 0 { 356 s.TLSKeepAlive = s.KeepAlive 357 } 358 // Use http read timeout if https read timeout wasn't defined 359 if int64(s.TLSReadTimeout) == 0 { 360 s.TLSReadTimeout = s.ReadTimeout 361 } 362 // Use http write timeout if https write timeout wasn't defined 363 if int64(s.TLSWriteTimeout) == 0 { 364 s.TLSWriteTimeout = s.WriteTimeout 365 } 366 } 367 368 if s.hasScheme(schemeUnix) { 369 domSockListener, err := net.Listen("unix", string(s.SocketPath)) 370 if err != nil { 371 return err 372 } 373 s.domainSocketL = domSockListener 374 } 375 376 if s.hasScheme(schemeHTTP) { 377 listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port))) 378 if err != nil { 379 return err 380 } 381 382 h, p, err := swag.SplitHostPort(listener.Addr().String()) 383 if err != nil { 384 return err 385 } 386 s.Host = h 387 s.Port = p 388 s.httpServerL = listener 389 } 390 391 if s.hasScheme(schemeHTTPS) { 392 tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort))) 393 if err != nil { 394 return err 395 } 396 397 sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String()) 398 if err != nil { 399 return err 400 } 401 s.TLSHost = sh 402 s.TLSPort = sp 403 s.httpsServerL = tlsListener 404 } 405 406 s.hasListeners = true 407 return nil 408 } 409 410 // Shutdown server and clean up resources 411 func (s *Server) Shutdown() error { 412 if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) { 413 close(s.shutdown) 414 } 415 return nil 416 } 417 418 func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) { 419 // wg.Done must occur last, after s.api.ServerShutdown() 420 // (to preserve old behaviour) 421 defer wg.Done() 422 423 <-s.shutdown 424 425 servers := *serversPtr 426 427 ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout) 428 defer cancel() 429 430 shutdownChan := make(chan bool) 431 for i := range servers { 432 server := servers[i] 433 go func() { 434 var success bool 435 defer func() { 436 shutdownChan <- success 437 }() 438 if err := server.Shutdown(ctx); err != nil { 439 // Error from closing listeners, or context timeout: 440 s.Logf("HTTP server Shutdown: %v", err) 441 } else { 442 success = true 443 } 444 }() 445 } 446 447 // Wait until all listeners have successfully shut down before calling ServerShutdown 448 success := true 449 for range servers { 450 success = success && <-shutdownChan 451 } 452 if success { 453 s.api.ServerShutdown() 454 } 455 } 456 457 // GetHandler returns a handler useful for testing 458 func (s *Server) GetHandler() http.Handler { 459 return s.handler 460 } 461 462 // SetHandler allows for setting a http handler on this server 463 func (s *Server) SetHandler(handler http.Handler) { 464 s.handler = handler 465 } 466 467 // UnixListener returns the domain socket listener 468 func (s *Server) UnixListener() (net.Listener, error) { 469 if !s.hasListeners { 470 if err := s.Listen(); err != nil { 471 return nil, err 472 } 473 } 474 return s.domainSocketL, nil 475 } 476 477 // HTTPListener returns the http listener 478 func (s *Server) HTTPListener() (net.Listener, error) { 479 if !s.hasListeners { 480 if err := s.Listen(); err != nil { 481 return nil, err 482 } 483 } 484 return s.httpServerL, nil 485 } 486 487 // TLSListener returns the https listener 488 func (s *Server) TLSListener() (net.Listener, error) { 489 if !s.hasListeners { 490 if err := s.Listen(); err != nil { 491 return nil, err 492 } 493 } 494 return s.httpsServerL, nil 495 } 496 497 func handleInterrupt(once *sync.Once, s *Server) { 498 once.Do(func() { 499 for range s.interrupt { 500 if s.interrupted { 501 s.Logf("Server already shutting down") 502 continue 503 } 504 s.interrupted = true 505 s.Logf("Shutting down... ") 506 if err := s.Shutdown(); err != nil { 507 s.Logf("HTTP server Shutdown: %v", err) 508 } 509 } 510 }) 511 } 512 513 func signalNotify(interrupt chan<- os.Signal) { 514 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) 515 }