Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(diagnostics): add possibility to run diagnostics on TLS #5344

Merged
merged 6 commits into from
Nov 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions pkg/config/app/kuma-cp/kuma-cp.defaults.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,18 @@ diagnostics:
serverPort: 5680 # ENV: KUMA_DIAGNOSTICS_SERVER_PORT
# If true, enables https://golang.org/pkg/net/http/pprof/ debug endpoints
debugEndpoints: false # ENV: KUMA_DIAGNOSTICS_DEBUG_ENDPOINTS
# Whether tls is enabled or not
tlsEnabled: false # ENV: KUMA_DIAGNOSTICS_TLS_ENABLED
# TlsCertFile defines a path to a file with PEM-encoded TLS cert. If empty, autoconfigured from general.tlsCertFile
tlsCertFile: # ENV: KUMA_DIAGNOSTICS_TLS_CERT_FILE
# TlsKeyFile defines a path to a file with PEM-encoded TLS key. If empty, autoconfigured from general.tlsKeyFile
tlsKeyFile: # ENV: KUMA_DIAGNOSTICS_TLS_KEY_FILE
# TlsMinVersion the minimum version of TLS
tlsMinVersion: "TLSv1_2" # ENV: KUMA_DIAGNOSTICS_TLS_MIN_VERSION
# TlsMaxVersion the maximum version of TLS
tlsMaxVersion: # ENV: KUMA_DIAGNOSTICS_TLS_MAX_VERSION
# TlsCipherSuites the list of cipher suites
tlsCipherSuites: [] # ENV: KUMA_DIAGNOSTICS_TLS_CIPHER_SUITES

# Dataplane Server configuration that servers API like Bootstrap/XDS for the Dataplane.
dpServer:
Expand Down
42 changes: 38 additions & 4 deletions pkg/config/diagnostics/config.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,62 @@
package diagnostics

import (
"github.com/pkg/errors"
"go.uber.org/multierr"

"github.com/kumahq/kuma/pkg/config"
config_types "github.com/kumahq/kuma/pkg/config/types"
)

type DiagnosticsConfig struct {
// Port of Diagnostic Server for checking health and readiness of the Control Plane
ServerPort uint32 `json:"serverPort" envconfig:"kuma_diagnostics_server_port"`
// If true, enables https://golang.org/pkg/net/http/pprof/ debug endpoints
DebugEndpoints bool `json:"debugEndpoints" envconfig:"kuma_diagnostics_debug_endpoints"`
// TlsEnabled whether tls is enabled or not
TlsEnabled bool `json:"tlsEnabled" envconfig:"kuma_diagnostics_tls_enabled"`
// TlsCertFile defines a path to a file with PEM-encoded TLS cert. If empty, autoconfigured from general.tlsCertFile
TlsCertFile string `json:"tlsCertFile" envconfig:"kuma_diagnostics_tls_cert_file"`
// TlsKeyFile defines a path to a file with PEM-encoded TLS key. If empty, autoconfigured from general.tlsKeyFile
TlsKeyFile string `json:"tlsKeyFile" envconfig:"kuma_diagnostics_tls_key_file"`
// TlsMinVersion defines the minimum TLS version to be used
TlsMinVersion string `json:"tlsMinVersion" envconfig:"kuma_diagnostics_tls_min_version"`
// TlsMaxVersion defines the maximum TLS version to be used
TlsMaxVersion string `json:"tlsMaxVersion" envconfig:"kuma_diagnostics_tls_max_version"`
// TlsCipherSuites defines the list of ciphers to use
TlsCipherSuites []string `json:"tlsCipherSuites" envconfig:"kuma_diagnostics_tls_cipher_suites"`
}

var _ config.Config = &DiagnosticsConfig{}

func (d *DiagnosticsConfig) Sanitize() {
}

