From 7defb9f067b667ffece3a97fd49273dbf4fef288 Mon Sep 17 00:00:00 2001 From: "Gerald (Bob) Lee" Date: Mon, 10 Oct 2022 10:48:32 -0700 Subject: [PATCH] Test if cert valid dates and warn for thresholds check cert valid for date, info at longest threshold, warn at the shortest threshold, and error when out of date. Attempt to include these thresholds within the global configuration data. This commit for review and help integrating service into the rest of EVE. SEE: EVE-125 --- pkg/pillar/types/global.go | 10 ++++ pkg/pillar/zedcloud/authen.go | 91 +++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/pkg/pillar/types/global.go b/pkg/pillar/types/global.go index 3b26c833b9..b9c5d152fd 100644 --- a/pkg/pillar/types/global.go +++ b/pkg/pillar/types/global.go @@ -122,6 +122,9 @@ const ( MetricInterval GlobalSettingKey = "timer.metric.interval" // DiskScanMetricInterval global setting key DiskScanMetricInterval GlobalSettingKey = "timer.metric.diskscan.interval" + // CertExpireInfo and CertExpireWarn global thresholds (in days) + CertExpireInfo GlobalSettingKey = "timer.metric.certinfolog" + CertExpireWarn GlobalSettingKey = "timer.metric.certwarnlog" // ResetIfCloudGoneTime global setting key ResetIfCloudGoneTime GlobalSettingKey = "timer.reboot.no.network" // FallbackIfCloudGoneTime global setting key @@ -738,6 +741,13 @@ func NewConfigItemSpecMap() ConfigItemSpecMap { // Need to be careful about max value. Controller may use metric message to // update status of device (online / suspect etc ). configItemSpecMap.AddIntItem(MetricInterval, 60, 5, HourInSec) + // thresholds to log info then warn for x509 certificates + // default is to log Info at 60 days before expiry, and Warn at 30 + configItemSpecMap.AddIntItem(CertExpireInfo, 60*24*HourInSec, 24*HourInSec, 365*24*HourInSec) + configItemSpecMap.AddIntItem(CertExpireWarn, 30*24*HourInSec, 24*HourInSec, 365*24*HourInSec) + // ? Do we need to specify frequency of logging for each of the + // two levels -- that is, more frequent logging say hourly for + // the Warn level ? // timer.reboot.no.network (seconds) - reboot after no cloud connectivity // Max designed to allow the option of never rebooting even if device // can't connect to the cloud diff --git a/pkg/pillar/zedcloud/authen.go b/pkg/pillar/zedcloud/authen.go index 078ed235d0..95e68318e4 100644 --- a/pkg/pillar/zedcloud/authen.go +++ b/pkg/pillar/zedcloud/authen.go @@ -24,6 +24,7 @@ import ( "net/http" "os" "strings" + "time" zauth "github.com/lf-edge/eve/api/go/auth" zcert "github.com/lf-edge/eve/api/go/certs" @@ -511,6 +512,20 @@ func VerifySigningCertChain(log *base.LogObject, certs []*zcert.ZCert) ([]byte, } } } + + // verify date range of certificates + for _, cert := range certs { + realCert, err := x509.ParseCertificate(cert.Cert) + if err != nil { + errStr := fmt.Sprintf("certificate PEM parse fail") + log.Errorln("verifySignature: " + errStr) + return nil, err + } + if !verifyx509TimeStamps(log, realCert) { + return nil, errors.New("Cert not in valid time") + } + } + log.Tracef("VerifySigningCertChain: success\n") return sigCertBytes, nil } @@ -559,6 +574,82 @@ func verifySignature(log *base.LogObject, certByte []byte, interm *x509.CertPool return nil } +func verifyx509TimeStamps(log *base.LogObject, realCert *x509.Certificate) bool { + rightNow := time.Now() + notBefore := realCert.NotBefore + notAfter := realCert.NotAfter + certName := realCert.Subject + certValid := true + // HACK for compilation test DUMMY thresholds + type configInfo struct { + // only need to define thresholds to + // inform and warn the owners + infoThreshold time.Duration + warnThreshold time.Duration + } + + type certStatus uint8 + const ( + allCertsAreValid certStatus = iota + aCertIsInfo + aCertIsWarn + aCertIsINVALID + ) + + type statusInfo struct { + CertStatus certStatus + } + + // assume default will be 60 days before for Info + // and 30 days for the Warning + var SystemConfig = configInfo{time.Hour * 24 * 60, + time.Hour * 24 * 30} + + var CurrCertStatus = statusInfo{allCertsAreValid} + // HACK end + + // verify the NotBefore, and NotAfter + if rightNow.After(notBefore) { + // Cert is active (after NotBefore date) + // process thresholds + validDuration := notAfter.Sub(rightNow) + switch { + case validDuration < 0: + log.Errorf("%s: expired %v ago", + certName, + -validDuration) + certValid = false + CurrCertStatus.CertStatus = aCertIsINVALID + case validDuration < SystemConfig.warnThreshold: + log.Warnf("%s: expires in %v (%v days or less)", + certName, + validDuration, + SystemConfig.warnThreshold/24) + if CurrCertStatus.CertStatus < aCertIsWarn { + CurrCertStatus.CertStatus = aCertIsWarn + } + case validDuration < SystemConfig.infoThreshold: + log.Noticef("%s: expires in %v (%v Days or less)", + certName, + validDuration, + SystemConfig.infoThreshold/24) + if CurrCertStatus.CertStatus < aCertIsWarn { + CurrCertStatus.CertStatus = aCertIsWarn + } + default: + log.Errorf("%s: %v until it expires", + certName, validDuration) + } + } else { + // Current date is notBefore the Cert is valid + log.Errorf("%s: not yet valid, for %v", + certName, notBefore.Sub(rightNow)) + certValid = false + CurrCertStatus.CertStatus = aCertIsINVALID + } + return certValid +} + // SaveServerSigningCert saves server (i.e. controller) signing certificate into the persist // partition. func SaveServerSigningCert(ctx *ZedCloudContext, certByte []byte) error {