diff --git a/controllers/remote/cluster_cache_tracker.go b/controllers/remote/cluster_cache_tracker.go index 4add86f6f9f5..b9ac6973f9a4 100644 --- a/controllers/remote/cluster_cache_tracker.go +++ b/controllers/remote/cluster_cache_tracker.go @@ -18,6 +18,7 @@ package remote import ( "context" + "crypto/rsa" "fmt" "os" "sync" @@ -47,6 +48,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/source" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/cluster-api/util/certs" "sigs.k8s.io/cluster-api/util/conditions" ) @@ -165,12 +167,23 @@ func (t *ClusterCacheTracker) GetRESTConfig(ctc context.Context, cluster client. return accessor.config, nil } +// GetEtcdClientCertificateKey returns a cached certificate key to be used for generating certificates for accessing etcd in the given cluster. +func (t *ClusterCacheTracker) GetEtcdClientCertificateKey(ctx context.Context, cluster client.ObjectKey) (*rsa.PrivateKey, error) { + accessor, err := t.getClusterAccessor(ctx, cluster, t.indexes...) + if err != nil { + return nil, err + } + + return accessor.etcdClientCertificateKey, nil +} + // clusterAccessor represents the combination of a delegating client, cache, and watches for a remote cluster. type clusterAccessor struct { - cache *stoppableCache - client client.Client - watches sets.String - config *rest.Config + cache *stoppableCache + client client.Client + watches sets.String + config *rest.Config + etcdClientCertificateKey *rsa.PrivateKey } // clusterAccessorExists returns true if a clusterAccessor exists for cluster. @@ -334,11 +347,20 @@ func (t *ClusterCacheTracker) newClusterAccessor(ctx context.Context, cluster cl return nil, err } + // Generating a new private key to be used for generating temporary certificates to connect to + // etcd on the target cluster. + // NOTE: Generating a private key is an expensive operation, so we store it in the cluster accessor. + etcdKey, err := certs.NewPrivateKey() + if err != nil { + return nil, errors.Wrapf(err, "error creating etcd client key for remote cluster %q", cluster.String()) + } + return &clusterAccessor{ - cache: cache, - config: config, - client: delegatingClient, - watches: sets.NewString(), + cache: cache, + config: config, + client: delegatingClient, + watches: sets.NewString(), + etcdClientCertificateKey: etcdKey, }, nil } diff --git a/controlplane/kubeadm/internal/cluster.go b/controlplane/kubeadm/internal/cluster.go index 173cda0141a9..95dfab9d5913 100644 --- a/controlplane/kubeadm/internal/cluster.go +++ b/controlplane/kubeadm/internal/cluster.go @@ -140,7 +140,12 @@ func (m *Management) GetWorkloadCluster(ctx context.Context, clusterKey client.O // TODO: consider if we can detect if we are using external etcd in a more explicit way (e.g. looking at the config instead of deriving from the existing certificates) var clientCert tls.Certificate if keyData != nil { - clientCert, err = generateClientCert(crtData, keyData) + clientKey, err := m.Tracker.GetEtcdClientCertificateKey(ctx, clusterKey) + if err != nil { + return nil, err + } + + clientCert, err = generateClientCert(crtData, keyData, clientKey) if err != nil { return nil, err } diff --git a/controlplane/kubeadm/internal/workload_cluster.go b/controlplane/kubeadm/internal/workload_cluster.go index 99f3fc1b3524..6b8ba25fc2cb 100644 --- a/controlplane/kubeadm/internal/workload_cluster.go +++ b/controlplane/kubeadm/internal/workload_cluster.go @@ -489,11 +489,7 @@ func calculateAPIServerPort(config *bootstrapv1.KubeadmConfig) int32 { return 6443 } -func generateClientCert(caCertEncoded, caKeyEncoded []byte) (tls.Certificate, error) { - privKey, err := certs.NewPrivateKey() - if err != nil { - return tls.Certificate{}, err - } +func generateClientCert(caCertEncoded, caKeyEncoded []byte, clientKey *rsa.PrivateKey) (tls.Certificate, error) { caCert, err := certs.DecodeCertPEM(caCertEncoded) if err != nil { return tls.Certificate{}, err @@ -502,11 +498,11 @@ func generateClientCert(caCertEncoded, caKeyEncoded []byte) (tls.Certificate, er if err != nil { return tls.Certificate{}, err } - x509Cert, err := newClientCert(caCert, privKey, caKey) + x509Cert, err := newClientCert(caCert, clientKey, caKey) if err != nil { return tls.Certificate{}, err } - return tls.X509KeyPair(certs.EncodeCertPEM(x509Cert), certs.EncodePrivateKeyPEM(privKey)) + return tls.X509KeyPair(certs.EncodeCertPEM(x509Cert), certs.EncodePrivateKeyPEM(clientKey)) } func newClientCert(caCert *x509.Certificate, key *rsa.PrivateKey, caKey crypto.Signer) (*x509.Certificate, error) {