2021-04-02 11:53:31 +02:00
|
|
|
package prober
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/x509"
|
|
|
|
"encoding/base64"
|
|
|
|
"encoding/pem"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2022-05-07 10:31:08 +02:00
|
|
|
"github.com/ribbybibby/ssl_exporter/v2/config"
|
|
|
|
"github.com/ribbybibby/ssl_exporter/v2/test"
|
2021-04-02 11:53:31 +02:00
|
|
|
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
2021-06-23 18:22:22 +02:00
|
|
|
"gopkg.in/yaml.v3"
|
2021-04-02 11:53:31 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// TestProbeFile tests a file
|
|
|
|
func TestProbeKubeconfig(t *testing.T) {
|
|
|
|
cert, kubeconfig, err := createTestKubeconfig("", "kubeconfig")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf(err.Error())
|
|
|
|
}
|
|
|
|
defer os.Remove(kubeconfig)
|
|
|
|
|
|
|
|
module := config.Module{}
|
|
|
|
|
|
|
|
registry := prometheus.NewRegistry()
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
2021-06-23 18:22:22 +02:00
|
|
|
if err := ProbeKubeconfig(ctx, newTestLogger(), kubeconfig, module, registry); err != nil {
|
2021-04-02 11:53:31 +02:00
|
|
|
t.Fatalf("error: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
checkKubeconfigMetrics(cert, kubeconfig, registry, t)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseKubeConfigRelative(t *testing.T) {
|
|
|
|
tmpFile, err := ioutil.TempFile("", "kubeconfig")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Unable to create Tempfile: %s", err.Error())
|
|
|
|
}
|
|
|
|
defer os.Remove(tmpFile.Name())
|
|
|
|
file := []byte(`
|
|
|
|
clusters:
|
|
|
|
- cluster:
|
|
|
|
certificate-authority: certs/example/ca.pem
|
|
|
|
server: https://master.example.com
|
|
|
|
name: example
|
|
|
|
users:
|
|
|
|
- user:
|
|
|
|
client-certificate: test/ca.pem
|
|
|
|
name: example`)
|
|
|
|
if _, err := tmpFile.Write(file); err != nil {
|
|
|
|
t.Fatalf("Unable to write Tempfile: %s", err.Error())
|
|
|
|
}
|
|
|
|
expectedClusterPath := filepath.Join(filepath.Dir(tmpFile.Name()), "certs/example/ca.pem")
|
|
|
|
expectedUserPath := filepath.Join(filepath.Dir(tmpFile.Name()), "test/ca.pem")
|
|
|
|
k, err := ParseKubeConfig(tmpFile.Name())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Error parsing kubeconfig: %s", err.Error())
|
|
|
|
}
|
|
|
|
if len(k.Clusters) != 1 {
|
|
|
|
t.Fatalf("Unexpected length for Clusters, got %d", len(k.Clusters))
|
|
|
|
}
|
|
|
|
if k.Clusters[0].Cluster.CertificateAuthority != expectedClusterPath {
|
|
|
|
t.Errorf("Unexpected CertificateAuthority value\nExpected: %s\nGot: %s", expectedClusterPath, k.Clusters[0].Cluster.CertificateAuthority)
|
|
|
|
}
|
|
|
|
if len(k.Users) != 1 {
|
|
|
|
t.Fatalf("Unexpected length for Users, got %d", len(k.Users))
|
|
|
|
}
|
|
|
|
if k.Users[0].User.ClientCertificate != expectedUserPath {
|
|
|
|
t.Errorf("Unexpected ClientCertificate value\nExpected: %s\nGot: %s", expectedUserPath, k.Users[0].User.ClientCertificate)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a certificate and write it to a file
|
|
|
|
func createTestKubeconfig(dir, filename string) (*x509.Certificate, string, error) {
|
|
|
|
certPEM, _ := test.GenerateTestCertificate(time.Now().Add(time.Hour * 1))
|
|
|
|
clusterCert := KubeConfigClusterCert{CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(certPEM))}
|
|
|
|
clusters := []KubeConfigCluster{KubeConfigCluster{Name: "kubernetes", Cluster: clusterCert}}
|
|
|
|
userCert := KubeConfigUserCert{ClientCertificateData: base64.StdEncoding.EncodeToString([]byte(certPEM))}
|
|
|
|
users := []KubeConfigUser{KubeConfigUser{Name: "kubernetes-admin", User: userCert}}
|
|
|
|
k := KubeConfig{
|
|
|
|
Clusters: clusters,
|
|
|
|
Users: users,
|
|
|
|
}
|
|
|
|
block, _ := pem.Decode([]byte(certPEM))
|
|
|
|
cert, err := x509.ParseCertificate(block.Bytes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, "", err
|
|
|
|
}
|
|
|
|
tmpFile, err := ioutil.TempFile(dir, filename)
|
|
|
|
if err != nil {
|
|
|
|
return nil, tmpFile.Name(), err
|
|
|
|
}
|
|
|
|
k.Path = tmpFile.Name()
|
|
|
|
d, err := yaml.Marshal(&k)
|
|
|
|
if err != nil {
|
|
|
|
return nil, tmpFile.Name(), err
|
|
|
|
}
|
|
|
|
if _, err := tmpFile.Write(d); err != nil {
|
|
|
|
return nil, tmpFile.Name(), err
|
|
|
|
}
|
|
|
|
if err := tmpFile.Close(); err != nil {
|
|
|
|
return nil, tmpFile.Name(), err
|
|
|
|
}
|
|
|
|
|
|
|
|
return cert, tmpFile.Name(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check metrics
|
|
|
|
func checkKubeconfigMetrics(cert *x509.Certificate, kubeconfig string, 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() + ","
|
|
|
|
}
|
|
|
|
expectedResults := []*registryResult{
|
|
|
|
®istryResult{
|
|
|
|
Name: "ssl_kubeconfig_cert_not_after",
|
|
|
|
LabelValues: map[string]string{
|
|
|
|
"kubeconfig": kubeconfig,
|
|
|
|
"name": "kubernetes",
|
|
|
|
"type": "cluster",
|
|
|
|
"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, ",") + ",",
|
|
|
|
},
|
|
|
|
Value: float64(cert.NotAfter.Unix()),
|
|
|
|
},
|
|
|
|
®istryResult{
|
|
|
|
Name: "ssl_kubeconfig_cert_not_before",
|
|
|
|
LabelValues: map[string]string{
|
|
|
|
"kubeconfig": kubeconfig,
|
|
|
|
"name": "kubernetes",
|
|
|
|
"type": "cluster",
|
|
|
|
"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, ",") + ",",
|
|
|
|
},
|
|
|
|
Value: float64(cert.NotBefore.Unix()),
|
|
|
|
},
|
|
|
|
®istryResult{
|
|
|
|
Name: "ssl_kubeconfig_cert_not_after",
|
|
|
|
LabelValues: map[string]string{
|
|
|
|
"kubeconfig": kubeconfig,
|
|
|
|
"name": "kubernetes-admin",
|
|
|
|
"type": "user",
|
|
|
|
"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, ",") + ",",
|
|
|
|
},
|
|
|
|
Value: float64(cert.NotAfter.Unix()),
|
|
|
|
},
|
|
|
|
®istryResult{
|
|
|
|
Name: "ssl_kubeconfig_cert_not_before",
|
|
|
|
LabelValues: map[string]string{
|
|
|
|
"kubeconfig": kubeconfig,
|
|
|
|
"name": "kubernetes-admin",
|
|
|
|
"type": "user",
|
|
|
|
"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, ",") + ",",
|
|
|
|
},
|
|
|
|
Value: float64(cert.NotBefore.Unix()),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
checkRegistryResults(expectedResults, mfs, t)
|
|
|
|
}
|