From 13bf214be7e706c3403d9b8e7e63cb477fa2ec98 Mon Sep 17 00:00:00 2001 From: Julien Pivotto Date: Fri, 15 Apr 2022 23:42:19 +0200 Subject: [PATCH] TLS config: Enable selection of min and max TLS version go1.18 changes the default minimum TLS version to 1.2. In order not to break our users, let's make the default minimum version configurable, with conservative defaults. The allowed values (TLS10, ..) come from the exporter-toolkit: https://github.com/prometheus/exporter-toolkit/blob/master/docs/web-configuration.md TLSVersion is exported so the exporter toolkit can reuse them later. Signed-off-by: Julien Pivotto --- config/http_config.go | 87 ++++++++++++++++--- config/http_config_test.go | 12 ++- .../testdata/tls_config.tlsversion.good.yml | 2 + config/tls_config_test.go | 7 +- 4 files changed, 90 insertions(+), 18 deletions(-) create mode 100644 config/testdata/tls_config.tlsversion.good.yml diff --git a/config/http_config.go b/config/http_config.go index 37cb37ce..a4f3bda8 100644 --- a/config/http_config.go +++ b/config/http_config.go @@ -36,25 +36,64 @@ import ( "gopkg.in/yaml.v2" ) -// DefaultHTTPClientConfig is the default HTTP client configuration. -var DefaultHTTPClientConfig = HTTPClientConfig{ - FollowRedirects: true, - EnableHTTP2: true, -} +var ( + // DefaultHTTPClientConfig is the default HTTP client configuration. + DefaultHTTPClientConfig = HTTPClientConfig{ + FollowRedirects: true, + EnableHTTP2: true, + } -// defaultHTTPClientOptions holds the default HTTP client options. -var defaultHTTPClientOptions = httpClientOptions{ - keepAlivesEnabled: true, - http2Enabled: true, - // 5 minutes is typically above the maximum sane scrape interval. So we can - // use keepalive for all configurations. - idleConnTimeout: 5 * time.Minute, -} + // defaultHTTPClientOptions holds the default HTTP client options. + defaultHTTPClientOptions = httpClientOptions{ + keepAlivesEnabled: true, + http2Enabled: true, + // 5 minutes is typically above the maximum sane scrape interval. So we can + // use keepalive for all configurations. + idleConnTimeout: 5 * time.Minute, + } + + // defaultTLSConfig holds the default TLS configuration. + defaultTLSConfig = TLSConfig{ + MinVersion: tls.VersionTLS10, + MaxVersion: tls.VersionTLS13, + } +) type closeIdler interface { CloseIdleConnections() } +type TLSVersion uint16 + +var TLSVersions = map[string]TLSVersion{ + "TLS13": (TLSVersion)(tls.VersionTLS13), + "TLS12": (TLSVersion)(tls.VersionTLS12), + "TLS11": (TLSVersion)(tls.VersionTLS11), + "TLS10": (TLSVersion)(tls.VersionTLS10), +} + +func (tv *TLSVersion) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + err := unmarshal((*string)(&s)) + if err != nil { + return err + } + if v, ok := TLSVersions[s]; ok { + *tv = v + return nil + } + return fmt.Errorf("unknown TLS version: %s", s) +} + +func (tv *TLSVersion) MarshalYAML() (interface{}, error) { + for s, v := range TLSVersions { + if *tv == v { + return s, nil + } + } + return fmt.Sprintf("%v", tv), nil +} + // BasicAuth contains basic HTTP authentication credentials. type BasicAuth struct { Username string `yaml:"username" json:"username"` @@ -669,7 +708,22 @@ func cloneRequest(r *http.Request) *http.Request { // NewTLSConfig creates a new tls.Config from the given TLSConfig. func NewTLSConfig(cfg *TLSConfig) (*tls.Config, error) { - tlsConfig := &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify} + // Ensure that min and max versions are set even if TLSConfig is from its + // zero-value. + minv := uint16(cfg.MinVersion) + if minv == 0 { + minv = tls.VersionTLS10 + } + maxv := uint16(cfg.MaxVersion) + if maxv == 0 { + maxv = tls.VersionTLS13 + } + + tlsConfig := &tls.Config{ + InsecureSkipVerify: cfg.InsecureSkipVerify, + MinVersion: minv, + MaxVersion: maxv, + } // If a CA cert is provided then let's read it in so we can validate the // scrape target's certificate properly. @@ -714,6 +768,10 @@ type TLSConfig struct { ServerName string `yaml:"server_name,omitempty" json:"server_name,omitempty"` // Disable target certificate validation. InsecureSkipVerify bool `yaml:"insecure_skip_verify" json:"insecure_skip_verify"` + // Minimum TLS version. + MinVersion TLSVersion `yaml:"min_version,omitempty"` + // Maximum TLS version. + MaxVersion TLSVersion `yaml:"max_version,omitempty"` } // SetDirectory joins any relative file paths with dir. @@ -728,6 +786,7 @@ func (c *TLSConfig) SetDirectory(dir string) { // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { + *c = defaultTLSConfig type plain TLSConfig return unmarshal((*plain)(c)) } diff --git a/config/http_config_test.go b/config/http_config_test.go index 884f344c..72700a16 100644 --- a/config/http_config_test.go +++ b/config/http_config_test.go @@ -627,7 +627,10 @@ func TestTLSConfig(t *testing.T) { CertFile: ClientCertificatePath, KeyFile: ClientKeyNoPassPath, ServerName: "localhost", - InsecureSkipVerify: false} + InsecureSkipVerify: false, + MinVersion: tls.VersionTLS10, + MaxVersion: tls.VersionTLS11, + } tlsCAChain, err := ioutil.ReadFile(TLSCAChainPath) if err != nil { @@ -640,7 +643,10 @@ func TestTLSConfig(t *testing.T) { expectedTLSConfig := &tls.Config{ RootCAs: rootCAs, ServerName: configTLSConfig.ServerName, - InsecureSkipVerify: configTLSConfig.InsecureSkipVerify} + InsecureSkipVerify: configTLSConfig.InsecureSkipVerify, + MinVersion: tls.VersionTLS10, + MaxVersion: tls.VersionTLS11, + } tlsConfig, err := NewTLSConfig(&configTLSConfig) if err != nil { @@ -683,6 +689,8 @@ func TestTLSConfigEmpty(t *testing.T) { expectedTLSConfig := &tls.Config{ InsecureSkipVerify: configTLSConfig.InsecureSkipVerify, + MinVersion: tls.VersionTLS10, + MaxVersion: tls.VersionTLS13, } tlsConfig, err := NewTLSConfig(&configTLSConfig) diff --git a/config/testdata/tls_config.tlsversion.good.yml b/config/testdata/tls_config.tlsversion.good.yml new file mode 100644 index 00000000..468fd50f --- /dev/null +++ b/config/testdata/tls_config.tlsversion.good.yml @@ -0,0 +1,2 @@ +min_version: TLS11 +max_version: TLS12 diff --git a/config/tls_config_test.go b/config/tls_config_test.go index 2b965ea6..971140f6 100644 --- a/config/tls_config_test.go +++ b/config/tls_config_test.go @@ -41,10 +41,13 @@ var expectedTLSConfigs = []struct { }{ { filename: "tls_config.empty.good.yml", - config: &tls.Config{}, + config: &tls.Config{MinVersion: tls.VersionTLS10, MaxVersion: tls.VersionTLS13}, }, { filename: "tls_config.insecure.good.yml", - config: &tls.Config{InsecureSkipVerify: true}, + config: &tls.Config{InsecureSkipVerify: true, MinVersion: tls.VersionTLS10, MaxVersion: tls.VersionTLS13}, + }, { + filename: "tls_config.tlsversion.good.yml", + config: &tls.Config{MinVersion: tls.VersionTLS11, MaxVersion: tls.VersionTLS12}, }, }