mirror of
https://github.com/enix/x509-certificate-exporter.git
synced 2026-02-14 18:10:01 +00:00
Implement regex support for secret-type flag
This commit is contained in:
committed by
Thibault VINCENT
parent
ab35b9e0ca
commit
4cfad6fe50
@@ -57,8 +57,8 @@ func main() {
|
||||
|
||||
kubeConfig := getopt.StringLong("kubeconfig", 0, "", "Path to the kubeconfig file to use for requests. Takes precedence over the KUBECONFIG environment variable, and default path (~/.kube/config).", "path")
|
||||
|
||||
kubeSecretTypes := stringArrayFlag{}
|
||||
getopt.FlagLong(&kubeSecretTypes, "secret-type", 's', "one or more kubernetes secret type & key to watch (e.g. \"kubernetes.io/tls:tls.crt\"")
|
||||
kubeSecretTypePatterns := stringArrayFlag{}
|
||||
getopt.FlagLong(&kubeSecretTypePatterns, "secret-type", 's', "one or more kubernetes secret type & key to watch (e.g. \"kubernetes.io/tls:tls.crt\"")
|
||||
|
||||
kubeConfigMapKeys := stringArrayFlag{}
|
||||
getopt.FlagLong(&kubeConfigMapKeys, "configmap-keys", 'c', "keys in configmaps to watch")
|
||||
@@ -118,6 +118,15 @@ func main() {
|
||||
slog.Error("Cannot set GOMEMLIMIT with automemlimit", "reason", err.Error())
|
||||
}
|
||||
|
||||
kubeSecretTypes := make([]internal.KubeSecretType, 0)
|
||||
for _, pattern := range kubeSecretTypePatterns {
|
||||
kst, err := internal.ParseSecretType(pattern)
|
||||
if err != nil {
|
||||
log.Fatal("failed to parse --secret-type argument: ", err)
|
||||
}
|
||||
kubeSecretTypes = append(kubeSecretTypes, kst)
|
||||
}
|
||||
|
||||
exporter := internal.Exporter{
|
||||
ListenAddress: *listenAddress,
|
||||
SystemdSocket: *systemdSocket,
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -38,8 +39,8 @@ type Exporter struct {
|
||||
ExposeRelativeMetrics bool
|
||||
ExposeErrorMetrics bool
|
||||
ExposeLabels []string
|
||||
KubeSecretTypes []string
|
||||
ConfigMapKeys []string
|
||||
KubeSecretTypes []KubeSecretType
|
||||
KubeIncludeNamespaces []string
|
||||
KubeExcludeNamespaces []string
|
||||
KubeIncludeLabels []string
|
||||
@@ -53,6 +54,33 @@ type Exporter struct {
|
||||
configMapsCache *cache.Cache
|
||||
}
|
||||
|
||||
type KubeSecretType struct {
|
||||
Type string
|
||||
Regexp *regexp.Regexp
|
||||
}
|
||||
|
||||
func ParseSecretType(s string) (KubeSecretType, error) {
|
||||
ty, pattern, found := strings.Cut(s, ":")
|
||||
if !found {
|
||||
return KubeSecretType{}, errors.New("secret type needs to contain at least a single colon")
|
||||
}
|
||||
compiled, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return KubeSecretType{}, err
|
||||
}
|
||||
return KubeSecretType{
|
||||
Type: ty,
|
||||
Regexp: compiled,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (kst *KubeSecretType) Matches(secretType, key string) bool {
|
||||
if kst.Type != secretType {
|
||||
return false
|
||||
}
|
||||
return kst.Regexp.MatchString(key)
|
||||
}
|
||||
|
||||
// ListenAndServe : Convenience function to start exporter
|
||||
func (exporter *Exporter) ListenAndServe() error {
|
||||
exporter.DiscoverCertificates()
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -991,7 +992,7 @@ func checkLabels(t *testing.T, labels []*model.LabelPair, path string, isKube bo
|
||||
func testRequest(t *testing.T, exporter *Exporter, cb func(metrics []model.MetricFamily)) {
|
||||
exporter.ListenAddress = listenAddress
|
||||
if exporter.KubeSecretTypes == nil {
|
||||
exporter.KubeSecretTypes = []string{"kubernetes.io/tls:tls.crt"}
|
||||
exporter.KubeSecretTypes = []KubeSecretType{{Type: "kubernetes.io/tls", Regexp: regexp.MustCompile(`tls\.crt`)}}
|
||||
}
|
||||
exporter.DiscoverCertificates()
|
||||
|
||||
|
||||
@@ -32,15 +32,15 @@ func (exporter *Exporter) parseAllKubeObjects() ([]*certificateRef, []error) {
|
||||
readCertificatesFromSecrets := func(secrets []v1.Secret) (outputs []*certificateRef) {
|
||||
for _, secret := range secrets {
|
||||
for _, secretType := range exporter.KubeSecretTypes {
|
||||
typeAndKey := strings.Split(secretType, ":")
|
||||
|
||||
if secret.Type == v1.SecretType(typeAndKey[0]) && len(secret.Data[typeAndKey[1]]) > 0 {
|
||||
outputs = append(outputs, &certificateRef{
|
||||
path: fmt.Sprintf("k8s/%s/%s", secret.GetNamespace(), secret.GetName()),
|
||||
format: certificateFormatKubeSecret,
|
||||
kubeSecret: secret,
|
||||
kubeSecretKey: typeAndKey[1],
|
||||
})
|
||||
for key := range secret.Data {
|
||||
if secretType.Matches(string(secret.Type), key) {
|
||||
output = append(output, &certificateRef{
|
||||
path: fmt.Sprintf("k8s/%s/%s", secret.GetNamespace(), secret.GetName()),
|
||||
format: certificateFormatKubeSecret,
|
||||
kubeSecret: secret,
|
||||
kubeSecretKey: key,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,17 +247,12 @@ func (exporter *Exporter) filterSecrets(secrets []v1.Secret, includedLabels, exc
|
||||
|
||||
func (exporter *Exporter) checkHasIncludedType(secret *v1.Secret) (bool, error) {
|
||||
for _, secretType := range exporter.KubeSecretTypes {
|
||||
typeAndKey := strings.Split(secretType, ":")
|
||||
|
||||
if len(typeAndKey) != 2 {
|
||||
return false, fmt.Errorf("malformed kube secret type: \"%s\"", secretType)
|
||||
}
|
||||
|
||||
if secret.Type == v1.SecretType(typeAndKey[0]) && len(secret.Data[typeAndKey[1]]) > 0 {
|
||||
return true, nil
|
||||
for key := range secret.Data {
|
||||
if secretType.Matches(string(secret.Type), key) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -272,9 +267,10 @@ func (exporter *Exporter) shrinkSecret(secret v1.Secret) v1.Secret {
|
||||
}
|
||||
|
||||
for _, secretType := range exporter.KubeSecretTypes {
|
||||
typeAndKey := strings.Split(secretType, ":")
|
||||
if secret.Type == v1.SecretType(typeAndKey[0]) && len(secret.Data[typeAndKey[1]]) > 0 {
|
||||
result.Data[typeAndKey[1]] = secret.Data[typeAndKey[1]]
|
||||
for key := range secret.Data {
|
||||
if secretType.Matches(string(secret.Type), key) {
|
||||
result.Data[key] = secret.Data[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -263,9 +264,9 @@ func TestKubeIncludeExcludeLabelMix4(t *testing.T) {
|
||||
|
||||
func TestKubeCustomSecret(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeSecretTypes: []string{
|
||||
"istio.io/cert-and-key:cert-chain.pem",
|
||||
"istio.io/cert-and-key:root-cert.pem",
|
||||
KubeSecretTypes: []KubeSecretType{
|
||||
{Type: "istio.io/cert-and-key", Regexp: regexp.MustCompile(`cert-chain\.pem`)},
|
||||
{Type: "istio.io/cert-and-key", Regexp: regexp.MustCompile(`root-cert\.pem`)},
|
||||
},
|
||||
}, func(m []model.MetricFamily) {
|
||||
metric := getMetricsForName(m, "x509_cert_expired")
|
||||
@@ -349,19 +350,18 @@ func TestKubeInvalidConfig3(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestKubeInvalidSecretType(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeNamespaces: []string{"default"},
|
||||
KubeSecretTypes: []string{"aze"},
|
||||
}, func(m []model.MetricFamily) {
|
||||
metrics := getMetricsForName(m, "x509_read_errors")
|
||||
assert.Equal(t, 1., metrics[0].GetGauge().GetValue())
|
||||
})
|
||||
_, err := ParseSecretType("aze")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestKubeEmptyStringKey(t *testing.T) {
|
||||
testRequestKube(t, &Exporter{
|
||||
KubeIncludeLabels: []string{"empty=true"},
|
||||
KubeSecretTypes: []string{"kubernetes.io/tls:tls.crt", "kubernetes.io/tls:tls.key", "kubernetes.io/tls:nil.key"},
|
||||
KubeSecretTypes: []KubeSecretType{
|
||||
{Type: "kubernetes.io/tls", Regexp: regexp.MustCompile(`tls\.crt`)},
|
||||
{Type: "kubernetes.io/tls", Regexp: regexp.MustCompile(`tls\.key`)},
|
||||
{Type: "kubernetes.io/tls", Regexp: regexp.MustCompile(`nil\.key`)},
|
||||
},
|
||||
}, func(m []model.MetricFamily) {
|
||||
metrics := getMetricsForName(m, "x509_read_errors")
|
||||
assert.Equal(t, 0., metrics[0].GetGauge().GetValue())
|
||||
|
||||
Reference in New Issue
Block a user