diff --git a/CONFIGURATION.md b/CONFIGURATION.md index dbb852a3..70aa7a62 100644 --- a/CONFIGURATION.md +++ b/CONFIGURATION.md @@ -97,6 +97,9 @@ The other placeholders are specified separately. [ preferred_ip_protocol: | default = "ip6" ] [ ip_protocol_fallback: | default = true ] + # The source IP address. + [ source_ip_address: ] + # The body of the HTTP request used in probe. body: [ ] diff --git a/config/config.go b/config/config.go index ee460106..f637c0bb 100644 --- a/config/config.go +++ b/config/config.go @@ -177,6 +177,7 @@ type HTTPProbe struct { ValidHTTPVersions []string `yaml:"valid_http_versions,omitempty"` IPProtocol string `yaml:"preferred_ip_protocol,omitempty"` IPProtocolFallback bool `yaml:"ip_protocol_fallback,omitempty"` + SourceIPAddress string `yaml:"source_ip_address,omitempty"` NoFollowRedirects bool `yaml:"no_follow_redirects,omitempty"` FailIfSSL bool `yaml:"fail_if_ssl,omitempty"` FailIfNotSSL bool `yaml:"fail_if_not_ssl,omitempty"` diff --git a/go.mod b/go.mod index 3f135c29..b08d9e2e 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/prometheus/blackbox_exporter require ( github.com/go-kit/kit v0.10.0 github.com/miekg/dns v1.1.40 + github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.9.0 github.com/prometheus/client_model v0.2.0 diff --git a/prober/http.go b/prober/http.go index 500c92b5..1d95e99a 100644 --- a/prober/http.go +++ b/prober/http.go @@ -32,6 +32,7 @@ import ( "github.com/go-kit/kit/log" "github.com/go-kit/kit/log/level" + "github.com/mwitkow/go-conntrack" "github.com/prometheus/client_golang/prometheus" pconfig "github.com/prometheus/common/config" "golang.org/x/net/publicsuffix" @@ -329,6 +330,7 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr // the hostname of the target. httpClientConfig.TLSConfig.ServerName = targetHost } + client, err := pconfig.NewClientFromConfig(httpClientConfig, "http_probe", true, true) if err != nil { level.Error(logger).Log("msg", "Error generating HTTP client", "err", err) @@ -352,6 +354,30 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr // Inject transport that tracks traces for each redirect, // and does not set TLS ServerNames on redirect if needed. tt := newTransport(client.Transport, noServerName, logger) + + var localAddr net.Addr + if len(module.HTTP.SourceIPAddress) > 0 { + srcIP := net.ParseIP(module.HTTP.SourceIPAddress) + if srcIP == nil { + level.Error(logger).Log("msg", "Error parsing source ip address", "srcIP", module.HTTP.SourceIPAddress) + return false + } + level.Info(logger).Log("msg", "Using local address", "srcIP", srcIP) + + localAddr = &net.TCPAddr{IP: srcIP} + } + + dialContext := conntrack.NewDialContextFunc( + conntrack.DialWithTracing(), + conntrack.DialWithName("http_probe"), + conntrack.DialWithDialContextFunc(func(ctx context.Context, network string, address string) (net.Conn, error) { + return (&net.Dialer{LocalAddr: localAddr}).DialContext(ctx, network, address) + }), + ) + + tt.Transport.(*http.Transport).DialContext = dialContext + tt.NoServerNameTransport.(*http.Transport).DialContext = dialContext + client.Transport = tt client.CheckRedirect = func(r *http.Request, via []*http.Request) error {