Skip to content

Commit

Permalink
Add TLS support to metrics server
Browse files Browse the repository at this point in the history
Add a link to metrics endpoint on root
  • Loading branch information
mostafa committed Sep 26, 2023
1 parent f4e6be2 commit 682ac8f
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 11 deletions.
62 changes: 54 additions & 8 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,12 @@ var runCmd = &cobra.Command{
return
}

fqdn, err := url.Parse("http://" + metricsConfig.Address)
scheme := "http://"
if metricsConfig.KeyFile != "" && metricsConfig.CertFile != "" {
scheme = "https://"
}

fqdn, err := url.Parse(scheme + metricsConfig.Address)
if err != nil {
logger.Error().Err(err).Msg("Failed to parse metrics address")
span.RecordError(err)
Expand Down Expand Up @@ -386,15 +391,26 @@ var runCmd = &cobra.Command{
)
}()

logger.Info().Str("address", address).Msg("Metrics are exposed")
mux := http.NewServeMux()
mux.HandleFunc("/", func(responseWriter http.ResponseWriter, request *http.Request) {
// Serve a static page with a link to the metrics endpoint.
if _, err := responseWriter.Write([]byte(fmt.Sprintf(
`<html><head><title>GatewayD Prometheus Metrics Server</title></head><body><a href="%s">Metrics</a></body></html>`,
address,
))); err != nil {
logger.Error().Err(err).Msg("Failed to write metrics")
span.RecordError(err)
sentry.CaptureException(err)
}
})

if conf.Plugin.EnableMetricsMerger && metricsMerger != nil {
handler = mergedMetricsHandler(handler)
}

// Check if the metrics server is already running before registering the handler.
if _, err = http.Get(address); err != nil { //nolint:gosec
http.Handle(metricsConfig.Path, gziphandler.GzipHandler(handler))
mux.Handle(metricsConfig.Path, gziphandler.GzipHandler(handler))
} else {
logger.Warn().Msg("Metrics server is already running, consider changing the port")
span.RecordError(err)
Expand All @@ -403,14 +419,44 @@ var runCmd = &cobra.Command{
// Create a new metrics server.
metricsServer = &http.Server{
Addr: metricsConfig.Address,
Handler: handler,
Handler: mux,
ReadHeaderTimeout: metricsConfig.GetReadHeaderTimeout(),
}

// Start the metrics server.
if err = metricsServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
logger.Error().Err(err).Msg("Failed to start metrics server")
span.RecordError(err)
logger.Info().Str("address", address).Msg("Metrics are exposed")

if metricsConfig.CertFile != "" && metricsConfig.KeyFile != "" {
// Set up TLS.
metricsServer.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS13,
CurvePreferences: []tls.CurveID{
tls.CurveP521,
tls.CurveP384,
tls.CurveP256,
},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
},
}
metricsServer.TLSNextProto = make(
map[string]func(*http.Server, *tls.Conn, http.Handler), 0)
logger.Debug().Msg("Metrics server is running with TLS")

// Start the metrics server with TLS.
if err = metricsServer.ListenAndServeTLS(
metricsConfig.CertFile, metricsConfig.KeyFile); !errors.Is(err, http.ErrServerClosed) {
logger.Error().Err(err).Msg("Failed to start metrics server")
span.RecordError(err)
}
} else {
// Start the metrics server without TLS.
if err = metricsServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
logger.Error().Err(err).Msg("Failed to start metrics server")
span.RecordError(err)
}
}
}(conf.Global.Metrics[config.Default], logger)

Expand Down
7 changes: 4 additions & 3 deletions config/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,10 @@ const (
ChecksumBufferSize = 65536

// Metrics constants.
DefaultMetricsAddress = "localhost:9090"
DefaultMetricsPath = "/metrics"
DefaultReadHeaderTimeout = 10 * time.Second
DefaultMetricsAddress = "localhost:9090"
DefaultMetricsPath = "/metrics"
DefaultReadHeaderTimeout = 10 * time.Second
DefaultMetricsServerTimeout = 10 * time.Second

// Sentry constants.
DefaultTraceSampleRate = 0.2
Expand Down
7 changes: 7 additions & 0 deletions config/getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,10 @@ func (m Metrics) GetReadHeaderTimeout() time.Duration {
}
return m.ReadHeaderTimeout
}

func (m Metrics) GetTimeout() time.Duration {
if m.Timeout <= 0 {
return DefaultMetricsServerTimeout
}
return m.Timeout
}
3 changes: 3 additions & 0 deletions config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ type Metrics struct {
Address string `json:"address"`
Path string `json:"path"`
ReadHeaderTimeout time.Duration `json:"readHeaderTimeout" jsonschema:"oneof_type=string;integer"`
Timeout time.Duration `json:"timeout" jsonschema:"oneof_type=string;integer"`
CertFile string `json:"certFile"`
KeyFile string `json:"keyFile"`
}

type Pool struct {
Expand Down
3 changes: 3 additions & 0 deletions gatewayd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ metrics:
address: localhost:9090
path: /metrics
readHeaderTimeout: 10s # duration, prevents Slowloris attacks
timeout: 10s # duration
certFile: "" # Certificate file in PEM format
keyFile: "" # Private key file in PEM format

clients:
default:
Expand Down

0 comments on commit 682ac8f

Please sign in to comment.