From d37057105dda97aa0665ccb572763bf0f611c40f Mon Sep 17 00:00:00 2001 From: Conor Broderick Date: Tue, 11 Jul 2017 14:58:36 +0100 Subject: [PATCH] Expose loaded blackbox configuration on /config (#185) --- config.go | 66 +++++++++++++++++++------------------- config_test.go | 30 ++++++++++++++++- main.go | 28 ++++++++++++---- testdata/blackbox-good.yml | 3 ++ 4 files changed, 86 insertions(+), 41 deletions(-) diff --git a/config.go b/config.go index 1a5ebc0..5e29788 100644 --- a/config.go +++ b/config.go @@ -22,12 +22,12 @@ type SafeConfig struct { } type Module struct { - Prober string `yaml:"prober"` - Timeout time.Duration `yaml:"timeout"` - HTTP HTTPProbe `yaml:"http"` - TCP TCPProbe `yaml:"tcp"` - ICMP ICMPProbe `yaml:"icmp"` - DNS DNSProbe `yaml:"dns"` + Prober string `yaml:"prober,omitempty"` + Timeout time.Duration `yaml:"timeout,omitempty"` + HTTP HTTPProbe `yaml:"http,omitempty"` + TCP TCPProbe `yaml:"tcp,omitempty"` + ICMP ICMPProbe `yaml:"icmp,omitempty"` + DNS DNSProbe `yaml:"dns,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` @@ -35,16 +35,16 @@ type Module struct { type HTTPProbe struct { // Defaults to 2xx. - ValidStatusCodes []int `yaml:"valid_status_codes"` - PreferredIPProtocol string `yaml:"preferred_ip_protocol"` - NoFollowRedirects bool `yaml:"no_follow_redirects"` - FailIfSSL bool `yaml:"fail_if_ssl"` - FailIfNotSSL bool `yaml:"fail_if_not_ssl"` - Method string `yaml:"method"` - Headers map[string]string `yaml:"headers"` - FailIfMatchesRegexp []string `yaml:"fail_if_matches_regexp"` - FailIfNotMatchesRegexp []string `yaml:"fail_if_not_matches_regexp"` - Body string `yaml:"body"` + ValidStatusCodes []int `yaml:"valid_status_codes,omitempty"` + PreferredIPProtocol string `yaml:"preferred_ip_protocol,omitempty"` + NoFollowRedirects bool `yaml:"no_follow_redirects,omitempty"` + FailIfSSL bool `yaml:"fail_if_ssl,omitempty"` + FailIfNotSSL bool `yaml:"fail_if_not_ssl,omitempty"` + Method string `yaml:"method,omitempty"` + Headers map[string]string `yaml:"headers,omitempty"` + FailIfMatchesRegexp []string `yaml:"fail_if_matches_regexp,omitempty"` + FailIfNotMatchesRegexp []string `yaml:"fail_if_not_matches_regexp,omitempty"` + Body string `yaml:"body,omitempty"` HTTPClientConfig config.HTTPClientConfig `yaml:"http_client_config,inline"` // Catches all undefined fields and must be empty after parsing. @@ -52,47 +52,47 @@ type HTTPProbe struct { } type QueryResponse struct { - Expect string `yaml:"expect"` - Send string `yaml:"send"` + Expect string `yaml:"expect,omitempty"` + Send string `yaml:"send,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } type TCPProbe struct { - PreferredIPProtocol string `yaml:"preferred_ip_protocol"` - QueryResponse []QueryResponse `yaml:"query_response"` - TLS bool `yaml:"tls"` - TLSConfig config.TLSConfig `yaml:"tls_config"` + PreferredIPProtocol string `yaml:"preferred_ip_protocol,omitempty"` + QueryResponse []QueryResponse `yaml:"query_response,omitempty"` + TLS bool `yaml:"tls,omitempty"` + TLSConfig config.TLSConfig `yaml:"tls_config,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } type ICMPProbe struct { - PreferredIPProtocol string `yaml:"preferred_ip_protocol"` // Defaults to "ip6". + PreferredIPProtocol string `yaml:"preferred_ip_protocol,omitempty"` // Defaults to "ip6". // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } type DNSProbe struct { - PreferredIPProtocol string `yaml:"preferred_ip_protocol"` - TransportProtocol string `yaml:"transport_protocol"` - QueryName string `yaml:"query_name"` - QueryType string `yaml:"query_type"` // Defaults to ANY. - ValidRcodes []string `yaml:"valid_rcodes"` // Defaults to NOERROR. - ValidateAnswer DNSRRValidator `yaml:"validate_answer_rrs"` - ValidateAuthority DNSRRValidator `yaml:"validate_authority_rrs"` - ValidateAdditional DNSRRValidator `yaml:"validate_additional_rrs"` + PreferredIPProtocol string `yaml:"preferred_ip_protocol,omitempty"` + TransportProtocol string `yaml:"transport_protocol,omitempty"` + QueryName string `yaml:"query_name,omitempty"` + QueryType string `yaml:"query_type,omitempty"` // Defaults to ANY. + ValidRcodes []string `yaml:"valid_rcodes,omitempty"` // Defaults to NOERROR. + ValidateAnswer DNSRRValidator `yaml:"validate_answer_rrs,omitempty"` + ValidateAuthority DNSRRValidator `yaml:"validate_authority_rrs,omitempty"` + ValidateAdditional DNSRRValidator `yaml:"validate_additional_rrs,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` } type DNSRRValidator struct { - FailIfMatchesRegexp []string `yaml:"fail_if_matches_regexp"` - FailIfNotMatchesRegexp []string `yaml:"fail_if_not_matches_regexp"` + FailIfMatchesRegexp []string `yaml:"fail_if_matches_regexp,omitempty"` + FailIfNotMatchesRegexp []string `yaml:"fail_if_not_matches_regexp,omitempty"` // Catches all undefined fields and must be empty after parsing. XXX map[string]interface{} `yaml:",inline"` diff --git a/config_test.go b/config_test.go index bf42847..cea82c4 100644 --- a/config_test.go +++ b/config_test.go @@ -1,6 +1,11 @@ package main -import "testing" +import ( + "strings" + "testing" + + yaml "gopkg.in/yaml.v2" +) func TestLoadConfig(t *testing.T) { sc := &SafeConfig{ @@ -25,3 +30,26 @@ func TestLoadBadConfig(t *testing.T) { t.Errorf("\nexpected:\n%v\ngot:\n%v", expected, err.Error()) } } + +func TestHideConfigSecrets(t *testing.T) { + + sc := &SafeConfig{ + C: &Config{}, + } + + err := sc.reloadConfig("testdata/blackbox-good.yml") + if err != nil { + t.Errorf("Error loading config %v: %v", "testdata/blackbox-good.yml", err) + } + + // String method must not reveal authentication credentials. + sc.RLock() + c, err := yaml.Marshal(sc.C) + sc.RUnlock() + if err != nil { + t.Errorf("Error marshalling config: %v", err) + } + if strings.Contains(string(c), "mysecret") { + t.Fatal("config's String method reveals authentication credentials.") + } +} diff --git a/main.go b/main.go index fef6c15..5eff6bc 100644 --- a/main.go +++ b/main.go @@ -176,15 +176,29 @@ func main() { http.Error(w, fmt.Sprintf("failed to reload config: %s", err), http.StatusInternalServerError) } }) + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(` - Blackbox Exporter - -

Blackbox Exporter

-

Probe prometheus.io for http_2xx

-

Metrics

- - `)) + Blackbox Exporter + +

Blackbox Exporter

+

Probe prometheus.io for http_2xx

+

Metrics

+

Configuration

+ + `)) + }) + + http.HandleFunc("/config", func(w http.ResponseWriter, r *http.Request) { + sc.RLock() + c, err := yaml.Marshal(sc.C) + sc.RUnlock() + if err != nil { + log.Warnf("Error marshalling configuration: %v", err) + http.Error(w, err.Error(), 500) + return + } + w.Write(c) }) log.Infoln("Listening on", *listenAddress) diff --git a/testdata/blackbox-good.yml b/testdata/blackbox-good.yml index c5c8076..9025394 100644 --- a/testdata/blackbox-good.yml +++ b/testdata/blackbox-good.yml @@ -8,6 +8,9 @@ modules: timeout: 5s http: method: POST + basic_auth: + username: "username" + password: "mysecret" tcp_connect: prober: tcp timeout: 5s -- 2.25.1