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

Add possibility to setting up tls security levels #6246

Merged
merged 3 commits into from
Aug 19, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
36 changes: 36 additions & 0 deletions internal/tls/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/x509"
"fmt"
"io/ioutil"
"strings"
)

// ClientConfig represents the standard client TLS config.
Expand All @@ -25,6 +26,9 @@ type ServerConfig struct {
TLSCert string `toml:"tls_cert"`
TLSKey string `toml:"tls_key"`
TLSAllowedCACerts []string `toml:"tls_allowed_cacerts"`
TLSCipherSuites []string `toml:"tls_cipher_suites"`
TLSMinVersion string `toml:"tls_min_version"`
TLSMaxVersion string `toml:"tls_max_version"`
}

// TLSConfig returns a tls.Config, may be nil without error if TLS is not
Expand Down Expand Up @@ -97,6 +101,38 @@ func (c *ServerConfig) TLSConfig() (*tls.Config, error) {
}
}

if len(c.TLSCipherSuites) != 0 {
cipherSuites, err := ParseCiphers(c.TLSCipherSuites)
if err != nil {
return nil, fmt.Errorf(
"could not parse server cipher suites %s: %v", strings.Join(c.TLSCipherSuites, ","), err)
}
tlsConfig.CipherSuites = cipherSuites
}

if c.TLSMaxVersion != "" {
version, err := ParseTLSVersion(c.TLSMaxVersion)
if err != nil {
return nil, fmt.Errorf(
"could not parse tls max version %q: %v", c.TLSMaxVersion, err)
}
tlsConfig.MaxVersion = version
}

if c.TLSMinVersion != "" {
version, err := ParseTLSVersion(c.TLSMinVersion)
if err != nil {
return nil, fmt.Errorf(
"could not parse tls min version %q: %v", c.TLSMinVersion, err)
}
tlsConfig.MinVersion = version
}

if tlsConfig.MinVersion != 0 && tlsConfig.MaxVersion != 0 && tlsConfig.MinVersion > tlsConfig.MaxVersion {
return nil, fmt.Errorf(
"tls min version %q can't be greater then tls max version %q", tlsConfig.MinVersion, tlsConfig.MaxVersion)
}

return tlsConfig, nil
}

