From 668f7f1b459a5a983e7f5d5c245b02179a56c7f7 Mon Sep 17 00:00:00 2001 From: Derek Nola Date: Tue, 7 Jan 2025 10:19:23 -0800 Subject: [PATCH] Add "k3s certificate check" clause for better test coverage (#11485) * Add "k3s certificate check" clause for better test coverage Signed-off-by: Derek Nola * Add table support to cert check Signed-off-by: Derek Nola --------- Signed-off-by: Derek Nola --- pkg/cli/cert/cert.go | 68 ++++++++++++++----- pkg/cli/cmds/certs.go | 6 +- .../certrotation/certrotation_int_test.go | 19 ++++-- 3 files changed, 71 insertions(+), 22 deletions(-) diff --git a/pkg/cli/cert/cert.go b/pkg/cli/cert/cert.go index d5b0b96b8ae5..5d97ccd8c82d 100644 --- a/pkg/cli/cert/cert.go +++ b/pkg/cli/cert/cert.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "strings" + "text/tabwriter" "time" "github.com/k3s-io/k3s/pkg/agent/util" @@ -92,27 +93,60 @@ func check(app *cli.Context, cfg *cmds.Server) error { now := time.Now() warn := now.Add(time.Hour * 24 * config.CertificateRenewDays) - - for service, files := range fileMap { - logrus.Info("Checking certificates for " + service) - for _, file := range files { - // ignore errors, as some files may not exist, or may not contain certs. - // Only check whatever exists and has certs. - certs, _ := certutil.CertsFromFile(file) - for _, cert := range certs { - if now.Before(cert.NotBefore) { - logrus.Errorf("%s: certificate %s is not valid before %s", file, cert.Subject, cert.NotBefore.Format(time.RFC3339)) - } else if now.After(cert.NotAfter) { - logrus.Errorf("%s: certificate %s expired at %s", file, cert.Subject, cert.NotAfter.Format(time.RFC3339)) - } else if warn.After(cert.NotAfter) { - logrus.Warnf("%s: certificate %s will expire within %d days at %s", file, cert.Subject, config.CertificateRenewDays, cert.NotAfter.Format(time.RFC3339)) - } else { - logrus.Infof("%s: certificate %s is ok, expires at %s", file, cert.Subject, cert.NotAfter.Format(time.RFC3339)) + outFmt := app.String("output") + switch outFmt { + case "text": + for service, files := range fileMap { + logrus.Info("Checking certificates for " + service) + for _, file := range files { + // ignore errors, as some files may not exist, or may not contain certs. + // Only check whatever exists and has certs. + certs, _ := certutil.CertsFromFile(file) + for _, cert := range certs { + if now.Before(cert.NotBefore) { + logrus.Errorf("%s: certificate %s is not valid before %s", file, cert.Subject, cert.NotBefore.Format(time.RFC3339)) + } else if now.After(cert.NotAfter) { + logrus.Errorf("%s: certificate %s expired at %s", file, cert.Subject, cert.NotAfter.Format(time.RFC3339)) + } else if warn.After(cert.NotAfter) { + logrus.Warnf("%s: certificate %s will expire within %d days at %s", file, cert.Subject, config.CertificateRenewDays, cert.NotAfter.Format(time.RFC3339)) + } else { + logrus.Infof("%s: certificate %s is ok, expires at %s", file, cert.Subject, cert.NotAfter.Format(time.RFC3339)) + } } } } + case "table": + var tabBuffer bytes.Buffer + w := tabwriter.NewWriter(&tabBuffer, 0, 0, 2, ' ', 0) + fmt.Fprintf(w, "\n") + fmt.Fprintf(w, "CERTIFICATE\tSUBJECT\tSTATUS\tEXPIRES\n") + fmt.Fprintf(w, "-----------\t-------\t------\t-------") + for _, files := range fileMap { + for _, file := range files { + certs, _ := certutil.CertsFromFile(file) + for _, cert := range certs { + baseName := filepath.Base(file) + var status string + expiration := cert.NotAfter.Format(time.RFC3339) + if now.Before(cert.NotBefore) { + status = "NOT YET VALID" + expiration = cert.NotBefore.Format(time.RFC3339) + } else if now.After(cert.NotAfter) { + status = "EXPIRED" + } else if warn.After(cert.NotAfter) { + status = "WARNING" + } else { + status = "OK" + } + fmt.Fprintf(w, "\n%s\t%s\t%s\t%s", baseName, cert.Subject, status, expiration) + } + } + } + w.Flush() + fmt.Println(tabBuffer.String()) + default: + return fmt.Errorf("invalid output format %s", outFmt) } - return nil } diff --git a/pkg/cli/cmds/certs.go b/pkg/cli/cmds/certs.go index 0c3ee9c7026d..44440a040db8 100644 --- a/pkg/cli/cmds/certs.go +++ b/pkg/cli/cmds/certs.go @@ -63,7 +63,11 @@ func NewCertCommands(check, rotate, rotateCA func(ctx *cli.Context) error) cli.C SkipFlagParsing: false, SkipArgReorder: true, Action: check, - Flags: CertRotateCommandFlags, + Flags: append(CertRotateCommandFlags, &cli.StringFlag{ + Name: "output,o", + Usage: "Format output. Options: text, table", + Value: "text", + }), }, { Name: "rotate", diff --git a/tests/integration/certrotation/certrotation_int_test.go b/tests/integration/certrotation/certrotation_int_test.go index ffd7fad53a4f..9574a6b02ec3 100644 --- a/tests/integration/certrotation/certrotation_int_test.go +++ b/tests/integration/certrotation/certrotation_int_test.go @@ -46,15 +46,15 @@ var _ = Describe("certificate rotation", Ordered, func() { certHash, err = testutil.RunCommand("md5sum " + tmpdDataDir + "/server/tls/serving-kube-apiserver.crt | cut -f 1 -d' '") Expect(err).ToNot(HaveOccurred()) }) - It("stop k3s", func() { + It("stops k3s", func() { Expect(testutil.K3sKillServer(server)).To(Succeed()) }) - It("certificate rotate", func() { + It("rotates certificates", func() { _, err := testutil.K3sCmd("certificate", "rotate", "-d", tmpdDataDir) Expect(err).ToNot(HaveOccurred()) }) - It("start k3s server", func() { + It("starts k3s server", func() { var err error server2, err = testutil.K3sStartServer(serverArgs...) Expect(err).ToNot(HaveOccurred()) @@ -64,7 +64,18 @@ var _ = Describe("certificate rotation", Ordered, func() { return testutil.K3sDefaultDeployments() }, "360s", "5s").Should(Succeed()) }) - It("get certificate hash", func() { + It("checks the certificate status", func() { + res, err := testutil.K3sCmd("certificate", "check", "-d", tmpdDataDir) + Expect(err).ToNot(HaveOccurred()) + for i, line := range strings.Split(res, "\n") { + // First line is just server info + if i == 0 || line == "" { + continue + } + Expect(line).To(MatchRegexp("certificate.*is ok|Checking certificates"), res) + } + }) + It("gets certificate hash", func() { // get md5sum of the CA certs var err error caCertHashAfter, err := testutil.RunCommand("md5sum " + tmpdDataDir + "/server/tls/client-ca.crt | cut -f 1 -d' '")