Skip to content

Commit

Permalink
Merge pull request influxdata#2198 from influxdata/ga-tls
Browse files Browse the repository at this point in the history
Add TLS configuration options
  • Loading branch information
nathanielc authored May 8, 2019
2 parents 5e10277 + 7c915cc commit c5a0bf7
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features

- [#2154](https://github.com/influxdata/kapacitor/pull/2154): Add ability to skip ssl verification with an alert post node. Thanks @itsHabib!
- [#2193](https://github.com/influxdata/kapacitor/issues/2193): Add TLS configuration options.

### Bugfixes

Expand Down
18 changes: 18 additions & 0 deletions etc/kapacitor/kapacitor.conf
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,24 @@ default-retention-policy = ""
### Use a separate private key location.
# https-private-key = ""

[tls]
# Determines the available set of cipher suites. See https://golang.org/pkg/crypto/tls/#pkg-constants
# for a list of available ciphers, which depends on the version of Go (use the query
# SHOW DIAGNOSTICS to see the version of Go used to build Kapacitor). If not specified, uses
# the default settings from Go's crypto/tls package.
# ciphers = [
# "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
# "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
# ]

# Minimum version of the tls protocol that will be negotiated. If not specified, uses the
# default settings from Go's crypto/tls package.
# min-version = "tls1.2"

# Maximum version of the tls protocol that will be negotiated. If not specified, uses the
# default settings from Go's crypto/tls package.
# max-version = "tls1.2"

[config-override]
# Enable/Disable the service for overridding configuration via the HTTP API.
enabled = true
Expand Down
3 changes: 2 additions & 1 deletion integrations/helpers_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package integrations

import (
"crypto/tls"
"errors"
"fmt"
"net/http"
Expand All @@ -23,7 +24,7 @@ func newHTTPDService() *httpd.Service {
config := httpd.NewConfig()
config.BindAddress = ":0" // Choose port dynamically
config.LogEnabled = testing.Verbose()
httpService := httpd.NewService(config, "localhost", diagService.NewHTTPDHandler())
httpService := httpd.NewService(config, "localhost", new(tls.Config), diagService.NewHTTPDHandler())
err := httpService.Open()
if err != nil {
panic(err)
Expand Down
6 changes: 6 additions & 0 deletions server/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import (
"github.com/influxdata/kapacitor/services/udf"
"github.com/influxdata/kapacitor/services/udp"
"github.com/influxdata/kapacitor/services/victorops"
"github.com/influxdata/kapacitor/tlsconfig"
"github.com/pkg/errors"

"github.com/influxdata/influxdb/services/collectd"
Expand All @@ -75,6 +76,7 @@ type Config struct {
InfluxDB []influxdb.Config `toml:"influxdb" override:"influxdb,element-key=name"`
Logging diagnostic.Config `toml:"logging"`
ConfigOverride config.Config `toml:"config-override"`
TLS tlsconfig.Config `toml:"tls"`

// Input services
Graphite []graphite.Config `toml:"graphite"`
Expand Down Expand Up @@ -147,6 +149,7 @@ func NewConfig() *Config {
c.InfluxDB = []influxdb.Config{influxdb.NewConfig()}
c.Logging = diagnostic.NewConfig()
c.ConfigOverride = config.NewConfig()
c.TLS = tlsconfig.NewConfig()

c.Collectd = collectd.NewConfig()
c.OpenTSDB = opentsdb.NewConfig()
Expand Down Expand Up @@ -222,6 +225,9 @@ func (c *Config) Validate() error {
if err := c.Task.Validate(); err != nil {
return errors.Wrap(err, "task")
}
if err := c.TLS.Validate(); err != nil {
return errors.Wrap(err, "tls")
}
if err := c.Load.Validate(); err != nil {
return err
}
Expand Down
15 changes: 13 additions & 2 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package server

import (
"crypto/tls"
"fmt"
"io/ioutil"
"os"
Expand Down Expand Up @@ -98,7 +99,8 @@ type Server struct {
dataDir string
hostname string

config *Config
config *Config
tlsConfig *tls.Config

err chan error

Expand Down Expand Up @@ -158,9 +160,18 @@ func New(c *Config, buildInfo BuildInfo, diagService *diagnostic.Service) (*Serv
if err != nil {
return nil, fmt.Errorf("invalid configuration: %s. To generate a valid configuration file run `kapacitord config > kapacitor.generated.conf`.", err)
}
// Setup base TLS config used for the Kapacitor API
tlsConfig, err := c.TLS.Parse()
if err != nil {
return nil, errors.Wrap(err, "tls configuration")
}
if tlsConfig == nil {
tlsConfig = new(tls.Config)
}
d := diagService.NewServerHandler()
s := &Server{
config: c,
tlsConfig: tlsConfig,
BuildInfo: buildInfo,
dataDir: c.DataDir,
hostname: c.Hostname,
Expand Down Expand Up @@ -448,7 +459,7 @@ func (s *Server) appendInfluxDBService() error {

func (s *Server) initHTTPDService() {
d := s.DiagService.NewHTTPDHandler()
srv := httpd.NewService(s.config.HTTP, s.hostname, d)
srv := httpd.NewService(s.config.HTTP, s.hostname, s.tlsConfig, d)

srv.LocalHandler.PointsWriter = s.TaskMaster
srv.Handler.PointsWriter = s.TaskMaster
Expand Down
22 changes: 12 additions & 10 deletions services/httpd/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,13 @@ type Diagnostic interface {
}

type Service struct {
ln net.Listener
addr string
https bool
cert string
key string
err chan error
ln net.Listener
addr string
https bool
cert string
tlsConfig *tls.Config
key string
err chan error

externalURL string

Expand All @@ -87,7 +88,7 @@ type Service struct {
httpServerErrorLogger *log.Logger
}

func NewService(c Config, hostname string, d Diagnostic) *Service {
func NewService(c Config, hostname string, t *tls.Config, d Diagnostic) *Service {
statMap := &expvar.Map{}
statMap.Init()

Expand All @@ -108,6 +109,7 @@ func NewService(c Config, hostname string, d Diagnostic) *Service {
key: c.HTTPSPrivateKey,
externalURL: u.String(),
err: make(chan error, 1),
tlsConfig: t,
shutdownTimeout: time.Duration(c.ShutdownTimeout),
Handler: NewHandler(
c.AuthEnabled,
Expand Down Expand Up @@ -153,9 +155,9 @@ func (s *Service) Open() error {
return err
}

listener, err := tls.Listen("tcp", s.addr, &tls.Config{
Certificates: []tls.Certificate{cert},
})
tlsConfig := s.tlsConfig.Clone()
tlsConfig.Certificates = []tls.Certificate{cert}
listener, err := tls.Listen("tcp", s.addr, tlsConfig)
if err != nil {
return err
}
Expand Down
122 changes: 122 additions & 0 deletions tlsconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"errors"
"fmt"
"io/ioutil"
"sort"
"strings"
)

// Create creates a new tls.Config object from the given certs, key, and CA files.
Expand Down Expand Up @@ -42,3 +44,123 @@ func Create(
}
return t, nil
}

type Config struct {
Ciphers []string `toml:"ciphers"`
MinVersion string `toml:"min-version"`
MaxVersion string `toml:"max-version"`
}

func NewConfig() Config {
return Config{}
}

func (c Config) Validate() error {
_, err := c.Parse()
return err
}

func (c Config) Parse() (out *tls.Config, err error) {
if len(c.Ciphers) > 0 {
if out == nil {
out = new(tls.Config)
}

for _, name := range c.Ciphers {
cipher, ok := ciphersMap[strings.ToUpper(name)]
if !ok {
return nil, unknownCipher(name)
}
out.CipherSuites = append(out.CipherSuites, cipher)
}
}

if c.MinVersion != "" {
if out == nil {
out = new(tls.Config)
}

version, ok := versionsMap[strings.ToUpper(c.MinVersion)]
if !ok {
return nil, unknownVersion(c.MinVersion)
}
out.MinVersion = version
}

if c.MaxVersion != "" {
if out == nil {
out = new(tls.Config)
}

version, ok := versionsMap[strings.ToUpper(c.MaxVersion)]
if !ok {
return nil, unknownVersion(c.MaxVersion)
}
out.MaxVersion = version
}

return out, nil
}

var ciphersMap = map[string]uint16{
"TLS_RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA,
"TLS_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
"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_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
"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_ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
"TLS_ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_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_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
"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_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
}

func unknownCipher(name string) error {
available := make([]string, 0, len(ciphersMap))
for name := range ciphersMap {
available = append(available, name)
}
sort.Strings(available)

return fmt.Errorf("unknown cipher suite: %q. available ciphers: %s",
name, strings.Join(available, ", "))
}

var versionsMap = map[string]uint16{
"SSL3.0": tls.VersionSSL30,
"TLS1.0": tls.VersionTLS10,
"1.0": tls.VersionTLS10,
"TLS1.1": tls.VersionTLS11,
"1.1": tls.VersionTLS11,
"TLS1.2": tls.VersionTLS12,
"1.2": tls.VersionTLS12,
}

func unknownVersion(name string) error {
available := make([]string, 0, len(versionsMap))
for name := range versionsMap {
// skip the ones that just begin with a number. they may be confusing
// due to the duplication, and just help if the user specifies without
// the TLS part.
if name[0] == '1' {
continue
}
available = append(available, name)
}
sort.Strings(available)

return fmt.Errorf("unknown tls version: %q. available versions: %s",
name, strings.Join(available, ", "))
}

0 comments on commit c5a0bf7

Please sign in to comment.