diff --git a/pkg/server/metrics.go b/pkg/server/metrics.go index 1ae011aca1..86fc0711fd 100644 --- a/pkg/server/metrics.go +++ b/pkg/server/metrics.go @@ -18,6 +18,7 @@ package server import ( "context" + "crypto/tls" "encoding/json" "expvar" "net/http" @@ -61,16 +62,20 @@ type MetricsCollection struct { RemoteStorageKind *prometheus.GaugeVec computeLoadedDBSize func() float64 - LoadedDatabases prometheus.Gauge + LoadedDatabases prometheus.Gauge computeSessionCount func() float64 - ActiveSessions prometheus.Gauge + ActiveSessions prometheus.Gauge } var metricsNamespace = "immudb" // WithUptimeCounter ... func (mc *MetricsCollection) WithUptimeCounter(f func() float64) { + if mc.UptimeCounter != nil { + return + } + mc.UptimeCounter = promauto.NewCounterFunc( prometheus.CounterOpts{ Namespace: metricsNamespace, @@ -196,6 +201,7 @@ var Metrics = MetricsCollection{ func StartMetrics( updateInterval time.Duration, addr string, + tlsConfig *tls.Config, l logger.Logger, uptimeCounter func() float64, computeDBSizes func() map[string]float64, @@ -204,7 +210,6 @@ func StartMetrics( computeSessionCount func() float64, addPProf bool, ) *http.Server { - Metrics.WithUptimeCounter(uptimeCounter) Metrics.WithComputeDBSizes(computeDBSizes) Metrics.WithComputeDBEntries(computeDBEntries) @@ -232,19 +237,26 @@ func StartMetrics( mux.HandleFunc("/readyz", corsHandlerFunc(ImmudbHealthHandlerFunc())) mux.HandleFunc("/livez", corsHandlerFunc(ImmudbHealthHandlerFunc())) mux.HandleFunc("/version", corsHandlerFunc(ImmudbVersionHandlerFunc)) + server := &http.Server{Addr: addr, Handler: mux} + server.TLSConfig = tlsConfig go func() { - if err := server.ListenAndServe(); err != nil { - if err == http.ErrServerClosed { - l.Debugf("Metrics http server closed") - } else { - l.Errorf("Metrics error: %s", err) - } + var err error + if tlsConfig != nil && len(tlsConfig.Certificates) > 0 { + l.Infof("metrics server enabled on %s (https)", addr) + err = server.ListenAndServeTLS("", "") + } else { + l.Infof("metrics server enabled on %s (http)", addr) + err = server.ListenAndServe() + } + if err == http.ErrServerClosed { + l.Debugf("Metrics http server closed") + } else { + l.Errorf("Metrics error: %s", err) } }() - return server } diff --git a/pkg/server/metrics_test.go b/pkg/server/metrics_test.go index 767dfb5815..b8953b32bb 100644 --- a/pkg/server/metrics_test.go +++ b/pkg/server/metrics_test.go @@ -18,6 +18,7 @@ package server import ( "context" + "crypto/tls" "encoding/json" "net" "net/http" @@ -31,10 +32,16 @@ import ( "google.golang.org/grpc/peer" ) -func TestStartMetrics(t *testing.T) { +func TestStartMetricsHTTP(t *testing.T) { + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{}, + ClientAuth: tls.VerifyClientCertIfGiven, + } + server := StartMetrics( 100*time.Millisecond, "0.0.0.0:9999", + tlsConfig, &mockLogger{}, func() float64 { return 0 }, func() map[string]float64 { return make(map[string]float64) }, @@ -49,6 +56,34 @@ func TestStartMetrics(t *testing.T) { require.IsType(t, &http.Server{}, server) } +func TestStartMetricsHTTPS(t *testing.T) { + tlsConfig := tlsConfigTest(t) + + server := StartMetrics( + 100*time.Millisecond, + "0.0.0.0:9999", + tlsConfig, + &mockLogger{}, + func() float64 { return 0 }, + func() map[string]float64 { return make(map[string]float64) }, + func() map[string]float64 { return make(map[string]float64) }, + func() float64 { return 1.0 }, + func() float64 { return 2.0 }, + false, + ) + time.Sleep(200 * time.Millisecond) + defer server.Close() + + require.IsType(t, &http.Server{}, server) + + tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} + client := &http.Client{Transport: tr} + require.Eventually(t, func() bool { + _, err := client.Get("https://0.0.0.0:9999") + return err == nil + }, 10*time.Second, 30*time.Millisecond) +} + func TestStartMetricsFail(t *testing.T) { save_metricsNamespace := metricsNamespace metricsNamespace = "failimmudb" @@ -57,6 +92,7 @@ func TestStartMetricsFail(t *testing.T) { server := StartMetrics( 100*time.Millisecond, "999.999.999.999:9999", + nil, &mockLogger{}, func() float64 { return 0 }, func() map[string]float64 { return make(map[string]float64) }, diff --git a/pkg/server/server.go b/pkg/server/server.go index d239610412..c183b26a86 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -281,7 +281,7 @@ func (s *ImmuServer) Start() (err error) { startedAt = time.Now() if s.Options.MetricsServer { - s.metricsServer = StartMetrics(1*time.Minute, s.Options.MetricsBind(), s.Logger, s.metricFuncServerUptimeCounter, + s.metricsServer = StartMetrics(1*time.Minute, s.Options.MetricsBind(), s.Options.TLSConfig, s.Logger, s.metricFuncServerUptimeCounter, s.metricFuncComputeDBSizes, s.metricFuncComputeDBEntries, s.metricFuncComputeLoadedDBSize, s.metricFuncComputeSessionCount, s.Options.PProf) defer func() { diff --git a/pkg/server/webserver_test.go b/pkg/server/webserver_test.go index b0a0e45594..dc121b54b1 100644 --- a/pkg/server/webserver_test.go +++ b/pkg/server/webserver_test.go @@ -61,27 +61,8 @@ func TestStartWebServerHTTPS(t *testing.T) { options := DefaultOptions().WithDir(dir) server := DefaultServer().WithOptions(options).(*ImmuServer) - certPem := []byte(`-----BEGIN CERTIFICATE----- -MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw -DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow -EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d -7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B -5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr -BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1 -NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l -Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc -6MF9+Yw1Yy0t ------END CERTIFICATE-----`) - keyPem := []byte(`-----BEGIN EC PRIVATE KEY----- -MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49 -AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q -EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA== ------END EC PRIVATE KEY-----`) - - cert, err := tls.X509KeyPair(certPem, keyPem) - require.NoError(t, err) - tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}} + tlsConfig := tlsConfigTest(t) webServer, err := startWebServer( context.Background(), options.Bind(), @@ -101,3 +82,26 @@ EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA== return err == nil }, 10*time.Second, 30*time.Millisecond) } + +func tlsConfigTest(t *testing.T) *tls.Config { + certPem := []byte(`-----BEGIN CERTIFICATE----- +MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw +DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow +EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d +7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B +5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr +BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1 +NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l +Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc +6MF9+Yw1Yy0t +-----END CERTIFICATE-----`) + keyPem := []byte(`-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49 +AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q +EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA== +-----END EC PRIVATE KEY-----`) + + cert, err := tls.X509KeyPair(certPem, keyPem) + require.NoError(t, err) + return &tls.Config{Certificates: []tls.Certificate{cert}} +}