Support protocols other than HTTPS by reimplementing the exporter as a TLS client

This commit is contained in:
Rob Best
2019-03-20 14:04:24 +00:00
parent 1bb799094c
commit e45a499637
2 changed files with 21 additions and 30 deletions
+14 -23
View File
@@ -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>`))
+7 -7
View File
@@ -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.")