Skip to content

Commit 601d003

Browse files
authored
Merge pull request #336 from gatewayd-io/tls-support-in-metrics-server
TLS support in metrics server
2 parents f4e6be2 + 682ac8f commit 601d003

File tree

5 files changed

+71
-11
lines changed

5 files changed

+71
-11
lines changed

cmd/run.go

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,12 @@ var runCmd = &cobra.Command{
343343
return
344344
}
345345

346-
fqdn, err := url.Parse("http://" + metricsConfig.Address)
346+
scheme := "http://"
347+
if metricsConfig.KeyFile != "" && metricsConfig.CertFile != "" {
348+
scheme = "https://"
349+
}
350+
351+
fqdn, err := url.Parse(scheme + metricsConfig.Address)
347352
if err != nil {
348353
logger.Error().Err(err).Msg("Failed to parse metrics address")
349354
span.RecordError(err)
@@ -386,15 +391,26 @@ var runCmd = &cobra.Command{
386391
)
387392
}()
388393

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

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

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

410-
// Start the metrics server.
411-
if err = metricsServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
412-
logger.Error().Err(err).Msg("Failed to start metrics server")
413-
span.RecordError(err)
426+
logger.Info().Str("address", address).Msg("Metrics are exposed")
427+
428+
if metricsConfig.CertFile != "" && metricsConfig.KeyFile != "" {
429+
// Set up TLS.
430+
metricsServer.TLSConfig = &tls.Config{
431+
MinVersion: tls.VersionTLS13,
432+
CurvePreferences: []tls.CurveID{
433+
tls.CurveP521,
434+
tls.CurveP384,
435+
tls.CurveP256,
436+
},
437+
PreferServerCipherSuites: true,
438+
CipherSuites: []uint16{
439+
tls.TLS_AES_128_GCM_SHA256,
440+
tls.TLS_AES_256_GCM_SHA384,
441+
tls.TLS_CHACHA20_POLY1305_SHA256,
442+
},
443+
}
444+
metricsServer.TLSNextProto = make(
445+
map[string]func(*http.Server, *tls.Conn, http.Handler), 0)
446+
logger.Debug().Msg("Metrics server is running with TLS")
447+
448+
// Start the metrics server with TLS.
449+
if err = metricsServer.ListenAndServeTLS(
450+
metricsConfig.CertFile, metricsConfig.KeyFile); !errors.Is(err, http.ErrServerClosed) {
451+
logger.Error().Err(err).Msg("Failed to start metrics server")
452+
span.RecordError(err)
453+
}
454+
} else {
455+
// Start the metrics server without TLS.
456+
if err = metricsServer.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
457+
logger.Error().Err(err).Msg("Failed to start metrics server")
458+
span.RecordError(err)
459+
}
414460
}
415461
}(conf.Global.Metrics[config.Default], logger)
416462

config/constants.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,10 @@ const (
123123
ChecksumBufferSize = 65536
124124

125125
// Metrics constants.
126-
DefaultMetricsAddress = "localhost:9090"
127-
DefaultMetricsPath = "/metrics"
128-
DefaultReadHeaderTimeout = 10 * time.Second
126+
DefaultMetricsAddress = "localhost:9090"
127+
DefaultMetricsPath = "/metrics"
128+
DefaultReadHeaderTimeout = 10 * time.Second
129+
DefaultMetricsServerTimeout = 10 * time.Second
129130

130131
// Sentry constants.
131132
DefaultTraceSampleRate = 0.2

config/getters.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,10 @@ func (m Metrics) GetReadHeaderTimeout() time.Duration {
276276
}
277277
return m.ReadHeaderTimeout
278278
}
279+
280+
func (m Metrics) GetTimeout() time.Duration {
281+
if m.Timeout <= 0 {
282+
return DefaultMetricsServerTimeout
283+
}
284+
return m.Timeout
285+
}

config/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ type Metrics struct {
7272
Address string `json:"address"`
7373
Path string `json:"path"`
7474
ReadHeaderTimeout time.Duration `json:"readHeaderTimeout" jsonschema:"oneof_type=string;integer"`
75+
Timeout time.Duration `json:"timeout" jsonschema:"oneof_type=string;integer"`
76+
CertFile string `json:"certFile"`
77+
KeyFile string `json:"keyFile"`
7578
}
7679

7780
type Pool struct {

gatewayd.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ metrics:
2525
address: localhost:9090
2626
path: /metrics
2727
readHeaderTimeout: 10s # duration, prevents Slowloris attacks
28+
timeout: 10s # duration
29+
certFile: "" # Certificate file in PEM format
30+
keyFile: "" # Private key file in PEM format
2831

2932
clients:
3033
default:

0 commit comments

Comments
 (0)