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  }