Move TLS options to scrape config

Fixes #1013, fixes #989
This commit is contained in:
Jimmi Dyson 2015-09-07 00:07:44 +01:00
parent 1ef5ed0cf2
commit a1574aa2b3
9 changed files with 127 additions and 96 deletions

View file

@ -108,13 +108,18 @@ func checkConfig(t cli.Term, filename string) ([]string, error) {
return nil, fmt.Errorf("error checking bearer token file %q: %s", scfg.BearerTokenFile, err)
}
if scfg.ClientCert != nil {
if err := check(scfg.ClientCert.Cert); err != nil {
return nil, fmt.Errorf("error checking client cert file %q: %s", scfg.ClientCert.Cert, err)
}
if err := check(scfg.ClientCert.Key); err != nil {
return nil, fmt.Errorf("error checking client key file %q: %s", scfg.ClientCert.Key, err)
}
if err := check(scfg.TLSConfig.CertFile); err != nil {
return nil, fmt.Errorf("error checking client cert file %q: %s", scfg.TLSConfig.CertFile, err)
}
if err := check(scfg.TLSConfig.KeyFile); err != nil {
return nil, fmt.Errorf("error checking client key file %q: %s", scfg.TLSConfig.KeyFile, err)
}
if len(scfg.TLSConfig.CertFile) > 0 && len(scfg.TLSConfig.KeyFile) == 0 {
return nil, fmt.Errorf("client cert file %s specified without client key file", scfg.TLSConfig.CertFile)
}
if len(scfg.TLSConfig.KeyFile) > 0 && len(scfg.TLSConfig.CertFile) == 0 {
return nil, fmt.Errorf("client key file %s specified without client cert file", scfg.TLSConfig.KeyFile)
}
}

View file

