certificate collector (#1112)

* adding  pkg/collect/certificates.go and pkg/collect/certificates_test.go files
* adding  collector.go and collector_shared.go files
* ran make schemas
* update zz_generated.deepcopy.go

Co-authored-by: Dexter Yan <yanshaocong@gmail.com>
Co-authored-by: Chris Sanders <sanders.chris@gmail.com>
Co-authored-by: Xav Paice <xav@replicated.com>
This commit is contained in:
David Rohnow
2023-04-12 14:34:02 +12:00
committed by GitHub
co-authored by Dexter Yan Chris Sanders Xav Paice
parent b020c76977
commit 64d5330ae5
11 changed files with 703 additions and 0 deletions
@@ -82,6 +82,27 @@ spec:
required:
- namespace
type: object
certificates:
properties:
collectorName:
type: string
configMaps:
additionalProperties:
items:
type: string
type: array
type: object
exclude:
type: BoolString
name:
type: string
secrets:
additionalProperties:
items:
type: string
type: array
type: object
type: object
clusterInfo:
properties:
collectorName:
@@ -1526,6 +1526,27 @@ spec:
required:
- namespace
type: object
certificates:
properties:
collectorName:
type: string
configMaps:
additionalProperties:
items:
type: string
type: array
type: object
exclude:
type: BoolString
name:
type: string
secrets:
additionalProperties:
items:
type: string
type: array
type: object
type: object
clusterInfo:
properties:
collectorName:
@@ -1557,6 +1557,27 @@ spec:
required:
- namespace
type: object
certificates:
properties:
collectorName:
type: string
configMaps:
additionalProperties:
items:
type: string
type: array
type: object
exclude:
type: BoolString
name:
type: string
secrets:
additionalProperties:
items:
type: string
type: array
type: object
type: object
clusterInfo:
properties:
collectorName:
@@ -216,6 +216,13 @@ type RegistryImages struct {
ImagePullSecrets *ImagePullSecrets `json:"imagePullSecret,omitempty" yaml:"imagePullSecret,omitempty"`
}
type Certificates struct {
CollectorMeta `json:",inline" yaml:",inline"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Secrets map[string][]string `json:"secrets,omitempty" yaml:"secrets,omitempty"`
ConfigMaps map[string][]string `json:"configMaps,omitempty" yaml:"configMaps,omitempty"`
}
type Collect struct {
ClusterInfo *ClusterInfo `json:"clusterInfo,omitempty" yaml:"clusterInfo,omitempty"`
ClusterResources *ClusterResources `json:"clusterResources,omitempty" yaml:"clusterResources,omitempty"`
@@ -238,6 +245,7 @@ type Collect struct {
Longhorn *Longhorn `json:"longhorn,omitempty" yaml:"longhorn,omitempty"`
RegistryImages *RegistryImages `json:"registryImages,omitempty" yaml:"registryImages,omitempty"`
Sysctl *Sysctl `json:"sysctl,omitempty" yaml:"sysctl,omitempty"`
Certificates *Certificates `json:"certificates,omitempty" yaml:"certificates,omitempty"`
}
func (c *Collect) AccessReviewSpecs(overrideNS string) []authorizationv1.SelfSubjectAccessReviewSpec {
@@ -539,6 +547,10 @@ func (c *Collect) GetName() string {
collector = "sysctl"
name = c.Sysctl.Name
}
if c.Certificates != nil {
collector = "certificates"
name = c.Certificates.CollectorName
}
if collector == "" {
return "<none>"
@@ -559,6 +559,52 @@ func (in *CertificateAnalyze) DeepCopy() *CertificateAnalyze {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Certificates) DeepCopyInto(out *Certificates) {
*out = *in
in.CollectorMeta.DeepCopyInto(&out.CollectorMeta)
if in.Secrets != nil {
in, out := &in.Secrets, &out.Secrets
*out = make(map[string][]string, len(*in))
for key, val := range *in {
var outVal []string
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = make([]string, len(*in))
copy(*out, *in)
}
(*out)[key] = outVal
}
}
if in.ConfigMaps != nil {
in, out := &in.ConfigMaps, &out.ConfigMaps
*out = make(map[string][]string, len(*in))
for key, val := range *in {
var outVal []string
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = make([]string, len(*in))
copy(*out, *in)
}
(*out)[key] = outVal
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Certificates.
func (in *Certificates) DeepCopy() *Certificates {
if in == nil {
return nil
}
out := new(Certificates)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterInfo) DeepCopyInto(out *ClusterInfo) {
*out = *in
@@ -790,6 +836,11 @@ func (in *Collect) DeepCopyInto(out *Collect) {
*out = new(Sysctl)
(*in).DeepCopyInto(*out)
}
if in.Certificates != nil {
in, out := &in.Certificates, &out.Certificates
*out = new(Certificates)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Collect.
+254
View File
@@ -0,0 +1,254 @@
package collect
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/pem"
"time"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)
type CollectCertificates struct {
Collector *troubleshootv1beta2.Certificates
BundlePath string
Namespace string
ClientConfig *rest.Config
Client kubernetes.Interface
Context context.Context
RBACErrors
}
// Certificate collection struct
type CertCollection struct {
Source *CertificateSource `json:"source"`
Errors []string `json:"errors"`
CertificateChain []ParsedCertificate `json:"certificateChain"`
}
// Certificate source
type CertificateSource struct {
SecretName string `json:"secret,omitempty"`
ConfigMapName string `json:"configMap,omitempty"`
Namespace string `json:"namespace,omitempty"`
}
// Certificate Struct
type ParsedCertificate struct {
CertName string `json:"certificate"`
Subject string `json:"subject"`
SubjectAlternativeNames []string `json:"subjectAlternativeNames"`
Issuer string `json:"issuer"`
NotAfter time.Time `json:"notAfter"`
NotBefore time.Time `json:"notBefore"`
IsValid bool `json:"isValid"`
IsCA bool `json:"isCA"`
}
func (c *CollectCertificates) Title() string {
return getCollectorName(c)
}
func (c *CollectCertificates) IsExcluded() (bool, error) {
return isExcluded(c.Collector.Exclude)
}
func (c *CollectCertificates) Collect(progressChan chan<- interface{}) (CollectorResult, error) {
output := NewResult()
results := []CertCollection{}
// collect certificates from secrets
for secretName, namespaces := range c.Collector.Secrets {
for _, namespace := range namespaces {
secretCollections := secretCertCollector(secretName, namespace, c.Client)
results = append(results, secretCollections...)
}
}
// collect certificates from configMaps
for configMapName, namespaces := range c.Collector.ConfigMaps {
for _, namespace := range namespaces {
configMapCollections := configMapCertCollector(configMapName, namespace, c.Client)
results = append(results, configMapCollections...)
}
}
certsJson, errCertJson := json.MarshalIndent(results, "", "\t")
if errCertJson != nil {
return nil, errCertJson
}
filePath := "certificates/certificates.json"
err := output.SaveResult(c.BundlePath, filePath, bytes.NewBuffer(certsJson))
if err != nil {
return nil, err
}
return output, nil
}
// configmap certificate collector
func configMapCertCollector(configMapName string, namespace string, client kubernetes.Interface) []CertCollection {
results := []CertCollection{}
trackErrors := []string{}
collection := []ParsedCertificate{}
getOptions := metav1.GetOptions{}
// Collect from configMaps
configMap, err := client.CoreV1().ConfigMaps(namespace).Get(context.Background(), configMapName, getOptions)
if err != nil {
// collect certificate source information
source := &CertificateSource{
SecretName: configMapName,
Namespace: namespace,
}
trackErrors = append(trackErrors, "Either the configMap does not exist in this namespace or RBAC permissions are preventing certificate collection")
results = append(results, CertCollection{
Source: source,
Errors: trackErrors,
CertificateChain: collection,
})
return results
}
//Collect from configMap
source := &CertificateSource{
ConfigMapName: configMap.Name,
Namespace: configMap.Namespace,
}
for certName, c := range configMap.Data {
certs := []byte(c)
certInfo, _ := CertParser(certName, certs)
collection = append(collection, certInfo...)
}
results = append(results, CertCollection{
Source: source,
Errors: trackErrors,
CertificateChain: collection,
})
return results
}
// secret certificate collector
func secretCertCollector(secretName string, namespace string, client kubernetes.Interface) []CertCollection {
results := []CertCollection{}
trackErrors := []string{}
collection := []ParsedCertificate{}
getOptions := metav1.GetOptions{}
// Collect from secrets
secret, err := client.CoreV1().Secrets(namespace).Get(context.Background(), secretName, getOptions)
if err != nil {
// collect certificate source information
source := &CertificateSource{
SecretName: secretName,
Namespace: namespace,
}
trackErrors = append(trackErrors, "Either the secret does not exist in this namespace or RBAC permissions are prenventing certificate collection")
results = append(results, CertCollection{
Source: source,
Errors: trackErrors,
CertificateChain: collection,
})
return results
}
// Collect from secret
source := &CertificateSource{
SecretName: secret.Name,
Namespace: secret.Namespace,
}
for certName, certs := range secret.Data {
certInfo, _ := CertParser(certName, certs)
collection = append(collection, certInfo...)
}
results = append(results, CertCollection{
Source: source,
Errors: trackErrors,
CertificateChain: collection,
})
return results
}
// decode pem and validate data source contains
func decodePem(certInput string) (tls.Certificate, []string) {
var cert tls.Certificate
trackErrors := []string{}
certPEMBlock := []byte(certInput)
var certDERBlock *pem.Block
for {
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
trackErrors = append(trackErrors, "decodePem function error: cert block is empty")
break
}
if certDERBlock.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
}
}
return cert, trackErrors
}
// Certificate parser
func CertParser(certName string, certs []byte) ([]ParsedCertificate, []string) {
//TODO: return trackErrors as well.
currentTime := time.Now()
data := string(certs)
certInfo := []ParsedCertificate{}
trackErrors := []string{}
certChain, decodePemTrackErrors := decodePem(data)
trackErrors = append(trackErrors, decodePemTrackErrors...)
for _, cert := range certChain.Certificate {
//parsed SSL certificate
parsedCert, errParse := x509.ParseCertificate(cert)
if errParse != nil {
trackErrors = append(trackErrors, errParse.Error())
continue
}
certInfo = append(certInfo, ParsedCertificate{
CertName: certName,
Subject: parsedCert.Subject.ToRDNSequence().String(),
SubjectAlternativeNames: parsedCert.DNSNames,
Issuer: parsedCert.Issuer.ToRDNSequence().String(),
NotAfter: parsedCert.NotAfter,
NotBefore: parsedCert.NotBefore,
IsValid: currentTime.Before(parsedCert.NotAfter),
IsCA: parsedCert.IsCA,
})
}
return certInfo, trackErrors
}
+222
View File
@@ -0,0 +1,222 @@
package collect
import (
"strings"
"testing"
)
var chain = `-----BEGIN CERTIFICATE-----
MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANLJ
hPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wok/4xIA+ui35/MmNa
rtNuC+BdZ1tMuVCPFZcCAwEAAaNQME4wHQYDVR0OBBYEFJvKs8RfJaXTH08W+SGv
zQyKn0H8MB8GA1UdIwQYMBaAFJvKs8RfJaXTH08W+SGvzQyKn0H8MAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADQQBJlffJHybjDGxRMqaRmDhX0+6v02TUKZsW
r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V
-----END CERTIFICATE-----
`
var chain2 = `-----BEGIN CERTIFICATE-----
MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/
PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC
7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw
PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6
4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo
LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U
pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy
BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH
AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH
AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o
dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0
AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1
AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp
AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl
AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo
AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg
AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg
AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB
gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy
dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy
dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw
gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB
c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0
LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE
FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI
Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU
nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/
roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU
xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+
BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu
zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
+OkuE6N36B9K
-----END CERTIFICATE-----`
var chain3 = `-----BEGIN CERTIFICATE-----
MIIDejCCAmKgAwIBAgIEAZaq0DANBgkqhkiG9w0BAQsFADAuMRgwFgYDVQQDEw9Q
cm9qZWN0IENvbnRvdXIxEjAQBgNVBAUTCTYxNTkyOTg5MTAeFw0yMzAyMjQwNDI3
MThaFw0yNDAyMjUwNDI3MTZaMBAxDjAMBgNVBAMTBWVudm95MIIBIjANBgkqhkiG
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqsNmmxb1ICso6Ay25lapcRyvLxAX/5u422uV
eiNn5jseCVXfg1jJr2Symrgou2dgMtIpZoVKT7w0el8sNpD+az5oMOWUNfTGEpYI
5zNAhtiedxCWcX15gfezOZ/DECL7HP8U37JFVdazm0CjvlQWI+8rFGvFQJJFDLYJ
h0fHGfbX6L/ST6cANtkXZyIU6CYFSgniuuDjHmQnQr6CC8lkisJxY5QVS7MZ02RR
nU/dK14ABY+mo/0ZeBKR5si04hr4i18nJJnk4DnHN+jQ/WWWSO1yLqr9kAOj37dA
nRAeKuzkx/VbU8DC/3sh0otcazoWO470D+irOy1is0whDArLNwIDAQABo4G9MIG6
MA4GA1UdDwEB/wQEAwIE8DAdBgNVHQ4EFgQUbAavOY+vIXgc44k8GvLHH6mdzzYw
HwYDVR0jBBgwFoAUb+S3Mu7cbwZiqZaEHTEMyUhLyH4waAYDVR0RBGEwX4IFZW52
b3mCFGVudm95LnByb2plY3Rjb250b3VyghhlbnZveS5wcm9qZWN0Y29udG91ci5z
dmOCJmVudm95LnByb2plY3Rjb250b3VyLnN2Yy5jbHVzdGVyLmxvY2FsMA0GCSqG
SIb3DQEBCwUAA4IBAQBIKpBD1T9tugzJF7lajbdulXTb9qGibwQALqauskX9Sq57
po/R2TjyxywLn4DgM7BAzzu9qfHWf+S4eQjRUHQshPbUEX9CEsSd5tCu8ZHVbBds
6qFagl2+YQ9ng0Xwta9ezvctM3T6Dy9Kkf5OOe9ysMEsBX7s8NFxe68Qku+cExr3
78oERlIoNOlT0cNbFLAlH2svNv1uB4qOThRDha52L+mlUdZfTMYZAwNDJWm52t/M
NCIm5NJ5jAJpcJmoEb+JMP3j0x6wydHDXFtGm3WRggZRcrjasyodSKK6szbf96+9
6syzAwvg9xxNtFxwbhRqqplMEz2sDWaggTrxCQzd
-----END CERTIFICATE-----
`
var chain4 = `-----BEGIN CERTIFICATE-----
Oy1is0whDArLNwIDAQABo4G9MIG6
MA4GA1UdDwEB/wQEAwIE8DAdBgNVHQ4EFgQUbAavOY+vIXgc44k8GvLHH6mdzzYw
HwYDVR0jBBgwFoAUb+S3Mu7cbwZiqZaEHTEMyUhLyH4waAYDVR0RBGEwX4IFZW52
b3mCFGVudm95LnByb2plY3Rjb250b3VyghhlbnZveS5wcm9qZWN0Y29udG91ci5z
dmOCJmVudm95LnByb2plY3Rjb250b3VyLnN2Yy5jbHVzdGVyLmxvY2FsMA0GCSqG
SIb3DQEBCwUAA4IBAQBIKpBD1T9tugzJF7lajbdulXTb9qGibwQALqauskX9Sq57
po/R2TjyxywLn4DgM7BAzzu9qfHWf+S4eQjRUHQshPbUEX9CEsSd5tCu8ZHVbBds
6qFagl2+YQ9ng0Xwta9ezvctM3T6Dy9Kkf5OOe9ysMEsBX7s8NFxe68Qku+cExr3
78oERlIoNOlT0cNbFLAlH2svNv1uB4qOThRDha52L+mlUdZfTMYZAwNDJWm52t/M
NCIm5NJ5jAJpcJmoEb+JMP3j0x6wydHDXFtGm3WRggZRcrjasyodSKK6szbf96+9
6syzAwvg9xxNtFxwbhRqqplMEz2sDWaggTrxCQzd
-----END CERTIFICATE-----
`
// tests validate that the certParser function correctly parses a certificate
func TestCertParser(t *testing.T) {
expiredCert := []byte(chain)
multiCert := []byte(chain2)
validCert := []byte(chain3)
nonCert := []byte(chain4)
//add docs
certParserTests := []struct {
Name string
Certs []byte
CertQty int
IsSubject bool
IsCert bool
IsValid bool
}{
// Name, Certs, CertQty, IsSubject, IsCert, IsValid
{"Widgits", expiredCert, 1, true, true, false},
{"digicert", multiCert, 2, true, true, false}, //IsValid will not be evaluated for multicerts
{"envoy", validCert, 1, true, true, true},
{"non.crt", nonCert, 1, false, false, false}, // IsSubject and IsValid n/a for non.crt
}
for _, e := range certParserTests {
results, _ := CertParser(e.Name, e.Certs)
// tests if results contains a list of parsed certificates
var isCert bool
if len(results) == 0 {
isCert = false
} else {
isCert = true
}
if isCert != e.IsCert {
t.Errorf("Expected %v, but got %v that %s is a certificate", e.IsCert, isCert, e.Name)
continue
}
for _, cert := range results {
t.Log(e.Name, cert.Subject)
// test checks if certificate subject contains a matching string; validates that can parse cert and pull back information
if !strings.Contains(cert.Subject, e.Name) {
t.Error("unable to parse certificate: ", e.Name)
} else {
isSubject := true
if e.IsSubject != isSubject {
t.Errorf("You expected that %s certificate contains %s in the Subject line but it was not present; here is the entire subject string: %v", e.Name, e.Name, cert.Subject)
}
}
// test checks for expected quantity of certificates in a certificate file
numCerts := len(results)
if numCerts != e.CertQty {
t.Errorf("expected %d certificates in slice but got %d", e.CertQty, numCerts)
}
if numCerts > 1 {
continue
}
// test checks for expected certificate expired
if cert.IsValid != e.IsValid {
t.Errorf("When checing that %v certificate is expired, you expected %v, but got %v ", e.Name, e.IsValid, cert.IsValid)
}
}
}
}
// validates that certificate count is correct when parsing a certificate input string.
func Test_decodePem(t *testing.T) {
certDecoderTests := []struct {
Name string
certInput string
certCount int
}{
{"widgets-cert", chain, 1},
{"digi-cert", chain2, 2},
}
for _, e := range certDecoderTests {
results, _ := decodePem(e.certInput)
certCount := 0
for _, cert := range results.Certificate {
t.Log(cert)
certCount++
}
if certCount != e.certCount {
t.Errorf("cert count -- expected %d, but got %d", e.certCount, certCount)
}
}
}
+5
View File
@@ -101,6 +101,8 @@ func GetCollector(collector *troubleshootv1beta2.Collect, bundlePath string, nam
return &CollectRegistry{collector.RegistryImages, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.Sysctl != nil:
return &CollectSysctl{collector.Sysctl, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
case collector.Certificates != nil:
return &CollectCertificates{collector.Certificates, bundlePath, namespace, clientConfig, client, ctx, RBACErrors}, true
default:
return nil, false
}
@@ -176,6 +178,9 @@ func getCollectorName(c interface{}) string {
case *CollectSysctl:
collector = "sysctl"
name = v.Collector.Name
case *CollectCertificates:
collector = "certificates"
name = v.Collector.Name
default:
collector = "<none>"
}
@@ -88,6 +88,38 @@
}
}
},
"certificates": {
"type": "object",
"properties": {
"collectorName": {
"type": "string"
},
"configMaps": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"exclude": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
},
"name": {
"type": "string"
},
"secrets": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"clusterInfo": {
"type": "object",
"properties": {
@@ -2312,6 +2312,38 @@
}
}
},
"certificates": {
"type": "object",
"properties": {
"collectorName": {
"type": "string"
},
"configMaps": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"exclude": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
},
"name": {
"type": "string"
},
"secrets": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"clusterInfo": {
"type": "object",
"properties": {
@@ -2358,6 +2358,38 @@
}
}
},
"certificates": {
"type": "object",
"properties": {
"collectorName": {
"type": "string"
},
"configMaps": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
},
"exclude": {
"oneOf": [{"type": "string"},{"type": "boolean"}]
},
"name": {
"type": "string"
},
"secrets": {
"type": "object",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"clusterInfo": {
"type": "object",
"properties": {