diff --git a/ssl_exporter.go b/ssl_exporter.go index 9b0329e..f84d09d 100644 --- a/ssl_exporter.go +++ b/ssl_exporter.go @@ -5,6 +5,7 @@ import ( "crypto/x509" "fmt" "io/ioutil" + "net" "net/http" "strconv" "strings" @@ -22,8 +23,8 @@ const ( ) var ( - httpsConnectSuccess = prometheus.NewDesc( - prometheus.BuildFQName(namespace, "", "https_connect_success"), + tlsConnectSuccess = prometheus.NewDesc( + prometheus.BuildFQName(namespace, "", "tls_connect_success"), "If the TLS connection was a success", nil, nil, ) @@ -73,7 +74,7 @@ type Exporter struct { // Describe metrics func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { - ch <- httpsConnectSuccess + ch <- tlsConnectSuccess ch <- notAfter ch <- commonName ch <- subjectAlernativeDNSNames @@ -85,41 +86,31 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { // Collect metrics func (e *Exporter) Collect(ch chan<- prometheus.Metric) { - // Create the HTTP client and make a get request of the target - tr := &http.Transport{ - TLSClientConfig: e.tlsConfig, - } - client := &http.Client{ - CheckRedirect: func(req *http.Request, via []*http.Request) error { - return http.ErrUseLastResponse - }, - Transport: tr, - Timeout: e.timeout, - } - resp, err := client.Get(e.target) - + conn, err := tls.DialWithDialer(&net.Dialer{Timeout: e.timeout}, "tcp", e.target, e.tlsConfig) if err != nil { log.Errorln(err) ch <- prometheus.MustNewConstMetric( - httpsConnectSuccess, prometheus.GaugeValue, 0, + tlsConnectSuccess, prometheus.GaugeValue, 0, ) return } - if resp.TLS == nil { - log.Errorln("The response from " + e.target + " is unencrypted") + state := conn.ConnectionState() + + if len(state.PeerCertificates) < 1 { + log.Errorln("No certificates found in connection state") ch <- prometheus.MustNewConstMetric( - httpsConnectSuccess, prometheus.GaugeValue, 0, + tlsConnectSuccess, prometheus.GaugeValue, 0, ) return } ch <- prometheus.MustNewConstMetric( - httpsConnectSuccess, prometheus.GaugeValue, 1, + tlsConnectSuccess, prometheus.GaugeValue, 1, ) // Remove duplicate certificates from the response - peerCertificates := uniq(resp.TLS.PeerCertificates) + peerCertificates := uniq(state.PeerCertificates) // Loop through returned certificates and create metrics for _, cert := range peerCertificates { @@ -298,7 +289,7 @@ func main() { SSL Exporter

SSL Exporter

-

Probe https://example.com for SSL cert metrics

+

Probe example.com:443 for SSL cert metrics

Metrics

`)) diff --git a/ssl_exporter_test.go b/ssl_exporter_test.go index 86f3ea9..658ebed 100644 --- a/ssl_exporter_test.go +++ b/ssl_exporter_test.go @@ -43,23 +43,23 @@ func TestProbeHandler(t *testing.T) { tlsConfig *tls.Config }{ // Test against an assumed valid, reachable and functioning HTTPS address - {uri: "https://google.com", ok: true, tlsConfig: &tls.Config{}}, + {uri: "google.com:443", ok: true, tlsConfig: &tls.Config{}}, // Test against a HTTP address - {uri: "http://google.com", ok: false, tlsConfig: &tls.Config{}}, + {uri: "google.com:80", ok: false, tlsConfig: &tls.Config{}}, // Test against an expired certificate when we're rejecting invalid certs - {uri: "https://expired.badssl.com", ok: false, tlsConfig: &tls.Config{}}, + {uri: "expired.badssl.com:443", ok: false, tlsConfig: &tls.Config{}}, // Test against an expired certificate when we're accepting invalid certs - {uri: "https://expired.badssl.com", ok: true, tlsConfig: &tls.Config{InsecureSkipVerify: true}}, - // Test against a target with no protocol + {uri: "expired.badssl.com:443", ok: true, tlsConfig: &tls.Config{InsecureSkipVerify: true}}, + // Test against a target with no port {uri: "google.com", ok: false, tlsConfig: &tls.Config{}}, // Test against a string with spaces {uri: "with spaces", ok: false, tlsConfig: &tls.Config{}}, // Test against nothing {uri: "", ok: false, tlsConfig: &tls.Config{}}, // Test with client authentication - {uri: "https://client.badssl.com", ok: true, tlsConfig: &tls.Config{Certificates: []tls.Certificate{certificate}}}, + {uri: "client.badssl.com:443", ok: true, tlsConfig: &tls.Config{Certificates: []tls.Certificate{certificate}}}, // Test with an empty root CA bundle - {uri: "https://google.com", ok: false, tlsConfig: &tls.Config{RootCAs: emptyRootCAs}}, + {uri: "google.com:443", ok: false, tlsConfig: &tls.Config{RootCAs: emptyRootCAs}}, } fmt.Println("Note: The error logs in these tests are expected. One of the important tests is that we return the expected body, even in the face of errors.")