mirror of
https://github.com/ribbybibby/ssl_exporter.git
synced 2026-08-26 02:57:19 +00:00
Support protocols other than HTTPS by reimplementing the exporter as a TLS client
This commit is contained in:
+14
-23
@@ -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() {
|
||||
<head><title>SSL Exporter</title></head>
|
||||
<body>
|
||||
<h1>SSL Exporter</h1>
|
||||
<p><a href="` + *probePath + `?target=https://example.com">Probe https://example.com for SSL cert metrics</a></p>
|
||||
<p><a href="` + *probePath + `?target=example.com:443">Probe example.com:443 for SSL cert metrics</a></p>
|
||||
<p><a href='` + *metricsPath + `'>Metrics</a></p>
|
||||
</body>
|
||||
</html>`))
|
||||
|
||||
@@ -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.")
|
||||
|
||||
Reference in New Issue
Block a user