github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/lib/server/server.go (about)

     1  package server
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"fmt"
     7  	"net/http"
     8  	"path/filepath"
     9  
    10  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/filemonitor"
    11  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/profile"
    12  	"github.com/prometheus/client_golang/prometheus/promhttp"
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  // Option applies a configuration option to the given config.
    17  type Option func(s *serverConfig)
    18  
    19  func GetListenAndServeFunc(options ...Option) (func() error, error) {
    20  	sc := defaultServerConfig()
    21  	sc.apply(options)
    22  
    23  	return sc.getListenAndServeFunc()
    24  }
    25  
    26  func WithTLS(tlsCertPath, tlsKeyPath, clientCAPath *string) Option {
    27  	return func(sc *serverConfig) {
    28  		sc.tlsCertPath = tlsCertPath
    29  		sc.tlsKeyPath = tlsKeyPath
    30  		sc.clientCAPath = clientCAPath
    31  	}
    32  }
    33  
    34  func WithLogger(logger *logrus.Logger) Option {
    35  	return func(sc *serverConfig) {
    36  		sc.logger = logger
    37  	}
    38  }
    39  
    40  func WithDebug(debug bool) Option {
    41  	return func(sc *serverConfig) {
    42  		sc.debug = debug
    43  	}
    44  }
    45  
    46  type serverConfig struct {
    47  	logger       *logrus.Logger
    48  	tlsCertPath  *string
    49  	tlsKeyPath   *string
    50  	clientCAPath *string
    51  	debug        bool
    52  }
    53  
    54  func (sc *serverConfig) apply(options []Option) {
    55  	for _, o := range options {
    56  		o(sc)
    57  	}
    58  }
    59  
    60  func defaultServerConfig() serverConfig {
    61  	return serverConfig{
    62  		tlsCertPath:  nil,
    63  		tlsKeyPath:   nil,
    64  		clientCAPath: nil,
    65  		logger:       nil,
    66  		debug:        false,
    67  	}
    68  }
    69  func (sc *serverConfig) tlsEnabled() (bool, error) {
    70  	if *sc.tlsCertPath != "" && *sc.tlsKeyPath != "" {
    71  		return true, nil
    72  	}
    73  	if *sc.tlsCertPath != "" || *sc.tlsKeyPath != "" {
    74  		return false, fmt.Errorf("both --tls-key and --tls-crt must be provided for TLS to be enabled")
    75  	}
    76  	return false, nil
    77  }
    78  
    79  func (sc *serverConfig) getAddress(tlsEnabled bool) string {
    80  	if tlsEnabled {
    81  		return ":8443"
    82  	}
    83  	return ":8080"
    84  }
    85  
    86  func (sc serverConfig) getListenAndServeFunc() (func() error, error) {
    87  	tlsEnabled, err := sc.tlsEnabled()
    88  	if err != nil {
    89  		return nil, fmt.Errorf("both --tls-key and --tls-crt must be provided for TLS to be enabled")
    90  	}
    91  
    92  	mux := http.NewServeMux()
    93  	mux.Handle("/metrics", promhttp.Handler())
    94  	mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    95  		w.WriteHeader(http.StatusOK)
    96  	})
    97  	profile.RegisterHandlers(mux, profile.WithTLS(tlsEnabled || !sc.debug))
    98  
    99  	s := http.Server{
   100  		Handler: mux,
   101  		Addr:    sc.getAddress(tlsEnabled),
   102  	}
   103  
   104  	if !tlsEnabled {
   105  		return s.ListenAndServe, nil
   106  	}
   107  
   108  	sc.logger.Info("TLS keys set, using https for metrics")
   109  	certStore, err := filemonitor.NewCertStore(*sc.tlsCertPath, *sc.tlsKeyPath)
   110  	if err != nil {
   111  		return nil, fmt.Errorf("certificate monitoring for metrics (https) failed: %v", err)
   112  	}
   113  
   114  	csw, err := filemonitor.NewWatch(sc.logger, []string{filepath.Dir(*sc.tlsCertPath), filepath.Dir(*sc.tlsKeyPath)}, certStore.HandleFilesystemUpdate)
   115  	if err != nil {
   116  		return nil, fmt.Errorf("error creating cert file watcher: %v", err)
   117  	}
   118  	csw.Run(context.Background())
   119  	certPoolStore, err := filemonitor.NewCertPoolStore(*sc.clientCAPath)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("certificate monitoring for client-ca failed: %v", err)
   122  	}
   123  	cpsw, err := filemonitor.NewWatch(sc.logger, []string{filepath.Dir(*sc.clientCAPath)}, certPoolStore.HandleCABundleUpdate)
   124  	if err != nil {
   125  		return nil, fmt.Errorf("error creating cert file watcher: %v", err)
   126  	}
   127  	cpsw.Run(context.Background())
   128  
   129  	s.TLSConfig = &tls.Config{
   130  		GetCertificate: func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
   131  			return certStore.GetCertificate(), nil
   132  		},
   133  		GetConfigForClient: func(_ *tls.ClientHelloInfo) (*tls.Config, error) {
   134  			var certs []tls.Certificate
   135  			if cert := certStore.GetCertificate(); cert != nil {
   136  				certs = append(certs, *cert)
   137  			}
   138  			return &tls.Config{
   139  				Certificates: certs,
   140  				ClientCAs:    certPoolStore.GetCertPool(),
   141  				ClientAuth:   tls.VerifyClientCertIfGiven,
   142  			}, nil
   143  		},
   144  	}
   145  	return func() error {
   146  		return s.ListenAndServeTLS("", "")
   147  	}, nil
   148  }