check node certificate expiration
Reference:- https://hackmd.io/aGaz7YXSSHybGcyol8vYEw Relates-To: #391 Change-Id: I8c9c83dfb2eb11af48857fb96404dcf2eb3eaa55
This commit is contained in:
parent
b5b03bb1de
commit
426340e31c
@ -32,7 +32,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
kubeconfigIdentifierSuffix = "-kubeconfig"
|
||||
kubeconfigIdentifierSuffix = "-kubeconfig"
|
||||
timeFormat = "Jan 02, 2006 15:04 MST"
|
||||
nodeCertExpirationAnnotation = "cert-expiration"
|
||||
)
|
||||
|
||||
// CertificateExpirationStore is the customized client store
|
||||
@ -258,3 +260,91 @@ func (store CertificateExpirationStore) getKubeconfSecrets() ([]corev1.Secret, e
|
||||
kubeconfigs = filterOwners(kubeconfigs, "KubeadmControlPlane")
|
||||
return kubeconfigs, nil
|
||||
}
|
||||
|
||||
// GetExpiringNodeCertificates runs through all the nodes and identifies expiration
|
||||
func (store CertificateExpirationStore) GetExpiringNodeCertificates() ([]NodeCert, error) {
|
||||
// Node will be updated with an annotation with the expiry content (Activity
|
||||
// of HostConfig Operator - 'check-expiry' CR Object) every day (Cron like
|
||||
// activity is performed by reconcile tag in the Operator) Below code is
|
||||
// implemented to just read the annotation, parse it, identify expirable
|
||||
// content and report back
|
||||
|
||||
// Expected Annotation Format:
|
||||
// "cert-expiration": "{ admin.conf: Aug 06, 2021 12:36 UTC },
|
||||
// { apiserver: Aug 06, 2021 12:36 UTC },
|
||||
// { apiserver-etcd-client: Aug 06, 2021 12:36 UTC },
|
||||
// { apiserver-kubelet-client: Aug 06, 2021 12:36 UTC },
|
||||
// { controller-manager.conf: Aug 06, 2021 12:36 UTC },
|
||||
// { etcd-healthcheck-client: Aug 06, 2021 12:36 UTC },
|
||||
// { etcd-peer: Aug 06, 2021 12:36 UTC },
|
||||
// { etcd-server: Aug 06, 2021 12:36 UTC },
|
||||
// { front-proxy-client: Aug 06, 2021 12:36 UTC },
|
||||
// { scheduler.conf: Aug 06, 2021 12:36 UTC },
|
||||
// { ca: Aug 04, 2030 12:36 UTC },
|
||||
// { etcd-ca: Aug 04, 2030 12:36 UTC },
|
||||
// { front-proxy-ca: Aug 04, 2030 12:36 UTC }"
|
||||
|
||||
nodes, err := store.getNodes(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nodeData := make([]NodeCert, 0)
|
||||
for _, node := range nodes.Items {
|
||||
expiringNodeCertificates := store.getExpiringNodeCertificates(node)
|
||||
if len(expiringNodeCertificates) > 0 {
|
||||
nodeData = append(nodeData, NodeCert{
|
||||
Name: node.Name,
|
||||
Namespace: node.Namespace,
|
||||
ExpiringCertificates: expiringNodeCertificates,
|
||||
})
|
||||
}
|
||||
}
|
||||
return nodeData, nil
|
||||
}
|
||||
|
||||
// getSecrets returns the Nodes list based on the listOptions
|
||||
func (store CertificateExpirationStore) getNodes(listOptions metav1.ListOptions) (*corev1.NodeList, error) {
|
||||
return store.Kclient.ClientSet().CoreV1().Nodes().List(listOptions)
|
||||
}
|
||||
|
||||
// getExpiringNodeCertificates skims through all the node certificates and returns
|
||||
// the ones lesser than threshold
|
||||
func (store CertificateExpirationStore) getExpiringNodeCertificates(node corev1.Node) map[string]string {
|
||||
if cert, found := node.ObjectMeta.Annotations[nodeCertExpirationAnnotation]; found {
|
||||
certificateList := splitAsList(cert)
|
||||
|
||||
expiringCertificates := map[string]string{}
|
||||
for _, certificate := range certificateList {
|
||||
certificateName, expirationDate := identifyCertificateNameAndExpirationDate(certificate)
|
||||
if certificateName != "" && isWithinDuration(expirationDate, store.ExpirationThreshold) {
|
||||
expiringCertificates[certificateName] = expirationDate.String()
|
||||
}
|
||||
}
|
||||
return expiringCertificates
|
||||
}
|
||||
log.Printf("%s annotation missing for node %s in %s", nodeCertExpirationAnnotation,
|
||||
node.Name, node.Namespace)
|
||||
return nil
|
||||
}
|
||||
|
||||
// splitAsList performes the required string manipulations and returns list of items
|
||||
func splitAsList(value string) []string {
|
||||
return strings.Split(strings.ReplaceAll(value, "{", ""), "},")
|
||||
}
|
||||
|
||||
// identifyCertificateNameAndExpirationDate performs string manipulations and returns
|
||||
// certificate name and its expiration date
|
||||
func identifyCertificateNameAndExpirationDate(certificate string) (string, time.Time) {
|
||||
certificateName := strings.TrimSpace(strings.Split(certificate, ":")[0])
|
||||
expirationDate := strings.TrimSpace(strings.Split(certificate, ":")[1]) +
|
||||
":" +
|
||||
strings.TrimSpace(strings.ReplaceAll(strings.Split(certificate, ":")[2], "}", ""))
|
||||
|
||||
formattedExpirationDate, err := time.Parse(timeFormat, expirationDate)
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
return "", time.Time{}
|
||||
}
|
||||
return certificateName, formattedExpirationDate
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ type CheckCommand struct {
|
||||
type ExpirationStore struct {
|
||||
TLSSecrets []TLSSecret `json:"tlsSecrets,omitempty" yaml:"tlsSecrets,omitempty"`
|
||||
Kubeconfs []Kubeconfig `json:"kubeconfs,omitempty" yaml:"kubeconfs,omitempty"`
|
||||
NodeCerts []NodeCert `json:"nodeCerts,omitempty" yaml:"nodeCerts,omitempty"`
|
||||
}
|
||||
|
||||
// TLSSecret captures expiration information of certificates embedded in TLS secrets
|
||||
@ -68,6 +69,13 @@ type kubeconfData struct {
|
||||
ExpirationDate string `json:"expirationDate,omitempty" yaml:"expirationDate,omitempty"`
|
||||
}
|
||||
|
||||
// NodeCert captures certificate expiry information for certificates on each node
|
||||
type NodeCert struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
ExpiringCertificates map[string]string `json:"certificate,omitempty" yaml:"certificate,omitempty"`
|
||||
}
|
||||
|
||||
// RunE is the implementation of check command
|
||||
func (c *CheckCommand) RunE(w io.Writer) error {
|
||||
if !strings.EqualFold(c.Options.FormatType, "json") && !strings.EqualFold(c.Options.FormatType, "yaml") {
|
||||
@ -112,8 +120,14 @@ func (store CertificateExpirationStore) GetExpiringCertificates() ExpirationStor
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
|
||||
expiringNodeCertificates, err := store.GetExpiringNodeCertificates()
|
||||
if err != nil {
|
||||
log.Printf(err.Error())
|
||||
}
|
||||
|
||||
return ExpirationStore{
|
||||
TLSSecrets: expiringTLSCertificates,
|
||||
Kubeconfs: expiringKubeConfCertificates,
|
||||
NodeCerts: expiringNodeCertificates,
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,27 @@ const (
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
"nodeCerts": [
|
||||
{
|
||||
"name": "test-node",
|
||||
"certificate": {
|
||||
"admin.conf": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"apiserver": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"apiserver-etcd-client": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"apiserver-kubelet-client": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"ca": "2021-08-04 12:36:00 +0000 UTC",
|
||||
"controller-manager.conf": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"etcd-ca": "2021-08-04 12:36:00 +0000 UTC",
|
||||
"etcd-healthcheck-client": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"etcd-peer": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"etcd-server": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"front-proxy-ca": "2021-08-04 12:36:00 +0000 UTC",
|
||||
"front-proxy-client": "2021-08-06 12:36:00 +0000 UTC",
|
||||
"scheduler.conf": "2021-08-06 12:36:00 +0000 UTC"
|
||||
}
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
expectedYAMLOutput = `
|
||||
@ -88,6 +108,22 @@ tlsSecrets:
|
||||
tls.crt: 2030-08-31 10:12:49 +0000 UTC
|
||||
name: test-cluster-etcd
|
||||
namespace: default
|
||||
nodeCerts:
|
||||
- name: test-node
|
||||
certificate:
|
||||
admin.conf: 2021-08-06 12:36:00 +0000 UTC
|
||||
apiserver: 2021-08-06 12:36:00 +0000 UTC
|
||||
apiserver-etcd-client: 2021-08-06 12:36:00 +0000 UTC
|
||||
apiserver-kubelet-client: 2021-08-06 12:36:00 +0000 UTC
|
||||
ca: 2021-08-04 12:36:00 +0000 UTC
|
||||
controller-manager.conf: 2021-08-06 12:36:00 +0000 UTC
|
||||
etcd-ca: 2021-08-04 12:36:00 +0000 UTC
|
||||
etcd-healthcheck-client: 2021-08-06 12:36:00 +0000 UTC
|
||||
etcd-peer: 2021-08-06 12:36:00 +0000 UTC
|
||||
etcd-server: 2021-08-06 12:36:00 +0000 UTC
|
||||
front-proxy-ca: 2021-08-04 12:36:00 +0000 UTC
|
||||
front-proxy-client: 2021-08-06 12:36:00 +0000 UTC
|
||||
scheduler.conf: 2021-08-06 12:36:00 +0000 UTC
|
||||
...
|
||||
`
|
||||
)
|
||||
@ -143,8 +179,9 @@ func TestRunE(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.testCaseName, func(t *testing.T) {
|
||||
objects := []runtime.Object{
|
||||
getObject(t, "testdata/tls-secret.yaml"),
|
||||
getObject(t, "testdata/kubeconfig.yaml"),
|
||||
getSecretObject(t, "testdata/tls-secret.yaml"),
|
||||
getSecretObject(t, "testdata/kubeconfig.yaml"),
|
||||
getNodeObject(t, "testdata/node.yaml"),
|
||||
}
|
||||
ra := fake.WithTypedObjects(objects...)
|
||||
|
||||
@ -176,7 +213,7 @@ func TestRunE(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func getObject(t *testing.T, fileName string) *v1.Secret {
|
||||
func getSecretObject(t *testing.T, fileName string) *v1.Secret {
|
||||
t.Helper()
|
||||
|
||||
object := readObjectFromFile(t, fileName)
|
||||
@ -186,6 +223,16 @@ func getObject(t *testing.T, fileName string) *v1.Secret {
|
||||
return secret
|
||||
}
|
||||
|
||||
func getNodeObject(t *testing.T, fileName string) *v1.Node {
|
||||
t.Helper()
|
||||
|
||||
object := readObjectFromFile(t, fileName)
|
||||
node, ok := object.(*v1.Node)
|
||||
require.True(t, ok)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
func readObjectFromFile(t *testing.T, fileName string) runtime.Object {
|
||||
t.Helper()
|
||||
|
||||
|
6
pkg/cluster/checkexpiration/testdata/node.yaml
vendored
Normal file
6
pkg/cluster/checkexpiration/testdata/node.yaml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
apiVersion: v1
|
||||
kind: Node
|
||||
metadata:
|
||||
annotations:
|
||||
cert-expiration: "{ admin.conf: Aug 06, 2021 12:36 UTC },{ apiserver: Aug 06, 2021 12:36 UTC },{ apiserver-etcd-client: Aug 06, 2021 12:36 UTC },{ apiserver-kubelet-client: Aug 06, 2021 12:36 UTC },{ controller-manager.conf: Aug 06, 2021 12:36 UTC },{ etcd-healthcheck-client: Aug 06, 2021 12:36 UTC },{ etcd-peer: Aug 06, 2021 12:36 UTC },{ etcd-server: Aug 06, 2021 12:36 UTC },{ front-proxy-client: Aug 06, 2021 12:36 UTC },{ scheduler.conf: Aug 06, 2021 12:36 UTC },{ ca: Aug 04, 2021 12:36 UTC },{ etcd-ca: Aug 04, 2021 12:36 UTC },{ front-proxy-ca: Aug 04, 2021 12:36 UTC }"
|
||||
name: test-node
|
Loading…
Reference in New Issue
Block a user