@ -187,10 +187,15 @@ func resolveFilepaths(baseDir string, cfg *Config) {
for _, scfg := range cfg.ScrapeConfigs {
scfg.BearerTokenFile = join(scfg.BearerTokenFile)
scfg.TLSConfig.CAFile = join(scfg.TLSConfig.CAFile)
scfg.TLSConfig.CertFile = join(scfg.TLSConfig.CertFile)
scfg.TLSConfig.KeyFile = join(scfg.TLSConfig.KeyFile)
if scfg.ClientCert != nil {
scfg.ClientCert.Cert = join(scfg.ClientCert.Cert)
scfg.ClientCert.Key = join(scfg.ClientCert.Key)
for _, kcfg := range scfg.KubernetesSDConfigs {
kcfg.BearerTokenFile = join(kcfg.BearerTokenFile)
kcfg.TLSConfig.CAFile = join(kcfg.TLSConfig.CAFile)
kcfg.TLSConfig.CertFile = join(kcfg.TLSConfig.CertFile)
kcfg.TLSConfig.KeyFile = join(kcfg.TLSConfig.KeyFile)
}
}
}
@ -293,6 +298,18 @@ func (c *GlobalConfig) isZero() bool {
c.EvaluationInterval == 0
}
// TLSConfig configures the options for TLS connections.
type TLSConfig struct {
// The CA cert to use for the targets.
CAFile string `yaml:"ca_file,omitempty"`
// The client cert file for the targets.
CertFile string `yaml:"cert_file,omitempty"`
// The client key file for the targets.
KeyFile string `yaml:"key_file,omitempty"`
// Disable target certificate validation.
InsecureSkipVerify bool `yaml:"insecure_skip_verify"`
}
// ScrapeConfig configures a scraping unit for Prometheus.
type ScrapeConfig struct {
// The job name to which the job label is set by default.
@ -315,12 +332,10 @@ type ScrapeConfig struct {
BearerToken string `yaml:"bearer_token,omitempty"`
// The bearer token file for the targets.
BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
// The ca cert to use for the targets.
CACert string `yaml:"ca_cert,omitempty"`
// The client cert authentication credentials for the targets.
ClientCert *ClientCert `yaml:"client_cert,omitempty"`
// HTTP proxy server to use to connect to the targets.
ProxyURL URL `yaml:"proxy_url,omitempty"`
// Inlined TLSConfig.
TLSConfig TLSConfig `yaml:"tls_config,omitempty"`
// List of labeled target groups for this job.
TargetGroups []*TargetGroup `yaml:"target_groups,omitempty"`
@ -605,18 +620,15 @@ type MarathonSDConfig struct {
// KubernetesSDConfig is the configuration for Kubernetes service discovery.
type KubernetesSDConfig struct {
Masters []URL `yaml:"masters"`
KubeletPort int `yaml:"kubelet_port,omitempty"`
InCluster bool `yaml:"in_cluster,omitempty"`
BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
Username string `yaml:"username,omitempty"`
Password string `yaml:"password,omitempty"`
Insecure bool `yaml:"insecure,omitempty"`
CertFile string `yaml:"cert_file,omitempty"`
KeyFile string `yaml:"key_file,omitempty"`
CAFile string `yaml:"ca_file,omitempty"`
RetryInterval Duration `yaml:"retry_interval,omitempty"`
RequestTimeout Duration `yaml:"request_timeout,omitempty"`
Masters []URL `yaml:"masters"`
KubeletPort int `yaml:"kubelet_port,omitempty"`
InCluster bool `yaml:"in_cluster,omitempty"`
BearerTokenFile string `yaml:"bearer_token_file,omitempty"`
Username string `yaml:"username,omitempty"`
Password string `yaml:"password,omitempty"`
RetryInterval Duration `yaml:"retry_interval,omitempty"`
RequestTimeout Duration `yaml:"request_timeout,omitempty"`
TLSConfig TLSConfig `yaml:"tls_config,omitempty"`
// Catches all undefined fields and must be empty after parsing.
XXX map[string]interface{} `yaml:",inline"`

View file

@ -180,10 +180,11 @@ var expectedConf = &Config{
MetricsPath: "/metrics",
Scheme: "http",
ClientCert: &ClientCert{
Cert: "testdata/valid_cert_file",
Key: "testdata/valid_key_file",
TLSConfig: TLSConfig{
CertFile: "testdata/valid_cert_file",
KeyFile: "testdata/valid_key_file",
},
BearerToken: "avalidtoken",
},
{

View file

@ -94,9 +94,9 @@ scrape_configs:
- job_name: service-z
client_cert:
cert: valid_cert_file
key: valid_key_file
tls_config:
cert_file: valid_cert_file
key_file: valid_key_file
bearer_token: avalidtoken

View file

@ -8,7 +8,9 @@ global:
scrape_configs:
- job_name: 'kubernetes'
ca_cert: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
kubernetes_sd_configs:

View file

@ -14,8 +14,6 @@
package kubernetes
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io/ioutil"
@ -574,7 +572,7 @@ func (kd *Discovery) updateServiceEndpoints(endpoints *Endpoints, eventType Even
func newKubernetesHTTPClient(conf *config.KubernetesSDConfig) (*http.Client, error) {
bearerTokenFile := conf.BearerTokenFile
caFile := conf.CAFile
caFile := conf.TLSConfig.CAFile
if conf.InCluster {
if len(bearerTokenFile) == 0 {
bearerTokenFile = serviceAccountToken
@ -582,46 +580,31 @@ func newKubernetesHTTPClient(conf *config.KubernetesSDConfig) (*http.Client, err
if len(caFile) == 0 {
// With recent versions, the CA certificate is provided as a token
// but we need to handle older versions too. In this case, don't
// set the CAFile & the configuration will have to use Insecure.
// set the CAFile & the configuration will have to use InsecureSkipVerify.
if _, err := os.Stat(serviceAccountCACert); err == nil {
caFile = serviceAccountCACert
}
}
}
tlsConfig := &tls.Config{InsecureSkipVerify: conf.Insecure}
// Load client cert if specified.
if len(conf.CertFile) > 0 && len(conf.KeyFile) > 0 {
cert, err := tls.LoadX509KeyPair(conf.CertFile, conf.KeyFile)
if err != nil {
return nil, err
}
tlsConfig.Certificates = []tls.Certificate{cert}
tlsOpts := httputil.TLSOptions{
InsecureSkipVerify: conf.TLSConfig.InsecureSkipVerify,
CAFile: caFile,
CertFile: conf.TLSConfig.CertFile,
KeyFile: conf.TLSConfig.KeyFile,
}
tlsConfig, err := httputil.NewTLSConfig(tlsOpts)
if err != nil {
return nil, err
}
caCertPool := x509.NewCertPool()
if len(caFile) > 0 {
// Load CA cert.
caCert, err := ioutil.ReadFile(caFile)
if err != nil {
return nil, err
}
caCertPool.AppendCertsFromPEM(caCert)
}
tlsConfig.RootCAs = caCertPool
tlsConfig.BuildNameToCertificate()
tr := &http.Transport{
var rt http.RoundTripper = &http.Transport{
Dial: func(netw, addr string) (c net.Conn, err error) {
c, err = net.DialTimeout(netw, addr, time.Duration(conf.RequestTimeout))
return
},
TLSClientConfig: tlsConfig,
}
tr.TLSClientConfig = tlsConfig
var rt http.RoundTripper
rt = tr
bearerToken, err := ioutil.ReadFile(bearerTokenFile)
if err != nil {

View file

@ -14,8 +14,6 @@
package retrieval
import (
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"io"
@ -249,33 +247,21 @@ func (t *Target) Update(cfg *config.ScrapeConfig, baseLabels, metaLabels model.L
}
func newHTTPClient(cfg *config.ScrapeConfig) (*http.Client, error) {
tlsConfig := &tls.Config{}
// If a CA cert is provided then let's read it in so we can validate the
// scrape target's certificate properly.
if len(cfg.CACert) > 0 {
caCertPool := x509.NewCertPool()
// Load CA cert.
caCert, err := ioutil.ReadFile(cfg.CACert)
if err != nil {
return nil, fmt.Errorf("unable to use specified CA cert %s: %s", cfg.CACert, err)
}
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig.RootCAs = caCertPool
}
// If a client cert & key is provided then configure TLS config accordingly.
if cfg.ClientCert != nil && len(cfg.ClientCert.Cert) > 0 && len(cfg.ClientCert.Key) > 0 {
cert, err := tls.LoadX509KeyPair(cfg.ClientCert.Cert, cfg.ClientCert.Key)
if err != nil {
return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", cfg.ClientCert.Cert, cfg.ClientCert.Key, err)
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
tlsConfig.BuildNameToCertificate()
// Get a default roundtripper with the scrape timeout.
rt := httputil.NewDeadlineRoundTripper(time.Duration(cfg.ScrapeTimeout), cfg.ProxyURL.URL)
tlsOpts := httputil.TLSOptions{
InsecureSkipVerify: cfg.TLSConfig.InsecureSkipVerify,
CAFile: cfg.TLSConfig.CAFile,
}
if len(cfg.TLSConfig.CertFile) > 0 && len(cfg.TLSConfig.KeyFile) > 0 {
tlsOpts.CertFile = cfg.TLSConfig.CertFile
tlsOpts.KeyFile = cfg.TLSConfig.KeyFile
}
tlsConfig, err := httputil.NewTLSConfig(tlsOpts)
if err != nil {
return nil, err
}
// Get a default roundtripper with the scrape timeout.
tr := rt.(*http.Transport)
// Set the TLS config from above
tr.TLSClientConfig = tlsConfig

View file

@ -563,7 +563,9 @@ func TestNewHTTPCACert(t *testing.T) {
cfg := &config.ScrapeConfig{
ScrapeTimeout: config.Duration(1 * time.Second),
CACert: "testdata/ca.cer",
TLSConfig: config.TLSConfig{
CAFile: "testdata/ca.cer",
},
}
c, err := newHTTPClient(cfg)
if err != nil {
@ -594,10 +596,10 @@ func TestNewHTTPClientCert(t *testing.T) {
cfg := &config.ScrapeConfig{
ScrapeTimeout: config.Duration(1 * time.Second),
CACert: "testdata/ca.cer",
ClientCert: &config.ClientCert{
Cert: "testdata/client.cer",
Key: "testdata/client.key",
TLSConfig: config.TLSConfig{
CAFile: "testdata/ca.cer",
CertFile: "testdata/client.cer",
KeyFile: "testdata/client.key",
},
}
c, err := newHTTPClient(cfg)

View file

@ -14,6 +14,10 @@
package httputil
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
@ -108,3 +112,39 @@ func cloneRequest(r *http.Request) *http.Request {
}
return r2
}
type TLSOptions struct {
InsecureSkipVerify bool
CAFile string
CertFile string
KeyFile string
}
func NewTLSConfig(opts TLSOptions) (*tls.Config, error) {
tlsConfig := &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify}
// If a CA cert is provided then let's read it in so we can validate the
// scrape target's certificate properly.
if len(opts.CAFile) > 0 {
caCertPool := x509.NewCertPool()
// Load CA cert.
caCert, err := ioutil.ReadFile(opts.CAFile)
if err != nil {
return nil, fmt.Errorf("unable to use specified CA cert %s: %s", opts.CAFile, err)
}
caCertPool.AppendCertsFromPEM(caCert)
tlsConfig.RootCAs = caCertPool
}
// If a client cert & key is provided then configure TLS config accordingly.
if len(opts.CertFile) > 0 && len(opts.KeyFile) > 0 {
cert, err := tls.LoadX509KeyPair(opts.CertFile, opts.KeyFile)
if err != nil {
return nil, fmt.Errorf("unable to use specified client cert (%s) & key (%s): %s", opts.CertFile, opts.KeyFile, err)
}
tlsConfig.Certificates = []tls.Certificate{cert}
}
tlsConfig.BuildNameToCertificate()
return tlsConfig, nil
}