Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api/bootstrap/kubeadm/v1beta1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ func RestoreKubeadmConfigSpec(restored *bootstrapv1.KubeadmConfigSpec, dst *boot
dst.ClusterConfiguration.CACertificateValidityPeriodDays = restored.ClusterConfiguration.CACertificateValidityPeriodDays
}
}
if restored.ClusterConfiguration.EncryptionAlgorithm != "" {
dst.ClusterConfiguration.EncryptionAlgorithm = restored.ClusterConfiguration.EncryptionAlgorithm
}
}

func RestoreBoolIntentKubeadmConfigSpec(src *KubeadmConfigSpec, dst *bootstrapv1.KubeadmConfigSpec, hasRestored bool, restored *bootstrapv1.KubeadmConfigSpec) error {
Expand Down
1 change: 1 addition & 0 deletions api/bootstrap/kubeadm/v1beta1/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions api/bootstrap/kubeadm/v1beta2/kubeadm_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,23 @@ const (
KubeadmConfigDataSecretNotAvailableReason = clusterv1.NotAvailableReason
)

// EncryptionAlgorithmType can define an asymmetric encryption algorithm type.
// +kubebuilder:validation:Enum=ECDSA-P256;ECDSA-P384;RSA-2048;RSA-3072;RSA-4096
type EncryptionAlgorithmType string

const (
// EncryptionAlgorithmECDSAP256 defines the ECDSA encryption algorithm type with curve P256.
EncryptionAlgorithmECDSAP256 EncryptionAlgorithmType = "ECDSA-P256"
// EncryptionAlgorithmECDSAP384 defines the ECDSA encryption algorithm type with curve P384.
EncryptionAlgorithmECDSAP384 EncryptionAlgorithmType = "ECDSA-P384"
// EncryptionAlgorithmRSA2048 defines the RSA encryption algorithm type with key size 2048 bits.
EncryptionAlgorithmRSA2048 EncryptionAlgorithmType = "RSA-2048"
// EncryptionAlgorithmRSA3072 defines the RSA encryption algorithm type with key size 3072 bits.
EncryptionAlgorithmRSA3072 EncryptionAlgorithmType = "RSA-3072"
// EncryptionAlgorithmRSA4096 defines the RSA encryption algorithm type with key size 4096 bits.
EncryptionAlgorithmRSA4096 EncryptionAlgorithmType = "RSA-4096"
)

// InitConfiguration contains a list of elements that is specific "kubeadm init"-only runtime
// information.
// +kubebuilder:validation:MinProperties=1
Expand Down Expand Up @@ -199,6 +216,16 @@ type ClusterConfiguration struct {
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=36500
CACertificateValidityPeriodDays int32 `json:"caCertificateValidityPeriodDays,omitempty"`

// encryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates.
// Can be one of "RSA-2048", "RSA-3072", "RSA-4096", "ECDSA-P256" or "ECDSA-P384".
// For Kubernetes 1.34 or above, "ECDSA-P384" is supported.
// If not specified, Cluster API will use RSA-2048 as default.
// When this field is modified every certificate generated afterward will use the new
// encryptionAlgorithm. Existing CA certificates and service account keys are not rotated.
// This field is only supported with Kubernetes v1.31 or above.
// +optional
EncryptionAlgorithm EncryptionAlgorithmType `json:"encryptionAlgorithm,omitempty"`
}

// IsDefined returns true if the ClusterConfiguration is defined.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions bootstrap/kubeadm/types/upstreamv1beta3/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ func hubClusterConfigurationFuzzer(obj *bootstrapv1.ClusterConfiguration, c rand

obj.CertificateValidityPeriodDays = 0
obj.CACertificateValidityPeriodDays = 0
obj.EncryptionAlgorithm = ""

for i, arg := range obj.APIServer.ExtraArgs {
if arg.Value == nil {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion bootstrap/kubeadm/types/upstreamv1beta4/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ func (dst *JoinConfiguration) ConvertFrom(srcRaw conversion.Hub) error {
func Convert_upstreamv1beta4_ClusterConfiguration_To_v1beta2_ClusterConfiguration(in *ClusterConfiguration, out *bootstrapv1.ClusterConfiguration, s apimachineryconversion.Scope) error {
// Following fields do not exist in CABPK v1beta1 version:
// - Proxy (Not supported yet)
// - EncryptionAlgorithm (Not supported yet)
if err := autoConvert_upstreamv1beta4_ClusterConfiguration_To_v1beta2_ClusterConfiguration(in, out, s); err != nil {
return err
}
Expand Down
1 change: 0 additions & 1 deletion bootstrap/kubeadm/types/upstreamv1beta4/conversion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ func spokeClusterConfigurationFuzzer(obj *ClusterConfiguration, c randfill.Conti
c.FillNoCustom(obj)

obj.Proxy = Proxy{}
obj.EncryptionAlgorithm = ""
obj.CertificateValidityPeriod = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31n(3*365)+1) * time.Hour * 24})
obj.CACertificateValidityPeriod = ptr.To[metav1.Duration](metav1.Duration{Duration: time.Duration(c.Int31n(100*365)+1) * time.Hour * 24})

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 11 additions & 11 deletions controlplane/kubeadm/internal/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"

bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2"
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
"sigs.k8s.io/cluster-api/controllers/clustercache"
"sigs.k8s.io/cluster-api/util/cache"
Expand All @@ -43,7 +44,7 @@ type ManagementCluster interface {

GetMachinesForCluster(ctx context.Context, cluster *clusterv1.Cluster, filters ...collections.Func) (collections.Machines, error)
GetMachinePoolsForCluster(ctx context.Context, cluster *clusterv1.Cluster) (*clusterv1.MachinePoolList, error)
GetWorkloadCluster(ctx context.Context, clusterKey client.ObjectKey) (WorkloadCluster, error)
GetWorkloadCluster(ctx context.Context, clusterKey client.ObjectKey, keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType) (WorkloadCluster, error)
}

// Management holds operations on the management cluster.
Expand All @@ -59,13 +60,14 @@ type Management struct {

// ClientCertEntry is an Entry for the Cache that stores the client cert.
type ClientCertEntry struct {
Cluster client.ObjectKey
ClientCert *tls.Certificate
Cluster client.ObjectKey
ClientCert *tls.Certificate
EncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType
}

// Key returns the cache key of a ClientCertEntry.
func (r ClientCertEntry) Key() string {
return r.Cluster.String()
return fmt.Sprintf("%s/%s", r.Cluster.String(), r.EncryptionAlgorithm)
}

// RemoteClusterConnectionError represents a failure to connect to a remote cluster.
Expand All @@ -77,7 +79,7 @@ type RemoteClusterConnectionError struct {
// Error satisfies the error interface.
func (e *RemoteClusterConnectionError) Error() string { return e.Name + ": " + e.Err.Error() }

// Unwrap satisfies the unwrap error inteface.
// Unwrap satisfies the unwrap error interface.
func (e *RemoteClusterConnectionError) Unwrap() error { return e.Err }

// Get implements client.Reader.
Expand Down Expand Up @@ -111,7 +113,7 @@ func (m *Management) GetMachinePoolsForCluster(ctx context.Context, cluster *clu

// GetWorkloadCluster builds a cluster object.
// The cluster comes with an etcd client generator to connect to any etcd pod living on a managed machine.
func (m *Management) GetWorkloadCluster(ctx context.Context, clusterKey client.ObjectKey) (WorkloadCluster, error) {
func (m *Management) GetWorkloadCluster(ctx context.Context, clusterKey client.ObjectKey, keyEncryptionAlgorithm bootstrapv1.EncryptionAlgorithmType) (WorkloadCluster, error) {
// TODO(chuckha): Inject this dependency.
// TODO(chuckha): memoize this function. The workload client only exists as long as a reconciliation loop.
restConfig, err := m.ClusterCache.GetRESTConfig(ctx, clusterKey)
Expand Down Expand Up @@ -140,17 +142,15 @@ func (m *Management) GetWorkloadCluster(ctx context.Context, clusterKey client.O
var clientCert tls.Certificate
if keyData != nil {
// Get client cert from cache if possible, otherwise generate it and add it to the cache.
// TODO: When we implement ClusterConfiguration.EncryptionAlgorithm we should add it to
// the ClientCertEntries and make it part of the key.
if entry, ok := m.ClientCertCache.Has(ClientCertEntry{Cluster: clusterKey}.Key()); ok {
if entry, ok := m.ClientCertCache.Has(ClientCertEntry{Cluster: clusterKey, EncryptionAlgorithm: keyEncryptionAlgorithm}.Key()); ok {
clientCert = *entry.ClientCert
} else {
// The client cert expires after 10 years, but that's okay as the cache has a TTL of 1 day.
clientCert, err = generateClientCert(crtData, keyData)
clientCert, err = generateClientCert(crtData, keyData, keyEncryptionAlgorithm)
if err != nil {
return nil, err
}
m.ClientCertCache.Add(ClientCertEntry{Cluster: clusterKey, ClientCert: &clientCert})
m.ClientCertCache.Add(ClientCertEntry{Cluster: clusterKey, ClientCert: &clientCert, EncryptionAlgorithm: keyEncryptionAlgorithm})
}
} else {
clientCert, err = m.getAPIServerEtcdClientCert(ctx, clusterKey)
Expand Down
3 changes: 2 additions & 1 deletion controlplane/kubeadm/internal/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

bootstrapv1 "sigs.k8s.io/cluster-api/api/bootstrap/kubeadm/v1beta2"
clusterv1 "sigs.k8s.io/cluster-api/api/core/v1beta2"
"sigs.k8s.io/cluster-api/controllers/clustercache"
"sigs.k8s.io/cluster-api/controllers/remote"
Expand Down Expand Up @@ -249,7 +250,7 @@ func TestGetWorkloadCluster(t *testing.T) {
})
g.Expect(err).ToNot(HaveOccurred())

workloadCluster, err := m.GetWorkloadCluster(ctx, tt.clusterKey)
workloadCluster, err := m.GetWorkloadCluster(ctx, tt.clusterKey, bootstrapv1.EncryptionAlgorithmRSA2048)
if tt.expectErr {
g.Expect(err).To(HaveOccurred())
g.Expect(workloadCluster).To(BeNil())
Expand Down
11 changes: 10 additions & 1 deletion controlplane/kubeadm/internal/control_plane.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ func (c *ControlPlane) GetWorkloadCluster(ctx context.Context) (WorkloadCluster,
return c.workloadCluster, nil
}

workloadCluster, err := c.managementCluster.GetWorkloadCluster(ctx, client.ObjectKeyFromObject(c.Cluster))
workloadCluster, err := c.managementCluster.GetWorkloadCluster(ctx, client.ObjectKeyFromObject(c.Cluster), c.GetKeyEncryptionAlgorithm())
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -467,3 +467,12 @@ func (c *ControlPlane) StatusToLogKeyAndValues(newMachine, deletedMachine *clust
"etcdMembers", strings.Join(etcdMembers, ", "),
}
}

// GetKeyEncryptionAlgorithm returns the control plane EncryptionAlgorithm.
// If its unset the default encryption algorithm is returned.
func (c *ControlPlane) GetKeyEncryptionAlgorithm() bootstrapv1.EncryptionAlgorithmType {
if c.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.EncryptionAlgorithm == "" {
return bootstrapv1.EncryptionAlgorithmRSA2048
}
return c.KCP.Spec.KubeadmConfigSpec.ClusterConfiguration.EncryptionAlgorithm
}
2 changes: 1 addition & 1 deletion controlplane/kubeadm/internal/controllers/fakes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (f *fakeManagementCluster) List(ctx context.Context, list client.ObjectList
return f.Reader.List(ctx, list, opts...)
}

func (f *fakeManagementCluster) GetWorkloadCluster(_ context.Context, _ client.ObjectKey) (internal.WorkloadCluster, error) {
func (f *fakeManagementCluster) GetWorkloadCluster(_ context.Context, _ client.ObjectKey, _ bootstrapv1.EncryptionAlgorithmType) (internal.WorkloadCluster, error) {
return f.Workload, f.WorkloadErr
}

Expand Down
4 changes: 2 additions & 2 deletions controlplane/kubeadm/internal/controllers/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ func (r *KubeadmControlPlaneReconciler) reconcileKubeconfig(ctx context.Context,
if endpoint.IsZero() {
return ctrl.Result{}, nil
}

controllerOwnerRef := *metav1.NewControllerRef(controlPlane.KCP, controlplanev1.GroupVersion.WithKind(kubeadmControlPlaneKind))
clusterName := util.ObjectKey(controlPlane.Cluster)
configSecret, err := secret.GetFromNamespacedName(ctx, r.SecretCachingClient, clusterName, secret.Kubeconfig)
Expand All @@ -64,6 +63,7 @@ func (r *KubeadmControlPlaneReconciler) reconcileKubeconfig(ctx context.Context,
clusterName,
endpoint.String(),
controllerOwnerRef,
kubeconfig.KeyEncryptionAlgorithm(controlPlane.GetKeyEncryptionAlgorithm()),
)
if errors.Is(createErr, kubeconfig.ErrDependentCertificateNotFound) {
return ctrl.Result{RequeueAfter: dependentCertRequeueAfter}, nil
Expand All @@ -90,7 +90,7 @@ func (r *KubeadmControlPlaneReconciler) reconcileKubeconfig(ctx context.Context,

if needsRotation {
log.Info("Rotating kubeconfig secret")
if err := kubeconfig.RegenerateSecret(ctx, r.Client, configSecret); err != nil {
if err := kubeconfig.RegenerateSecret(ctx, r.Client, configSecret, kubeconfig.KeyEncryptionAlgorithm(controlPlane.GetKeyEncryptionAlgorithm())); err != nil {
return ctrl.Result{}, errors.Wrap(err, "failed to regenerate kubeconfig")
}
}
Expand Down
Loading