prometheus/blackbox_exporter

Inconsistent support of IPv6 literals

candlerb opened this issue · 4 comments

Host operating system: output of uname -a

Linux prometheus 5.4.0-162-generic #179-Ubuntu SMP Mon Aug 14 08:51:31 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

blackbox_exporter version: output of blackbox_exporter --version

blackbox_exporter, version 0.24.0 (branch: HEAD, revision: 0b0467473916fd9e8526e2635c2a0b1c56011dff)
  build user:       root@e5bbfcc8184e
  build date:       20230516-11:07:25
  go version:       go1.20.4
  platform:         linux/amd64
  tags:             netgo

What is the blackbox.yml module config.

modules:
  icmp:
    prober: icmp
    timeout: 3s

  dns_version:
    prober: dns
    timeout: 5s
    dns:
      query_name: version.bind
      query_type: TXT
      query_class: CH
      validate_answer_rrs:
        fail_if_none_matches_regexp:
          - '.*'

  certificate_invalid:
    prober: tcp
    timeout: 5s
    tcp:
      tls: true
      tls_config:
        insecure_skip_verify: true

  http_example:
    prober: http
    timeout: 5s
    http:
      method: GET
      follow_redirects: true
      fail_if_ssl: false
      fail_if_not_ssl: false
      tls_config:
        insecure_skip_verify: true

What did you do that produced an error?

Supplying an IPv6 literal with square brackets but without a port.

icmp prober (where port makes no sense)

  • Works: curl -gsSv 'localhost:9115/probe?target=2001:db8::1&module=icmp'
  • FAILS: curl -gsSv 'localhost:9115/probe?target=[2001:db8::1]&module=icmp'
    • Response code 200
    • probe_success=0, probe_ip_addr_hash=0, probe_ip_protocol=0
    • With debug=true: caller=icmp.go:91 module=icmp target=[2001:db8::1] level=error msg="Resolution with IP protocol failed" target=[2001:db8::1] err="lookup [2001:db8::1]: no such host"

dns prober (port is optional, default is 53)

  • Works: curl -gsSv 'localhost:9115/probe?target=2606:4700:58::adf5:3b60&module=dns_version'
  • Works: curl -gsSv 'localhost:9115/probe?target=[2606:4700:58::adf5:3b60]:53&module=dns_version'
  • FAILS: curl -gsSv 'localhost:9115/probe?target=[2606:4700:58::adf5:3b60]&module=dns_version'
    • Response code 200
    • probe_success=0
    • With debug=true: caller=dns.go:200 module=dns_version target=[2606:4700:58::adf5:3b60] level=error msg="Resolution with IP protocol failed" target=[2606:4700:58::adf5:3b60] err="lookup [2606:4700:58::adf5:3b60]: no such host"

