Files
Dexter Yan acb1099bda feat(analyzer): add certificate analyzer (#1128)
* feat(analyzer): add cert analyzer
* feat(analyzer): add certificates tests
2023-05-29 16:40:10 +12:00

133 lines
4.1 KiB
Go

package analyzer
import (
"encoding/json"
"fmt"
"regexp"
"strconv"
"time"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/collect"
)
type AnalyzeCertificates struct {
analyzer *troubleshootv1beta2.CertificatesAnalyze
}
func (a *AnalyzeCertificates) Title() string {
title := a.analyzer.CheckName
if title == "" {
return "Certificates Verification"
}
return title
}
func (a *AnalyzeCertificates) IsExcluded() (bool, error) {
return isExcluded(a.analyzer.Exclude)
}
func (a *AnalyzeCertificates) Analyze(getFile getCollectedFileContents, findFiles getChildCollectedFileContents) ([]*AnalyzeResult, error) {
result, err := a.AnalyzeCertificates(a.analyzer, getFile)
if err != nil {
return nil, err
}
return result, nil
}
func (a *AnalyzeCertificates) AnalyzeCertificates(analyzer *troubleshootv1beta2.CertificatesAnalyze, getCollectedFileContents func(string) ([]byte, error)) ([]*AnalyzeResult, error) {
certificatesInfo, err := getCollectedFileContents("certificates/certificates.json")
if err != nil {
return nil, errors.Wrap(err, "failed to get contents of certificates.json")
}
collectorCertificates := []collect.CertCollection{}
if err := json.Unmarshal(certificatesInfo, &collectorCertificates); err != nil {
return nil, errors.Wrap(err, "failed to parse certificates.json")
}
return a.analyzeAnalyzeCertificatesResult(collectorCertificates, analyzer.Outcomes)
}
func (a *AnalyzeCertificates) analyzeAnalyzeCertificatesResult(certificates []collect.CertCollection, outcomes []*troubleshootv1beta2.Outcome) ([]*AnalyzeResult, error) {
var results []*AnalyzeResult
for _, cert := range certificates {
var passResults []*AnalyzeResult
for _, certChain := range cert.CertificateChain {
when := ""
message := ""
source := ""
if cert.Source.ConfigMapName != "" {
source = fmt.Sprintf("obtained from %s configmap within %s namespace", cert.Source.ConfigMapName, cert.Source.Namespace)
}
if cert.Source.SecretName != "" {
source = fmt.Sprintf("obtained from %s secret within %s namespace", cert.Source.SecretName, cert.Source.Namespace)
}
for _, outcome := range outcomes {
result := AnalyzeResult{
Title: a.Title(),
}
if outcome.Fail != nil {
result.IsFail = true
when = outcome.Fail.When
message = outcome.Fail.Message
} else if outcome.Warn != nil {
result.IsWarn = true
when = outcome.Warn.When
message = outcome.Warn.Message
} else if outcome.Pass != nil {
result.IsPass = true
when = outcome.Pass.When
message = outcome.Pass.Message
} else {
return nil, errors.New("empty outcome")
}
if result.IsPass && certChain.IsValid {
result.Message = fmt.Sprintf("%s %s, %s", certChain.CertName, message, source)
// if the certificate is valid, we need to wait for the warning check whether the certificate is going to expire
passResults = append(passResults, &result)
}
if result.IsFail && !certChain.IsValid {
result.Message = fmt.Sprintf("%s %s, %s", certChain.CertName, message, source)
// return the result immediately if the certificate is invalid
results = append(results, &result)
}
if result.IsWarn && certChain.IsValid {
warnDate, _ := regexp.Compile(`notAfter \< Today \+ (\d+) days`)
warnMatch := warnDate.FindStringSubmatch(when)
if warnMatch != nil {
warnMatchDays, err := strconv.Atoi(warnMatch[1])
if err != nil {
return nil, errors.Wrap(err, "failed to convert string to integer")
}
targetTime := time.Now().AddDate(0, 0, warnMatchDays)
if targetTime.After(certChain.NotAfter) {
result.Message = fmt.Sprintf("%s %s in %d days, %s", certChain.CertName, message, warnMatchDays, source)
// discard passResults if the certificate is going to expire in certain days
passResults = []*AnalyzeResult{}
results = append(results, &result)
}
}
}
}
// append passResults if the certificate is valid and not going to expire in certain days
results = append(results, passResults...)
}
}
return results, nil
}