Skip to content

Commit

Permalink
Add "k3s certificate check" clause for better test coverage (#11485)
Browse files Browse the repository at this point in the history
* Add "k3s certificate check" clause for better test coverage

Signed-off-by: Derek Nola <[email protected]>

* Add table support to cert check

Signed-off-by: Derek Nola <[email protected]>

---------

Signed-off-by: Derek Nola <[email protected]>
  • Loading branch information
dereknola committed Jan 13, 2025
1 parent db6dd12 commit 668f7f1
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 22 deletions.
68 changes: 51 additions & 17 deletions pkg/cli/cert/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"strings"
"text/tabwriter"
"time"

"github.com/k3s-io/k3s/pkg/agent/util"
Expand Down Expand Up @@ -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
}

Expand Down
6 changes: 5 additions & 1 deletion pkg/cli/cmds/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
19 changes: 15 additions & 4 deletions tests/integration/certrotation/certrotation_int_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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' '")
Expand Down

0 comments on commit 668f7f1

Please sign in to comment.