From 6c17de76e2a984cb01285eddfb7a70883e4ca1ff Mon Sep 17 00:00:00 2001 From: Nathaniel Cook Date: Mon, 14 Mar 2016 12:16:19 -0600 Subject: [PATCH] support detailed TLS config for connecting to InfluxDB --- CHANGELOG.md | 1 + etc/kapacitor/kapacitor.conf | 11 ++++++++ services/influxdb/config.go | 25 ++++++++++++----- services/influxdb/service.go | 52 ++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbeeb1aa9..24c5d1d66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ With #144 you can now join streams with differing group by dimensions. - [#249](https://github.com/influxdata/kapacitor/issues/249): Can now use InfluxQL functions directly instead of via the MapReduce method. Example `stream.from().count()`. - [#233](https://github.com/influxdata/kapacitor/issues/233): BREAKING: Now you can use multiple InfluxDB clusters. The config changes to make this possible are breaking. See notes above for changes. - [#302](https://github.com/influxdata/kapacitor/issues/302): Can now use .Time in alert message. +- [#239](https://github.com/influxdata/kapacitor/issues/239): Support more detailed TLS config when connecting to an InfluxDB host. ### Bugfixes diff --git a/etc/kapacitor/kapacitor.conf b/etc/kapacitor/kapacitor.conf index 9ccba9ec5..50c0ca6eb 100644 --- a/etc/kapacitor/kapacitor.conf +++ b/etc/kapacitor/kapacitor.conf @@ -65,6 +65,17 @@ data_dir = "/var/lib/kapacitor" username = "" password = "" timeout = 0 + # Absolute path to pem encoded CA file. + # A CA can be provided without a key/cert pair + # ssl-ca = "/etc/kapacitor/ca.pem" + # Absolutes paths to pem encoded key and cert files. + # ssl-cert = "/etc/kapacitor/cert.pem" + # ssl-key = "/etc/kapacitor/key.pem" + + # Do not verify the TLS/SSL certificate. + # This is insecure. + insecure-skip-verify = false + # Subscriptions use the UDP network protocl. # The following options of for the created UDP listeners for each subscription. # Number of packets to buffer when reading packets off the socket. diff --git a/services/influxdb/config.go b/services/influxdb/config.go index 99cfe0431..c6e93dc0b 100644 --- a/services/influxdb/config.go +++ b/services/influxdb/config.go @@ -16,12 +16,21 @@ const ( ) type Config struct { - Enabled bool `toml:"enabled"` - Name string `toml:"name"` - Default bool `toml:"default"` - URLs []string `toml:"urls"` - Username string `toml:"username"` - Password string `toml:"password"` + Enabled bool `toml:"enabled"` + Name string `toml:"name"` + Default bool `toml:"default"` + URLs []string `toml:"urls"` + Username string `toml:"username"` + Password string `toml:"password"` + // Path to CA file + SSLCA string `toml:"ssl-ca"` + // Path to host cert file + SSLCert string `toml:"ssl-cert"` + // Path to cert key file + SSLKey string `toml:"ssl-key"` + // Use SSL but skip chain & host verification + InsecureSkipVerify bool `toml:"insecure-skip-verify"` + Timeout toml.Duration `toml:"timeout"` Subscriptions map[string][]string `toml:"subscriptions"` ExcludedSubscriptions map[string][]string `toml:"excluded-subscriptions"` @@ -59,6 +68,10 @@ func (c Config) Validate() error { if err != nil { return err } + _, err = getTLSConfig(c.SSLCA, c.SSLCert, c.SSLKey, c.InsecureSkipVerify) + if err != nil { + return err + } } return nil } diff --git a/services/influxdb/service.go b/services/influxdb/service.go index 6fab1e047..a3577f95e 100644 --- a/services/influxdb/service.go +++ b/services/influxdb/service.go @@ -2,7 +2,11 @@ package influxdb import ( "bytes" + "crypto/tls" + "crypto/x509" + "errors" "fmt" + "io/ioutil" "log" "net" "net/url" @@ -43,6 +47,14 @@ func NewService(configs []Config, defaultInfluxDB int, hostname string, l *log.L var defaultInfluxDBName string for i, c := range configs { urls := make([]client.HTTPConfig, len(c.URLs)) + tlsConfig, err := getTLSConfig(c.SSLCA, c.SSLCert, c.SSLKey, c.InsecureSkipVerify) + if err != nil { + // Config should have been validated already + panic(err) + } + if c.InsecureSkipVerify { + l.Printf("W! Using InsecureSkipVerify when connecting to InfluxDB @ %v this is insecure!", c.URLs) + } for i, u := range c.URLs { urls[i] = client.HTTPConfig{ Addr: u, @@ -50,6 +62,7 @@ func NewService(configs []Config, defaultInfluxDB int, hostname string, l *log.L Password: c.Password, UserAgent: "Kapacitor", Timeout: time.Duration(c.Timeout), + TLSConfig: tlsConfig, } } subs := make(map[subEntry]bool, len(c.Subscriptions)) @@ -445,3 +458,42 @@ func (s *influxdb) execQuery(cli client.Client, q string) (*client.Response, err } return resp, nil } + +// getTLSConfig creates a tls.Config object from the given certs, key, and CA files. +// you must give the full path to the files. +func getTLSConfig( + SSLCA, SSLCert, SSLKey string, + InsecureSkipVerify bool, +) (*tls.Config, error) { + t := &tls.Config{ + InsecureSkipVerify: InsecureSkipVerify, + } + if SSLCert != "" && SSLKey != "" { + cert, err := tls.LoadX509KeyPair(SSLCert, SSLKey) + log.Println(SSLCert, SSLKey) + + if err != nil { + return nil, fmt.Errorf( + "Could not load TLS client key/certificate: %s", + err) + } + + t.Certificates = []tls.Certificate{cert} + } else if SSLCert != "" { + return nil, errors.New("Must provide both key and cert files: only cert file provided.") + } else if SSLKey != "" { + return nil, errors.New("Must provide both key and cert files: only key file provided.") + } + + if SSLCA != "" { + caCert, err := ioutil.ReadFile(SSLCA) + if err != nil { + return nil, fmt.Errorf("Could not load TLS CA: %s", + err) + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + t.RootCAs = caCertPool + } + return t, nil +}