feat(common.tls): Implement minimum TLS version for clients (#11493)

This commit is contained in:
Sven Rebhan 2022-08-09 13:36:47 +02:00 committed by GitHub
parent b07e94b662
commit e96bbe83c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 196 additions and 1 deletions

View File

@ -1,6 +1,10 @@
<!-- markdownlint-disable MD024 -->
# Changelog
### BREAKING CHANGES
- [#11493](https://github.com/influxdata/telegraf/pull/11493) `common.tls` Set default minimum TLS version to v1.2 for security reasons on both server and client connections. This is a change from the previous defaults (TLS v1.0) on the server configuration and might break clients relying on older TLS versions. You can manually revert to older versions on a per-plugin basis using the `tls_min_version` option in the plugins required.
## v1.23.3 [2022-07-25]
### Bugfixes

View File

@ -10,12 +10,15 @@ import (
"github.com/influxdata/telegraf/internal/choice"
)
const TLSMinVersionDefault = tls.VersionTLS12
// ClientConfig represents the standard client TLS config.
type ClientConfig struct {
TLSCA string `toml:"tls_ca"`
TLSCert string `toml:"tls_cert"`
TLSKey string `toml:"tls_key"`
TLSKeyPwd string `toml:"tls_key_pwd"`
TLSMinVersion string `toml:"tls_min_version"`
InsecureSkipVerify bool `toml:"insecure_skip_verify"`
ServerName string `toml:"tls_server_name"`
@ -81,6 +84,19 @@ func (c *ClientConfig) TLSConfig() (*tls.Config, error) {
}
}
// Explicitly and consistently set the minimal accepted version using the
// defined default. We use this setting for both clients and servers
// instead of relying on Golang's default that is different for clients
// and servers and might change over time.
tlsConfig.MinVersion = TLSMinVersionDefault
if c.TLSMinVersion != "" {
version, err := ParseTLSVersion(c.TLSMinVersion)
if err != nil {
return nil, fmt.Errorf("could not parse tls min version %q: %w", c.TLSMinVersion, err)
}
tlsConfig.MinVersion = version
}
if c.ServerName != "" {
tlsConfig.ServerName = c.ServerName
}
@ -131,6 +147,11 @@ func (c *ServerConfig) TLSConfig() (*tls.Config, error) {
tlsConfig.MaxVersion = version
}
// Explicitly and consistently set the minimal accepted version using the
// defined default. We use this setting for both clients and servers
// instead of relying on Golang's default that is different for clients
// and servers and might change over time.
tlsConfig.MinVersion = TLSMinVersionDefault
if c.TLSMinVersion != "" {
version, err := ParseTLSVersion(c.TLSMinVersion)
if err != nil {

View File

@ -1,6 +1,7 @@
package tls_test
import (
cryptotls "crypto/tls"
"net/http"
"net/http/httptest"
"testing"
@ -350,6 +351,141 @@ func TestConnect(t *testing.T) {
require.Equal(t, 200, resp.StatusCode)
}
func TestConnectClientMinTLSVersion(t *testing.T) {
serverConfig := tls.ServerConfig{
TLSCert: pki.ServerCertPath(),
TLSKey: pki.ServerKeyPath(),
TLSAllowedCACerts: []string{pki.CACertPath()},
TLSAllowedDNSNames: []string{"localhost", "127.0.0.1"},
}
tests := []struct {
name string
cfg tls.ClientConfig
}{
{
name: "TLS version default",
cfg: tls.ClientConfig{
TLSCA: pki.CACertPath(),
TLSCert: pki.ClientCertPath(),
TLSKey: pki.ClientKeyPath(),
},
},
{
name: "TLS version 1.0",
cfg: tls.ClientConfig{
TLSCA: pki.CACertPath(),
TLSCert: pki.ClientCertPath(),
TLSKey: pki.ClientKeyPath(),
TLSMinVersion: "TLS10",
},
},
{
name: "TLS version 1.1",
cfg: tls.ClientConfig{
TLSCA: pki.CACertPath(),
TLSCert: pki.ClientCertPath(),
TLSKey: pki.ClientKeyPath(),
TLSMinVersion: "TLS11",
},
},
{
name: "TLS version 1.2",
cfg: tls.ClientConfig{
TLSCA: pki.CACertPath(),
TLSCert: pki.ClientCertPath(),
TLSKey: pki.ClientKeyPath(),
TLSMinVersion: "TLS12",
},
},
{
name: "TLS version 1.3",
cfg: tls.ClientConfig{
TLSCA: pki.CACertPath(),
TLSCert: pki.ClientCertPath(),
TLSKey: pki.ClientKeyPath(),
TLSMinVersion: "TLS13",
},
},
}
tlsVersions := []uint16{
cryptotls.VersionTLS10,
cryptotls.VersionTLS11,
cryptotls.VersionTLS12,
cryptotls.VersionTLS13,
}
tlsVersionNames := []string{
"TLS 1.0",
"TLS 1.1",
"TLS 1.2",
"TLS 1.3",
}
for _, tt := range tests {
clientTLSConfig, err := tt.cfg.TLSConfig()
require.NoError(t, err)
client := http.Client{
Transport: &http.Transport{
TLSClientConfig: clientTLSConfig,
},
Timeout: 1 * time.Second,
}
clientMinVersion := clientTLSConfig.MinVersion
if tt.cfg.TLSMinVersion == "" {
clientMinVersion = tls.TLSMinVersionDefault
}
for i, serverTLSMaxVersion := range tlsVersions {
serverVersionName := tlsVersionNames[i]
t.Run(tt.name+" vs "+serverVersionName, func(t *testing.T) {
// Constrain the server's maximum TLS version
serverTLSConfig, err := serverConfig.TLSConfig()
require.NoError(t, err)
serverTLSConfig.MinVersion = cryptotls.VersionTLS10
serverTLSConfig.MaxVersion = serverTLSMaxVersion
// Start the server
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
ts.TLS = serverTLSConfig
ts.StartTLS()
// Do the connection and cleanup
resp, err := client.Get(ts.URL)
ts.Close()
// Things should fail if the currently tested "serverTLSMaxVersion"
// is below the client minimum version.
if serverTLSMaxVersion < clientMinVersion {
require.ErrorContains(t, err, "tls: protocol version not supported")
} else {
require.NoErrorf(t, err, "server=%v client=%v", serverTLSMaxVersion, clientMinVersion)
require.Equal(t, 200, resp.StatusCode)
resp.Body.Close()
}
})
}
}
}
func TestConnectClientInvalidMinTLSVersion(t *testing.T) {
clientConfig := tls.ClientConfig{
TLSCA: pki.CACertPath(),
TLSCert: pki.ClientCertPath(),
TLSKey: pki.ClientKeyPath(),
TLSMinVersion: "garbage",
}
_, err := clientConfig.TLSConfig()
expected := `could not parse tls min version "garbage": unsupported version "garbage" (available: TLS10,TLS11,TLS12,TLS13)`
require.EqualError(t, err, expected)
}
func TestConnectWrongDNS(t *testing.T) {
clientConfig := tls.ClientConfig{
TLSCA: pki.CACertPath(),

View File

@ -2,6 +2,8 @@ package tls
import (
"fmt"
"sort"
"strings"
)
// ParseCiphers returns a `[]uint16` by received `[]string` key that represents ciphers from crypto/tls.
@ -26,5 +28,11 @@ func ParseTLSVersion(version string) (uint16, error) {
if v, ok := tlsVersionMap[version]; ok {
return v, nil
}
return 0, fmt.Errorf("unsupported version %q", version)
var available []string
for n := range tlsVersionMap {
available = append(available, n)
}
sort.Strings(available)
return 0, fmt.Errorf("unsupported version %q (available: %s)", version, strings.Join(available, ","))
}

View File

@ -32,6 +32,9 @@ It has been optimized to support gNMI telemetry as produced by Cisco IOS XR
## enable client-side TLS and define CA to authenticate the device
# enable_tls = true
# tls_ca = "/etc/telegraf/ca.pem"
## Minimal TLS version to accept by the client
# tls_min_version = "TLS12"
## Use TLS but skip chain & host verification
# insecure_skip_verify = true
## define client-side TLS certificate & key to authenticate to the device

View File

@ -16,6 +16,9 @@
## enable client-side TLS and define CA to authenticate the device
# enable_tls = true
# tls_ca = "/etc/telegraf/ca.pem"
## Minimal TLS version to accept by the client
# tls_min_version = "TLS12"
## Use TLS but skip chain & host verification
# insecure_skip_verify = true
## define client-side TLS certificate & key to authenticate to the device

View File

@ -50,6 +50,8 @@ configuration.
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Minimal TLS version to accept by the client
# tls_min_version = "TLS12"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false

View File

@ -39,6 +39,8 @@
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Minimal TLS version to accept by the client
# tls_min_version = "TLS12"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false

View File

@ -49,6 +49,9 @@ InfluxDB it is recommended to use [`influxdb_listener`][influxdb_listener] or
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Minimal TLS version accepted by the server
# tls_min_version = "TLS12"
## Optional username and password to accept for HTTP basic authentication.
## You probably want to make sure you have TLS configured above for this.
# basic_username = "foobar"
@ -71,6 +74,8 @@ InfluxDB it is recommended to use [`influxdb_listener`][influxdb_listener] or
Metrics are collected from the part of the request specified by the
`data_source` param and are parsed depending on the value of `data_format`.
## Example Output
## Troubleshooting
Send Line Protocol:

View File

@ -33,6 +33,9 @@
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Minimal TLS version accepted by the server
# tls_min_version = "TLS12"
## Optional username and password to accept for HTTP basic authentication.
## You probably want to make sure you have TLS configured above for this.
# basic_username = "foobar"

View File

@ -50,6 +50,8 @@ from listed sensors using Junos Telemetry Interface. Refer to
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Minimal TLS version to accept by the client
# tls_min_version = "TLS12"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false
@ -65,3 +67,7 @@ from listed sensors using Junos Telemetry Interface. Refer to
- All measurements are tagged appropriately using the identifier information
in incoming data
## Example Output
## Metrics

View File

@ -38,6 +38,8 @@
# tls_ca = "/etc/telegraf/ca.pem"
# tls_cert = "/etc/telegraf/cert.pem"
# tls_key = "/etc/telegraf/key.pem"
## Minimal TLS version to accept by the client
# tls_min_version = "TLS12"
## Use TLS but skip chain & host verification
# insecure_skip_verify = false