Expand Down
91 changes: 91 additions & 0 deletions internal/tls/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,47 @@ func TestServerConfig(t *testing.T) {
TLSCert: pki.ServerCertPath(),
TLSKey: pki.ServerKeyPath(),
TLSAllowedCACerts: []string{pki.CACertPath()},
TLSCipherSuites: []string{pki.CipherSuite()},
TLSMinVersion: pki.TLSMinVersion(),
TLSMaxVersion: pki.TLSMaxVersion(),
},
},
{
name: "missing tls cipher suites is okay",
server: tls.ServerConfig{
TLSCert: pki.ServerCertPath(),
TLSKey: pki.ServerKeyPath(),
TLSAllowedCACerts: []string{pki.CACertPath()},
TLSCipherSuites: []string{pki.CipherSuite()},
},
},
{
name: "missing tls max version is okay",
server: tls.ServerConfig{
TLSCert: pki.ServerCertPath(),
TLSKey: pki.ServerKeyPath(),
TLSAllowedCACerts: []string{pki.CACertPath()},
TLSCipherSuites: []string{pki.CipherSuite()},
TLSMaxVersion: pki.TLSMaxVersion(),
},
},
{
name: "missing tls min version is okay",
server: tls.ServerConfig{
TLSCert: pki.ServerCertPath(),
TLSKey: pki.ServerKeyPath(),
TLSAllowedCACerts: []string{pki.CACertPath()},
TLSCipherSuites: []string{pki.CipherSuite()},
TLSMinVersion: pki.TLSMinVersion(),
},
},
{
name: "missing tls min/max versions is okay",
server: tls.ServerConfig{
TLSCert: pki.ServerCertPath(),
TLSKey: pki.ServerKeyPath(),
TLSAllowedCACerts: []string{pki.CACertPath()},
TLSCipherSuites: []string{pki.CipherSuite()},
},
},
{
Expand Down Expand Up @@ -172,6 +213,56 @@ func TestServerConfig(t *testing.T) {
expNil: true,
expErr: true,
},
{
name: "invalid cipher suites",
server: tls.ServerConfig{
TLSCert: pki.ServerCertPath(),
TLSKey: pki.ServerKeyPath(),
TLSAllowedCACerts: []string{pki.CACertPath()},
TLSCipherSuites: []string{pki.CACertPath()},
},
expNil: true,
expErr: true,
},
{
name: "TLS Max Version less then TLS Min version",
server: tls.ServerConfig{
TLSCert: pki.ServerCertPath(),
TLSKey: pki.ServerKeyPath(),
TLSAllowedCACerts: []string{pki.CACertPath()},
TLSCipherSuites: []string{pki.CACertPath()},
TLSMinVersion: pki.TLSMaxVersion(),
TLSMaxVersion: pki.TLSMinVersion(),
},
expNil: true,
expErr: true,
},
{
name: "invalid tls min version",
server: tls.ServerConfig{
TLSCert: pki.ServerCertPath(),
TLSKey: pki.ServerKeyPath(),
TLSAllowedCACerts: []string{pki.CACertPath()},
TLSCipherSuites: []string{pki.CipherSuite()},
TLSMinVersion: pki.ServerKeyPath(),
TLSMaxVersion: pki.TLSMaxVersion(),
},
expNil: true,
expErr: true,
},
{
name: "invalid tls max version",
server: tls.ServerConfig{
TLSCert: pki.ServerCertPath(),
TLSKey: pki.ServerKeyPath(),
TLSAllowedCACerts: []string{pki.CACertPath()},
TLSCipherSuites: []string{pki.CACertPath()},
TLSMinVersion: pki.TLSMinVersion(),
TLSMaxVersion: pki.ServerCertPath(),
},
expNil: true,
expErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
59 changes: 59 additions & 0 deletions internal/tls/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package tls

import (
"crypto/tls"
"fmt"
)

func ParseCiphers(ciphers []string) ([]uint16, error) {
suites := []uint16{}

cipherMap := map[string]uint16{
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
"TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
"TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
"TLS_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
"TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
"TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
}
for _, cipher := range ciphers {
if v, ok := cipherMap[cipher]; ok {
suites = append(suites, v)
} else {
return suites, fmt.Errorf("unsupported cipher %q", cipher)
svagner marked this conversation as resolved.
Show resolved Hide resolved
}
}

return suites, nil
}

func ParseTLSVersion(version string) (uint16, error) {
versionMap := map[string]uint16{
"SSL30": tls.VersionSSL30,
svagner marked this conversation as resolved.
Show resolved Hide resolved
"TLS10": tls.VersionTLS10,
"TLS11": tls.VersionTLS11,
"TLS12": tls.VersionTLS12,
// for go >= 1.12
//"TLS13": tls.VersionTLS13,
danielnelson marked this conversation as resolved.
Show resolved Hide resolved
}
if v, ok := versionMap[version]; ok {
return v, nil
}
return 0, fmt.Errorf("unsupported version %q", version)
}
36 changes: 36 additions & 0 deletions plugins/outputs/prometheus_client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,42 @@ This plugin starts a [Prometheus](https://prometheus.io/) Client, it exposes all
## enable mutually authenticated TLS connections
# tls_allowed_cacerts = ["/etc/telegraf/clientca.pem"]

## Use only tls ciphers defined in this list
danielnelson marked this conversation as resolved.
Show resolved Hide resolved
## Possible values:
## TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
## TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
## TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
## TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
## TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
## TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
## TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
## TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
## TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
## TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
## TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
## TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
## TLS_RSA_WITH_AES_128_GCM_SHA256
## TLS_RSA_WITH_AES_256_GCM_SHA384
## TLS_RSA_WITH_AES_128_CBC_SHA256
## TLS_RSA_WITH_AES_128_CBC_SHA
## TLS_RSA_WITH_AES_256_CBC_SHA
## TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
## TLS_RSA_WITH_3DES_EDE_CBC_SHA
## TLS_RSA_WITH_RC4_128_SHA
## TLS_ECDHE_RSA_WITH_RC4_128_SHA
## TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
## If value wasn't defined default will be used
# tls_cipher_suites = ["TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_AES_128_GCM_SHA256", "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA"]

## contains the minimum SSL/TLS version that is acceptable.
## If not set, then TLS 1.0 is taken as the minimum.
# tls_min_version = "TLS11"

## contains the maximum SSL/TLS version that is acceptable.
## If not set, then the maximum version supported by this package is used,
svagner marked this conversation as resolved.
Show resolved Hide resolved
## which is currently TLS 1.2 (for go < 1.12) or TLS 1.3 (for go >= 1.12).
# tls_max_version = "TLS12"

## Export metric collection time.
# export_timestamp = false
```
2 changes: 2 additions & 0 deletions plugins/outputs/prometheus_client/prometheus_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ var sampleConfig = `
## enable mutually authenticated TLS connections
# tls_allowed_cacerts = ["/etc/telegraf/clientca.pem"]
# tls_cipher_suites = ["TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_RSA_WITH_AES_128_GCM_SHA256", "TLS_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA"]
## Export metric collection time.
# export_timestamp = false
`
Expand Down
15 changes: 14 additions & 1 deletion plugins/outputs/prometheus_client/prometheus_client_tls_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"net/http"
"testing"

inttls "github.com/influxdata/telegraf/internal/tls"
"github.com/influxdata/telegraf/plugins/outputs/prometheus_client"
"github.com/influxdata/telegraf/testutil"
"github.com/influxdata/toml"
Expand All @@ -19,7 +20,9 @@ var configWithTLS = fmt.Sprintf(`
tls_allowed_cacerts = ["%s"]
tls_cert = "%s"
tls_key = "%s"
`, pki.TLSServerConfig().TLSAllowedCACerts[0], pki.TLSServerConfig().TLSCert, pki.TLSServerConfig().TLSKey)
tls_cipher_suites = ["%s"]
tls_min_version = "%s"
`, pki.TLSServerConfig().TLSAllowedCACerts[0], pki.TLSServerConfig().TLSCert, pki.TLSServerConfig().TLSKey, pki.CipherSuite(), pki.TLSMaxVersion())

var configWithoutTLS = `
listen = "127.0.0.1:0"
Expand Down Expand Up @@ -50,12 +53,22 @@ func TestWorksWithTLS(t *testing.T) {
require.NoError(t, err)
defer tc.Output.Close()

serverCiphers, err := inttls.ParseCiphers(tc.Output.ServerConfig.TLSCipherSuites)
require.NoError(t, err)
require.Equal(t, 1, len(serverCiphers))

tlsVersion, err := inttls.ParseTLSVersion(tc.Output.ServerConfig.TLSMinVersion)
require.NoError(t, err)

response, err := tc.Client.Get(tc.Output.URL())
require.NoError(t, err)

require.NoError(t, err)
require.Equal(t, response.StatusCode, http.StatusOK)

require.Equal(t, response.TLS.CipherSuite, serverCiphers[0])
require.Equal(t, response.TLS.Version, tlsVersion)

tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
Expand Down
15 changes: 15 additions & 0 deletions testutil/tls.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ func (p *pki) TLSServerConfig() *tls.ServerConfig {
TLSAllowedCACerts: []string{p.CACertPath()},
TLSCert: p.ServerCertPath(),
TLSKey: p.ServerKeyPath(),
TLSCipherSuites: []string{p.CipherSuite()},
TLSMinVersion: p.TLSMinVersion(),
TLSMaxVersion: p.TLSMaxVersion(),
}
}

Expand All @@ -41,6 +44,18 @@ func (p *pki) CACertPath() string {
return path.Join(p.path, "cacert.pem")
}

func (p *pki) CipherSuite() string {
return "TLS_RSA_WITH_3DES_EDE_CBC_SHA"
}

func (p *pki) TLSMinVersion() string {
return "TLS11"
}

func (p *pki) TLSMaxVersion() string {
return "TLS12"
}

func (p *pki) ReadClientCert() string {
return readCertificate(p.ClientCertPath())
}
Expand Down