Skip to content

Commit

Permalink
Add a way to export labels with content matched by the probe (#1284)
Browse files Browse the repository at this point in the history
* Add a way to export labels with content matched by the probe.

Produces scraping values like these:

```
probe_content{ssh_comments="FreeBSD-20240806",ssh_version="OpenSSH_9.7"} 1
```

Signed-off-by: Lapo Luchini <[email protected]>

* Update name of metric as suggested upstream.

```
# HELP probe_expect_info Explicit content matched
# TYPE probe_expect_info gauge
probe_expect_info{ssh_comments="FreeBSD-20240806",ssh_version="OpenSSH_9.7"} 1
```

Signed-off-by: Lapo Luchini <[email protected]>

* Add some documentation for new feature.

Signed-off-by: Lapo Luchini <[email protected]>

* Refactor new feature in a function.

Signed-off-by: Lapo Luchini <[email protected]>

* Probe expect info test.

Signed-off-by: Lapo Luchini <[email protected]>

* Refactor to pass bytes directly, to ease unit test.

This involves an unnecessary copy, but I didn't find the proper way to avoid it.

Signed-off-by: Lapo Luchini <[email protected]>

* Unit test for new `probeExpectInfo` function.

Signed-off-by: Lapo Luchini <[email protected]>

---------

Signed-off-by: Lapo Luchini <[email protected]>
  • Loading branch information
lapo-luchini authored Sep 1, 2024
1 parent 8003b91 commit cbfad77
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 5 deletions.
11 changes: 10 additions & 1 deletion CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,18 @@ regexp: <regex>,
[ source_ip_address: <string> ]
# The query sent in the TCP probe and the expected associated response.
# starttls upgrades TCP connection to TLS.
# "expect" matches a regular expression;
# "labels" can define labels which will be exported on metric "probe_expect_info";
# "send" sends some content;
# "send" and "labels.value" can contain values matched by "expect" (such as "${1}");
# "starttls" upgrades TCP connection to TLS.
query_response:
[ - [ [ expect: <string> ],
[ labels:
- [ name: <string>
value: <string>
], ...
],
[ send: <string> ],
[ starttls: <boolean | default = false> ]
], ...
Expand Down
11 changes: 11 additions & 0 deletions blackbox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ modules:
query_response:
- expect: "^SSH-2.0-"
- send: "SSH-2.0-blackbox-ssh-check"
ssh_banner_extract:
prober: tcp
timeout: 5s
tcp:
query_response:
- expect: "^SSH-2.0-([^ -]+)(?: (.*))?$"
labels:
- name: ssh_version
value: "${1}"
- name: ssh_comments
value: "${2}"
irc_banner:
prober: tcp
tcp:
Expand Down
12 changes: 9 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,16 @@ type HeaderMatch struct {
AllowMissing bool `yaml:"allow_missing,omitempty"`
}

type Label struct {
Name string `yaml:"name,omitempty"`
Value string `yaml:"value,omitempty"`
}

type QueryResponse struct {
Expect Regexp `yaml:"expect,omitempty"`
Send string `yaml:"send,omitempty"`
StartTLS bool `yaml:"starttls,omitempty"`
Expect Regexp `yaml:"expect,omitempty"`
Labels []Label `yaml:"labels,omitempty"`
Send string `yaml:"send,omitempty"`
StartTLS bool `yaml:"starttls,omitempty"`
}

type TCPProbe struct {
Expand Down
21 changes: 21 additions & 0 deletions prober/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,24 @@ func dialTCP(ctx context.Context, target string, module config.Module, registry
return tls.DialWithDialer(dialer, dialProtocol, dialTarget, tlsConfig)
}

func probeExpectInfo(registry *prometheus.Registry, qr *config.QueryResponse, bytes []byte, match []int) {
var names []string
var values []string
for _, s := range qr.Labels {
names = append(names, s.Name)
values = append(values, string(qr.Expect.Regexp.Expand(nil, []byte(s.Value), bytes, match)))
}
metric := prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Name: "probe_expect_info",
Help: "Explicit content matched",
},
names,
)
registry.MustRegister(metric)
metric.WithLabelValues(values...).Set(1)
}

func ProbeTCP(ctx context.Context, target string, module config.Module, registry *prometheus.Registry, logger log.Logger) bool {
probeSSLEarliestCertExpiry := prometheus.NewGauge(sslEarliestCertExpiryGaugeOpts)
probeSSLLastChainExpiryTimestampSeconds := prometheus.NewGauge(sslChainExpiryInTimeStampGaugeOpts)
Expand Down Expand Up @@ -158,6 +176,9 @@ func ProbeTCP(ctx context.Context, target string, module config.Module, registry
}
probeFailedDueToRegex.Set(0)
send = string(qr.Expect.Regexp.Expand(nil, []byte(send), scanner.Bytes(), match))
if qr.Labels != nil {
probeExpectInfo(registry, &qr, scanner.Bytes(), match)
}
}
if send != "" {
level.Debug(logger).Log("msg", "Sending line", "line", send)
Expand Down
55 changes: 54 additions & 1 deletion prober/tcp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,8 +525,18 @@ func TestTCPConnectionQueryResponseMatching(t *testing.T) {
IPProtocolFallback: true,
QueryResponse: []config.QueryResponse{
{
Expect: config.MustNewRegexp("SSH-2.0-(OpenSSH_6.9p1) Debian-2"),
Expect: config.MustNewRegexp("^SSH-2.0-([^ -]+)(?: (.*))?$"),
Send: "CONFIRM ${1}",
Labels: []config.Label{
{
Name: "ssh_version",
Value: "${1}",
},
{
Name: "ssh_comments",
Value: "${2}",
},
},
},
},
},
Expand Down Expand Up @@ -560,6 +570,14 @@ func TestTCPConnectionQueryResponseMatching(t *testing.T) {
"probe_failed_due_to_regex": 0,
}
checkRegistryResults(expectedResults, mfs, t)
// Check labels
expectedLabels := map[string]map[string]string{
"probe_expect_info": {
"ssh_version": "OpenSSH_6.9p1",
"ssh_comments": "Debian-2",
},
}
checkRegistryLabels(expectedLabels, mfs, t)

}

Expand Down Expand Up @@ -683,3 +701,38 @@ func TestPrometheusTimeoutTCP(t *testing.T) {
}
<-ch
}

func TestProbeExpectInfo(t *testing.T) {
registry := prometheus.NewRegistry()
qr := config.QueryResponse{
Expect: config.MustNewRegexp("^SSH-2.0-([^ -]+)(?: (.*))?$"),
Labels: []config.Label{
{
Name: "label1",
Value: "got ${1} here",
},
{
Name: "label2",
Value: "${1} on ${2}",
},
},
}
bytes := []byte("SSH-2.0-OpenSSH_6.9p1 Debian-2")
match := qr.Expect.Regexp.FindSubmatchIndex(bytes)

probeExpectInfo(registry, &qr, bytes, match)

mfs, err := registry.Gather()
if err != nil {
t.Fatal(err)
}
// Check labels
expectedLabels := map[string]map[string]string{
"probe_expect_info": {
"label1": "got OpenSSH_6.9p1 here",
"label2": "OpenSSH_6.9p1 on Debian-2",
},
}
checkRegistryLabels(expectedLabels, mfs, t)

}

0 comments on commit cbfad77

Please sign in to comment.