This release is built with go 1.16.4, which contains a [bugfix](https://github.com/golang/go/issues/45712)
that can cause an untrusted target to make Blackbox Exporter crash.
+In the HTTP probe, `no_follow_redirect` has been changed to `follow_redirect`.
+This release accepts both, with a precedence to the `no_follow_redirect` parameter.
+In the next release, `no_follow_redirect` will be removed.
+
+* [CHANGE] HTTP proble: no_follow_redirect has been renamed to follow_redirect.
* [FEATURE] Add support for decompression of HTTP responses. #764
* [FEATURE] Enable TLS and basic authentication. #784
* [FEATURE] HTTP probe: *experimental* OAuth2 support.
[ compression: <string> | default = "" ]
# Whether or not the probe will follow any redirects.
- [ no_follow_redirects: <boolean> | default = false ]
+ [ follow_redirects: <boolean> | default = true ]
# Probe fails if SSL is present.
[ fail_if_ssl: <boolean> | default = false ]
yaml "gopkg.in/yaml.v3"
+ "github.com/go-kit/kit/log"
+ "github.com/go-kit/kit/log/level"
"github.com/miekg/dns"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/config"
// DefaultHTTPProbe set default value for HTTPProbe
DefaultHTTPProbe = HTTPProbe{
IPProtocolFallback: true,
+ HTTPClientConfig: config.DefaultHTTPClientConfig,
}
// DefaultTCPProbe set default value for TCPProbe
C *Config
}
-func (sc *SafeConfig) ReloadConfig(confFile string) (err error) {
+func (sc *SafeConfig) ReloadConfig(confFile string, logger log.Logger) (err error) {
var c = &Config{}
defer func() {
if err != nil {
return fmt.Errorf("error parsing config file: %s", err)
}
+ for name, module := range c.Modules {
+ if module.HTTP.NoFollowRedirects != nil {
+ // Hide the old flag from the /config page.
+ module.HTTP.NoFollowRedirects = nil
+ c.Modules[name] = module
+ if logger != nil {
+ level.Warn(logger).Log("msg", "no_follow_redirects is deprecated and will be removed in the next release. It is replaced by follow_redirects.", "module", name)
+ }
+ }
+ }
+
sc.Lock()
sc.C = c
sc.Unlock()
ValidHTTPVersions []string `yaml:"valid_http_versions,omitempty"`
IPProtocol string `yaml:"preferred_ip_protocol,omitempty"`
IPProtocolFallback bool `yaml:"ip_protocol_fallback,omitempty"`
- NoFollowRedirects bool `yaml:"no_follow_redirects,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"`
return err
}
+ if s.NoFollowRedirects != nil {
+ s.HTTPClientConfig.FollowRedirects = !*s.NoFollowRedirects
+ }
+
for key, value := range s.Headers {
switch strings.Title(key) {
case "Accept-Encoding":
C: &Config{},
}
- err := sc.ReloadConfig("testdata/blackbox-good.yml")
+ err := sc.ReloadConfig("testdata/blackbox-good.yml", nil)
if err != nil {
t.Errorf("Error loading config %v: %v", "blackbox.yml", err)
}
}
for _, test := range tests {
t.Run(test.input, func(t *testing.T) {
- got := sc.ReloadConfig(test.input)
+ got := sc.ReloadConfig(test.input, nil)
if got == nil || got.Error() != test.want {
t.Fatalf("ReloadConfig(%q) = %v; want %q", test.input, got, test.want)
}
C: &Config{},
}
- err := sc.ReloadConfig("testdata/blackbox-good.yml")
+ err := sc.ReloadConfig("testdata/blackbox-good.yml", nil)
if err != nil {
t.Errorf("Error loading config %v: %v", "testdata/blackbox-good.yml", err)
}
level.Info(logger).Log("msg", "Starting blackbox_exporter", "version", version.Info())
level.Info(logger).Log("build_context", version.BuildContext())
- if err := sc.ReloadConfig(*configFile); err != nil {
+ if err := sc.ReloadConfig(*configFile, logger); err != nil {
level.Error(logger).Log("msg", "Error loading config", "err", err)
return 1
}
for {
select {
case <-hup:
- if err := sc.ReloadConfig(*configFile); err != nil {
+ if err := sc.ReloadConfig(*configFile, logger); err != nil {
level.Error(logger).Log("msg", "Error reloading config", "err", err)
continue
}
level.Info(logger).Log("msg", "Reloaded config file")
case rc := <-reloadCh:
- if err := sc.ReloadConfig(*configFile); err != nil {
+ if err := sc.ReloadConfig(*configFile, logger); err != nil {
level.Error(logger).Log("msg", "Error reloading config", "err", err)
rc <- err
} else {
client.CheckRedirect = func(r *http.Request, via []*http.Request) error {
level.Info(logger).Log("msg", "Received redirect", "location", r.Response.Header.Get("Location"))
redirects = len(via)
- if redirects > 10 || httpConfig.NoFollowRedirects {
+ if redirects > 10 || !httpConfig.HTTPClientConfig.FollowRedirects {
level.Info(logger).Log("msg", "Not following redirect")
return errors.New("don't follow redirects")
}
registry := prometheus.NewRegistry()
testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
- result := ProbeHTTP(testCTX, ts.URL, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true}}, registry, log.NewNopLogger())
+ result := ProbeHTTP(testCTX, ts.URL, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.DefaultHTTPClientConfig}}, registry, log.NewNopLogger())
body := recorder.Body.String()
if !result {
t.Fatalf("Redirect test failed unexpectedly, got %s", body)
testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
result := ProbeHTTP(testCTX, ts.URL,
- config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, NoFollowRedirects: true, ValidStatusCodes: []int{302}}}, registry, log.NewNopLogger())
+ config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.HTTPClientConfig{FollowRedirects: false}, ValidStatusCodes: []int{302}}}, registry, log.NewNopLogger())
body := recorder.Body.String()
if !result {
t.Fatalf("Redirect test failed unexpectedly, got %s", body)
result := ProbeHTTP(
testCTX,
ts.URL,
- config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true}},
+ config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.DefaultHTTPClientConfig}},
registry,
log.NewNopLogger())
if result {
testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
result := ProbeHTTP(testCTX, ts.URL,
- config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true}}, registry, log.NewNopLogger())
+ config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.DefaultHTTPClientConfig}}, registry, log.NewNopLogger())
if !result {
t.Fatalf("Redirect test failed unexpectedly")
}
registry := prometheus.NewRegistry()
testCTX, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
- result := ProbeHTTP(testCTX, ts.URL, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true}}, registry, log.NewNopLogger())
+ result := ProbeHTTP(testCTX, ts.URL, config.Module{Timeout: time.Second, HTTP: config.HTTPProbe{IPProtocolFallback: true, HTTPClientConfig: pconfig.DefaultHTTPClientConfig}}, registry, log.NewNopLogger())
body := recorder.Body.String()
if !result {
t.Fatalf("Redirect test failed unexpectedly, got %s", body)