You've already forked ssl_exporter
mirror of
https://github.com/ribbybibby/ssl_exporter.git
synced 2025-07-12 23:50:14 +02:00
add TLS version metric (#24)
This commit is contained in:
@ -85,6 +85,7 @@ metric indicates if the probe has been successful.
|
|||||||
| ssl_cert_not_before | The date before which the certificate is not valid. Expressed as a Unix Epoch Time. | serial_no, issuer_cn, cn, dnsnames, ips, emails, ou |
|
| ssl_cert_not_before | The date before which the certificate is not valid. Expressed as a Unix Epoch Time. | serial_no, issuer_cn, cn, dnsnames, ips, emails, ou |
|
||||||
| ssl_client_protocol | The protocol used by the exporter to connect to the target. Boolean. | protocol |
|
| ssl_client_protocol | The protocol used by the exporter to connect to the target. Boolean. | protocol |
|
||||||
| ssl_tls_connect_success | Was the TLS connection successful? Boolean. | |
|
| ssl_tls_connect_success | Was the TLS connection successful? Boolean. | |
|
||||||
|
| ssl_tls_version_info | The TLS version used. Always 1. | version |
|
||||||
|
|
||||||
## Prometheus
|
## Prometheus
|
||||||
|
|
||||||
|
@ -30,6 +30,11 @@ var (
|
|||||||
"If the TLS connection was a success",
|
"If the TLS connection was a success",
|
||||||
nil, nil,
|
nil, nil,
|
||||||
)
|
)
|
||||||
|
tlsVersion = prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(namespace, "", "tls_version_info"),
|
||||||
|
"The TLS version used",
|
||||||
|
[]string{"version"}, nil,
|
||||||
|
)
|
||||||
clientProtocol = prometheus.NewDesc(
|
clientProtocol = prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(namespace, "", "client_protocol"),
|
prometheus.BuildFQName(namespace, "", "client_protocol"),
|
||||||
"The protocol used by the exporter to connect to the target",
|
"The protocol used by the exporter to connect to the target",
|
||||||
@ -64,7 +69,7 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
|
|||||||
|
|
||||||
// Collect metrics
|
// Collect metrics
|
||||||
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
||||||
var peerCertificates []*x509.Certificate
|
var state tls.ConnectionState
|
||||||
|
|
||||||
// Parse the target and return the appropriate connection protocol and target address
|
// Parse the target and return the appropriate connection protocol and target address
|
||||||
target, proto, err := parseTarget(e.target)
|
target, proto, err := parseTarget(e.target)
|
||||||
@ -116,7 +121,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
peerCertificates = resp.TLS.PeerCertificates
|
state = *resp.TLS
|
||||||
|
|
||||||
} else if proto == "tcp" {
|
} else if proto == "tcp" {
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
@ -132,17 +137,7 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
state := conn.ConnectionState()
|
state = conn.ConnectionState()
|
||||||
|
|
||||||
peerCertificates = state.PeerCertificates
|
|
||||||
|
|
||||||
if len(peerCertificates) < 1 {
|
|
||||||
log.Errorln("No certificates found in connection state for " + target)
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
|
||||||
tlsConnectSuccess, prometheus.GaugeValue, 0,
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log.Errorln("Unrecognised protocol: " + string(proto) + " for target: " + target)
|
log.Errorln("Unrecognised protocol: " + string(proto) + " for target: " + target)
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
@ -151,6 +146,21 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the TLS version from the connection state and export it as a metric
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
tlsVersion, prometheus.GaugeValue, 1, getTLSVersion(&state),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Retrieve certificates from the connection state
|
||||||
|
peerCertificates := state.PeerCertificates
|
||||||
|
if len(peerCertificates) < 1 {
|
||||||
|
log.Errorln("No certificates found in connection state for " + target)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
tlsConnectSuccess, prometheus.GaugeValue, 0,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
tlsConnectSuccess, prometheus.GaugeValue, 1,
|
tlsConnectSuccess, prometheus.GaugeValue, 1,
|
||||||
)
|
)
|
||||||
@ -281,6 +291,21 @@ func parseTarget(target string) (parsedTarget string, proto string, err error) {
|
|||||||
return u.Host, "tcp", nil
|
return u.Host, "tcp", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTLSVersion(state *tls.ConnectionState) string {
|
||||||
|
switch state.Version {
|
||||||
|
case tls.VersionTLS10:
|
||||||
|
return "TLS 1.0"
|
||||||
|
case tls.VersionTLS11:
|
||||||
|
return "TLS 1.1"
|
||||||
|
case tls.VersionTLS12:
|
||||||
|
return "TLS 1.2"
|
||||||
|
case tls.VersionTLS13:
|
||||||
|
return "TLS 1.3"
|
||||||
|
default:
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
prometheus.MustRegister(version.NewCollector(namespace + "_exporter"))
|
prometheus.MustRegister(version.NewCollector(namespace + "_exporter"))
|
||||||
}
|
}
|
||||||
|
@ -454,6 +454,26 @@ func TestProbeHandlerExpiredInsecure(t *testing.T) {
|
|||||||
server.Close()
|
server.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test against a server with TLS v1.2
|
||||||
|
func TestProbeHandlerTLSVersion12(t *testing.T) {
|
||||||
|
server, err := serverTLSVersion12()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
rr, err := probe(server.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := strings.Contains(rr.Body.String(), "ssl_tls_version_info{version=\"TLS 1.2\"} 1")
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("expected `ssl_tls_version_info{version=\"TLS 1.2\"} 1`")
|
||||||
|
}
|
||||||
|
|
||||||
|
server.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func probe(url string) (*httptest.ResponseRecorder, error) {
|
func probe(url string) (*httptest.ResponseRecorder, error) {
|
||||||
uri := "/probe?target=" + url
|
uri := "/probe?target=" + url
|
||||||
req, err := http.NewRequest("GET", uri, nil)
|
req, err := http.NewRequest("GET", uri, nil)
|
||||||
@ -615,6 +635,26 @@ func serverHTTP() (*httptest.Server, error) {
|
|||||||
return server, nil
|
return server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func serverTLSVersion12() (*httptest.Server, error) {
|
||||||
|
serverCertificate, err := tls.X509KeyPair([]byte(serverCert), []byte(serverKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, "Hello world")
|
||||||
|
}))
|
||||||
|
|
||||||
|
server.TLS = &tls.Config{
|
||||||
|
Certificates: []tls.Certificate{serverCertificate},
|
||||||
|
MinVersion: tls.VersionTLS12,
|
||||||
|
MaxVersion: tls.VersionTLS12,
|
||||||
|
}
|
||||||
|
|
||||||
|
server.StartTLS()
|
||||||
|
return server, nil
|
||||||
|
}
|
||||||
|
|
||||||
func certPool() *x509.CertPool {
|
func certPool() *x509.CertPool {
|
||||||
certPool := x509.NewCertPool()
|
certPool := x509.NewCertPool()
|
||||||
certPool.AppendCertsFromPEM([]byte(caCert))
|
certPool.AppendCertsFromPEM([]byte(caCert))
|
||||||
|
Reference in New Issue
Block a user