func (d *DiagnosticsConfig) Validate() error {
return nil
func (d *DiagnosticsConfig) Validate() (errs error) {
if d.TlsCertFile == "" && d.TlsKeyFile != "" {
errs = multierr.Append(errs, errors.New(".TlsCertFile cannot be empty if TlsKeyFile has been set"))
}
if d.TlsKeyFile == "" && d.TlsCertFile != "" {
errs = multierr.Append(errs, errors.New(".TlsKeyFile cannot be empty if TlsCertFile has been set"))
}
if _, err := config_types.TLSVersion(d.TlsMinVersion); err != nil {
errs = multierr.Append(errs, errors.New(".TlsMinVersion"+err.Error()))
}
if _, err := config_types.TLSVersion(d.TlsMaxVersion); err != nil {
errs = multierr.Append(errs, errors.New(".TlsMaxVersion"+err.Error()))
}
if _, err := config_types.TLSCiphers(d.TlsCipherSuites); err != nil {
errs = multierr.Append(errs, errors.New(".TlsCipherSuites"+err.Error()))
}
return
}

func DefaultDiagnosticsConfig() *DiagnosticsConfig {
return &DiagnosticsConfig{
ServerPort: 5680,
DebugEndpoints: false,
ServerPort: 5680,
DebugEndpoints: false,
TlsEnabled: false,
TlsMinVersion: "TLSv1_2",
TlsCipherSuites: []string{},
}
}
18 changes: 18 additions & 0 deletions pkg/config/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,12 @@ var _ = Describe("Config loader", func() {

Expect(cfg.Diagnostics.ServerPort).To(Equal(uint32(5003)))
Expect(cfg.Diagnostics.DebugEndpoints).To(BeTrue())
Expect(cfg.Diagnostics.TlsEnabled).To(Equal(true))
Expect(cfg.Diagnostics.TlsCertFile).To(Equal("/cert"))
Expect(cfg.Diagnostics.TlsKeyFile).To(Equal("/key"))
Expect(cfg.Diagnostics.TlsMinVersion).To(Equal("TLSv1_3"))
Expect(cfg.Diagnostics.TlsMaxVersion).To(Equal("TLSv1_3"))
Expect(cfg.Diagnostics.TlsCipherSuites).To(Equal([]string{"TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_AES_256_GCM_SHA384"}))

Expect(cfg.DNSServer.Domain).To(Equal("test-domain"))
Expect(cfg.DNSServer.CIDR).To(Equal("127.1.0.0/16"))
Expand Down Expand Up @@ -490,6 +496,12 @@ defaults:
diagnostics:
serverPort: 5003
debugEndpoints: true
tlsEnabled: true
tlsCertFile: "/cert"
tlsKeyFile: "/key"
tlsMinVersion: TLSv1_3
tlsMaxVersion: TLSv1_3
tlsCipherSuites: ["TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_AES_256_GCM_SHA384"]
xdsServer:
dataplaneConfigurationRefreshInterval: 21s
dataplaneStatusFlushInterval: 7s
Expand Down Expand Up @@ -700,6 +712,12 @@ proxy:
"KUMA_DEFAULTS_ENABLE_LOCALHOST_INBOUND_CLUSTERS": "true",
"KUMA_DIAGNOSTICS_SERVER_PORT": "5003",
"KUMA_DIAGNOSTICS_DEBUG_ENDPOINTS": "true",
"KUMA_DIAGNOSTICS_TLS_ENABLED": "true",
"KUMA_DIAGNOSTICS_TLS_CERT_FILE": "/cert",
"KUMA_DIAGNOSTICS_TLS_KEY_FILE": "/key",
"KUMA_DIAGNOSTICS_TLS_MIN_VERSION": "TLSv1_3",
"KUMA_DIAGNOSTICS_TLS_MAX_VERSION": "TLSv1_3",
"KUMA_DIAGNOSTICS_TLS_CIPHER_SUITES": "TLS_RSA_WITH_AES_128_CBC_SHA,TLS_AES_256_GCM_SHA384",
"KUMA_XDS_SERVER_DATAPLANE_STATUS_FLUSH_INTERVAL": "7s",
"KUMA_XDS_SERVER_DATAPLANE_CONFIGURATION_REFRESH_INTERVAL": "21s",
"KUMA_XDS_DATAPLANE_DEREGISTRATION_DELAY": "11s",
Expand Down
7 changes: 7 additions & 0 deletions pkg/core/bootstrap/autoconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ func autoconfigureServersTLS(cfg *kuma_cp.Config) {
cfg.Multizone.Global.KDS.TlsCertFile = cfg.General.TlsCertFile
cfg.Multizone.Global.KDS.TlsKeyFile = cfg.General.TlsKeyFile
}
if cfg.Diagnostics.TlsCertFile == "" {
cfg.Diagnostics.TlsCertFile = cfg.General.TlsCertFile
cfg.Diagnostics.TlsKeyFile = cfg.General.TlsKeyFile
}
if cfg.DpServer.TlsCertFile == "" {
cfg.DpServer.TlsCertFile = cfg.General.TlsCertFile
cfg.DpServer.TlsKeyFile = cfg.General.TlsKeyFile
Expand All @@ -77,18 +81,21 @@ func autoconfigureServersTLS(cfg *kuma_cp.Config) {
cfg.MonitoringAssignmentServer.TlsKeyFile = cfg.General.TlsKeyFile
}
if cfg.General.TlsMinVersion != "" {
cfg.Diagnostics.TlsMinVersion = cfg.General.TlsMinVersion
cfg.Multizone.Global.KDS.TlsMinVersion = cfg.General.TlsMinVersion
cfg.DpServer.TlsMinVersion = cfg.General.TlsMinVersion
cfg.ApiServer.HTTPS.TlsMinVersion = cfg.General.TlsMinVersion
cfg.MonitoringAssignmentServer.TlsMinVersion = cfg.General.TlsMinVersion
}
if cfg.General.TlsMaxVersion != "" {
cfg.Diagnostics.TlsMaxVersion = cfg.General.TlsMaxVersion
cfg.Multizone.Global.KDS.TlsMaxVersion = cfg.General.TlsMaxVersion
cfg.DpServer.TlsMaxVersion = cfg.General.TlsMaxVersion
cfg.ApiServer.HTTPS.TlsMaxVersion = cfg.General.TlsMaxVersion
cfg.MonitoringAssignmentServer.TlsMaxVersion = cfg.General.TlsMaxVersion
}
if len(cfg.General.TlsCipherSuites) > 0 {
cfg.Diagnostics.TlsCipherSuites = cfg.General.TlsCipherSuites
cfg.Multizone.Global.KDS.TlsCipherSuites = cfg.General.TlsCipherSuites
cfg.DpServer.TlsCipherSuites = cfg.General.TlsCipherSuites
cfg.ApiServer.HTTPS.TlsCipherSuites = cfg.General.TlsCipherSuites
Expand Down
5 changes: 2 additions & 3 deletions pkg/diagnostics/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import (
func SetupServer(rt core_runtime.Runtime) error {
return rt.Add(
&diagnosticsServer{
metrics: rt.Metrics(),
port: rt.Config().Diagnostics.ServerPort,
debugEndpoints: rt.Config().Diagnostics.DebugEndpoints,
config: rt.Config().Diagnostics,
metrics: rt.Metrics(),
},
)
}
54 changes: 44 additions & 10 deletions pkg/diagnostics/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ package diagnostics

import (
"context"
"crypto/tls"
"fmt"
"net/http"
pprof "net/http/pprof"

"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus/promhttp"

diagnostics_config "github.com/kumahq/kuma/pkg/config/diagnostics"
config_types "github.com/kumahq/kuma/pkg/config/types"
"github.com/kumahq/kuma/pkg/core"
"github.com/kumahq/kuma/pkg/core/runtime/component"
"github.com/kumahq/kuma/pkg/metrics"
Expand All @@ -18,9 +22,8 @@ var (
)

type diagnosticsServer struct {
port uint32
metrics metrics.Metrics
debugEndpoints bool
config *diagnostics_config.DiagnosticsConfig
metrics metrics.Metrics
}

func (s *diagnosticsServer) NeedLeaderElection() bool {
Expand All @@ -41,26 +44,57 @@ func (s *diagnosticsServer) Start(stop <-chan struct{}) error {
resp.WriteHeader(http.StatusOK)
})
mux.Handle("/metrics", promhttp.InstrumentMetricHandler(s.metrics, promhttp.HandlerFor(s.metrics, promhttp.HandlerOpts{})))
if s.debugEndpoints {
if s.config.DebugEndpoints {
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
}

httpServer := &http.Server{Addr: fmt.Sprintf(":%d", s.port), Handler: mux}
httpServer := &http.Server{Addr: fmt.Sprintf(":%d", s.config.ServerPort), Handler: mux}

diagnosticsServerLog.Info("starting diagnostic server", "interface", "0.0.0.0", "port", s.port)
if s.config.TlsEnabled {
cert, err := tls.LoadX509KeyPair(s.config.TlsCertFile, s.config.TlsKeyFile)
if err != nil {
return errors.Wrap(err, "failed to load TLS certificate")
}
tlsConfig := &tls.Config{Certificates: []tls.Certificate{cert}}
if tlsConfig.MinVersion, err = config_types.TLSVersion(s.config.TlsMinVersion); err != nil {
return err
}
if tlsConfig.MaxVersion, err = config_types.TLSVersion(s.config.TlsMaxVersion); err != nil {
return err
}
if tlsConfig.CipherSuites, err = config_types.TLSCiphers(s.config.TlsCipherSuites); err != nil {
return err
}
httpServer.TLSConfig = tlsConfig
}

diagnosticsServerLog.Info("starting diagnostic server", "interface", "0.0.0.0", "port", s.config.ServerPort, "tls", s.config.TlsEnabled)
errChan := make(chan error)
go func() {
defer close(errChan)
if err := httpServer.ListenAndServe(); err != nil {
if err.Error() != "http: Server closed" {
diagnosticsServerLog.Error(err, "terminated with an error")
var err error
if s.config.TlsEnabled {
err = httpServer.ListenAndServeTLS(s.config.TlsCertFile, s.config.TlsKeyFile)
} else {
err = httpServer.ListenAndServe()
}
if err != nil {
switch err {
case http.ErrServerClosed:
diagnosticsServerLog.Info("shutting down server")
default:
if s.config.TlsEnabled {
diagnosticsServerLog.Error(err, "could not start HTTPS Server")
} else {
diagnosticsServerLog.Error(err, "could not start HTTP Server")
}
errChan <- err
return
}
return
}
diagnosticsServerLog.Info("terminated normally")
}()
Expand Down