mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-12 13:31:52 +00:00
kubeadm: add support for custom cert validity period in v1beta4
Allow the user to pass custom cert validity period with ClusterConfiguration.CertificateValidityPeriod and CACertificateValidityPeriod. The defaults remain 1 year for regular cert and 10 years for CA. Show warnings if the provided values are more than the defaults. Additional changes: - In "certs show-expiration" use HumanDuration() to print more detailed durations instead of ShortHumanDuration(). - Add a new kubeadm util GetStartTime() which can be used to consistently get a UTC time for tasks like writing certs and unit tests. - Update unit tests to validate the new customizable NotAfter.
This commit is contained in:
parent
031e6c3f4d
commit
74e1438d86
@ -26,6 +26,7 @@ import (
|
||||
|
||||
bootstraptokenv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
)
|
||||
|
||||
// Funcs returns the fuzzer functions for the kubeadm apis.
|
||||
@ -97,6 +98,8 @@ func fuzzClusterConfiguration(obj *kubeadm.ClusterConfiguration, c fuzz.Continue
|
||||
obj.Etcd.Local.ExtraEnvs = []kubeadm.EnvVar{}
|
||||
obj.EncryptionAlgorithm = kubeadm.EncryptionAlgorithmRSA2048
|
||||
obj.Proxy.Disabled = false
|
||||
obj.CertificateValidityPeriod = &metav1.Duration{Duration: constants.CertificateValidityPeriod}
|
||||
obj.CACertificateValidityPeriod = &metav1.Duration{Duration: constants.CACertificateValidityPeriod}
|
||||
}
|
||||
|
||||
func fuzzDNS(obj *kubeadm.DNS, c fuzz.Continue) {
|
||||
|
@ -149,6 +149,14 @@ type ClusterConfiguration struct {
|
||||
// EncryptionAlgorithm holds the type of asymmetric encryption algorithm used for keys and certificates.
|
||||
// Can be one of "RSA-2048" (default), "RSA-3072", "RSA-4096" or "ECDSA-P256".
|
||||
EncryptionAlgorithm EncryptionAlgorithmType
|
||||
|
||||
// CertificateValidityPeriod specifies the validity period for a non-CA certificate generated by kubeadm.
|
||||
// Default value: 8760h (365 days * 24 hours = 1 year)
|
||||
CertificateValidityPeriod *metav1.Duration
|
||||
|
||||
// CACertificateValidityPeriod specifies the validity period for a CA certificate generated by kubeadm.
|
||||
// Default value: 87600h (365 days * 24 hours * 10 = 10 years)
|
||||
CACertificateValidityPeriod *metav1.Duration
|
||||
}
|
||||
|
||||
// ControlPlaneComponent holds settings common to control plane component of the cluster
|
||||
|
@ -19,10 +19,12 @@ package v1beta3
|
||||
import (
|
||||
"sort"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
"k8s.io/utils/ptr"
|
||||
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
)
|
||||
|
||||
// Convert_kubeadm_InitConfiguration_To_v1beta3_InitConfiguration converts a private InitConfiguration to public InitConfiguration.
|
||||
@ -38,9 +40,11 @@ func Convert_v1beta3_InitConfiguration_To_kubeadm_InitConfiguration(in *InitConf
|
||||
}
|
||||
err = Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(&ClusterConfiguration{}, &out.ClusterConfiguration, s)
|
||||
// Required to pass fuzzer tests. This ClusterConfiguration is empty and is never defaulted.
|
||||
// If we call Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration() it will receive
|
||||
// a default value, thus here we need to reset it back to "".
|
||||
// If we call Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration() these fields will receive
|
||||
// a default value, thus here we need to reset them back to empty.
|
||||
out.EncryptionAlgorithm = ""
|
||||
out.CertificateValidityPeriod = nil
|
||||
out.CACertificateValidityPeriod = nil
|
||||
// Set default timeouts.
|
||||
kubeadm.SetDefaultTimeouts(&out.Timeouts)
|
||||
return err
|
||||
@ -64,9 +68,11 @@ func Convert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration(in *ku
|
||||
|
||||
// Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration is required due to missing EncryptionAlgorithm in v1beta3.
|
||||
func Convert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in *ClusterConfiguration, out *kubeadm.ClusterConfiguration, s conversion.Scope) error {
|
||||
// Required to pass validation and fuzzer tests. The field is missing in v1beta3, thus we have to
|
||||
// default it to a sane (default) value in the internal type.
|
||||
// Required to pass validation and fuzzer tests. These fields are missing in v1beta3, thus we have to
|
||||
// default them to a sane (default) value in the internal type.
|
||||
out.EncryptionAlgorithm = kubeadm.EncryptionAlgorithmRSA2048
|
||||
out.CertificateValidityPeriod = &metav1.Duration{Duration: constants.CertificateValidityPeriod}
|
||||
out.CACertificateValidityPeriod = &metav1.Duration{Duration: constants.CACertificateValidityPeriod}
|
||||
return autoConvert_v1beta3_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
|
@ -361,6 +361,8 @@ func autoConvert_kubeadm_ClusterConfiguration_To_v1beta3_ClusterConfiguration(in
|
||||
out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
|
||||
out.ClusterName = in.ClusterName
|
||||
// WARNING: in.EncryptionAlgorithm requires manual conversion: does not exist in peer-type
|
||||
// WARNING: in.CertificateValidityPeriod requires manual conversion: does not exist in peer-type
|
||||
// WARNING: in.CACertificateValidityPeriod requires manual conversion: does not exist in peer-type
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -108,6 +108,17 @@ func SetDefaults_ClusterConfiguration(obj *ClusterConfiguration) {
|
||||
obj.EncryptionAlgorithm = DefaultEncryptionAlgorithm
|
||||
}
|
||||
|
||||
if obj.CertificateValidityPeriod == nil {
|
||||
obj.CertificateValidityPeriod = &metav1.Duration{
|
||||
Duration: constants.CertificateValidityPeriod,
|
||||
}
|
||||
}
|
||||
if obj.CACertificateValidityPeriod == nil {
|
||||
obj.CACertificateValidityPeriod = &metav1.Duration{
|
||||
Duration: constants.CACertificateValidityPeriod,
|
||||
}
|
||||
}
|
||||
|
||||
SetDefaults_Etcd(obj)
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,10 @@ limitations under the License.
|
||||
// and will be ignored when passing --config to upgrade subcommands.
|
||||
// - Add a `Timeouts` structure to `InitConfiguration`, `JoinConfiguration`, `ResetConfiguration` and `UpgradeConfiguration`
|
||||
// that can be used to configure various timeouts.
|
||||
// - Add a `CertificateValidityPeriod` and `CACertificateValidityPeriod` fields to `ClusterConfiguration`. These fields
|
||||
// can be used to control the validity period of certificates generated by kubeadm during sub-commands such as `init`,
|
||||
// `join`, `upgrade` and `certs`. Default values continue to be 1 year for non-CA certificates and 10 years for CA
|
||||
// certificates. Only non-CA certificates continue to be renewable by `kubeadm certs renew`.
|
||||
//
|
||||
// Migration from old kubeadm config versions
|
||||
//
|
||||
|
@ -152,6 +152,16 @@ type ClusterConfiguration struct {
|
||||
// Can be one of "RSA-2048" (default), "RSA-3072", "RSA-4096" or "ECDSA-P256".
|
||||
// +optional
|
||||
EncryptionAlgorithm EncryptionAlgorithmType `json:"encryptionAlgorithm,omitempty"`
|
||||
|
||||
// CertificateValidityPeriod specifies the validity period for a non-CA certificate generated by kubeadm.
|
||||
// Default value: 8760h (365 days * 24 hours = 1 year)
|
||||
// +optional
|
||||
CertificateValidityPeriod *metav1.Duration `json:"certificateValidityPeriod,omitempty"`
|
||||
|
||||
// CACertificateValidityPeriod specifies the validity period for a CA certificate generated by kubeadm.
|
||||
// Default value: 87600h (365 days * 24 hours * 10 = 10 years)
|
||||
// +optional
|
||||
CACertificateValidityPeriod *metav1.Duration `json:"caCertificateValidityPeriod,omitempty"`
|
||||
}
|
||||
|
||||
// ControlPlaneComponent holds settings common to control plane component of the cluster
|
||||
|
@ -24,8 +24,8 @@ package v1beta4
|
||||
import (
|
||||
unsafe "unsafe"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
bootstraptokenv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1"
|
||||
@ -453,6 +453,8 @@ func autoConvert_v1beta4_ClusterConfiguration_To_kubeadm_ClusterConfiguration(in
|
||||
out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
|
||||
out.ClusterName = in.ClusterName
|
||||
out.EncryptionAlgorithm = kubeadm.EncryptionAlgorithmType(in.EncryptionAlgorithm)
|
||||
out.CertificateValidityPeriod = (*v1.Duration)(unsafe.Pointer(in.CertificateValidityPeriod))
|
||||
out.CACertificateValidityPeriod = (*v1.Duration)(unsafe.Pointer(in.CACertificateValidityPeriod))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -488,6 +490,8 @@ func autoConvert_kubeadm_ClusterConfiguration_To_v1beta4_ClusterConfiguration(in
|
||||
out.FeatureGates = *(*map[string]bool)(unsafe.Pointer(&in.FeatureGates))
|
||||
out.ClusterName = in.ClusterName
|
||||
out.EncryptionAlgorithm = EncryptionAlgorithmType(in.EncryptionAlgorithm)
|
||||
out.CertificateValidityPeriod = (*v1.Duration)(unsafe.Pointer(in.CertificateValidityPeriod))
|
||||
out.CACertificateValidityPeriod = (*v1.Duration)(unsafe.Pointer(in.CACertificateValidityPeriod))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -654,7 +658,7 @@ func autoConvert_v1beta4_HostPathMount_To_kubeadm_HostPathMount(in *HostPathMoun
|
||||
out.HostPath = in.HostPath
|
||||
out.MountPath = in.MountPath
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.PathType = v1.HostPathType(in.PathType)
|
||||
out.PathType = corev1.HostPathType(in.PathType)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -668,7 +672,7 @@ func autoConvert_kubeadm_HostPathMount_To_v1beta4_HostPathMount(in *kubeadm.Host
|
||||
out.HostPath = in.HostPath
|
||||
out.MountPath = in.MountPath
|
||||
out.ReadOnly = in.ReadOnly
|
||||
out.PathType = v1.HostPathType(in.PathType)
|
||||
out.PathType = corev1.HostPathType(in.PathType)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -856,10 +860,10 @@ func Convert_kubeadm_Networking_To_v1beta4_Networking(in *kubeadm.Networking, ou
|
||||
func autoConvert_v1beta4_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions(in *NodeRegistrationOptions, out *kubeadm.NodeRegistrationOptions, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.CRISocket = in.CRISocket
|
||||
out.Taints = *(*[]v1.Taint)(unsafe.Pointer(&in.Taints))
|
||||
out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints))
|
||||
out.KubeletExtraArgs = *(*[]kubeadm.Arg)(unsafe.Pointer(&in.KubeletExtraArgs))
|
||||
out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
|
||||
out.ImagePullPolicy = v1.PullPolicy(in.ImagePullPolicy)
|
||||
out.ImagePullPolicy = corev1.PullPolicy(in.ImagePullPolicy)
|
||||
out.ImagePullSerial = (*bool)(unsafe.Pointer(in.ImagePullSerial))
|
||||
return nil
|
||||
}
|
||||
@ -872,10 +876,10 @@ func Convert_v1beta4_NodeRegistrationOptions_To_kubeadm_NodeRegistrationOptions(
|
||||
func autoConvert_kubeadm_NodeRegistrationOptions_To_v1beta4_NodeRegistrationOptions(in *kubeadm.NodeRegistrationOptions, out *NodeRegistrationOptions, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.CRISocket = in.CRISocket
|
||||
out.Taints = *(*[]v1.Taint)(unsafe.Pointer(&in.Taints))
|
||||
out.Taints = *(*[]corev1.Taint)(unsafe.Pointer(&in.Taints))
|
||||
out.KubeletExtraArgs = *(*[]Arg)(unsafe.Pointer(&in.KubeletExtraArgs))
|
||||
out.IgnorePreflightErrors = *(*[]string)(unsafe.Pointer(&in.IgnorePreflightErrors))
|
||||
out.ImagePullPolicy = v1.PullPolicy(in.ImagePullPolicy)
|
||||
out.ImagePullPolicy = corev1.PullPolicy(in.ImagePullPolicy)
|
||||
out.ImagePullSerial = (*bool)(unsafe.Pointer(in.ImagePullSerial))
|
||||
return nil
|
||||
}
|
||||
@ -962,13 +966,13 @@ func Convert_kubeadm_ResetConfiguration_To_v1beta4_ResetConfiguration(in *kubead
|
||||
}
|
||||
|
||||
func autoConvert_v1beta4_Timeouts_To_kubeadm_Timeouts(in *Timeouts, out *kubeadm.Timeouts, s conversion.Scope) error {
|
||||
out.ControlPlaneComponentHealthCheck = (*metav1.Duration)(unsafe.Pointer(in.ControlPlaneComponentHealthCheck))
|
||||
out.KubeletHealthCheck = (*metav1.Duration)(unsafe.Pointer(in.KubeletHealthCheck))
|
||||
out.KubernetesAPICall = (*metav1.Duration)(unsafe.Pointer(in.KubernetesAPICall))
|
||||
out.EtcdAPICall = (*metav1.Duration)(unsafe.Pointer(in.EtcdAPICall))
|
||||
out.TLSBootstrap = (*metav1.Duration)(unsafe.Pointer(in.TLSBootstrap))
|
||||
out.Discovery = (*metav1.Duration)(unsafe.Pointer(in.Discovery))
|
||||
out.UpgradeManifests = (*metav1.Duration)(unsafe.Pointer(in.UpgradeManifests))
|
||||
out.ControlPlaneComponentHealthCheck = (*v1.Duration)(unsafe.Pointer(in.ControlPlaneComponentHealthCheck))
|
||||
out.KubeletHealthCheck = (*v1.Duration)(unsafe.Pointer(in.KubeletHealthCheck))
|
||||
out.KubernetesAPICall = (*v1.Duration)(unsafe.Pointer(in.KubernetesAPICall))
|
||||
out.EtcdAPICall = (*v1.Duration)(unsafe.Pointer(in.EtcdAPICall))
|
||||
out.TLSBootstrap = (*v1.Duration)(unsafe.Pointer(in.TLSBootstrap))
|
||||
out.Discovery = (*v1.Duration)(unsafe.Pointer(in.Discovery))
|
||||
out.UpgradeManifests = (*v1.Duration)(unsafe.Pointer(in.UpgradeManifests))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -978,13 +982,13 @@ func Convert_v1beta4_Timeouts_To_kubeadm_Timeouts(in *Timeouts, out *kubeadm.Tim
|
||||
}
|
||||
|
||||
func autoConvert_kubeadm_Timeouts_To_v1beta4_Timeouts(in *kubeadm.Timeouts, out *Timeouts, s conversion.Scope) error {
|
||||
out.ControlPlaneComponentHealthCheck = (*metav1.Duration)(unsafe.Pointer(in.ControlPlaneComponentHealthCheck))
|
||||
out.KubeletHealthCheck = (*metav1.Duration)(unsafe.Pointer(in.KubeletHealthCheck))
|
||||
out.KubernetesAPICall = (*metav1.Duration)(unsafe.Pointer(in.KubernetesAPICall))
|
||||
out.EtcdAPICall = (*metav1.Duration)(unsafe.Pointer(in.EtcdAPICall))
|
||||
out.TLSBootstrap = (*metav1.Duration)(unsafe.Pointer(in.TLSBootstrap))
|
||||
out.Discovery = (*metav1.Duration)(unsafe.Pointer(in.Discovery))
|
||||
out.UpgradeManifests = (*metav1.Duration)(unsafe.Pointer(in.UpgradeManifests))
|
||||
out.ControlPlaneComponentHealthCheck = (*v1.Duration)(unsafe.Pointer(in.ControlPlaneComponentHealthCheck))
|
||||
out.KubeletHealthCheck = (*v1.Duration)(unsafe.Pointer(in.KubeletHealthCheck))
|
||||
out.KubernetesAPICall = (*v1.Duration)(unsafe.Pointer(in.KubernetesAPICall))
|
||||
out.EtcdAPICall = (*v1.Duration)(unsafe.Pointer(in.EtcdAPICall))
|
||||
out.TLSBootstrap = (*v1.Duration)(unsafe.Pointer(in.TLSBootstrap))
|
||||
out.Discovery = (*v1.Duration)(unsafe.Pointer(in.Discovery))
|
||||
out.UpgradeManifests = (*v1.Duration)(unsafe.Pointer(in.UpgradeManifests))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -23,9 +23,9 @@ package v1beta4
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
v1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1"
|
||||
bootstraptokenv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
@ -121,6 +121,16 @@ func (in *ClusterConfiguration) DeepCopyInto(out *ClusterConfiguration) {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.CertificateValidityPeriod != nil {
|
||||
in, out := &in.CertificateValidityPeriod, &out.CertificateValidityPeriod
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.CACertificateValidityPeriod != nil {
|
||||
in, out := &in.CACertificateValidityPeriod, &out.CACertificateValidityPeriod
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -336,7 +346,7 @@ func (in *InitConfiguration) DeepCopyInto(out *InitConfiguration) {
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.BootstrapTokens != nil {
|
||||
in, out := &in.BootstrapTokens, &out.BootstrapTokens
|
||||
*out = make([]v1.BootstrapToken, len(*in))
|
||||
*out = make([]bootstraptokenv1.BootstrapToken, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
@ -618,37 +628,37 @@ func (in *Timeouts) DeepCopyInto(out *Timeouts) {
|
||||
*out = *in
|
||||
if in.ControlPlaneComponentHealthCheck != nil {
|
||||
in, out := &in.ControlPlaneComponentHealthCheck, &out.ControlPlaneComponentHealthCheck
|
||||
*out = new(metav1.Duration)
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.KubeletHealthCheck != nil {
|
||||
in, out := &in.KubeletHealthCheck, &out.KubeletHealthCheck
|
||||
*out = new(metav1.Duration)
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.KubernetesAPICall != nil {
|
||||
in, out := &in.KubernetesAPICall, &out.KubernetesAPICall
|
||||
*out = new(metav1.Duration)
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.EtcdAPICall != nil {
|
||||
in, out := &in.EtcdAPICall, &out.EtcdAPICall
|
||||
*out = new(metav1.Duration)
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.TLSBootstrap != nil {
|
||||
in, out := &in.TLSBootstrap, &out.TLSBootstrap
|
||||
*out = new(metav1.Duration)
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.Discovery != nil {
|
||||
in, out := &in.Discovery, &out.Discovery
|
||||
*out = new(metav1.Duration)
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.UpgradeManifests != nil {
|
||||
in, out := &in.UpgradeManifests, &out.UpgradeManifests
|
||||
*out = new(metav1.Duration)
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
|
@ -75,6 +75,9 @@ func ValidateClusterConfiguration(c *kubeadm.ClusterConfiguration) field.ErrorLi
|
||||
allErrs = append(allErrs, ValidateEtcd(&c.Etcd, field.NewPath("etcd"))...)
|
||||
allErrs = append(allErrs, ValidateEncryptionAlgorithm(c.EncryptionAlgorithm, field.NewPath("encryptionAlgorithm"))...)
|
||||
allErrs = append(allErrs, componentconfigs.Validate(c)...)
|
||||
for _, certError := range ValidateCertValidity(c) {
|
||||
klog.Warningf("WARNING: %s", certError.Error())
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
@ -770,3 +773,19 @@ func ValidateUpgradeConfiguration(c *kubeadm.UpgradeConfiguration) field.ErrorLi
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateCertValidity validates if the values for cert validity are too big
|
||||
func ValidateCertValidity(cfg *kubeadm.ClusterConfiguration) []error {
|
||||
var allErrs []error
|
||||
if cfg.CertificateValidityPeriod != nil && cfg.CertificateValidityPeriod.Duration > constants.CertificateValidityPeriod {
|
||||
allErrs = append(allErrs,
|
||||
errors.Errorf("certificateValidityPeriod: the value %v is more than the recommended default for certificate expiration: %v",
|
||||
cfg.CertificateValidityPeriod.Duration, constants.CertificateValidityPeriod))
|
||||
}
|
||||
if cfg.CACertificateValidityPeriod != nil && cfg.CACertificateValidityPeriod.Duration > constants.CACertificateValidityPeriod {
|
||||
allErrs = append(allErrs,
|
||||
errors.Errorf("caCertificateValidityPeriod: the value %v is more than the recommended default for CA certificate expiration: %v",
|
||||
cfg.CACertificateValidityPeriod.Duration, constants.CACertificateValidityPeriod))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
@ -25,11 +25,13 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
)
|
||||
|
||||
func TestValidateToken(t *testing.T) {
|
||||
@ -1541,3 +1543,51 @@ func TestPullPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCertValidity(t *testing.T) {
|
||||
var tests = []struct {
|
||||
name string
|
||||
cfg *kubeadmapi.ClusterConfiguration
|
||||
expectedErrors int
|
||||
}{
|
||||
{
|
||||
name: "no errors from nil values",
|
||||
cfg: &kubeadmapi.ClusterConfiguration{
|
||||
CertificateValidityPeriod: nil,
|
||||
CACertificateValidityPeriod: nil,
|
||||
},
|
||||
expectedErrors: 0,
|
||||
},
|
||||
{
|
||||
name: "no errors from defaults",
|
||||
cfg: &kubeadmapi.ClusterConfiguration{
|
||||
CertificateValidityPeriod: &metav1.Duration{
|
||||
Duration: constants.CertificateValidityPeriod,
|
||||
},
|
||||
CACertificateValidityPeriod: &metav1.Duration{
|
||||
Duration: constants.CACertificateValidityPeriod,
|
||||
},
|
||||
},
|
||||
expectedErrors: 0,
|
||||
},
|
||||
{
|
||||
name: "two errors from long durations",
|
||||
cfg: &kubeadmapi.ClusterConfiguration{
|
||||
CertificateValidityPeriod: &metav1.Duration{
|
||||
Duration: constants.CertificateValidityPeriod * 2,
|
||||
},
|
||||
CACertificateValidityPeriod: &metav1.Duration{
|
||||
Duration: constants.CACertificateValidityPeriod * 2,
|
||||
},
|
||||
},
|
||||
expectedErrors: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
actual := ValidateCertValidity(tc.cfg)
|
||||
if len(actual) != tc.expectedErrors {
|
||||
t.Errorf("case %q:\n\t expected errors: %v\n\t got: %v\n\t errors: %v", tc.name, tc.expectedErrors, len(actual), actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,6 +133,16 @@ func (in *ClusterConfiguration) DeepCopyInto(out *ClusterConfiguration) {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.CertificateValidityPeriod != nil {
|
||||
in, out := &in.CertificateValidityPeriod, &out.CertificateValidityPeriod
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.CACertificateValidityPeriod != nil {
|
||||
in, out := &in.CACertificateValidityPeriod, &out.CACertificateValidityPeriod
|
||||
*out = new(v1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -516,7 +516,7 @@ func (p *certTextPrinter) PrintObj(obj runtime.Object, writer io.Writer) error {
|
||||
s := fmt.Sprintf("%s\t%s\t%s\t%s\t%-8v",
|
||||
cert.Name,
|
||||
cert.ExpirationDate.Format("Jan 02, 2006 15:04 MST"),
|
||||
duration.ShortHumanDuration(time.Duration(cert.ResidualTimeSeconds)*time.Second),
|
||||
duration.HumanDuration(time.Duration(cert.ResidualTimeSeconds)*time.Second),
|
||||
cert.CAName,
|
||||
yesNo(cert.ExternallyManaged),
|
||||
)
|
||||
@ -535,7 +535,7 @@ func (p *certTextPrinter) PrintObj(obj runtime.Object, writer io.Writer) error {
|
||||
s := fmt.Sprintf("%s\t%s\t%s\t%-8v",
|
||||
ca.Name,
|
||||
ca.ExpirationDate.Format("Jan 02, 2006 15:04 MST"),
|
||||
duration.ShortHumanDuration(time.Duration(ca.ResidualTimeSeconds)*time.Second),
|
||||
duration.HumanDuration(time.Duration(ca.ResidualTimeSeconds)*time.Second),
|
||||
yesNo(ca.ExternallyManaged),
|
||||
)
|
||||
fmt.Fprintln(tabw, s)
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||
@ -29,6 +30,7 @@ import (
|
||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
|
||||
)
|
||||
|
||||
@ -89,20 +91,27 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
if validityPeriod > kubeadmconstants.CertificateValidity {
|
||||
if validityPeriod > kubeadmconstants.CertificateValidityPeriod {
|
||||
klog.Warningf("WARNING: the specified certificate validity period %v is longer than the default duration %v, this may increase security risks.",
|
||||
validityPeriod, kubeadmconstants.CertificateValidity)
|
||||
validityPeriod, kubeadmconstants.CertificateValidityPeriod)
|
||||
}
|
||||
internalCfg.ClusterConfiguration.CertificateValidityPeriod = &metav1.Duration{
|
||||
Duration: validityPeriod,
|
||||
}
|
||||
|
||||
notAfter := time.Now().Add(validityPeriod).UTC()
|
||||
startTime := kubeadmutil.StartTimeUTC()
|
||||
notAfter := startTime.Add(kubeadmconstants.CertificateValidityPeriod)
|
||||
if internalCfg.ClusterConfiguration.CertificateValidityPeriod != nil {
|
||||
notAfter = startTime.Add(validityPeriod)
|
||||
}
|
||||
|
||||
// if the kubeconfig file for an additional user has to use a token, use it
|
||||
if token != "" {
|
||||
return kubeconfigphase.WriteKubeConfigWithToken(out, internalCfg, clientName, token, ¬After)
|
||||
return kubeconfigphase.WriteKubeConfigWithToken(out, internalCfg, clientName, token, notAfter)
|
||||
}
|
||||
|
||||
// Otherwise, write a kubeconfig file with a generate client cert
|
||||
return kubeconfigphase.WriteKubeConfigWithClientCert(out, internalCfg, clientName, organizations, ¬After)
|
||||
return kubeconfigphase.WriteKubeConfigWithClientCert(out, internalCfg, clientName, organizations, notAfter)
|
||||
},
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
@ -113,7 +122,7 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command {
|
||||
cmd.Flags().StringVar(&token, options.TokenStr, token, "The token that should be used as the authentication mechanism for this kubeconfig, instead of client certificates")
|
||||
cmd.Flags().StringVar(&clientName, "client-name", clientName, "The name of user. It will be used as the CN if client certificates are created")
|
||||
cmd.Flags().StringSliceVar(&organizations, "org", organizations, "The organizations of the client certificate. It will be used as the O if client certificates are created")
|
||||
cmd.Flags().DurationVar(&validityPeriod, "validity-period", kubeadmconstants.CertificateValidity, "The validity period of the client certificate. It is an offset from the current time.")
|
||||
cmd.Flags().DurationVar(&validityPeriod, "validity-period", kubeadmconstants.CertificateValidityPeriod, "The validity period of the client certificate. It is an offset from the current time.")
|
||||
|
||||
cmd.MarkFlagRequired("client-name")
|
||||
return cmd
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
@ -29,6 +30,7 @@ import (
|
||||
|
||||
kubeadmapiv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
|
||||
kubeconfigtestutil "k8s.io/kubernetes/cmd/kubeadm/test/kubeconfig"
|
||||
@ -89,12 +91,13 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
|
||||
}
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
command string
|
||||
clusterName string
|
||||
withClientCert bool
|
||||
withToken bool
|
||||
additionalFlags []string
|
||||
name string
|
||||
command string
|
||||
clusterName string
|
||||
withClientCert bool
|
||||
withToken bool
|
||||
additionalFlags []string
|
||||
expectedValidityPeriod time.Duration
|
||||
}{
|
||||
{
|
||||
name: "user subCommand withClientCert",
|
||||
@ -120,6 +123,13 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
|
||||
clusterName: "my-cluster-with-token",
|
||||
additionalFlags: []string{"--token=123456"},
|
||||
},
|
||||
{
|
||||
name: "user subCommand with validity period",
|
||||
withClientCert: true,
|
||||
command: "user",
|
||||
additionalFlags: []string{"--validity-period=12h"},
|
||||
expectedValidityPeriod: 12 * time.Hour,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@ -156,8 +166,13 @@ func TestKubeConfigSubCommandsThatWritesToOut(t *testing.T) {
|
||||
kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
|
||||
|
||||
if test.withClientCert {
|
||||
if test.expectedValidityPeriod == 0 {
|
||||
test.expectedValidityPeriod = kubeadmconstants.CertificateValidityPeriod
|
||||
}
|
||||
// checks that kubeconfig files have expected client cert
|
||||
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, "myUser")
|
||||
startTime := kubeadmutil.StartTimeUTC()
|
||||
notAfter := startTime.Add(test.expectedValidityPeriod)
|
||||
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, notAfter, "myUser")
|
||||
}
|
||||
|
||||
if test.withToken {
|
||||
|
@ -44,8 +44,10 @@ const (
|
||||
|
||||
// CertificateBackdate defines the offset applied to notBefore for CA certificates generated by kubeadm
|
||||
CertificateBackdate = time.Minute * 5
|
||||
// CertificateValidity defines the validity for all the signed certificates generated by kubeadm
|
||||
CertificateValidity = time.Hour * 24 * 365
|
||||
// CertificateValidityPeriod defines the validity period for all the signed certificates generated by kubeadm
|
||||
CertificateValidityPeriod = time.Hour * 24 * 365
|
||||
// CACertificateValidityPeriod defines the validity period for all the signed CA certificates generated by kubeadm
|
||||
CACertificateValidityPeriod = time.Hour * 24 * 365 * 10
|
||||
|
||||
// DefaultCertificateDir defines default certificate directory
|
||||
DefaultCertificateDir = "pki"
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@ -30,6 +31,7 @@ import (
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||
)
|
||||
|
||||
@ -50,6 +52,8 @@ type KubeadmCert struct {
|
||||
// These functions will be run in series, passed both the InitConfiguration and a cert Config.
|
||||
configMutators []configMutatorsFunc
|
||||
config pkiutil.CertConfig
|
||||
// Used for unit tests.
|
||||
creationTime time.Time
|
||||
}
|
||||
|
||||
// GetConfig returns the definition for the given cert given the provided InitConfiguration
|
||||
@ -60,6 +64,30 @@ func (k *KubeadmCert) GetConfig(ic *kubeadmapi.InitConfiguration) (*pkiutil.Cert
|
||||
}
|
||||
}
|
||||
|
||||
// creationTime should be set only during unit tests, otherwise the kubeadm start time
|
||||
// should be
|
||||
if k.creationTime.IsZero() {
|
||||
k.creationTime = kubeadmutil.StartTimeUTC()
|
||||
}
|
||||
|
||||
// Backdate certificate to allow small time jumps.
|
||||
k.config.NotBefore = k.creationTime.Add(-kubeadmconstants.CertificateBackdate)
|
||||
|
||||
// Use the validity periods defined in the ClusterConfiguration.
|
||||
// If CAName is empty this is a CA cert.
|
||||
if len(k.CAName) != 0 {
|
||||
if ic.ClusterConfiguration.CertificateValidityPeriod != nil {
|
||||
k.config.NotAfter = k.creationTime.
|
||||
Add(ic.ClusterConfiguration.CertificateValidityPeriod.Duration)
|
||||
}
|
||||
} else {
|
||||
if ic.ClusterConfiguration.CACertificateValidityPeriod != nil {
|
||||
k.config.NotAfter = k.creationTime.
|
||||
Add(ic.ClusterConfiguration.CACertificateValidityPeriod.Duration)
|
||||
}
|
||||
}
|
||||
|
||||
// Use the encryption algorithm from ClusterConfiguration.
|
||||
k.config.EncryptionAlgorithm = ic.ClusterConfiguration.EncryptionAlgorithmType()
|
||||
return &k.config, nil
|
||||
}
|
||||
|
@ -23,7 +23,10 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
@ -301,3 +304,85 @@ func TestCreateKeyAndCSR(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetConfig(t *testing.T) {
|
||||
var (
|
||||
now = time.Now()
|
||||
backdate = kubeadmconstants.CertificateBackdate
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cert *KubeadmCert
|
||||
cfg *kubeadmapi.InitConfiguration
|
||||
expectedConfig *pkiutil.CertConfig
|
||||
}{
|
||||
{
|
||||
name: "encryption algorithm is set",
|
||||
cert: &KubeadmCert{
|
||||
creationTime: now,
|
||||
},
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmECDSAP256,
|
||||
},
|
||||
},
|
||||
expectedConfig: &pkiutil.CertConfig{
|
||||
Config: certutil.Config{
|
||||
NotBefore: now.Add(-backdate),
|
||||
},
|
||||
EncryptionAlgorithm: kubeadmapi.EncryptionAlgorithmECDSAP256,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cert validity is set",
|
||||
cert: &KubeadmCert{
|
||||
CAName: "some-ca",
|
||||
creationTime: now,
|
||||
},
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
CertificateValidityPeriod: &metav1.Duration{
|
||||
Duration: time.Hour * 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedConfig: &pkiutil.CertConfig{
|
||||
Config: certutil.Config{
|
||||
NotBefore: now.Add(-backdate),
|
||||
},
|
||||
NotAfter: now.Add(time.Hour * 1)},
|
||||
},
|
||||
{
|
||||
name: "CA cert validity is set",
|
||||
cert: &KubeadmCert{
|
||||
CAName: "",
|
||||
creationTime: now,
|
||||
},
|
||||
cfg: &kubeadmapi.InitConfiguration{
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
CACertificateValidityPeriod: &metav1.Duration{
|
||||
Duration: time.Hour * 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedConfig: &pkiutil.CertConfig{
|
||||
Config: certutil.Config{
|
||||
NotBefore: now.Add(-backdate),
|
||||
},
|
||||
NotAfter: now.Add(time.Hour * 10),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual, err := tt.cert.GetConfig(tt.cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if diff := cmp.Diff(actual, tt.expectedConfig); diff != "" {
|
||||
t.Fatalf("GetConfig() returned diff (-want,+got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||
)
|
||||
|
||||
@ -250,6 +251,17 @@ func (rm *Manager) RenewUsingLocalCA(name string) (bool, error) {
|
||||
EncryptionAlgorithm: rm.cfg.EncryptionAlgorithmType(),
|
||||
}
|
||||
|
||||
startTime := kubeadmutil.StartTimeUTC()
|
||||
|
||||
// Backdate certificate to allow small time jumps.
|
||||
cfg.NotBefore = startTime.Add(-kubeadmconstants.CertificateBackdate)
|
||||
|
||||
// Use the validity periods defined in the ClusterConfiguration.
|
||||
// Only use CertificateValidityPeriod as CA renewal is not supported.
|
||||
if rm.cfg.CertificateValidityPeriod != nil {
|
||||
cfg.NotAfter = startTime.Add(rm.cfg.CertificateValidityPeriod.Duration)
|
||||
}
|
||||
|
||||
// reads the CA
|
||||
caCert, caKey, err := certsphase.LoadCertificateAuthority(rm.cfg.CertificatesDir, handler.CABaseName)
|
||||
if err != nil {
|
||||
|
@ -28,10 +28,13 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
netutils "k8s.io/utils/net"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
certtestutil "k8s.io/kubernetes/cmd/kubeadm/app/util/certs"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||
testutil "k8s.io/kubernetes/cmd/kubeadm/test"
|
||||
@ -46,7 +49,7 @@ var (
|
||||
|
||||
testCertOrganization = []string{"sig-cluster-lifecycle"}
|
||||
|
||||
testCertCfg = makeTestCertConfig(testCertOrganization)
|
||||
testCertCfg = makeTestCertConfig(testCertOrganization, time.Time{}, time.Time{})
|
||||
)
|
||||
|
||||
type fakecertificateReadWriter struct {
|
||||
@ -117,12 +120,23 @@ func TestRenewUsingLocalCA(t *testing.T) {
|
||||
|
||||
cfg := &kubeadmapi.ClusterConfiguration{
|
||||
CertificatesDir: dir,
|
||||
CertificateValidityPeriod: &metav1.Duration{
|
||||
Duration: time.Hour * 10,
|
||||
},
|
||||
}
|
||||
rm, err := NewManager(cfg, dir)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create the certificate renewal manager: %v", err)
|
||||
}
|
||||
|
||||
// Prepare test certs with a past validity.
|
||||
startTime := kubeadmutil.StartTimeUTC()
|
||||
|
||||
fmt.Println("START TIME TEST", startTime)
|
||||
|
||||
notBefore := startTime.Add(-rm.cfg.CertificateValidityPeriod.Duration * 2)
|
||||
notAfter := startTime.Add(-rm.cfg.CertificateValidityPeriod.Duration)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
certName string
|
||||
@ -133,7 +147,7 @@ func TestRenewUsingLocalCA(t *testing.T) {
|
||||
name: "Certificate renewal for a PKI certificate",
|
||||
certName: "apiserver",
|
||||
createCertFunc: func() *x509.Certificate {
|
||||
return writeTestCertificate(t, dir, "apiserver", testCACert, testCAKey, testCertOrganization)
|
||||
return writeTestCertificate(t, dir, "apiserver", testCACert, testCAKey, testCertOrganization, notBefore, notAfter)
|
||||
},
|
||||
expectedOrganization: testCertOrganization,
|
||||
},
|
||||
@ -141,7 +155,7 @@ func TestRenewUsingLocalCA(t *testing.T) {
|
||||
name: "Certificate renewal for a certificate embedded in a kubeconfig file",
|
||||
certName: "admin.conf",
|
||||
createCertFunc: func() *x509.Certificate {
|
||||
return writeTestKubeconfig(t, dir, "admin.conf", testCACert, testCAKey)
|
||||
return writeTestKubeconfig(t, dir, "admin.conf", testCACert, testCAKey, notBefore, notAfter)
|
||||
},
|
||||
expectedOrganization: testCertOrganization,
|
||||
},
|
||||
@ -151,7 +165,9 @@ func TestRenewUsingLocalCA(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
cert := test.createCertFunc()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
notBefore := startTime.Add(-kubeadmconstants.CertificateBackdate)
|
||||
notAfter := startTime.Add(rm.cfg.CertificateValidityPeriod.Duration)
|
||||
testCertCfg := makeTestCertConfig(testCertOrganization, notBefore, notAfter)
|
||||
|
||||
_, err := rm.RenewUsingLocalCA(test.certName)
|
||||
if err != nil {
|
||||
@ -177,6 +193,8 @@ func TestRenewUsingLocalCA(t *testing.T) {
|
||||
certtestutil.AssertCertificateHasCommonName(t, newCert, testCertCfg.CommonName)
|
||||
certtestutil.AssertCertificateHasDNSNames(t, newCert, testCertCfg.AltNames.DNSNames...)
|
||||
certtestutil.AssertCertificateHasIPAddresses(t, newCert, testCertCfg.AltNames.IPs...)
|
||||
certtestutil.AssertCertificateHasNotBefore(t, newCert, testCertCfg.NotBefore)
|
||||
certtestutil.AssertCertificateHasNotAfter(t, newCert, testCertCfg.NotAfter)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -212,14 +230,14 @@ func TestCreateRenewCSR(t *testing.T) {
|
||||
name: "Creation of a CSR request for renewal of a PKI certificate",
|
||||
certName: "apiserver",
|
||||
createCertFunc: func() *x509.Certificate {
|
||||
return writeTestCertificate(t, dir, "apiserver", testCACert, testCAKey, testCertOrganization)
|
||||
return writeTestCertificate(t, dir, "apiserver", testCACert, testCAKey, testCertOrganization, time.Time{}, time.Time{})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Creation of a CSR request for renewal of a certificate embedded in a kubeconfig file",
|
||||
certName: "admin.conf",
|
||||
createCertFunc: func() *x509.Certificate {
|
||||
return writeTestKubeconfig(t, dir, "admin.conf", testCACert, testCAKey)
|
||||
return writeTestKubeconfig(t, dir, "admin.conf", testCACert, testCAKey, time.Time{}, time.Time{})
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -294,7 +312,7 @@ func TestCertToConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func makeTestCertConfig(organization []string) *pkiutil.CertConfig {
|
||||
func makeTestCertConfig(organization []string, notBefore, notAfter time.Time) *pkiutil.CertConfig {
|
||||
return &pkiutil.CertConfig{
|
||||
Config: certutil.Config{
|
||||
CommonName: "test-common-name",
|
||||
@ -303,8 +321,10 @@ func makeTestCertConfig(organization []string) *pkiutil.CertConfig {
|
||||
IPs: []net.IP{netutils.ParseIPSloppy("10.100.0.1")},
|
||||
DNSNames: []string{"test-domain.space"},
|
||||
},
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
NotBefore: notBefore,
|
||||
},
|
||||
NotAfter: notAfter,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
@ -42,7 +43,7 @@ func TestPKICertificateReadWriter(t *testing.T) {
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// creates a certificate
|
||||
cert := writeTestCertificate(t, dir, "test", testCACert, testCAKey, testCertOrganization)
|
||||
cert := writeTestCertificate(t, dir, "test", testCACert, testCAKey, testCertOrganization, time.Time{}, time.Time{})
|
||||
|
||||
// Creates a pkiCertificateReadWriter
|
||||
pkiReadWriter := newPKICertificateReadWriter(dir, "test")
|
||||
@ -100,7 +101,7 @@ func TestKubeconfigReadWriter(t *testing.T) {
|
||||
}
|
||||
|
||||
// creates a certificate and then embeds it into a kubeconfig file
|
||||
cert := writeTestKubeconfig(t, dirKubernetes, "test", testCACert, testCAKey)
|
||||
cert := writeTestKubeconfig(t, dirKubernetes, "test", testCACert, testCAKey, time.Time{}, time.Time{})
|
||||
|
||||
// Creates a KubeconfigReadWriter
|
||||
kubeconfigReadWriter := newKubeconfigReadWriter(dirKubernetes, "test", dirPKI, caName)
|
||||
@ -146,8 +147,8 @@ func TestKubeconfigReadWriter(t *testing.T) {
|
||||
}
|
||||
|
||||
// writeTestCertificate is a utility for creating a test certificate
|
||||
func writeTestCertificate(t *testing.T, dir, name string, caCert *x509.Certificate, caKey crypto.Signer, organization []string) *x509.Certificate {
|
||||
cert, key, err := pkiutil.NewCertAndKey(caCert, caKey, makeTestCertConfig(organization))
|
||||
func writeTestCertificate(t *testing.T, dir, name string, caCert *x509.Certificate, caKey crypto.Signer, organization []string, notBefore, notAfter time.Time) *x509.Certificate {
|
||||
cert, key, err := pkiutil.NewCertAndKey(caCert, caKey, makeTestCertConfig(organization, notBefore, notAfter))
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't generate certificate: %v", err)
|
||||
}
|
||||
@ -160,7 +161,7 @@ func writeTestCertificate(t *testing.T, dir, name string, caCert *x509.Certifica
|
||||
}
|
||||
|
||||
// writeTestKubeconfig is a utility for creating a test kubeconfig with an embedded certificate
|
||||
func writeTestKubeconfig(t *testing.T, dir, name string, caCert *x509.Certificate, caKey crypto.Signer) *x509.Certificate {
|
||||
func writeTestKubeconfig(t *testing.T, dir, name string, caCert *x509.Certificate, caKey crypto.Signer, notBefore, notAfter time.Time) *x509.Certificate {
|
||||
|
||||
cfg := &pkiutil.CertConfig{
|
||||
Config: certutil.Config{
|
||||
@ -171,7 +172,9 @@ func writeTestKubeconfig(t *testing.T, dir, name string, caCert *x509.Certificat
|
||||
IPs: []net.IP{netutils.ParseIPSloppy("10.100.0.1")},
|
||||
DNSNames: []string{"test-domain.space"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
},
|
||||
NotAfter: notAfter,
|
||||
}
|
||||
cert, key, err := pkiutil.NewCertAndKey(caCert, caKey, cfg)
|
||||
if err != nil {
|
||||
|
@ -66,11 +66,12 @@ type tokenAuth struct {
|
||||
|
||||
// kubeConfigSpec struct holds info required to build a KubeConfig object
|
||||
type kubeConfigSpec struct {
|
||||
CACert *x509.Certificate
|
||||
APIServer string
|
||||
ClientName string
|
||||
TokenAuth *tokenAuth `datapolicy:"token"`
|
||||
ClientCertAuth *clientCertAuth `datapolicy:"security-key"`
|
||||
CACert *x509.Certificate
|
||||
APIServer string
|
||||
ClientName string
|
||||
ClientCertNotAfter time.Time
|
||||
TokenAuth *tokenAuth `datapolicy:"token"`
|
||||
ClientCertAuth *clientCertAuth `datapolicy:"security-key"`
|
||||
}
|
||||
|
||||
// CreateJoinControlPlaneKubeConfigFiles will create and write to disk the kubeconfig files required by kubeadm
|
||||
@ -131,7 +132,7 @@ func createKubeConfigFiles(outDir string, cfg *kubeadmapi.InitConfiguration, kub
|
||||
}
|
||||
|
||||
// builds the KubeConfig object
|
||||
config, err := buildKubeConfigFromSpec(spec, cfg.ClusterName, nil)
|
||||
config, err := buildKubeConfigFromSpec(spec, cfg.ClusterName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -170,7 +171,7 @@ func getKubeConfigSpecs(cfg *kubeadmapi.InitConfiguration) (map[string]*kubeConf
|
||||
}
|
||||
|
||||
// buildKubeConfigFromSpec creates a kubeconfig object for the given kubeConfigSpec
|
||||
func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string, notAfter *time.Time) (*clientcmdapi.Config, error) {
|
||||
func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string) (*clientcmdapi.Config, error) {
|
||||
|
||||
// If this kubeconfig should use token
|
||||
if spec.TokenAuth != nil {
|
||||
@ -184,8 +185,8 @@ func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string, notAfter
|
||||
), nil
|
||||
}
|
||||
|
||||
// otherwise, create a client certs
|
||||
clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec, notAfter)
|
||||
// otherwise, create a client cert
|
||||
clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec)
|
||||
|
||||
clientCert, clientKey, err := pkiutil.NewCertAndKey(spec.CACert, spec.ClientCertAuth.CAKey, &clientCertConfig)
|
||||
if err != nil {
|
||||
@ -207,14 +208,14 @@ func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string, notAfter
|
||||
), nil
|
||||
}
|
||||
|
||||
func newClientCertConfigFromKubeConfigSpec(spec *kubeConfigSpec, notAfter *time.Time) pkiutil.CertConfig {
|
||||
func newClientCertConfigFromKubeConfigSpec(spec *kubeConfigSpec) pkiutil.CertConfig {
|
||||
return pkiutil.CertConfig{
|
||||
Config: certutil.Config{
|
||||
CommonName: spec.ClientName,
|
||||
Organization: spec.ClientCertAuth.Organizations,
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
},
|
||||
NotAfter: notAfter,
|
||||
NotAfter: spec.ClientCertNotAfter,
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,7 +304,7 @@ func createKubeConfigFileIfNotExists(outDir, filename string, config *clientcmda
|
||||
}
|
||||
|
||||
// WriteKubeConfigWithClientCert writes a kubeconfig file - with a client certificate as authentication info - to the given writer.
|
||||
func WriteKubeConfigWithClientCert(out io.Writer, cfg *kubeadmapi.InitConfiguration, clientName string, organizations []string, notAfter *time.Time) error {
|
||||
func WriteKubeConfigWithClientCert(out io.Writer, cfg *kubeadmapi.InitConfiguration, clientName string, organizations []string, notAfter time.Time) error {
|
||||
|
||||
// creates the KubeConfigSpecs, actualized for the current InitConfiguration
|
||||
caCert, caKey, err := pkiutil.TryLoadCertAndKeyFromDisk(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
|
||||
@ -326,13 +327,14 @@ func WriteKubeConfigWithClientCert(out io.Writer, cfg *kubeadmapi.InitConfigurat
|
||||
CAKey: caKey,
|
||||
Organizations: organizations,
|
||||
},
|
||||
ClientCertNotAfter: notAfter,
|
||||
}
|
||||
|
||||
return writeKubeConfigFromSpec(out, spec, cfg.ClusterName, notAfter)
|
||||
return writeKubeConfigFromSpec(out, spec, cfg.ClusterName)
|
||||
}
|
||||
|
||||
// WriteKubeConfigWithToken writes a kubeconfig file - with a token as client authentication info - to the given writer.
|
||||
func WriteKubeConfigWithToken(out io.Writer, cfg *kubeadmapi.InitConfiguration, clientName, token string, notAfter *time.Time) error {
|
||||
func WriteKubeConfigWithToken(out io.Writer, cfg *kubeadmapi.InitConfiguration, clientName, token string, notAfter time.Time) error {
|
||||
|
||||
// creates the KubeConfigSpecs, actualized for the current InitConfiguration
|
||||
caCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
|
||||
@ -354,16 +356,17 @@ func WriteKubeConfigWithToken(out io.Writer, cfg *kubeadmapi.InitConfiguration,
|
||||
TokenAuth: &tokenAuth{
|
||||
Token: token,
|
||||
},
|
||||
ClientCertNotAfter: notAfter,
|
||||
}
|
||||
|
||||
return writeKubeConfigFromSpec(out, spec, cfg.ClusterName, notAfter)
|
||||
return writeKubeConfigFromSpec(out, spec, cfg.ClusterName)
|
||||
}
|
||||
|
||||
// writeKubeConfigFromSpec creates a kubeconfig object from a kubeConfigSpec and writes it to the given writer.
|
||||
func writeKubeConfigFromSpec(out io.Writer, spec *kubeConfigSpec, clustername string, notAfter *time.Time) error {
|
||||
func writeKubeConfigFromSpec(out io.Writer, spec *kubeConfigSpec, clustername string) error {
|
||||
|
||||
// builds the KubeConfig object
|
||||
config, err := buildKubeConfigFromSpec(spec, clustername, notAfter)
|
||||
config, err := buildKubeConfigFromSpec(spec, clustername)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -439,6 +442,12 @@ func getKubeConfigSpecsBase(cfg *kubeadmapi.InitConfiguration) (map[string]*kube
|
||||
return nil, err
|
||||
}
|
||||
|
||||
startTime := kubeadmutil.StartTimeUTC()
|
||||
notAfter := startTime.Add(kubeadmconstants.CertificateValidityPeriod)
|
||||
if cfg.ClusterConfiguration.CertificateValidityPeriod != nil {
|
||||
notAfter = startTime.Add(cfg.ClusterConfiguration.CertificateValidityPeriod.Duration)
|
||||
}
|
||||
|
||||
return map[string]*kubeConfigSpec{
|
||||
kubeadmconstants.AdminKubeConfigFileName: {
|
||||
APIServer: controlPlaneEndpoint,
|
||||
@ -446,6 +455,7 @@ func getKubeConfigSpecsBase(cfg *kubeadmapi.InitConfiguration) (map[string]*kube
|
||||
ClientCertAuth: &clientCertAuth{
|
||||
Organizations: []string{kubeadmconstants.ClusterAdminsGroupAndClusterRoleBinding},
|
||||
},
|
||||
ClientCertNotAfter: notAfter,
|
||||
},
|
||||
kubeadmconstants.SuperAdminKubeConfigFileName: {
|
||||
APIServer: controlPlaneEndpoint,
|
||||
@ -453,6 +463,7 @@ func getKubeConfigSpecsBase(cfg *kubeadmapi.InitConfiguration) (map[string]*kube
|
||||
ClientCertAuth: &clientCertAuth{
|
||||
Organizations: []string{kubeadmconstants.SystemPrivilegedGroup},
|
||||
},
|
||||
ClientCertNotAfter: notAfter,
|
||||
},
|
||||
kubeadmconstants.KubeletKubeConfigFileName: {
|
||||
APIServer: controlPlaneEndpoint,
|
||||
@ -460,16 +471,19 @@ func getKubeConfigSpecsBase(cfg *kubeadmapi.InitConfiguration) (map[string]*kube
|
||||
ClientCertAuth: &clientCertAuth{
|
||||
Organizations: []string{kubeadmconstants.NodesGroup},
|
||||
},
|
||||
ClientCertNotAfter: notAfter,
|
||||
},
|
||||
kubeadmconstants.ControllerManagerKubeConfigFileName: {
|
||||
APIServer: localAPIEndpoint,
|
||||
ClientName: kubeadmconstants.ControllerManagerUser,
|
||||
ClientCertAuth: &clientCertAuth{},
|
||||
APIServer: localAPIEndpoint,
|
||||
ClientName: kubeadmconstants.ControllerManagerUser,
|
||||
ClientCertAuth: &clientCertAuth{},
|
||||
ClientCertNotAfter: notAfter,
|
||||
},
|
||||
kubeadmconstants.SchedulerKubeConfigFileName: {
|
||||
APIServer: localAPIEndpoint,
|
||||
ClientName: kubeadmconstants.SchedulerUser,
|
||||
ClientCertAuth: &clientCertAuth{},
|
||||
APIServer: localAPIEndpoint,
|
||||
ClientName: kubeadmconstants.SchedulerUser,
|
||||
ClientCertAuth: &clientCertAuth{},
|
||||
ClientCertNotAfter: notAfter,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@ -497,7 +511,7 @@ func createKubeConfigAndCSR(kubeConfigDir string, kubeadmConfig *kubeadmapi.Init
|
||||
return errors.Errorf("%s: csr: %s", errExist, kubeConfigPath)
|
||||
}
|
||||
|
||||
clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec, nil)
|
||||
clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec)
|
||||
|
||||
clientKey, err := pkiutil.NewPrivateKey(clientCertConfig.EncryptionAlgorithm)
|
||||
if err != nil {
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
@ -218,12 +219,14 @@ func TestBuildKubeConfigFromSpecWithClientAuth(t *testing.T) {
|
||||
// Creates a CA
|
||||
caCert, caKey := certstestutil.SetupCertificateAuthority(t)
|
||||
|
||||
notAfter, _ := time.Parse(time.RFC3339, "2026-01-02T15:04:05Z")
|
||||
|
||||
// Executes buildKubeConfigFromSpec passing a KubeConfigSpec with a ClientAuth
|
||||
config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "myClientName", "test-cluster", "myOrg1", "myOrg2")
|
||||
config := setupKubeConfigWithClientAuth(t, caCert, caKey, notAfter, "https://1.2.3.4:1234", "myClientName", "test-cluster", "myOrg1", "myOrg2")
|
||||
|
||||
// Asserts spec data are propagated to the kubeconfig
|
||||
kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
|
||||
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, "myClientName", "myOrg1", "myOrg2")
|
||||
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, notAfter, "myClientName", "myOrg1", "myOrg2")
|
||||
}
|
||||
|
||||
func TestBuildKubeConfigFromSpecWithTokenAuth(t *testing.T) {
|
||||
@ -231,7 +234,7 @@ func TestBuildKubeConfigFromSpecWithTokenAuth(t *testing.T) {
|
||||
caCert, _ := certstestutil.SetupCertificateAuthority(t)
|
||||
|
||||
// Executes buildKubeConfigFromSpec passing a KubeConfigSpec with a Token
|
||||
config := setupdKubeConfigWithTokenAuth(t, caCert, "https://1.2.3.4:1234", "myClientName", "123456", "test-cluster")
|
||||
config := setupKubeConfigWithTokenAuth(t, caCert, "https://1.2.3.4:1234", "myClientName", "123456", "test-cluster")
|
||||
|
||||
// Asserts spec data are propagated to the kubeconfig
|
||||
kubeconfigtestutil.AssertKubeConfigCurrentCluster(t, config, "https://1.2.3.4:1234", caCert)
|
||||
@ -244,11 +247,13 @@ func TestCreateKubeConfigFileIfNotExists(t *testing.T) {
|
||||
caCert, caKey := certstestutil.SetupCertificateAuthority(t)
|
||||
anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthority(t)
|
||||
|
||||
notAfter, _ := time.Parse(time.RFC3339, "2026-01-02T15:04:05Z")
|
||||
|
||||
// build kubeconfigs (to be used to test kubeconfigs equality/not equality)
|
||||
config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
|
||||
configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
|
||||
configWithAnotherClusterAddress := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://3.4.5.6:3456", "myOrg1", "test-cluster", "myOrg2")
|
||||
invalidConfig := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
|
||||
config := setupKubeConfigWithClientAuth(t, caCert, caKey, notAfter, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
|
||||
configWithAnotherClusterCa := setupKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, notAfter, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
|
||||
configWithAnotherClusterAddress := setupKubeConfigWithClientAuth(t, caCert, caKey, notAfter, "https://3.4.5.6:3456", "myOrg1", "test-cluster", "myOrg2")
|
||||
invalidConfig := setupKubeConfigWithClientAuth(t, caCert, caKey, notAfter, "https://1.2.3.4:1234", "test-cluster", "myOrg1", "myOrg2")
|
||||
invalidConfig.CurrentContext = "invalid context"
|
||||
|
||||
var tests = []struct {
|
||||
@ -384,6 +389,8 @@ func TestWriteKubeConfigFailsIfCADoesntExists(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
notAfter, _ := time.Parse(time.RFC3339, "2026-01-02T15:04:05Z")
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
writeKubeConfigFunction func(out io.Writer) error
|
||||
@ -391,13 +398,13 @@ func TestWriteKubeConfigFailsIfCADoesntExists(t *testing.T) {
|
||||
{
|
||||
name: "WriteKubeConfigWithClientCert",
|
||||
writeKubeConfigFunction: func(out io.Writer) error {
|
||||
return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}, nil)
|
||||
return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}, notAfter)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "WriteKubeConfigWithToken",
|
||||
writeKubeConfigFunction: func(out io.Writer) error {
|
||||
return WriteKubeConfigWithToken(out, cfg, "myUser", "12345", nil)
|
||||
return WriteKubeConfigWithToken(out, cfg, "myUser", "12345", notAfter)
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -433,9 +440,14 @@ func TestWriteKubeConfig(t *testing.T) {
|
||||
LocalAPIEndpoint: kubeadmapi.APIEndpoint{AdvertiseAddress: "1.2.3.4", BindPort: 1234},
|
||||
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
|
||||
CertificatesDir: pkidir,
|
||||
CertificateValidityPeriod: &metav1.Duration{
|
||||
Duration: time.Hour * 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
notAfter, _ := time.Parse(time.RFC3339, "2026-01-02T15:04:05Z")
|
||||
|
||||
var tests = []struct {
|
||||
name string
|
||||
writeKubeConfigFunction func(out io.Writer) error
|
||||
@ -445,14 +457,14 @@ func TestWriteKubeConfig(t *testing.T) {
|
||||
{
|
||||
name: "WriteKubeConfigWithClientCert",
|
||||
writeKubeConfigFunction: func(out io.Writer) error {
|
||||
return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}, nil)
|
||||
return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}, notAfter)
|
||||
},
|
||||
withClientCert: true,
|
||||
},
|
||||
{
|
||||
name: "WriteKubeConfigWithToken",
|
||||
writeKubeConfigFunction: func(out io.Writer) error {
|
||||
return WriteKubeConfigWithToken(out, cfg, "myUser", "12345", nil)
|
||||
return WriteKubeConfigWithToken(out, cfg, "myUser", "12345", notAfter)
|
||||
},
|
||||
withToken: true,
|
||||
},
|
||||
@ -480,7 +492,7 @@ func TestWriteKubeConfig(t *testing.T) {
|
||||
|
||||
if test.withClientCert {
|
||||
// checks that kubeconfig files have expected client cert
|
||||
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, "myUser", "myOrg")
|
||||
kubeconfigtestutil.AssertKubeConfigCurrentAuthInfoWithClientCert(t, config, caCert, notAfter, "myUser", "myOrg")
|
||||
}
|
||||
|
||||
if test.withToken {
|
||||
@ -495,9 +507,11 @@ func TestValidateKubeConfig(t *testing.T) {
|
||||
caCert, caKey := certstestutil.SetupCertificateAuthority(t)
|
||||
anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthority(t)
|
||||
|
||||
config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
|
||||
configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
|
||||
configWithAnotherServerURL := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://4.3.2.1:4321", "test-cluster", "myOrg1")
|
||||
notAfter, _ := time.Parse(time.RFC3339, "2026-01-02T15:04:05Z")
|
||||
|
||||
config := setupKubeConfigWithClientAuth(t, caCert, caKey, notAfter, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
|
||||
configWithAnotherClusterCa := setupKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, notAfter, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
|
||||
configWithAnotherServerURL := setupKubeConfigWithClientAuth(t, caCert, caKey, notAfter, "https://4.3.2.1:4321", "test-cluster", "myOrg1")
|
||||
|
||||
configWithSameClusterCaByExternalFile := config.DeepCopy()
|
||||
currentCtx, exists := configWithSameClusterCaByExternalFile.Contexts[configWithSameClusterCaByExternalFile.CurrentContext]
|
||||
@ -610,15 +624,17 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) {
|
||||
t.Fatalf("failure while deleting ca.key: %v", err)
|
||||
}
|
||||
|
||||
notAfter, _ := time.Parse(time.RFC3339, "2026-01-02T15:04:05Z")
|
||||
|
||||
// create a valid config
|
||||
config := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
|
||||
config := setupKubeConfigWithClientAuth(t, caCert, caKey, notAfter, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
|
||||
|
||||
// create a config with another CA
|
||||
anotherCaCert, anotherCaKey := certstestutil.SetupCertificateAuthority(t)
|
||||
configWithAnotherClusterCa := setupdKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
|
||||
configWithAnotherClusterCa := setupKubeConfigWithClientAuth(t, anotherCaCert, anotherCaKey, notAfter, "https://1.2.3.4:1234", "test-cluster", "myOrg1")
|
||||
|
||||
// create a config with another server URL
|
||||
configWithAnotherServerURL := setupdKubeConfigWithClientAuth(t, caCert, caKey, "https://4.3.2.1:4321", "test-cluster", "myOrg1")
|
||||
configWithAnotherServerURL := setupKubeConfigWithClientAuth(t, caCert, caKey, notAfter, "https://4.3.2.1:4321", "test-cluster", "myOrg1")
|
||||
|
||||
tests := map[string]struct {
|
||||
filesToWrite map[string]*clientcmdapi.Config
|
||||
@ -697,19 +713,20 @@ func TestValidateKubeconfigsForExternalCA(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// setupdKubeConfigWithClientAuth is a test utility function that wraps buildKubeConfigFromSpec for building a KubeConfig object With ClientAuth
|
||||
func setupdKubeConfigWithClientAuth(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer, APIServer, clientName, clustername string, organizations ...string) *clientcmdapi.Config {
|
||||
// setupKubeConfigWithClientAuth is a test utility function that wraps buildKubeConfigFromSpec for building a KubeConfig object With ClientAuth
|
||||
func setupKubeConfigWithClientAuth(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer, notAfter time.Time, apiServer, clientName, clustername string, organizations ...string) *clientcmdapi.Config {
|
||||
spec := &kubeConfigSpec{
|
||||
CACert: caCert,
|
||||
APIServer: APIServer,
|
||||
APIServer: apiServer,
|
||||
ClientName: clientName,
|
||||
ClientCertAuth: &clientCertAuth{
|
||||
CAKey: caKey,
|
||||
Organizations: organizations,
|
||||
},
|
||||
ClientCertNotAfter: notAfter,
|
||||
}
|
||||
|
||||
config, err := buildKubeConfigFromSpec(spec, clustername, nil)
|
||||
config, err := buildKubeConfigFromSpec(spec, clustername)
|
||||
if err != nil {
|
||||
t.Fatal("buildKubeConfigFromSpec failed!")
|
||||
}
|
||||
@ -717,18 +734,18 @@ func setupdKubeConfigWithClientAuth(t *testing.T, caCert *x509.Certificate, caKe
|
||||
return config
|
||||
}
|
||||
|
||||
// setupdKubeConfigWithClientAuth is a test utility function that wraps buildKubeConfigFromSpec for building a KubeConfig object With Token
|
||||
func setupdKubeConfigWithTokenAuth(t *testing.T, caCert *x509.Certificate, APIServer, clientName, token, clustername string) *clientcmdapi.Config {
|
||||
// setupKubeConfigWithClientAuth is a test utility function that wraps buildKubeConfigFromSpec for building a KubeConfig object With Token
|
||||
func setupKubeConfigWithTokenAuth(t *testing.T, caCert *x509.Certificate, apiServer, clientName, token, clustername string) *clientcmdapi.Config {
|
||||
spec := &kubeConfigSpec{
|
||||
CACert: caCert,
|
||||
APIServer: APIServer,
|
||||
APIServer: apiServer,
|
||||
ClientName: clientName,
|
||||
TokenAuth: &tokenAuth{
|
||||
Token: token,
|
||||
},
|
||||
}
|
||||
|
||||
config, err := buildKubeConfigFromSpec(spec, clustername, nil)
|
||||
config, err := buildKubeConfigFromSpec(spec, clustername)
|
||||
if err != nil {
|
||||
t.Fatal("buildKubeConfigFromSpec failed!")
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"net"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
"k8s.io/client-go/util/keyutil"
|
||||
@ -51,6 +52,26 @@ func AssertCertificateIsSignedByCa(t *testing.T, cert *x509.Certificate, signing
|
||||
}
|
||||
}
|
||||
|
||||
// AssertCertificateHasNotBefore is a utility function for kubeadm testing that asserts if a given certificate has
|
||||
// the expected NotBefore. Truncate (round) expectedNotBefore to 1 second, since the certificate stores
|
||||
// with seconds as the maximum precision.
|
||||
func AssertCertificateHasNotBefore(t *testing.T, cert *x509.Certificate, expectedNotBefore time.Time) {
|
||||
truncated := expectedNotBefore.Truncate(time.Second)
|
||||
if !cert.NotBefore.Equal(truncated) {
|
||||
t.Errorf("cert has NotBefore %v, expected %v", cert.NotBefore, truncated)
|
||||
}
|
||||
}
|
||||
|
||||
// AssertCertificateHasNotAfter is a utility function for kubeadm testing that asserts if a given certificate has
|
||||
// the expected NotAfter. Truncate (round) expectedNotAfter to 1 second, since the certificate stores
|
||||
// with seconds as the maximum precision.
|
||||
func AssertCertificateHasNotAfter(t *testing.T, cert *x509.Certificate, expectedNotAfter time.Time) {
|
||||
truncated := expectedNotAfter.Truncate(time.Second)
|
||||
if !cert.NotAfter.Equal(truncated) {
|
||||
t.Errorf("cert has NotAfter %v, expected %v", cert.NotAfter, truncated)
|
||||
}
|
||||
}
|
||||
|
||||
// AssertCertificateHasCommonName is a utility function for kubeadm testing that asserts if a given certificate has
|
||||
// the expected SubjectCommonName
|
||||
func AssertCertificateHasCommonName(t *testing.T, cert *x509.Certificate, commonName string) {
|
||||
|
@ -62,7 +62,7 @@ const (
|
||||
// CertConfig is a wrapper around certutil.Config extending it with EncryptionAlgorithm.
|
||||
type CertConfig struct {
|
||||
certutil.Config
|
||||
NotAfter *time.Time
|
||||
NotAfter time.Time
|
||||
EncryptionAlgorithm kubeadmapi.EncryptionAlgorithmType
|
||||
}
|
||||
|
||||
@ -72,10 +72,7 @@ func NewCertificateAuthority(config *CertConfig) (*x509.Certificate, crypto.Sign
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unable to create private key while generating CA certificate")
|
||||
}
|
||||
|
||||
// backdate CA certificate to allow small time jumps
|
||||
config.Config.NotBefore = time.Now().Add(-kubeadmconstants.CertificateBackdate)
|
||||
cert, err := certutil.NewSelfSignedCACert(config.Config, key)
|
||||
cert, err := NewSelfSignedCACert(config, key)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unable to create self-signed CA certificate")
|
||||
}
|
||||
@ -655,9 +652,14 @@ func NewSignedCert(cfg *CertConfig, key crypto.Signer, caCert *x509.Certificate,
|
||||
|
||||
RemoveDuplicateAltNames(&cfg.AltNames)
|
||||
|
||||
notAfter := time.Now().Add(kubeadmconstants.CertificateValidity).UTC()
|
||||
if cfg.NotAfter != nil {
|
||||
notAfter = *cfg.NotAfter
|
||||
notBefore := caCert.NotBefore
|
||||
if !cfg.NotBefore.IsZero() {
|
||||
notBefore = cfg.NotBefore
|
||||
}
|
||||
|
||||
notAfter := notBefore.Add(kubeadmconstants.CertificateValidityPeriod)
|
||||
if !cfg.NotAfter.IsZero() {
|
||||
notAfter = cfg.NotAfter
|
||||
}
|
||||
|
||||
certTmpl := x509.Certificate{
|
||||
@ -668,7 +670,7 @@ func NewSignedCert(cfg *CertConfig, key crypto.Signer, caCert *x509.Certificate,
|
||||
DNSNames: cfg.AltNames.DNSNames,
|
||||
IPAddresses: cfg.AltNames.IPs,
|
||||
SerialNumber: serial,
|
||||
NotBefore: caCert.NotBefore,
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
KeyUsage: keyUsage,
|
||||
ExtKeyUsage: cfg.Usages,
|
||||
@ -682,6 +684,48 @@ func NewSignedCert(cfg *CertConfig, key crypto.Signer, caCert *x509.Certificate,
|
||||
return x509.ParseCertificate(certDERBytes)
|
||||
}
|
||||
|
||||
// NewSelfSignedCACert creates a new self-signed CA certificate
|
||||
func NewSelfSignedCACert(cfg *CertConfig, key crypto.Signer) (*x509.Certificate, error) {
|
||||
// returns a uniform random value in [0, max-1), then add 1 to serial to make it a uniform random value in [1, max).
|
||||
serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64-1))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serial = new(big.Int).Add(serial, big.NewInt(1))
|
||||
|
||||
keyUsage := x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign
|
||||
|
||||
notBefore := time.Now().UTC()
|
||||
if !cfg.NotBefore.IsZero() {
|
||||
notBefore = cfg.NotBefore
|
||||
}
|
||||
|
||||
notAfter := notBefore.Add(kubeadmconstants.CACertificateValidityPeriod)
|
||||
if !cfg.NotAfter.IsZero() {
|
||||
notAfter = cfg.NotAfter
|
||||
}
|
||||
|
||||
tmpl := x509.Certificate{
|
||||
SerialNumber: serial,
|
||||
Subject: pkix.Name{
|
||||
CommonName: cfg.CommonName,
|
||||
Organization: cfg.Organization,
|
||||
},
|
||||
DNSNames: []string{cfg.CommonName},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
KeyUsage: keyUsage,
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
certDERBytes, err := x509.CreateCertificate(cryptorand.Reader, &tmpl, &tmpl, key.Public(), key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x509.ParseCertificate(certDERBytes)
|
||||
}
|
||||
|
||||
// RemoveDuplicateAltNames removes duplicate items in altNames.
|
||||
func RemoveDuplicateAltNames(altNames *certutil.AltNames) {
|
||||
if altNames == nil {
|
||||
@ -707,7 +751,7 @@ func RemoveDuplicateAltNames(altNames *certutil.AltNames) {
|
||||
// (+/- offset)
|
||||
func ValidateCertPeriod(cert *x509.Certificate, offset time.Duration) error {
|
||||
period := fmt.Sprintf("NotBefore: %v, NotAfter: %v", cert.NotBefore, cert.NotAfter)
|
||||
now := time.Now().Add(offset)
|
||||
now := time.Now().Add(offset).UTC()
|
||||
if now.Before(cert.NotBefore) {
|
||||
return errors.Errorf("the certificate is not valid yet: %s", period)
|
||||
}
|
||||
|
36
cmd/kubeadm/app/util/starttime.go
Normal file
36
cmd/kubeadm/app/util/starttime.go
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package util contains kubeadm utilities.
|
||||
package util
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// startTime is a variable that represents the start time of the kubeadm process.
|
||||
// It can be used to consistently use the same start time instead of calling time.Now()
|
||||
// in multiple locations and ending up with minor time deviations.
|
||||
var startTime time.Time
|
||||
|
||||
func init() {
|
||||
startTime = time.Now()
|
||||
}
|
||||
|
||||
// StartTimeUTC returns startTime with its location set to UTC.
|
||||
func StartTimeUTC() time.Time {
|
||||
return startTime.UTC()
|
||||
}
|
@ -20,6 +20,7 @@ import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
|
||||
@ -57,7 +58,7 @@ func AssertKubeConfigCurrentCluster(t *testing.T, config *clientcmdapi.Config, e
|
||||
|
||||
// AssertKubeConfigCurrentAuthInfoWithClientCert is a utility function for kubeadm testing that asserts if the CurrentAuthInfo in
|
||||
// the given KubeConfig object contains a clientCert that refers to a specific client name, is signed by the expected CA, includes the expected organizations
|
||||
func AssertKubeConfigCurrentAuthInfoWithClientCert(t *testing.T, config *clientcmdapi.Config, signinCa *x509.Certificate, expectedClientName string, expectedOrganizations ...string) {
|
||||
func AssertKubeConfigCurrentAuthInfoWithClientCert(t *testing.T, config *clientcmdapi.Config, signinCa *x509.Certificate, expectedNotAfter time.Time, expectedClientName string, expectedOrganizations ...string) {
|
||||
currentContext := config.Contexts[config.CurrentContext]
|
||||
currentAuthInfo := config.AuthInfos[currentContext.AuthInfo]
|
||||
|
||||
@ -77,6 +78,9 @@ func AssertKubeConfigCurrentAuthInfoWithClientCert(t *testing.T, config *clientc
|
||||
// Asserts the clientCert is signed by the signinCa
|
||||
certstestutil.AssertCertificateIsSignedByCa(t, currentClientCert, signinCa)
|
||||
|
||||
// Assert the clientCert has expected NotAfter
|
||||
certstestutil.AssertCertificateHasNotAfter(t, currentClientCert, expectedNotAfter)
|
||||
|
||||
// Asserts the clientCert has ClientAuth ExtKeyUsage
|
||||
certstestutil.AssertCertificateHasClientAuthUsage(t, currentClientCert)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user