ribbybibby / ssl_exporter

Exports Prometheus metrics for TLS certificates
Apache License 2.0
507 stars 95 forks source link

Allow passing `tls_config.server_name` as a query parameter #74

Open johanfleury opened 3 years ago

johanfleury commented 3 years ago

Hello

I’m using ssl_exporter to monitor certificates for a bunch of websites that are hosted behind multiple load balancers. My goal being to monitor that all the LB’s use the same certificate and that it’s not expired.

At the moment, to be able to specify which SNI to use when connecting to the LB, I need add a module in the configuration for every websites:

config.yaml: |
  modules:
    https:
      prober: https
    site1.example.com:
      prober: https
      tls_config:
        server_name: site1.example.com
    www.site1.example.com:
      prober: https
      tls_config:
        server_name: www.site1.example.com
    site2.example.com:
      prober: https
      tls_config:
        server_name: site1.example.com
    www.site2.example.com:
      prober: https
      tls_config:
        server_name: www.site1.example.com

   # And so on and so forth

This way I can scrape with target set as the load balancer’s IP (e.g.: curl -s 'http://127.0.0.1:9219/probe?module=site1.example.com&target=https://192.0.2.1 will give me the TLS metrics for site1.example.com on 192.0.2.1).

This works fine, but it’s quite cumbersome as I need to do this for every websites I want to monitor that way. It would be great to be able to pass server_name as a query parameter instead (e.g.: curl -s 'http://127.0.0.1:9219/probe?module=https&target=https://192.0.2.1&server_name=site1.example.com)

I’m willing to write a PR for that feature, but wanted to know your thoughts on that.

ribbybibby commented 3 years ago

What does your scrape config look like for this setup? Just so I can understand the setup more fully.

johanfleury commented 3 years ago

I’m using file_sd_config with a json file that looks like this:

[
  {
    "targets": [
      "https://site1.example.com"
    ],
    "labels": {}
  },
  {
    "targets": [
      "https://site2.example.com"
    ],
    "labels": {
      "__meta_ssl_exporter_module": "foo"
    }
  },
  {
    "targets": [
      "https://site3.example.com"
    ],
    "labels": {
      "__meta_ssl_exporter_target": "https://192.0.2.1"
    }
  }
]

My current scrape config looks like this:

- job_name: https
  scrape_interval: 2m
  scrape_timeout: 60s
  metrics_path: /probe
  params:
    module:
      - https
  file_sd_configs:
    - files:
        - /etc/prometheus/file-sd/websites.json
  relabel_configs:
    # Set __param_target to the value of __address__ by default
    - source_labels: [__address__]
      target_label: __param_target
    # If __meta_ssl_exporter_target is set, set __param_target to its value and __param_server_name to the value of __address__
    - source_labels: [__meta_ssl_exporter_target]
      regex: (.+)
      target_label: __param_target
      replacement: ${1}
    - source_labels: [__meta_ssl_exporter_target, __address__]
      regex: (.+);(?:https?://)([^/]*)
      target_label: __param_server_name
      replacement: ${2}
    # If __meta_ssl_exporter_module is set, set __param_module to its value
    - source_labels: [__meta_ssl_exporter_module]
      regex: (.+)
      target_label: __param_module
      replacement: ${1}
    # Set instance to the value of __address__ (minus the path)
    - source_labels: [__address__]
      regex: (https://[^/]*)(/.*)?
      target_label: instance
      replacement: ${1}
    # Set __address__ to ssl_exporter endpoint
    - target_label: __address__
      replacement: ssl-exporter:9219

With this setup, targets appears like this in Prometheus:

Endpoint State Labels Last Scrape Scrape Duration Error
http://ssl-exporter:9219/probe module="https" target="https://site1.example.com" UP instance="https://site1.example.com" job="https" -- -- --
http://ssl-exporter:9219/probe module="foo" target="https://site2.example.com" UP instance="https://site2.example.com" job="https" -- -- --
http://ssl-exporter:9219/probe module="https" target="https://192.0.2.1" server_name="site3.example.com" UP instance="https://site3.example.com" job="https" -- -- --

It could be done the other way around (by setting __address__ to the actual target and have a __meta_ssl_exporter_server_name label), but I made it that way to deal with the way the source of the file_sd works.

The patch on ssl_exporter is quite simple:

diff --git a/ssl_exporter.go b/ssl_exporter.go
index 2051c53..6a30b68 100644
--- a/ssl_exporter.go
+++ b/ssl_exporter.go
@@ -35,6 +35,12 @@ func probeHandler(logger log.Logger, w http.ResponseWriter, r *http.Request, con
                return
        }

+       if serverName := r.URL.Query().Get("server_name"); serverName != "" {
+               level.Debug(logger).Log("msg", fmt.Sprintf("Using %s as server name", serverName))
+               logger = log.With(logger, "server_name", serverName)
+               module.TLSConfig.ServerName = serverName
+       }
+
        timeout := module.Timeout
        if timeout == 0 {
                // The following timeout block was taken wholly from the blackbox exporter
johanfleury commented 2 years ago

I’ve opened a PR, let me know what you think.