(This is similar to prometheus/snmp_exporter#1076)

tcp prober (port is required)

  • Works: curl -gsSv 'localhost:9115/probe?target=[2001:db8::1]:443&module=certificate_invalid

There is no problem here because a port is always required. (A bare IPv4 address says that it can't split address/port, and a bare IPv6 address says there are too many colons)

Aside: this could change if the tcp prober gained an option to set a default port. For example, given this config:

  ssh_banner:
    prober: tcp
    tcp:
      query_response:
      - expect: "^SSH-2.0-"
      - send: "SSH-2.0-blackbox-ssh-check"

It would be nice if a default port 22 could be set in blackbox.yml.

http prober (default port 80 or 443)

  • Works: curl -gsSv 'localhost:9115/probe?target=http://[2001:db8::1]&module=http_example'
  • Works: curl -gsSv 'localhost:9115/probe?target=http://[2001:db8::1]:8080&module=http_example'
  • Works: curl -gsSv 'localhost:9115/probe?target=https://[2001:db8::1]&module=http_example'
  • Works: curl -gsSv 'localhost:9115/probe?target=https://[2001:db8::1]:8443&module=http_example'
  • Works (defaults to http on port 80): curl -gsSv 'localhost:9115/probe?target=[2001:db8::1]&module=http_example'

There is no problem here, because the URL syntax requires IPv6 addresses to be enclosed in square brackets anyway.

Proposal

Fix dns_prober to accept IPv6 literals with square brackets but without a port.

Make icmp_prober accept IPv6 literals with or without square brackets, for consistency with dns_prober (and snmp_exporter when fixed)

Oddly, Go's own net.SplitHostPort allows domain names and IPv4 literals inside square brackets.

  • Works: curl -gsSv 'localhost:9115/probe?target=[damon.ns.cloudflare.com]:53&module=dns_version'
  • Works: curl -gsSv 'localhost:9115/probe?target=[108.162.193.96]:53&module=dns_version'

Reading RFC 3986 3.2.2, which defines the square bracket syntax, I don't think IPv4 or DNS names with square brackets are allowed.

The host subcomponent of authority is identified by an IP literal
encapsulated within square brackets, an IPv4 address in dotted-
decimal form, or a registered name.

My understanding of that means you can have [IPv6], IPv4, example.com. Not square brackets with the other options.

Indeed, that's why it'd odd and I think the behaviour is too lax here - the last two examples should have been rejected. Changing it is backwards-incompatible though.

EDIT: thinking about it, [name-or-ip]:port is quite convenient when building targets from a database, as you don't need to special-case IPv6 addresses.

jpds commented

It would appear that this also doesn't support scoped v6 link-local addresses:

ts=2024-08-07T20:20:37.520908651Z caller=main.go:190 module=icmp target=[fe80::9ee0:41ff:fe5e:ebe8] level=info msg="Beginning probe" probe=icmp timeout_seconds=9.5
ts=2024-08-07T20:20:37.521554009Z caller=icmp.go:91 module=icmp target=[fe80::9ee0:41ff:fe5e:ebe8] level=info msg="Resolving target address" target=[fe80::9ee0:41ff:fe5e:ebe8] ip_protocol=ip6
ts=2024-08-07T20:20:37.52281406Z caller=icmp.go:91 module=icmp target=[fe80::9ee0:41ff:fe5e:ebe8] level=error msg="Resolution with IP protocol failed" target=[fe80::9ee0:41ff:fe5e:ebe8] err="lookup [fe80::9ee0:41ff:fe5e:ebe8]: no such host"
ts=2024-08-07T20:20:37.52290189Z caller=handler.go:119 module=icmp target=[fe80::9ee0:41ff:fe5e:ebe8] level=error msg="Error resolving address" err="lookup [fe80::9ee0:41ff:fe5e:ebe8]: no such host"
ts=2024-08-07T20:20:37.523090385Z caller=main.go:190 module=icmp target=[fe80::9ee0:41ff:fe5e:ebe8] level=error msg="Probe failed" duration_seconds=0.001953452
ts=2024-08-07T20:24:33.623290618Z caller=main.go:190 module=icmp target=[fe80::9ee0:41ff:fe5e:ebe8]%enp1s0 level=info msg="Beginning probe" probe=icmp timeout_seconds=9.5
ts=2024-08-07T20:24:33.623762539Z caller=icmp.go:91 module=icmp target=[fe80::9ee0:41ff:fe5e:ebe8]%enp1s0 level=info msg="Resolving target address" target=[fe80::9ee0:41ff:fe5e:ebe8]%enp1s0 ip_protocol=ip6
ts=2024-08-07T20:24:33.62477881Z caller=icmp.go:91 module=icmp target=[fe80::9ee0:41ff:fe5e:ebe8]%enp1s0 level=error msg="Resolution with IP protocol failed" target=[fe80::9ee0:41ff:fe5e:ebe8]%enp1s0 err="lookup [fe80::9ee0:41ff:fe5e:ebe8]%enp1s0: no such host"
ts=2024-08-07T20:24:33.624865349Z caller=handler.go:119 module=icmp target=[fe80::9ee0:41ff:fe5e:ebe8]%enp1s0 level=error msg="Error resolving address" err="lookup [fe80::9ee0:41ff:fe5e:ebe8]%enp1s0: no such host"
ts=2024-08-07T20:24:33.624949371Z caller=main.go:190 module=icmp target=[fe80::9ee0:41ff:fe5e:ebe8]%enp1s0 level=error msg="Probe failed" duration_seconds=0.001487811