ribbybibby / ssl_exporter

Exports Prometheus metrics for TLS certificates
Apache License 2.0
520 stars 97 forks source link

Support cases where the hostname is different to the SNI #17

Closed MichaelBerton closed 4 years ago

MichaelBerton commented 4 years ago

Hi @ribbybibby ,

We notice this repo which is very nice and maybe could help us...

we are using openssl and to query our ssl we use:

echo | openssl s_client -servername NAME -connect HOST:PORT 2>/dev/null | openssl x509 -noout -dates

and we got the following response:

notBefore=Oct 30 00:00:00 2019 GMT
notAfter=Oct 30 12:00:00 2020 GMT

I saw that this repo using http request to query the ssl certificate data, is there a way somehow to use the ssl_exporter to query this, or maybe some trick to make it work with the ssl_expoter.

if there is an option, it will be great if you could provide some example...

Thanks!

ribbybibby commented 4 years ago

Hi @MichaelBerton, thanks for the issue!

If I understand, you're asking if you can use curl to query the notBefore and notAfter values of a certificate through the exporter, instead of using openssl directly.

Technically, yes you can:

$ docker run -it --rm -p 9219:9219 ribbybibby/ssl-exporter
$ curl localhost:9219/probe?target=google.com:443 2> /dev/null | grep ^ssl_cert_not_
ssl_cert_not_after{issuer_cn="GTS CA 1O1",serial_no="321871034526857881021907384584995017158"} 1.580893724e+09
ssl_cert_not_after{issuer_cn="GlobalSign",serial_no="149699596615803609916394524856"} 1.639526442e+09
ssl_cert_not_before{issuer_cn="GTS CA 1O1",serial_no="321871034526857881021907384584995017158"} 1.573636124e+09
ssl_cert_not_before{issuer_cn="GlobalSign",serial_no="149699596615803609916394524856"} 1.497484842e+09

However, the output is in scientific notation (1.497484842e+09) which you'd need to translate into something readable, somehow. Also, the exporter returns all of the certificates in the chain, so you'd need to identify the certificate you want to monitor with grep or some other method.

Overall, using the exporter this way is much more convoluted than just using echo | openssl s_client -servername NAME -connect HOST:PORT 2>/dev/null | openssl x509 -noout -dates.

This is a Prometheus exporter and only really adds value when it is used as part of a Prometheus stack.

MichaelBerton commented 4 years ago

HI @ribbybibby ,

Thank you for your answer.

actually we are using prometheus on kubernetess and we want to leverage the alert manager capabilities to notify when the certificate is not valid.

I've the following questions :)

  1. is there a way somehow to use it like following with the api which we use ? openssl s_client -servername ... or do I miss something ?
      - job_name: ssl
        metrics_path: /probe
        static_configs:
          - targets:
              - kubernetes.default.svc:443 #here to repelace the target for the query which we needs maybe...
        relabel_configs:
          - source_labels: [__address__]
            target_label: __param_target
          - source_labels: [__param_target]
            target_label: instance
          - target_label: __address__
            replacement: ssl:9219
  1. In case our certificate should be invalid on 6 month from now, what is the best way to verify the we actually fire alarms with the alert manager ?

Thanks!

ribbybibby commented 4 years ago
  1. I'm still not exactly sure what you are asking. In most cases the exporter will connect to the target and return information about the certificate in much the same way that openssl does.

    Are you specifically asking about the -servername flag and support for SNI? I can see that it might not work if you have a case where the hostname of your target is different to the server name of your virtual host.

  2. If you want to verify that your alerts are actually going to fire when you want them to, there are a couple of things I would suggest.

    Trigger the alert: You could increase the expiry window to test that the alert fires. For instance, if you know your certificate expires in 6 months, set up an alert that fires if expiry is less than a year away:

    groups: 
      - name: ssl_expiry.rules 
        rules: 
          - alert: SSLCertExpiringSoon 
            expr: ssl_cert_not_after - time() < 86400 * 365 # Alert if certificate expires within a year (365 days)
            for: 10m

    Once you've confirmed that the alert reaches you, you can set the window back to something sensible like 30 days and have a high level of confidence that the alert is going to work.

    Unit tests: Although I haven't used them, I believe Prometheus now has functionality that lets you write unit tests for your alert rules: https://prometheus.io/docs/prometheus/latest/configuration/unit_testing_rules/

MichaelBerton commented 4 years ago

HI @ribbybibby ,

Thank I got an answer to my second question :) For the first I think I doesn't explain myself well ...

Think that we dont have any url endpoint , and we need to check it with the following command openssl s_client -servername NAME -connect HOST:PORT 2>/dev/null | openssl x509 -noout -dates which I suppose should be placed in the target entry inside the job definition on prometheus, my question is there a way to make it work?

is it more clear now ?

ribbybibby commented 4 years ago

Not much clearer.

Are you asking if the exporter can literally accept openssl s_client -servername NAME -connect HOST:PORT 2>/dev/null | openssl x509 -noout -dates as a target? If so, the answer is no. The exporter will not execute arbritrary shell commands passed as targets.

However, if you are trying to achieve the same result as something like openssl s_client -servername google.com -connect google.com:443 2>/dev/null, where the value of -servername and the host used with -connect are the same, then the typical usage of the exporter is roughly equivalent to that:

      - job_name: ssl
        metrics_path: /probe
        static_configs:
          - targets:
              - google.com:443
        relabel_configs:
          - source_labels: [__address__]
            target_label: __param_target
          - source_labels: [__param_target]
            target_label: instance
          - target_label: __address__
            replacement: ssl:9219

If you want to connect to one address but use a different servername, like openssl s_client -servername example.svc -connect 10.2.0.14:443 2>/dev/null, then I don't think the exporter can currently support that but I would be very interested in adding support for it.

MichaelBerton commented 4 years ago

Thank you it will be great if you can add it!

one question: Can we use the library for production scenario's ?

ribbybibby commented 4 years ago

When you say library, I'm assuming you're referring to the exporter and the answer to that question really relies on what your criteria for production readiness is.

I've ran this exporter myself in production (although I don't any more) and I can testify to its reliability and effectiveness. What it does is very simple and there's not really a lot that can go wrong with it.

ribbybibby commented 4 years ago

I'm going to reopen this issue because it was never actually resolved.

A servername parameter would allow the use of SNI where the hostname of the address you contact and the servername of the certificate you want to probe are different.

ribbybibby commented 4 years ago

Then again, so would an entry in /etc/hosts - assuming you know the IP. It's hard for me to reason about adding a feature to address this without knowing more specifics about cases where you'd want to do this.

Arkhamp commented 4 years ago

It would be really nice to have this feature, because in my case I do not have root to modify /etc/hosts, so I think this is the same problem for MichaelBerton

Then again, so would an entry in /etc/hosts - assuming you know the IP. It's hard for me to reason about adding a feature to address this without knowing more specifics about cases where you'd want to do this.

ribbybibby commented 4 years ago

Added in v2.0.0.