Sync the expiration time of kubeconfig cert file of the cluster

This commit is contained in:
Xinzhao Xu
2021-12-30 16:12:47 +08:00
parent f0210193c1
commit 7bbefdd30c
5 changed files with 85 additions and 22 deletions

View File

@@ -19,7 +19,9 @@ package cluster
import (
"bytes"
"context"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"net/http"
"reflect"
@@ -52,6 +54,7 @@ import (
clusterclient "kubesphere.io/kubesphere/pkg/client/clientset/versioned/typed/cluster/v1alpha1"
clusterinformer "kubesphere.io/kubesphere/pkg/client/informers/externalversions/cluster/v1alpha1"
clusterlister "kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/version"
)
@@ -614,6 +617,11 @@ func (c *clusterController) syncCluster(key string) error {
}
c.updateClusterCondition(cluster, readyConditon)
if err = c.updateKubeConfigExpirationDateCondition(cluster); err != nil {
klog.Errorf("sync KubeConfig expiration date for cluster %s failed: %v", cluster.Name, err)
return err
}
if !reflect.DeepEqual(oldCluster, cluster) {
_, err = c.clusterClient.Update(context.TODO(), cluster, metav1.UpdateOptions{})
if err != nil {
@@ -823,3 +831,52 @@ func (c *clusterController) unJoinFederation(clusterConfig *rest.Config, unjoini
}
}
}
func parseKubeConfigExpirationDate(kubeconfig []byte) (time.Time, error) {
config, err := k8sutil.LoadKubeConfigFromBytes(kubeconfig)
if err != nil {
return time.Time{}, err
}
if config.CertData == nil {
return time.Time{}, fmt.Errorf("empty CertData")
}
block, _ := pem.Decode(config.CertData)
if block == nil {
return time.Time{}, fmt.Errorf("pem.Decode failed, got empty block data")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return time.Time{}, err
}
return cert.NotAfter, nil
}
func (c *clusterController) updateKubeConfigExpirationDateCondition(cluster *clusterv1alpha1.Cluster) error {
if _, ok := cluster.Labels[clusterv1alpha1.HostCluster]; ok {
return nil
}
// we don't need to check member clusters which using proxy mode, their certs are managed and will be renewed by tower.
if cluster.Spec.Connection.Type == clusterv1alpha1.ConnectionTypeProxy {
return nil
}
klog.V(5).Infof("sync KubeConfig expiration date for cluster %s", cluster.Name)
notAfter, err := parseKubeConfigExpirationDate(cluster.Spec.Connection.KubeConfig)
if err != nil {
return fmt.Errorf("parseKubeConfigExpirationDate for cluster %s failed: %v", cluster.Name, err)
}
expiresInSevenDays := v1.ConditionFalse
if time.Now().AddDate(0, 0, 7).Sub(notAfter) > 0 {
expiresInSevenDays = v1.ConditionTrue
}
c.updateClusterCondition(cluster, clusterv1alpha1.ClusterCondition{
Type: clusterv1alpha1.ClusterKubeConfigCertExpiresInSevenDays,
Status: expiresInSevenDays,
LastUpdateTime: metav1.Now(),
LastTransitionTime: metav1.Now(),
Reason: string(clusterv1alpha1.ClusterKubeConfigCertExpiresInSevenDays),
Message: notAfter.String(),
})
return nil
}

View File

@@ -41,8 +41,6 @@ import (
"k8s.io/client-go/kubernetes"
v1 "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"kubesphere.io/api/cluster/v1alpha1"
"kubesphere.io/kubesphere/pkg/api"
@@ -51,6 +49,7 @@ import (
kubesphere "kubesphere.io/kubesphere/pkg/client/clientset/versioned"
"kubesphere.io/kubesphere/pkg/client/informers/externalversions"
clusterlister "kubesphere.io/kubesphere/pkg/client/listers/cluster/v1alpha1"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/version"
)
@@ -286,7 +285,7 @@ func (h *handler) updateKubeConfig(request *restful.Request, response *restful.R
api.HandleBadRequest(response, request, fmt.Errorf("cluster kubeconfig MUST NOT be empty"))
return
}
config, err := loadKubeConfigFromBytes(req.KubeConfig)
config, err := k8sutil.LoadKubeConfigFromBytes(req.KubeConfig)
if err != nil {
api.HandleBadRequest(response, request, err)
return
@@ -363,7 +362,7 @@ func (h *handler) validateCluster(request *restful.Request, response *restful.Re
// validateKubeConfig takes base64 encoded kubeconfig and check its validity
func (h *handler) validateKubeConfig(kubeconfig []byte) error {
config, err := loadKubeConfigFromBytes(kubeconfig)
config, err := k8sutil.LoadKubeConfigFromBytes(kubeconfig)
if err != nil {
return err
}
@@ -393,20 +392,6 @@ func (h *handler) validateKubeConfig(kubeconfig []byte) error {
return err
}
func loadKubeConfigFromBytes(kubeconfig []byte) (*rest.Config, error) {
clientConfig, err := clientcmd.NewClientConfigFromBytes(kubeconfig)
if err != nil {
return nil, err
}
config, err := clientConfig.ClientConfig()
if err != nil {
return nil, err
}
return config, nil
}
// validateKubeSphereAPIServer uses version api to check the accessibility
// If kubesphere apiserver endpoint is not provided, use kube-apiserver proxy instead
func validateKubeSphereAPIServer(ksEndpoint string, kubeconfig []byte) (*version.Info, error) {
@@ -426,7 +411,7 @@ func validateKubeSphereAPIServer(ksEndpoint string, kubeconfig []byte) (*version
return nil, err
}
} else {
config, err := loadKubeConfigFromBytes(kubeconfig)
config, err := k8sutil.LoadKubeConfigFromBytes(kubeconfig)
if err != nil {
return nil, err
}
@@ -485,7 +470,7 @@ func (h *handler) validateMemberClusterConfiguration(memberKubeconfig []byte) er
// getMemberClusterConfig returns KubeSphere running config by the given member cluster kubeconfig
func (h *handler) getMemberClusterConfig(kubeconfig []byte) (*config.Config, error) {
config, err := loadKubeConfigFromBytes(kubeconfig)
config, err := k8sutil.LoadKubeConfigFromBytes(kubeconfig)
if err != nil {
return nil, err
}

View File

@@ -41,6 +41,7 @@ import (
"kubesphere.io/kubesphere/pkg/client/clientset/versioned/fake"
"kubesphere.io/kubesphere/pkg/informers"
"kubesphere.io/kubesphere/pkg/utils/k8sutil"
"kubesphere.io/kubesphere/pkg/version"
)
@@ -339,7 +340,7 @@ func TestValidateKubeConfig(t *testing.T) {
"",
agentImage)
config, err := loadKubeConfigFromBytes([]byte(base64EncodedKubeConfig))
config, err := k8sutil.LoadKubeConfigFromBytes([]byte(base64EncodedKubeConfig))
if err != nil {
t.Fatal(err)
}
@@ -415,7 +416,7 @@ func TestValidateMemberClusterConfiguration(t *testing.T) {
"",
agentImage)
config, err := loadKubeConfigFromBytes([]byte(base64EncodedKubeConfig))
config, err := k8sutil.LoadKubeConfigFromBytes([]byte(base64EncodedKubeConfig))
if err != nil {
t.Fatal(err)
}

View File

@@ -18,6 +18,8 @@ package k8sutil
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
tenantv1alpha1 "kubesphere.io/api/tenant/v1alpha1"
tenantv1alpha2 "kubesphere.io/api/tenant/v1alpha2"
@@ -55,3 +57,18 @@ func GetWorkspaceOwnerName(ownerReferences []metav1.OwnerReference) string {
}
return ""
}
// LoadKubeConfigFromBytes parses the kubeconfig yaml data to the rest.Config struct.
func LoadKubeConfigFromBytes(kubeconfig []byte) (*rest.Config, error) {
clientConfig, err := clientcmd.NewClientConfigFromBytes(kubeconfig)
if err != nil {
return nil, err
}
config, err := clientConfig.ClientConfig()
if err != nil {
return nil, err
}
return config, nil
}

View File

@@ -121,6 +121,9 @@ const (
// Openpitrix runtime is created
ClusterOpenPitrixRuntimeReady ClusterConditionType = "OpenPitrixRuntimeReady"
// ClusterKubeConfigCertExpiresInSevenDays indicates that the cluster certificate is about to expire.
ClusterKubeConfigCertExpiresInSevenDays ClusterConditionType = "KubeConfigCertExpiresInSevenDays"
)
type ClusterCondition struct {