Skip to content

Commit 109cab9

Browse files
committed
feat: http 'source_interface' parameter
Somewhat based on #1152 Signed-off-by: Arnout Engelen <[email protected]>
1 parent eb9423a commit 109cab9

File tree

6 files changed

+95
-2
lines changed

6 files changed

+95
-2
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,21 @@ scrape_configs:
238238
target_label: vhost # and store it in 'vhost' label
239239
```
240240

241+
HTTP probes can also limit themselves to a particular
242+
interface with the `source_interface` parameter. This
243+
feature works only for 'regular' (non-HTTP3) HTTP
244+
probes, is likely Linux-specific and may require you
245+
to disable return-path (rp) filtering in the network
246+
configuration of the host on which blackbox runs:
247+
248+
```
249+
modules:
250+
http_2xx:
251+
prober: http
252+
http:
253+
source_interface: wlan0
254+
```
255+
241256
## Permissions
242257

243258
The ICMP probe requires elevated privileges to function:

config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ type HTTPProbe struct {
301301
ValidHTTPVersions []string `yaml:"valid_http_versions,omitempty"`
302302
IPProtocol string `yaml:"preferred_ip_protocol,omitempty"`
303303
IPProtocolFallback bool `yaml:"ip_protocol_fallback,omitempty"`
304+
SourceInterface string `yaml:"source_interface,omitempty"`
304305
SkipResolvePhaseWithProxy bool `yaml:"skip_resolve_phase_with_proxy,omitempty"`
305306
NoFollowRedirects *bool `yaml:"no_follow_redirects,omitempty"`
306307
FailIfSSL bool `yaml:"fail_if_ssl,omitempty"`

config/testdata/blackbox-good.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ modules:
33
prober: http
44
timeout: 5s
55
http:
6+
source_interface: wlan0
67
http_post_2xx:
78
prober: http
89
timeout: 5s

prober/http.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr
427427
}
428428
}
429429
}
430+
430431
var client *http.Client
431432
var noServerName http.RoundTripper
432433

@@ -455,7 +456,15 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr
455456

456457
} else {
457458
// For standard HTTP/HTTPS, create client from config
458-
client, err = pconfig.NewClientFromConfig(httpClientConfig, "http_probe", pconfig.WithKeepAlivesDisabled())
459+
httpClientOptions := []pconfig.HTTPClientOption{
460+
pconfig.WithKeepAlivesDisabled(),
461+
}
462+
463+
if len(module.HTTP.SourceInterface) > 0 {
464+
httpClientOptions = BindToInterface(httpClientOptions, module.HTTP.SourceInterface, logger)
465+
}
466+
467+
client, err = pconfig.NewClientFromConfig(httpClientConfig, "http_probe", httpClientOptions...)
459468
if err != nil {
460469
logger.Error("Error generating HTTP client", "err", err)
461470
return false
@@ -466,7 +475,7 @@ func ProbeHTTP(ctx context.Context, target string, module config.Module, registr
466475
serverNamelessConfig := httpClientConfig
467476
serverNamelessConfig.TLSConfig.ServerName = ""
468477

469-
noServerName, err = pconfig.NewRoundTripperFromConfig(serverNamelessConfig, "http_probe", pconfig.WithKeepAlivesDisabled())
478+
noServerName, err = pconfig.NewRoundTripperFromConfig(serverNamelessConfig, "http_probe", httpClientOptions...)
470479
if err != nil {
471480
logger.Error("Error generating HTTP client without ServerName", "err", err)
472481
return false

prober/http_linux.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright 2025 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
//go:build linux
15+
16+
package prober
17+
18+
import (
19+
"log/slog"
20+
"net"
21+
pconfig "github.com/prometheus/common/config"
22+
"syscall"
23+
)
24+
25+
func BindToInterface(options []pconfig.HTTPClientOption, sourceInterface string, _ *slog.Logger) []pconfig.HTTPClientOption {
26+
return append(options,
27+
pconfig.WithDialContextFunc((&net.Dialer{
28+
Control: func(network, address string, c syscall.RawConn) error {
29+
var err error
30+
c.Control(func(fd uintptr) {
31+
err = syscall.SetsockoptString(
32+
int(fd),
33+
syscall.SOL_SOCKET,
34+
syscall.SO_BINDTODEVICE,
35+
sourceInterface,
36+
)
37+
})
38+
return err
39+
},
40+
}).DialContext))
41+
}

prober/http_nonlinux.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2025 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
//go:build !linux
15+
16+
package prober
17+
18+
import (
19+
"log/slog"
20+
pconfig "github.com/prometheus/common/config"
21+
)
22+
23+
func BindToInterface(options []pconfig.HTTPClientOption, _ string, logger *slog.Logger) []pconfig.HTTPClientOption {
24+
logger.Error("Binding to a specific interface is only supported on Linux")
25+
return options
26+
}

0 commit comments

Comments
 (0)