Merge pull request #2596 from lonelyCZ/pr-agent-cert-rotation
Implement auto certificate rotation function for karmada-agent
This commit is contained in:
commit
5d42e82b7b
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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: \"<type>:<value>\").")
|
||||
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",
|
||||
|
|
Loading…
Reference in New Issue