From 0826fca2532f115dd83fdf3c88048366a3776d22 Mon Sep 17 00:00:00 2001 From: lonelyCZ <531187475@qq.com> Date: Thu, 27 Oct 2022 12:56:21 +0800 Subject: [PATCH 1/2] Implement the certificate rotation function for karmada-agent Signed-off-by: lonelyCZ <531187475@qq.com> --- cmd/agent/app/agent.go | 55 ++- cmd/agent/app/options/options.go | 11 + .../certificate/cert_rotation_controller.go | 315 ++++++++++++++++++ pkg/controllers/context/context.go | 9 + 4 files changed, 376 insertions(+), 14 deletions(-) create mode 100644 pkg/controllers/certificate/cert_rotation_controller.go diff --git a/cmd/agent/app/agent.go b/cmd/agent/app/agent.go index 6bb4a9f7f..b6b7a0168 100644 --- a/cmd/agent/app/agent.go +++ b/cmd/agent/app/agent.go @@ -24,6 +24,7 @@ import ( "github.com/karmada-io/karmada/cmd/agent/app/options" clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1" + "github.com/karmada-io/karmada/pkg/controllers/certificate" controllerscontext "github.com/karmada-io/karmada/pkg/controllers/context" "github.com/karmada-io/karmada/pkg/controllers/execution" "github.com/karmada-io/karmada/pkg/controllers/mcs" @@ -98,13 +99,16 @@ cluster and manifests to the Karmada control plane.`, var controllers = make(controllerscontext.Initializers) -var controllersDisabledByDefault = sets.NewString() +var controllersDisabledByDefault = sets.NewString( + "certRotation", +) func init() { controllers["clusterStatus"] = startClusterStatusController controllers["execution"] = startExecutionController controllers["workStatus"] = startWorkStatusController controllers["serviceExport"] = startServiceExportController + controllers["certRotation"] = startCertRotationController } func run(ctx context.Context, karmadaConfig karmadactl.KarmadaConfig, opts *options.Options) error { @@ -231,19 +235,22 @@ func setupControllers(mgr controllerruntime.Manager, opts *options.Options, stop Mgr: mgr, ObjectWatcher: objectWatcher, Opts: controllerscontext.Options{ - Controllers: opts.Controllers, - ClusterName: opts.ClusterName, - ClusterStatusUpdateFrequency: opts.ClusterStatusUpdateFrequency, - ClusterLeaseDuration: opts.ClusterLeaseDuration, - ClusterLeaseRenewIntervalFraction: opts.ClusterLeaseRenewIntervalFraction, - ClusterSuccessThreshold: opts.ClusterSuccessThreshold, - ClusterFailureThreshold: opts.ClusterFailureThreshold, - ClusterCacheSyncTimeout: opts.ClusterCacheSyncTimeout, - ClusterAPIQPS: opts.ClusterAPIQPS, - ClusterAPIBurst: opts.ClusterAPIBurst, - ConcurrentWorkSyncs: opts.ConcurrentWorkSyncs, - RateLimiterOptions: opts.RateLimiterOpts, - EnableClusterResourceModeling: opts.EnableClusterResourceModeling, + Controllers: opts.Controllers, + ClusterName: opts.ClusterName, + ClusterStatusUpdateFrequency: opts.ClusterStatusUpdateFrequency, + ClusterLeaseDuration: opts.ClusterLeaseDuration, + ClusterLeaseRenewIntervalFraction: opts.ClusterLeaseRenewIntervalFraction, + ClusterSuccessThreshold: opts.ClusterSuccessThreshold, + ClusterFailureThreshold: opts.ClusterFailureThreshold, + ClusterCacheSyncTimeout: opts.ClusterCacheSyncTimeout, + ClusterAPIQPS: opts.ClusterAPIQPS, + ClusterAPIBurst: opts.ClusterAPIBurst, + ConcurrentWorkSyncs: opts.ConcurrentWorkSyncs, + RateLimiterOptions: opts.RateLimiterOpts, + EnableClusterResourceModeling: opts.EnableClusterResourceModeling, + CertRotationCheckingInterval: opts.CertRotationCheckingInterval, + CertRotationRemainingTimeThreshold: opts.CertRotationRemainingTimeThreshold, + KarmadaKubeconfigNamespace: opts.KarmadaKubeconfigNamespace, }, StopChan: stopChan, ResourceInterpreter: resourceInterpreter, @@ -346,6 +353,26 @@ func startServiceExportController(ctx controllerscontext.Context) (bool, error) return true, nil } +func startCertRotationController(ctx controllerscontext.Context) (bool, error) { + certRotationController := &certificate.CertRotationController{ + Client: ctx.Mgr.GetClient(), + KubeClient: kubeclientset.NewForConfigOrDie(ctx.Mgr.GetConfig()), + EventRecorder: ctx.Mgr.GetEventRecorderFor(certificate.CertRotationControllerName), + RESTMapper: ctx.Mgr.GetRESTMapper(), + ClusterClientSetFunc: util.NewClusterClientSetForAgent, + PredicateFunc: helper.NewClusterPredicateOnAgent(ctx.Opts.ClusterName), + InformerManager: genericmanager.GetInstance(), + RatelimiterOptions: ctx.Opts.RateLimiterOptions, + CertRotationCheckingInterval: ctx.Opts.CertRotationCheckingInterval, + CertRotationRemainingTimeThreshold: ctx.Opts.CertRotationRemainingTimeThreshold, + KarmadaKubeconfigNamespace: ctx.Opts.KarmadaKubeconfigNamespace, + } + if err := certRotationController.SetupWithManager(ctx.Mgr); err != nil { + return false, err + } + return true, nil +} + func generateClusterInControllerPlane(opts util.ClusterRegisterOption) (*clusterv1alpha1.Cluster, error) { clusterObj := &clusterv1alpha1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: opts.ClusterName}} mutateFunc := func(cluster *clusterv1alpha1.Cluster) { diff --git a/cmd/agent/app/options/options.go b/cmd/agent/app/options/options.go index b88c20e39..64b03d7da 100644 --- a/cmd/agent/app/options/options.go +++ b/cmd/agent/app/options/options.go @@ -114,6 +114,14 @@ type Options struct { // in scenario of dynamic replica assignment based on cluster free resources. // Disable if it does not fit your cases for better performance. EnableClusterResourceModeling bool + + // CertRotationCheckingInterval defines the interval of checking if the certificate need to be rotated. + CertRotationCheckingInterval time.Duration + // CertRotationRemainingTimeThreshold defines the threshold of remaining time of the valid certificate. + // If the ratio of remaining time to total time is less than or equal to this threshold, the certificate rotation starts. + CertRotationRemainingTimeThreshold float64 + // KarmadaKubeconfigNamespace is the namespace of the secret containing karmada-agent certificate. + KarmadaKubeconfigNamespace string } // NewOptions builds an default scheduler options. @@ -184,6 +192,9 @@ func (o *Options) AddFlags(fs *pflag.FlagSet, allControllers []string) { fs.BoolVar(&o.EnableClusterResourceModeling, "enable-cluster-resource-modeling", true, "Enable means controller would build resource modeling for each cluster by syncing Nodes and Pods resources.\n"+ "The resource modeling might be used by the scheduler to make scheduling decisions in scenario of dynamic replica assignment based on cluster free resources.\n"+ "Disable if it does not fit your cases for better performance.") + fs.DurationVar(&o.CertRotationCheckingInterval, "cert-rotation-checking-interval", 5*time.Minute, "The interval of checking if the certificate need to be rotated. This is only applicable if cert rotation is enabled") + fs.Float64Var(&o.CertRotationRemainingTimeThreshold, "cert-rotation-remaining-time-threshold", 0.2, "The threshold of remaining time of the valid certificate. This is only applicable if cert rotation is enabled.") + fs.StringVar(&o.KarmadaKubeconfigNamespace, "karmada-kubeconfig-namespace", "karmada-system", "Namespace of the secret containing karmada-agent certificate. This is only applicable if cert rotation is enabled.") o.RateLimiterOpts.AddFlags(fs) features.FeatureGate.AddFlag(fs) o.ProfileOpts.AddFlags(fs) diff --git a/pkg/controllers/certificate/cert_rotation_controller.go b/pkg/controllers/certificate/cert_rotation_controller.go new file mode 100644 index 000000000..e93c5f80a --- /dev/null +++ b/pkg/controllers/certificate/cert_rotation_controller.go @@ -0,0 +1,315 @@ +package certificate + +import ( + "context" + "crypto/x509" + "errors" + "fmt" + "time" + + certificatesv1 "k8s.io/api/certificates/v1" + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + k8srand "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/apimachinery/pkg/util/wait" + clientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" + "k8s.io/client-go/tools/record" + certutil "k8s.io/client-go/util/cert" + "k8s.io/client-go/util/keyutil" + "k8s.io/klog/v2" + controllerruntime "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" + "github.com/karmada-io/karmada/pkg/sharedcli/ratelimiterflag" + "github.com/karmada-io/karmada/pkg/util" + "github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager" +) + +const ( + // CertRotationControllerName is the controller name that will be used when reporting events. + CertRotationControllerName = "cert-rotation-controller" + + // SignerName defines the signer name for csr, 'kubernetes.io/kube-apiserver-client-kubelet' can sign the csr automatically + SignerName = "kubernetes.io/kube-apiserver-client-kubelet" + + // KarmadaKubeconfigName is the name of the secret containing karmada-agent certificate. + KarmadaKubeconfigName = "karmada-kubeconfig" +) + +// CertRotationController is to rotate certificates. +type CertRotationController struct { + client.Client // used to operate cluster resources in the control plane. + KubeClient clientset.Interface + EventRecorder record.EventRecorder + RESTMapper meta.RESTMapper + ClusterClient *util.ClusterClient + ClusterClientSetFunc func(string, client.Client, *util.ClientOption) (*util.ClusterClient, error) + // ClusterClientOption holds the attributes that should be injected to a Kubernetes client. + ClusterClientOption *util.ClientOption + PredicateFunc predicate.Predicate + InformerManager genericmanager.MultiClusterInformerManager + RatelimiterOptions ratelimiterflag.Options + + // CertRotationCheckingInterval defines the interval of checking if the certificate need to be rotated. + CertRotationCheckingInterval time.Duration + // KarmadaKubeconfigNamespace is the namespace of the secret containing karmada-agent certificate. + KarmadaKubeconfigNamespace string + // CertRotationRemainingTimeThreshold defines the threshold of remaining time of the valid certificate. + // If the ratio of remaining time to total time is less than or equal to this threshold, the certificate rotation starts. + CertRotationRemainingTimeThreshold float64 +} + +// Reconcile performs a full reconciliation for the object referred to by the Request. +// The Controller will requeue the Request to be processed again if an error is non-nil or +// Result.Requeue is true, otherwise upon completion it will remove the work from the queue. +func (c *CertRotationController) Reconcile(ctx context.Context, req controllerruntime.Request) (controllerruntime.Result, error) { + klog.V(4).Infof("Rotating the certificate of karmada-agent for the member cluster: %s", req.NamespacedName.Name) + + var err error + + cluster := &clusterv1alpha1.Cluster{} + if err := c.Client.Get(context.TODO(), req.NamespacedName, cluster); err != nil { + // The resource may no longer exist, in which case we stop processing. + if apierrors.IsNotFound(err) { + return controllerruntime.Result{}, nil + } + + return controllerruntime.Result{Requeue: true}, err + } + + if !cluster.DeletionTimestamp.IsZero() { + return controllerruntime.Result{}, nil + } + + // create a ClusterClient for the given member cluster + c.ClusterClient, err = c.ClusterClientSetFunc(cluster.Name, c.Client, c.ClusterClientOption) + if err != nil { + klog.Errorf("Failed to create a ClusterClient for the given member cluster: %s, err is: %v", cluster.Name, err) + return controllerruntime.Result{Requeue: true}, err + } + + secret, err := c.ClusterClient.KubeClient.CoreV1().Secrets(c.KarmadaKubeconfigNamespace).Get(context.TODO(), KarmadaKubeconfigName, metav1.GetOptions{}) + if err != nil { + klog.Errorf("failed to get karmada kubeconfig secret: %v", err) + return controllerruntime.Result{Requeue: true}, err + } + + if err = c.syncCertRotation(secret); err != nil { + klog.Errorf("Failed to rotate the certificate of karmada-agent for the given member cluster: %s, err is: %v", cluster.Name, err) + return controllerruntime.Result{Requeue: true}, err + } + + return controllerruntime.Result{RequeueAfter: c.CertRotationCheckingInterval}, nil +} + +// SetupWithManager creates a controller and register to controller manager. +func (c *CertRotationController) SetupWithManager(mgr controllerruntime.Manager) error { + return controllerruntime.NewControllerManagedBy(mgr). + For(&clusterv1alpha1.Cluster{}, builder.WithPredicates(c.PredicateFunc)). + WithEventFilter(predicate.GenerationChangedPredicate{}). + WithOptions(controller.Options{ + RateLimiter: ratelimiterflag.DefaultControllerRateLimiter(c.RatelimiterOptions), + }). + Complete(c) +} + +func (c *CertRotationController) syncCertRotation(secret *corev1.Secret) error { + karmadaKubeconfig, err := getKubeconfigFromSecret(secret) + if err != nil { + return err + } + + clusterName := karmadaKubeconfig.Contexts[karmadaKubeconfig.CurrentContext].AuthInfo + if clusterName == "" { + return fmt.Errorf("failed to get cluster name, the current context is %s", karmadaKubeconfig.CurrentContext) + } + + oldCertData := karmadaKubeconfig.AuthInfos[clusterName].ClientCertificateData + + shouldRotate, err := c.shouldRotateCert(oldCertData) + if err != nil { + return err + } + if !shouldRotate { + return nil + } + + oldCert, err := certutil.ParseCertsPEM(oldCertData) + if err != nil { + return fmt.Errorf("unable to parse old certificate: %v", err) + } + + // create a new private key + keyData, err := keyutil.MakeEllipticPrivateKeyPEM() + if err != nil { + return err + } + + privateKey, err := keyutil.ParsePrivateKeyPEM(keyData) + if err != nil { + return fmt.Errorf("invalid private key for certificate request: %v", err) + } + + csr, err := c.createCSRInControlPlane(clusterName, privateKey, oldCert) + if err != nil { + return fmt.Errorf("failed to create csr in control plane, err is: %v", err) + } + + var newCertData []byte + klog.V(1).Infof("Waiting for the client certificate to be issued") + err = wait.Poll(1*time.Second, 5*time.Minute, func() (done bool, err error) { + csr, err := c.KubeClient.CertificatesV1().CertificateSigningRequests().Get(context.TODO(), csr, metav1.GetOptions{}) + if err != nil { + return false, fmt.Errorf("failed to get the cluster csr %s. err: %v", clusterName, err) + } + + if csr.Status.Certificate != nil { + klog.V(1).Infof("Signing certificate successfully") + newCertData = csr.Status.Certificate + return true, nil + } + + klog.V(1).Infof("Waiting for the client certificate to be issued") + return false, nil + }) + if err != nil { + return err + } + + karmadaKubeconfig.AuthInfos[clusterName].ClientCertificateData = newCertData + karmadaKubeconfig.AuthInfos[clusterName].ClientKeyData = keyData + + karmadaKubeconfigBytes, err := clientcmd.Write(*karmadaKubeconfig) + if err != nil { + return fmt.Errorf("failed to serialize karmada-agent kubeConfig. %v", err) + } + + secret.Data["karmada-kubeconfig"] = karmadaKubeconfigBytes + // Update the karmada-kubeconfig secret in the member cluster. + if _, err := c.ClusterClient.KubeClient.CoreV1().Secrets(secret.ObjectMeta.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}); err != nil { + return fmt.Errorf("Unable to update secret, err: %w", err) + } + + newCert, err := certutil.ParseCertsPEM(newCertData) + if err != nil { + klog.Errorf("Unable to parse new certificate: %v", err) + return err + } + + klog.V(4).Infof("The certificate has been rotated successfully, new expiration time is %v", newCert[0].NotAfter) + + return nil +} + +func (c *CertRotationController) createCSRInControlPlane(clusterName string, privateKey interface{}, oldCert []*x509.Certificate) (string, error) { + csrData, err := certutil.MakeCSR(privateKey, &oldCert[0].Subject, nil, nil) + if err != nil { + return "", fmt.Errorf("unable to generate certificate request: %v", err) + } + + csrName := clusterName + "-" + k8srand.String(5) + + // Expiration of the new certificate is same with the old certificate + certExpirationSeconds := int32((oldCert[0].NotAfter.Sub(oldCert[0].NotBefore)).Seconds()) + + certificateSigningRequest := &certificatesv1.CertificateSigningRequest{ + ObjectMeta: metav1.ObjectMeta{ + Name: csrName, + }, + Spec: certificatesv1.CertificateSigningRequestSpec{ + Request: csrData, + SignerName: SignerName, + ExpirationSeconds: &certExpirationSeconds, + Usages: []certificatesv1.KeyUsage{ + certificatesv1.UsageDigitalSignature, + certificatesv1.UsageKeyEncipherment, + certificatesv1.UsageClientAuth, + }, + }, + } + + _, err = c.KubeClient.CertificatesV1().CertificateSigningRequests().Create(context.TODO(), certificateSigningRequest, metav1.CreateOptions{}) + if err != nil { + return "", fmt.Errorf("unable to create certificate request in control plane: %v", err) + } + + return csrName, nil +} + +func getKubeconfigFromSecret(secret *corev1.Secret) (*clientcmdapi.Config, error) { + if secret.Data == nil { + return nil, fmt.Errorf("no client certificate found in secret %q", secret.Namespace+"/"+secret.Name) + } + + karmadaKubeconfigData, ok := secret.Data[KarmadaKubeconfigName] + if !ok { + return nil, fmt.Errorf("no karmada kubeconfig found in secret %q", secret.Namespace+"/"+secret.Name) + } + + karmadaKubeconfig, err := clientcmd.Load(karmadaKubeconfigData) + if err != nil { + return nil, err + } + + return karmadaKubeconfig, nil +} + +func (c *CertRotationController) shouldRotateCert(certData []byte) (bool, error) { + notBefore, notAfter, err := getCertValidityPeriod(certData) + if err != nil { + return false, err + } + + total := notAfter.Sub(*notBefore) + remaining := time.Until(*notAfter) + klog.V(4).Infof("The certificate of karmada-agent: time total=%v, remaining=%v, remaining/total=%v", total, remaining, remaining.Seconds()/total.Seconds()) + + if remaining.Seconds()/total.Seconds() > c.CertRotationRemainingTimeThreshold { + // Do nothing if the certificate of karmada-agent is valid and has more than a valid threshold of its life remaining + klog.V(4).Infof("The certificate of karmada-agent is valid and has more than %.2f%% of its life remaining", c.CertRotationRemainingTimeThreshold*100) + return false, nil + } + + klog.V(4).Infof("The certificate of karmada-agent has less than or equal %.2f%% of its life remaining and need to be rotated", c.CertRotationRemainingTimeThreshold*100) + return true, nil +} + +// getCertValidityPeriod returns the validity period of the certificate +func getCertValidityPeriod(certData []byte) (*time.Time, *time.Time, error) { + certs, err := certutil.ParseCertsPEM(certData) + if err != nil { + return nil, nil, fmt.Errorf("unable to parse TLS certificates: %v", err) + } + + if len(certs) == 0 { + return nil, nil, errors.New("no cert found in certificate") + } + + // find out the validity period for all certs in the certificate chain + var notBefore, notAfter *time.Time + for index, cert := range certs { + if index == 0 { + notBefore = &cert.NotBefore + notAfter = &cert.NotAfter + continue + } + + if notBefore.Before(cert.NotBefore) { + notBefore = &cert.NotBefore + } + + if notAfter.After(cert.NotAfter) { + notAfter = &cert.NotAfter + } + } + + return notBefore, notAfter, nil +} diff --git a/pkg/controllers/context/context.go b/pkg/controllers/context/context.go index 52448e0c1..605fc8867 100644 --- a/pkg/controllers/context/context.go +++ b/pkg/controllers/context/context.go @@ -1,6 +1,8 @@ package context import ( + "time" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/dynamic" @@ -69,6 +71,13 @@ type Options struct { // in scenario of dynamic replica assignment based on cluster free resources. // Disable if it does not fit your cases for better performance. EnableClusterResourceModeling bool + // CertRotationCheckingInterval defines the interval of checking if the certificate need to be rotated. + CertRotationCheckingInterval time.Duration + // CertRotationRemainingTimeThreshold defines the threshold of remaining time of the valid certificate. + // If the ratio of remaining time to total time is less than or equal to this threshold, the certificate rotation starts. + CertRotationRemainingTimeThreshold float64 + // KarmadaKubeconfigNamespace is the namespace of the secret containing karmada-agent certificate. + KarmadaKubeconfigNamespace string } // Context defines the context object for controller. From 241f722a59912edaf811da691b8072211f1406e0 Mon Sep 17 00:00:00 2001 From: lonelyCZ <531187475@qq.com> Date: Thu, 27 Oct 2022 12:56:41 +0800 Subject: [PATCH 2/2] Introduce --enable-cert-rotation option to karmadactl register command Signed-off-by: lonelyCZ <531187475@qq.com> --- pkg/karmadactl/register.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/pkg/karmadactl/register.go b/pkg/karmadactl/register.go index 8cf3a9ccc..3dc449737 100644 --- a/pkg/karmadactl/register.go +++ b/pkg/karmadactl/register.go @@ -167,7 +167,8 @@ func NewCmdRegister(parentCommand string) *cobra.Command { flags.StringVar(&opts.ClusterNamespace, "cluster-namespace", options.DefaultKarmadaClusterNamespace, "Namespace in the control plane where member cluster secrets are stored.") flags.StringVar(&opts.ClusterProvider, "cluster-provider", "", "Provider of the joining cluster. The Karmada scheduler can use this information to spread workloads across providers for higher availability.") flags.StringVar(&opts.ClusterRegion, "cluster-region", "", "The region of the joining cluster. The Karmada scheduler can use this information to spread workloads across regions for higher availability.") - flags.StringVar(&opts.CACertPath, "ca-cert-path", CACertPath, "The path to the SSL certificate authority used to secure comunications between member cluster and karmada-control-plane.") + flags.BoolVar(&opts.EnableCertRotation, "enable-cert-rotation", false, "Enable means controller would rotate certificate for karmada-agent when the certificate is about to expire.") + flags.StringVar(&opts.CACertPath, "ca-cert-path", CACertPath, "The path to the SSL certificate authority used to secure communications between member cluster and karmada-control-plane.") flags.StringVar(&opts.BootstrapToken.Token, "token", "", "For token-based discovery, the token used to validate cluster information fetched from the API server.") flags.StringSliceVar(&opts.BootstrapToken.CACertHashes, "discovery-token-ca-cert-hash", []string{}, "For token-based discovery, validate that the root CA public key matches this hash (format: \":\").") flags.BoolVar(&opts.BootstrapToken.UnsafeSkipCAVerification, "discovery-token-unsafe-skip-ca-verification", false, "For token-based discovery, allow joining without --discovery-token-ca-cert-hash pinning.") @@ -204,6 +205,9 @@ type CommandRegisterOption struct { // ClusterRegion represents the region of the cluster locate in. ClusterRegion string + // EnableCertRotation indicates if enable certificate rotation for karmada-agent. + EnableCertRotation bool + // CACertPath is the path to the SSL certificate authority used to // secure comunications between member cluster and karmada-control-plane. // Defaults to "/etc/karmada/pki/ca.crt". @@ -646,6 +650,13 @@ func (o *CommandRegisterOption) makeKarmadaAgentDeployment() *appsv1.Deployment }, } + var controllers []string + if o.EnableCertRotation { + controllers = []string{"*", "certRotation"} + } else { + controllers = []string{"*"} + } + podSpec := corev1.PodSpec{ ServiceAccountName: KarmadaAgentServiceAccountName, Containers: []corev1.Container{ @@ -659,6 +670,7 @@ func (o *CommandRegisterOption) makeKarmadaAgentDeployment() *appsv1.Deployment fmt.Sprintf("--cluster-api-endpoint=%s", o.memberClusterEndpoint), fmt.Sprintf("--cluster-provider=%s", o.ClusterProvider), fmt.Sprintf("--cluster-region=%s", o.ClusterRegion), + fmt.Sprintf("--controllers=%s", strings.Join(controllers, ",")), "--cluster-status-update-frequency=10s", "--bind-address=0.0.0.0", "--secure-port=10357",