You've already forked ssl_exporter
mirror of
https://github.com/ribbybibby/ssl_exporter.git
synced 2025-07-12 23:50:14 +02:00
Move tests to prober package
This commit is contained in:
@ -196,7 +196,5 @@ func checkFileMetrics(cert *x509.Certificate, certFile string, registry *prometh
|
|||||||
Value: float64(cert.NotBefore.Unix()),
|
Value: float64(cert.NotBefore.Unix()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, res := range expectedResults {
|
checkRegistryResults(expectedResults, mfs, t)
|
||||||
checkRegistryResults(res, mfs, t)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package prober
|
package prober
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -16,11 +20,12 @@ import (
|
|||||||
pconfig "github.com/prometheus/common/config"
|
pconfig "github.com/prometheus/common/config"
|
||||||
"github.com/ribbybibby/ssl_exporter/config"
|
"github.com/ribbybibby/ssl_exporter/config"
|
||||||
"github.com/ribbybibby/ssl_exporter/test"
|
"github.com/ribbybibby/ssl_exporter/test"
|
||||||
|
"golang.org/x/crypto/ocsp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestProbeHTTPS tests the typical case
|
// TestProbeHTTPS tests the typical case
|
||||||
func TestProbeHTTPS(t *testing.T) {
|
func TestProbeHTTPS(t *testing.T) {
|
||||||
server, _, _, caFile, teardown, err := test.SetupHTTPSServer()
|
server, certPEM, _, caFile, teardown, err := test.SetupHTTPSServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
@ -45,6 +50,45 @@ func TestProbeHTTPS(t *testing.T) {
|
|||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestProbeHTTPSTimeout tests that the https probe respects the timeout in the
|
||||||
|
// context
|
||||||
|
func TestProbeHTTPSTimeout(t *testing.T) {
|
||||||
|
server, _, _, caFile, teardown, err := test.SetupHTTPSServer()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
server.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
fmt.Fprintln(w, "Hello world")
|
||||||
|
})
|
||||||
|
|
||||||
|
server.StartTLS()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
module := config.Module{
|
||||||
|
TLSConfig: pconfig.TLSConfig{
|
||||||
|
CAFile: caFile,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registry := prometheus.NewRegistry()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := ProbeHTTPS(ctx, server.URL, module, registry); err == nil {
|
||||||
|
t.Fatalf("Expected error but returned error was nil")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestProbeHTTPSInvalidName tests hitting the server on an address which isn't
|
// TestProbeHTTPSInvalidName tests hitting the server on an address which isn't
|
||||||
@ -84,7 +128,7 @@ func TestProbeHTTPSInvalidName(t *testing.T) {
|
|||||||
// TestProbeHTTPSNoScheme tests that the probe is successful when the scheme is
|
// TestProbeHTTPSNoScheme tests that the probe is successful when the scheme is
|
||||||
// omitted from the target. The scheme should be added by the prober.
|
// omitted from the target. The scheme should be added by the prober.
|
||||||
func TestProbeHTTPSNoScheme(t *testing.T) {
|
func TestProbeHTTPSNoScheme(t *testing.T) {
|
||||||
server, _, _, caFile, teardown, err := test.SetupHTTPSServer()
|
server, certPEM, _, caFile, teardown, err := test.SetupHTTPSServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
@ -113,12 +157,19 @@ func TestProbeHTTPSNoScheme(t *testing.T) {
|
|||||||
if err := ProbeHTTPS(ctx, u.Host, module, registry); err != nil {
|
if err := ProbeHTTPS(ctx, u.Host, module, registry); err != nil {
|
||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestProbeHTTPSServername tests that the probe is successful when the
|
// TestProbeHTTPSServername tests that the probe is successful when the
|
||||||
// servername is provided in the TLS config
|
// servername is provided in the TLS config
|
||||||
func TestProbeHTTPSServerName(t *testing.T) {
|
func TestProbeHTTPSServerName(t *testing.T) {
|
||||||
server, _, _, caFile, teardown, err := test.SetupHTTPSServer()
|
server, certPEM, _, caFile, teardown, err := test.SetupHTTPSServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
@ -148,6 +199,13 @@ func TestProbeHTTPSServerName(t *testing.T) {
|
|||||||
if err := ProbeHTTPS(ctx, "https://localhost:"+u.Port(), module, registry); err != nil {
|
if err := ProbeHTTPS(ctx, "https://localhost:"+u.Port(), module, registry); err != nil {
|
||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestProbeHTTPSHTTP tests that the prober fails when hitting a HTTP server
|
// TestProbeHTTPSHTTP tests that the prober fails when hitting a HTTP server
|
||||||
@ -218,6 +276,13 @@ func TestProbeHTTPSClientAuth(t *testing.T) {
|
|||||||
if err := ProbeHTTPS(ctx, server.URL, module, registry); err != nil {
|
if err := ProbeHTTPS(ctx, server.URL, module, registry); err != nil {
|
||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestProbeHTTPSClientAuthWrongClientCert tests that the probe fails with a bad
|
// TestProbeHTTPSClientAuthWrongClientCert tests that the probe fails with a bad
|
||||||
@ -315,7 +380,7 @@ func TestProbeHTTPSExpired(t *testing.T) {
|
|||||||
// TestProbeHTTPSExpiredInsecure tests that the probe succeeds with an expired server cert
|
// TestProbeHTTPSExpiredInsecure tests that the probe succeeds with an expired server cert
|
||||||
// when skipping cert verification
|
// when skipping cert verification
|
||||||
func TestProbeHTTPSExpiredInsecure(t *testing.T) {
|
func TestProbeHTTPSExpiredInsecure(t *testing.T) {
|
||||||
server, _, _, caFile, teardown, err := test.SetupHTTPSServer()
|
server, certPEM, _, caFile, teardown, err := test.SetupHTTPSServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
@ -347,11 +412,18 @@ func TestProbeHTTPSExpiredInsecure(t *testing.T) {
|
|||||||
if err := ProbeHTTPS(ctx, server.URL, module, registry); err != nil {
|
if err := ProbeHTTPS(ctx, server.URL, module, registry); err != nil {
|
||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestProbeHTTPSProxy tests the proxy_url field in the configuration
|
// TestProbeHTTPSProxy tests the proxy_url field in the configuration
|
||||||
func TestProbeHTTPSProxy(t *testing.T) {
|
func TestProbeHTTPSProxy(t *testing.T) {
|
||||||
server, _, _, caFile, teardown, err := test.SetupHTTPSServer()
|
server, certPEM, _, caFile, teardown, err := test.SetupHTTPSServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
@ -404,4 +476,136 @@ func TestProbeHTTPSProxy(t *testing.T) {
|
|||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestProbeHTTPSOCSP tests a HTTPS probe with OCSP stapling
|
||||||
|
func TestProbeHTTPSOCSP(t *testing.T) {
|
||||||
|
server, certPEM, keyPEM, caFile, teardown, err := test.SetupHTTPSServer()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
key, err := newKey(keyPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := ocsp.CreateResponse(cert, cert, ocsp.Response{SerialNumber: big.NewInt(64), Status: 1}, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
server.TLS.Certificates[0].OCSPStaple = resp
|
||||||
|
|
||||||
|
server.StartTLS()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
module := config.Module{
|
||||||
|
TLSConfig: pconfig.TLSConfig{
|
||||||
|
CAFile: caFile,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registry := prometheus.NewRegistry()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := ProbeHTTPS(ctx, server.URL, module, registry); err != nil {
|
||||||
|
t.Fatalf("error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics(resp, registry, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestProbeHTTPSVerifiedChains tests the verified chain metrics returned by a
|
||||||
|
// https probe
|
||||||
|
func TestProbeHTTPSVerifiedChains(t *testing.T) {
|
||||||
|
rootPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCertExpiry := time.Now().AddDate(0, 0, 5)
|
||||||
|
rootCertTmpl := test.GenerateCertificateTemplate(rootCertExpiry)
|
||||||
|
rootCertTmpl.IsCA = true
|
||||||
|
rootCertTmpl.SerialNumber = big.NewInt(1)
|
||||||
|
rootCert, rootCertPem := test.GenerateSelfSignedCertificateWithPrivateKey(rootCertTmpl, rootPrivateKey)
|
||||||
|
|
||||||
|
olderRootCertExpiry := time.Now().AddDate(0, 0, 3)
|
||||||
|
olderRootCertTmpl := test.GenerateCertificateTemplate(olderRootCertExpiry)
|
||||||
|
olderRootCertTmpl.IsCA = true
|
||||||
|
olderRootCertTmpl.SerialNumber = big.NewInt(2)
|
||||||
|
olderRootCert, olderRootCertPem := test.GenerateSelfSignedCertificateWithPrivateKey(olderRootCertTmpl, rootPrivateKey)
|
||||||
|
|
||||||
|
oldestRootCertExpiry := time.Now().AddDate(0, 0, 1)
|
||||||
|
oldestRootCertTmpl := test.GenerateCertificateTemplate(oldestRootCertExpiry)
|
||||||
|
oldestRootCertTmpl.IsCA = true
|
||||||
|
oldestRootCertTmpl.SerialNumber = big.NewInt(3)
|
||||||
|
oldestRootCert, oldestRootCertPem := test.GenerateSelfSignedCertificateWithPrivateKey(oldestRootCertTmpl, rootPrivateKey)
|
||||||
|
|
||||||
|
serverCertExpiry := time.Now().AddDate(0, 0, 4)
|
||||||
|
serverCertTmpl := test.GenerateCertificateTemplate(serverCertExpiry)
|
||||||
|
serverCertTmpl.SerialNumber = big.NewInt(4)
|
||||||
|
serverCert, serverCertPem, serverKey := test.GenerateSignedCertificate(serverCertTmpl, olderRootCert, rootPrivateKey)
|
||||||
|
|
||||||
|
verifiedChains := [][]*x509.Certificate{
|
||||||
|
[]*x509.Certificate{
|
||||||
|
serverCert,
|
||||||
|
rootCert,
|
||||||
|
},
|
||||||
|
[]*x509.Certificate{
|
||||||
|
serverCert,
|
||||||
|
olderRootCert,
|
||||||
|
},
|
||||||
|
[]*x509.Certificate{
|
||||||
|
serverCert,
|
||||||
|
oldestRootCert,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
caCertPem := bytes.Join([][]byte{oldestRootCertPem, olderRootCertPem, rootCertPem}, []byte(""))
|
||||||
|
|
||||||
|
server, caFile, teardown, err := test.SetupHTTPSServerWithCertAndKey(
|
||||||
|
caCertPem,
|
||||||
|
serverCertPem,
|
||||||
|
serverKey,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
server.StartTLS()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
module := config.Module{
|
||||||
|
TLSConfig: pconfig.TLSConfig{
|
||||||
|
CAFile: caFile,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registry := prometheus.NewRegistry()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := ProbeHTTPS(ctx, server.URL, module, registry); err != nil {
|
||||||
|
t.Fatalf("error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCertificateMetrics(serverCert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
|
checkVerifiedChainMetrics(verifiedChains, registry, t)
|
||||||
}
|
}
|
||||||
|
@ -186,8 +186,5 @@ func checkKubernetesMetrics(cert *x509.Certificate, namespace, name, key string,
|
|||||||
Value: float64(cert.NotBefore.Unix()),
|
Value: float64(cert.NotBefore.Unix()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
checkRegistryResults(expectedResults, mfs, t)
|
||||||
for _, res := range expectedResults {
|
|
||||||
checkRegistryResults(res, mfs, t)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
package prober
|
package prober
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
dto "github.com/prometheus/client_model/go"
|
dto "github.com/prometheus/client_model/go"
|
||||||
|
"golang.org/x/crypto/ocsp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type registryResult struct {
|
type registryResult struct {
|
||||||
@ -27,19 +33,28 @@ func (rr *registryResult) String() string {
|
|||||||
return fmt.Sprintf("%s %f", m, rr.Value)
|
return fmt.Sprintf("%s %f", m, rr.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkRegistryResults(expRes *registryResult, mfs []*dto.MetricFamily, t *testing.T) {
|
func checkRegistryResults(expectedResults []*registryResult, mfs []*dto.MetricFamily, t *testing.T) {
|
||||||
|
for _, expRes := range expectedResults {
|
||||||
|
checkRegistryResult(expRes, mfs, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkRegistryResult(expRes *registryResult, mfs []*dto.MetricFamily, t *testing.T) {
|
||||||
var results []*registryResult
|
var results []*registryResult
|
||||||
for _, mf := range mfs {
|
for _, mf := range mfs {
|
||||||
for _, metric := range mf.Metric {
|
for _, metric := range mf.Metric {
|
||||||
labelValues := make(map[string]string)
|
result := ®istryResult{
|
||||||
for _, l := range metric.GetLabel() {
|
Name: mf.GetName(),
|
||||||
labelValues[l.GetName()] = l.GetValue()
|
Value: metric.GetGauge().GetValue(),
|
||||||
}
|
}
|
||||||
results = append(results, ®istryResult{
|
if len(metric.GetLabel()) > 0 {
|
||||||
Name: mf.GetName(),
|
labelValues := make(map[string]string)
|
||||||
LabelValues: labelValues,
|
for _, l := range metric.GetLabel() {
|
||||||
Value: metric.GetGauge().GetValue(),
|
labelValues[l.GetName()] = l.GetValue()
|
||||||
})
|
}
|
||||||
|
result.LabelValues = labelValues
|
||||||
|
}
|
||||||
|
results = append(results, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var ok bool
|
var ok bool
|
||||||
@ -54,3 +69,138 @@ func checkRegistryResults(expRes *registryResult, mfs []*dto.MetricFamily, t *te
|
|||||||
t.Fatalf("Expected %s, got: %s", expRes.String(), resStr)
|
t.Fatalf("Expected %s, got: %s", expRes.String(), resStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkCertificateMetrics(cert *x509.Certificate, registry *prometheus.Registry, t *testing.T) {
|
||||||
|
mfs, err := registry.Gather()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
ips := ","
|
||||||
|
for _, ip := range cert.IPAddresses {
|
||||||
|
ips = ips + ip.String() + ","
|
||||||
|
}
|
||||||
|
expectedLabels := map[string]string{
|
||||||
|
"serial_no": cert.SerialNumber.String(),
|
||||||
|
"issuer_cn": cert.Issuer.CommonName,
|
||||||
|
"cn": cert.Subject.CommonName,
|
||||||
|
"dnsnames": "," + strings.Join(cert.DNSNames, ",") + ",",
|
||||||
|
"ips": ips,
|
||||||
|
"emails": "," + strings.Join(cert.EmailAddresses, ",") + ",",
|
||||||
|
"ou": "," + strings.Join(cert.Subject.OrganizationalUnit, ",") + ",",
|
||||||
|
}
|
||||||
|
expectedResults := []*registryResult{
|
||||||
|
®istryResult{
|
||||||
|
Name: "ssl_cert_not_after",
|
||||||
|
LabelValues: expectedLabels,
|
||||||
|
Value: float64(cert.NotAfter.Unix()),
|
||||||
|
},
|
||||||
|
®istryResult{
|
||||||
|
Name: "ssl_cert_not_before",
|
||||||
|
LabelValues: expectedLabels,
|
||||||
|
Value: float64(cert.NotBefore.Unix()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
checkRegistryResults(expectedResults, mfs, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkVerifiedChainMetrics(verifiedChains [][]*x509.Certificate, registry *prometheus.Registry, t *testing.T) {
|
||||||
|
mfs, err := registry.Gather()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for i, chain := range verifiedChains {
|
||||||
|
for _, cert := range chain {
|
||||||
|
ips := ","
|
||||||
|
for _, ip := range cert.IPAddresses {
|
||||||
|
ips = ips + ip.String() + ","
|
||||||
|
}
|
||||||
|
expectedLabels := map[string]string{
|
||||||
|
"chain_no": strconv.Itoa(i),
|
||||||
|
"serial_no": cert.SerialNumber.String(),
|
||||||
|
"issuer_cn": cert.Issuer.CommonName,
|
||||||
|
"cn": cert.Subject.CommonName,
|
||||||
|
"dnsnames": "," + strings.Join(cert.DNSNames, ",") + ",",
|
||||||
|
"ips": ips,
|
||||||
|
"emails": "," + strings.Join(cert.EmailAddresses, ",") + ",",
|
||||||
|
"ou": "," + strings.Join(cert.Subject.OrganizationalUnit, ",") + ",",
|
||||||
|
}
|
||||||
|
expectedResults := []*registryResult{
|
||||||
|
®istryResult{
|
||||||
|
Name: "ssl_verified_cert_not_after",
|
||||||
|
LabelValues: expectedLabels,
|
||||||
|
Value: float64(cert.NotAfter.Unix()),
|
||||||
|
},
|
||||||
|
®istryResult{
|
||||||
|
Name: "ssl_verified_cert_not_before",
|
||||||
|
LabelValues: expectedLabels,
|
||||||
|
Value: float64(cert.NotBefore.Unix()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
checkRegistryResults(expectedResults, mfs, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkOCSPMetrics(resp []byte, registry *prometheus.Registry, t *testing.T) {
|
||||||
|
var (
|
||||||
|
stapled float64
|
||||||
|
status float64
|
||||||
|
nextUpdate float64
|
||||||
|
thisUpdate float64
|
||||||
|
revokedAt float64
|
||||||
|
producedAt float64
|
||||||
|
)
|
||||||
|
mfs, err := registry.Gather()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(resp) > 0 {
|
||||||
|
parsedResponse, err := ocsp.ParseResponse(resp, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
stapled = 1
|
||||||
|
status = float64(parsedResponse.Status)
|
||||||
|
nextUpdate = float64(parsedResponse.NextUpdate.Unix())
|
||||||
|
thisUpdate = float64(parsedResponse.ThisUpdate.Unix())
|
||||||
|
revokedAt = float64(parsedResponse.RevokedAt.Unix())
|
||||||
|
producedAt = float64(parsedResponse.ProducedAt.Unix())
|
||||||
|
}
|
||||||
|
expectedResults := []*registryResult{
|
||||||
|
®istryResult{
|
||||||
|
Name: "ssl_ocsp_response_stapled",
|
||||||
|
Value: stapled,
|
||||||
|
},
|
||||||
|
®istryResult{
|
||||||
|
Name: "ssl_ocsp_response_status",
|
||||||
|
Value: status,
|
||||||
|
},
|
||||||
|
®istryResult{
|
||||||
|
Name: "ssl_ocsp_response_next_update",
|
||||||
|
Value: nextUpdate,
|
||||||
|
},
|
||||||
|
®istryResult{
|
||||||
|
Name: "ssl_ocsp_response_this_update",
|
||||||
|
Value: thisUpdate,
|
||||||
|
},
|
||||||
|
®istryResult{
|
||||||
|
Name: "ssl_ocsp_response_revoked_at",
|
||||||
|
Value: revokedAt,
|
||||||
|
},
|
||||||
|
®istryResult{
|
||||||
|
Name: "ssl_ocsp_response_produced_at",
|
||||||
|
Value: producedAt,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
checkRegistryResults(expectedResults, mfs, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCertificate(certPEM []byte) (*x509.Certificate, error) {
|
||||||
|
block, _ := pem.Decode(certPEM)
|
||||||
|
return x509.ParseCertificate(block.Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newKey(keyPEM []byte) (*rsa.PrivateKey, error) {
|
||||||
|
block, _ := pem.Decode([]byte(keyPEM))
|
||||||
|
return x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
}
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
package prober
|
package prober
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ribbybibby/ssl_exporter/config"
|
"github.com/ribbybibby/ssl_exporter/config"
|
||||||
"github.com/ribbybibby/ssl_exporter/test"
|
"github.com/ribbybibby/ssl_exporter/test"
|
||||||
|
"golang.org/x/crypto/ocsp"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
pconfig "github.com/prometheus/common/config"
|
pconfig "github.com/prometheus/common/config"
|
||||||
@ -16,9 +22,9 @@ import (
|
|||||||
|
|
||||||
// TestProbeTCP tests the typical case
|
// TestProbeTCP tests the typical case
|
||||||
func TestProbeTCP(t *testing.T) {
|
func TestProbeTCP(t *testing.T) {
|
||||||
server, _, _, caFile, teardown, err := test.SetupTCPServer()
|
server, certPEM, _, caFile, teardown, err := test.SetupTCPServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer teardown()
|
defer teardown()
|
||||||
|
|
||||||
@ -40,6 +46,13 @@ func TestProbeTCP(t *testing.T) {
|
|||||||
if err := ProbeTCP(ctx, server.Listener.Addr().String(), module, registry); err != nil {
|
if err := ProbeTCP(ctx, server.Listener.Addr().String(), module, registry); err != nil {
|
||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestProbeTCPInvalidName tests hitting the server on an address which isn't
|
// TestProbeTCPInvalidName tests hitting the server on an address which isn't
|
||||||
@ -76,7 +89,7 @@ func TestProbeTCPInvalidName(t *testing.T) {
|
|||||||
// TestProbeTCPServerName tests that the probe is successful when the
|
// TestProbeTCPServerName tests that the probe is successful when the
|
||||||
// servername is provided in the TLS config
|
// servername is provided in the TLS config
|
||||||
func TestProbeTCPServerName(t *testing.T) {
|
func TestProbeTCPServerName(t *testing.T) {
|
||||||
server, _, _, caFile, teardown, err := test.SetupTCPServer()
|
server, certPEM, _, caFile, teardown, err := test.SetupTCPServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
@ -103,6 +116,13 @@ func TestProbeTCPServerName(t *testing.T) {
|
|||||||
if err := ProbeTCP(ctx, "localhost:"+listenPort, module, registry); err != nil {
|
if err := ProbeTCP(ctx, "localhost:"+listenPort, module, registry); err != nil {
|
||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestProbeTCPExpired tests that the probe fails with an expired server cert
|
// TestProbeTCPExpired tests that the probe fails with an expired server cert
|
||||||
@ -144,7 +164,7 @@ func TestProbeTCPExpired(t *testing.T) {
|
|||||||
// TestProbeTCPExpiredInsecure tests that the probe succeeds with an expired server cert
|
// TestProbeTCPExpiredInsecure tests that the probe succeeds with an expired server cert
|
||||||
// when skipping cert verification
|
// when skipping cert verification
|
||||||
func TestProbeTCPExpiredInsecure(t *testing.T) {
|
func TestProbeTCPExpiredInsecure(t *testing.T) {
|
||||||
server, _, _, caFile, teardown, err := test.SetupTCPServer()
|
server, certPEM, _, caFile, teardown, err := test.SetupTCPServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
@ -177,11 +197,17 @@ func TestProbeTCPExpiredInsecure(t *testing.T) {
|
|||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestProbeTCPStartTLSSMTP tests STARTTLS against a mock SMTP server
|
// TestProbeTCPStartTLSSMTP tests STARTTLS against a mock SMTP server
|
||||||
func TestProbeTCPStartTLSSMTP(t *testing.T) {
|
func TestProbeTCPStartTLSSMTP(t *testing.T) {
|
||||||
server, _, _, caFile, teardown, err := test.SetupTCPServer()
|
server, certPEM, _, caFile, teardown, err := test.SetupTCPServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
@ -208,11 +234,18 @@ func TestProbeTCPStartTLSSMTP(t *testing.T) {
|
|||||||
if err := ProbeTCP(ctx, server.Listener.Addr().String(), module, registry); err != nil {
|
if err := ProbeTCP(ctx, server.Listener.Addr().String(), module, registry); err != nil {
|
||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestProbeTCPStartTLSFTP tests STARTTLS against a mock FTP server
|
// TestProbeTCPStartTLSFTP tests STARTTLS against a mock FTP server
|
||||||
func TestProbeTCPStartTLSFTP(t *testing.T) {
|
func TestProbeTCPStartTLSFTP(t *testing.T) {
|
||||||
server, _, _, caFile, teardown, err := test.SetupTCPServer()
|
server, certPEM, _, caFile, teardown, err := test.SetupTCPServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
@ -239,11 +272,18 @@ func TestProbeTCPStartTLSFTP(t *testing.T) {
|
|||||||
if err := ProbeTCP(ctx, server.Listener.Addr().String(), module, registry); err != nil {
|
if err := ProbeTCP(ctx, server.Listener.Addr().String(), module, registry); err != nil {
|
||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestProbeTCPStartTLSIMAP tests STARTTLS against a mock IMAP server
|
// TestProbeTCPStartTLSIMAP tests STARTTLS against a mock IMAP server
|
||||||
func TestProbeTCPStartTLSIMAP(t *testing.T) {
|
func TestProbeTCPStartTLSIMAP(t *testing.T) {
|
||||||
server, _, _, caFile, teardown, err := test.SetupTCPServer()
|
server, certPEM, _, caFile, teardown, err := test.SetupTCPServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf(err.Error())
|
t.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
@ -270,4 +310,167 @@ func TestProbeTCPStartTLSIMAP(t *testing.T) {
|
|||||||
if err := ProbeTCP(ctx, server.Listener.Addr().String(), module, registry); err != nil {
|
if err := ProbeTCP(ctx, server.Listener.Addr().String(), module, registry); err != nil {
|
||||||
t.Fatalf("error: %s", err)
|
t.Fatalf("error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestProbeTCPTimeout tests that the TCP probe respects the timeout in the
|
||||||
|
// context
|
||||||
|
func TestProbeTCPTimeout(t *testing.T) {
|
||||||
|
server, _, _, caFile, teardown, err := test.SetupTCPServer()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
server.StartTLSWait(time.Second * 3)
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
module := config.Module{
|
||||||
|
TLSConfig: pconfig.TLSConfig{
|
||||||
|
CAFile: caFile,
|
||||||
|
InsecureSkipVerify: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registry := prometheus.NewRegistry()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := ProbeTCP(ctx, server.Listener.Addr().String(), module, registry); err == nil {
|
||||||
|
t.Fatalf("Expected error but returned error was nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestProbeTCPOCSP tests a TCP probe with OCSP stapling
|
||||||
|
func TestProbeTCPOCSP(t *testing.T) {
|
||||||
|
server, certPEM, keyPEM, caFile, teardown, err := test.SetupTCPServer()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
cert, err := newCertificate(certPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
key, err := newKey(keyPEM)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := ocsp.CreateResponse(cert, cert, ocsp.Response{SerialNumber: big.NewInt(64), Status: 1}, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
server.TLS.Certificates[0].OCSPStaple = resp
|
||||||
|
|
||||||
|
server.StartTLS()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
module := config.Module{
|
||||||
|
TLSConfig: pconfig.TLSConfig{
|
||||||
|
CAFile: caFile,
|
||||||
|
InsecureSkipVerify: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registry := prometheus.NewRegistry()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := ProbeTCP(ctx, server.Listener.Addr().String(), module, registry); err != nil {
|
||||||
|
t.Fatalf("error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCertificateMetrics(cert, registry, t)
|
||||||
|
checkOCSPMetrics(resp, registry, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestProbeTCPVerifiedChains tests the verified chain metrics returned by a tcp
|
||||||
|
// probe
|
||||||
|
func TestProbeTCPVerifiedChains(t *testing.T) {
|
||||||
|
rootPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
rootCertExpiry := time.Now().AddDate(0, 0, 5)
|
||||||
|
rootCertTmpl := test.GenerateCertificateTemplate(rootCertExpiry)
|
||||||
|
rootCertTmpl.IsCA = true
|
||||||
|
rootCertTmpl.SerialNumber = big.NewInt(1)
|
||||||
|
rootCert, rootCertPem := test.GenerateSelfSignedCertificateWithPrivateKey(rootCertTmpl, rootPrivateKey)
|
||||||
|
|
||||||
|
olderRootCertExpiry := time.Now().AddDate(0, 0, 3)
|
||||||
|
olderRootCertTmpl := test.GenerateCertificateTemplate(olderRootCertExpiry)
|
||||||
|
olderRootCertTmpl.IsCA = true
|
||||||
|
olderRootCertTmpl.SerialNumber = big.NewInt(2)
|
||||||
|
olderRootCert, olderRootCertPem := test.GenerateSelfSignedCertificateWithPrivateKey(olderRootCertTmpl, rootPrivateKey)
|
||||||
|
|
||||||
|
oldestRootCertExpiry := time.Now().AddDate(0, 0, 1)
|
||||||
|
oldestRootCertTmpl := test.GenerateCertificateTemplate(oldestRootCertExpiry)
|
||||||
|
oldestRootCertTmpl.IsCA = true
|
||||||
|
oldestRootCertTmpl.SerialNumber = big.NewInt(3)
|
||||||
|
oldestRootCert, oldestRootCertPem := test.GenerateSelfSignedCertificateWithPrivateKey(oldestRootCertTmpl, rootPrivateKey)
|
||||||
|
|
||||||
|
serverCertExpiry := time.Now().AddDate(0, 0, 4)
|
||||||
|
serverCertTmpl := test.GenerateCertificateTemplate(serverCertExpiry)
|
||||||
|
serverCertTmpl.SerialNumber = big.NewInt(4)
|
||||||
|
serverCert, serverCertPem, serverKey := test.GenerateSignedCertificate(serverCertTmpl, olderRootCert, rootPrivateKey)
|
||||||
|
|
||||||
|
verifiedChains := [][]*x509.Certificate{
|
||||||
|
[]*x509.Certificate{
|
||||||
|
serverCert,
|
||||||
|
rootCert,
|
||||||
|
},
|
||||||
|
[]*x509.Certificate{
|
||||||
|
serverCert,
|
||||||
|
olderRootCert,
|
||||||
|
},
|
||||||
|
[]*x509.Certificate{
|
||||||
|
serverCert,
|
||||||
|
oldestRootCert,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
caCertPem := bytes.Join([][]byte{oldestRootCertPem, olderRootCertPem, rootCertPem}, []byte(""))
|
||||||
|
|
||||||
|
server, caFile, teardown, err := test.SetupTCPServerWithCertAndKey(
|
||||||
|
caCertPem,
|
||||||
|
serverCertPem,
|
||||||
|
serverKey,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(err.Error())
|
||||||
|
}
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
server.StartTLS()
|
||||||
|
defer server.Close()
|
||||||
|
|
||||||
|
module := config.Module{
|
||||||
|
TLSConfig: pconfig.TLSConfig{
|
||||||
|
CAFile: caFile,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registry := prometheus.NewRegistry()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if err := ProbeTCP(ctx, server.Listener.Addr().String(), module, registry); err != nil {
|
||||||
|
t.Fatalf("error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCertificateMetrics(serverCert, registry, t)
|
||||||
|
checkOCSPMetrics([]byte{}, registry, t)
|
||||||
|
checkVerifiedChainMetrics(verifiedChains, registry, t)
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user