mirror of
https://github.com/replicatedhq/troubleshoot.git
synced 2026-08-27 00:37:20 +00:00
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:
co-authored by
Dexter Yan
Chris Sanders
Xav Paice
parent
b020c76977
commit
64d5330ae5
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user