From a657f069a1d3be9f04d037789b6b9394d4292306 Mon Sep 17 00:00:00 2001 From: Gyuho Lee Date: Mon, 17 Jun 2024 18:21:38 +0800 Subject: [PATCH] fix(server/embed): enforce non-empty client TLS if scheme is https/unixs Signed-off-by: Gyuho Lee --- server/embed/etcd.go | 27 +++++++++++++++++++-------- server/embed/etcd_test.go | 24 ++++++++++++++++++++++++ server/etcdmain/help.go | 2 +- 3 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 server/embed/etcd_test.go diff --git a/server/embed/etcd.go b/server/embed/etcd.go index ce3b3398643..6d9fc76fcae 100644 --- a/server/embed/etcd.go +++ b/server/embed/etcd.go @@ -822,6 +822,24 @@ func (e *Etcd) pickGRPCGatewayServeContext(splitHTTP bool) *serveCtx { panic("Expect at least one context able to serve grpc") } +var errMissingClientTLSInfoForMetricsURL = errors.New("client TLS key/cert (--cert-file, --key-file) must be provided for metrics url") + +func (e *Etcd) createMetricsListener(murl url.URL) (net.Listener, error) { + tlsInfo := &e.cfg.ClientTLSInfo + switch murl.Scheme { + case "http": + tlsInfo = nil + case "https", "unixs": + if e.cfg.ClientTLSInfo.Empty() { + return nil, errMissingClientTLSInfoForMetricsURL + } + } + return transport.NewListenerWithOpts(murl.Host, murl.Scheme, + transport.WithTLSInfo(tlsInfo), + transport.WithSocketOpts(&e.cfg.SocketOpts), + ) +} + func (e *Etcd) serveMetrics() (err error) { if e.cfg.Metrics == "extensive" { grpc_prometheus.EnableHandlingTimeHistogram() @@ -833,14 +851,7 @@ func (e *Etcd) serveMetrics() (err error) { etcdhttp.HandleHealth(e.cfg.logger, metricsMux, e.Server) for _, murl := range e.cfg.ListenMetricsUrls { - tlsInfo := &e.cfg.ClientTLSInfo - if murl.Scheme == "http" { - tlsInfo = nil - } - ml, err := transport.NewListenerWithOpts(murl.Host, murl.Scheme, - transport.WithTLSInfo(tlsInfo), - transport.WithSocketOpts(&e.cfg.SocketOpts), - ) + ml, err := e.createMetricsListener(murl) if err != nil { return err } diff --git a/server/embed/etcd_test.go b/server/embed/etcd_test.go new file mode 100644 index 00000000000..f90d3de6697 --- /dev/null +++ b/server/embed/etcd_test.go @@ -0,0 +1,24 @@ +package embed + +import ( + "net/url" + "testing" + + "go.etcd.io/etcd/client/pkg/v3/transport" +) + +func TestEmptyClientTLSInfo_createMetricsListener(t *testing.T) { + e := &Etcd{ + cfg: Config{ + ClientTLSInfo: transport.TLSInfo{}, + }, + } + + murl := url.URL{ + Scheme: "https", + Host: "localhost:8080", + } + if _, err := e.createMetricsListener(murl); err != errMissingClientTLSInfoForMetricsURL { + t.Fatalf("expected error %v, got %v", errMissingClientTLSInfoForMetricsURL, err) + } +} diff --git a/server/etcdmain/help.go b/server/etcdmain/help.go index 8633d7c2f6f..d9bb14525e5 100644 --- a/server/etcdmain/help.go +++ b/server/etcdmain/help.go @@ -238,7 +238,7 @@ Profiling and Monitoring: --metrics 'basic' Set level of detail for exported metrics, specify 'extensive' to include server side grpc histogram metrics. --listen-metrics-urls '' - List of URLs to listen on for the metrics and health endpoints. + List of URLs to listen on for the /metrics and /health endpoints. For https, the client URL TLS info is used. Logging: --logger 'zap'