diff --git a/pkg/cmd/init.go b/pkg/cmd/init.go index 2bb339f19b..3c75f4b2f1 100644 --- a/pkg/cmd/init.go +++ b/pkg/cmd/init.go @@ -61,6 +61,21 @@ func initCerts(cfg *config.Config) (*certchains.CertificateChains, error) { } func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { + // Anchor certificate expiration to the next day. This forces + // homogenous expiry dates for all certificates with the same validity. + startTime := time.Now() + nextMidnight := time.Date( + startTime.Year(), + startTime.Month(), + startTime.Day()+1, + 0, 0, 0, 0, + startTime.Location(), + ) + alignValidity := func(baseValidity time.Duration) time.Duration { + targetExpiration := nextMidnight.Add(baseValidity) + return time.Until(targetExpiration) + } + _, svcNet, err := net.ParseCIDR(cfg.Network.ServiceNetwork[0]) if err != nil { return nil, err @@ -97,33 +112,33 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { certchains.NewCertificateSigner( "kube-control-plane-signer", cryptomaterial.KubeControlPlaneSignerCertDir(certsDir), - cryptomaterial.ShortLivedCertificateValidity, + alignValidity(cryptomaterial.ShortLivedCertificateValidity), ).WithClientCertificates( &certchains.ClientCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "kube-controller-manager", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, UserInfo: &user.DefaultInfo{Name: "system:kube-controller-manager"}, }, &certchains.ClientCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "kube-scheduler", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, UserInfo: &user.DefaultInfo{Name: "system:kube-scheduler"}, }, &certchains.ClientCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "cluster-policy-controller", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, UserInfo: &user.DefaultInfo{Name: "system:kube-controller-manager"}, }, &certchains.ClientCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "route-controller-manager", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, UserInfo: serviceaccount.UserInfo("openshift-route-controller-manager", "route-controller-manager-sa", ""), }), @@ -132,12 +147,12 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { certchains.NewCertificateSigner( "kube-apiserver-to-kubelet-signer", cryptomaterial.KubeAPIServerToKubeletSignerCertDir(certsDir), - cryptomaterial.ShortLivedCertificateValidity, + alignValidity(cryptomaterial.ShortLivedCertificateValidity), ).WithClientCertificates( &certchains.ClientCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "kube-apiserver-to-kubelet-client", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, UserInfo: &user.DefaultInfo{Name: "system:kube-apiserver", Groups: []string{"kube-master"}}, }), @@ -146,19 +161,19 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { certchains.NewCertificateSigner( "admin-kubeconfig-signer", cryptomaterial.AdminKubeconfigSignerDir(certsDir), - cryptomaterial.LongLivedCertificateValidity, + alignValidity(cryptomaterial.LongLivedCertificateValidity), ).WithClientCertificates( &certchains.ClientCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "admin-kubeconfig-client", - Validity: cryptomaterial.LongLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.LongLivedCertificateValidity), }, UserInfo: &user.DefaultInfo{Name: "system:admin", Groups: []string{"system:masters"}}, }).WithClientCertificates( &certchains.ClientCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "openshift-observability-client", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, UserInfo: &user.DefaultInfo{Name: "openshift-observability-client", Groups: []string{""}}, }, @@ -168,17 +183,17 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { certchains.NewCertificateSigner( "kubelet-signer", cryptomaterial.KubeletCSRSignerSignerCertDir(certsDir), - cryptomaterial.ShortLivedCertificateValidity, + alignValidity(cryptomaterial.ShortLivedCertificateValidity), ).WithSubCAs( certchains.NewCertificateSigner( "kube-csr-signer", cryptomaterial.CSRSignerCertDir(certsDir), - cryptomaterial.ShortLivedCertificateValidity, + alignValidity(cryptomaterial.ShortLivedCertificateValidity), ).WithClientCertificates( &certchains.ClientCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "kubelet-client", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, // userinfo per https://kubernetes.io/docs/reference/access-authn-authz/node/#overview UserInfo: &user.DefaultInfo{Name: "system:node:" + cfg.CanonicalNodeName(), Groups: []string{"system:nodes"}}, @@ -187,7 +202,7 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { &certchains.ServingCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "kubelet-server", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, Hostnames: []string{cfg.Node.HostnameOverride, cfg.Node.NodeIP}, }, @@ -196,12 +211,12 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { certchains.NewCertificateSigner( "aggregator-signer", cryptomaterial.AggregatorSignerDir(certsDir), - cryptomaterial.ShortLivedCertificateValidity, + alignValidity(cryptomaterial.ShortLivedCertificateValidity), ).WithClientCertificates( &certchains.ClientCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "aggregator-client", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, UserInfo: &user.DefaultInfo{Name: "system:openshift-aggregator"}, }, @@ -213,12 +228,12 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { certchains.NewCertificateSigner( "service-ca", cryptomaterial.ServiceCADir(certsDir), - cryptomaterial.LongLivedCertificateValidity, + alignValidity(cryptomaterial.LongLivedCertificateValidity), ).WithServingCertificates( &certchains.ServingCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "route-controller-manager-serving", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, Hostnames: []string{ "route-controller-manager.openshift-route-controller-manager.svc", @@ -230,12 +245,12 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { certchains.NewCertificateSigner( "ingress-ca", cryptomaterial.IngressCADir(certsDir), - cryptomaterial.LongLivedCertificateValidity, + alignValidity(cryptomaterial.LongLivedCertificateValidity), ).WithServingCertificates( &certchains.ServingCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "router-default-serving", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, Hostnames: []string{ "*.apps." + cfg.DNS.BaseDomain, // wildcard for any additional auto-generated domains @@ -248,12 +263,12 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { certchains.NewCertificateSigner( "kube-apiserver-external-signer", cryptomaterial.KubeAPIServerExternalSigner(certsDir), - cryptomaterial.LongLivedCertificateValidity, + alignValidity(cryptomaterial.LongLivedCertificateValidity), ).WithServingCertificates( &certchains.ServingCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "kube-external-serving", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, Hostnames: externalCertNames, }, @@ -262,12 +277,12 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { certchains.NewCertificateSigner( "kube-apiserver-localhost-signer", cryptomaterial.KubeAPIServerLocalhostSigner(certsDir), - cryptomaterial.LongLivedCertificateValidity, + alignValidity(cryptomaterial.LongLivedCertificateValidity), ).WithServingCertificates( &certchains.ServingCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "kube-apiserver-localhost-serving", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, Hostnames: []string{ "localhost", @@ -278,12 +293,12 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { certchains.NewCertificateSigner( "kube-apiserver-service-network-signer", cryptomaterial.KubeAPIServerServiceNetworkSigner(certsDir), - cryptomaterial.LongLivedCertificateValidity, + alignValidity(cryptomaterial.LongLivedCertificateValidity), ).WithServingCertificates( &certchains.ServingCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "kube-apiserver-service-network-serving", - Validity: cryptomaterial.ShortLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.ShortLivedCertificateValidity), }, Hostnames: []string{ "kubernetes", @@ -308,12 +323,12 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { certchains.NewCertificateSigner( "etcd-signer", cryptomaterial.EtcdSignerDir(certsDir), - cryptomaterial.LongLivedCertificateValidity, + alignValidity(cryptomaterial.LongLivedCertificateValidity), ).WithClientCertificates( &certchains.ClientCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "apiserver-etcd-client", - Validity: cryptomaterial.LongLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.LongLivedCertificateValidity), }, UserInfo: &user.DefaultInfo{Name: "etcd", Groups: []string{"etcd"}}, }, @@ -321,7 +336,7 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { &certchains.PeerCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "etcd-peer", - Validity: cryptomaterial.LongLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.LongLivedCertificateValidity), }, UserInfo: &user.DefaultInfo{Name: "system:etcd-peer:etcd-client", Groups: []string{"system:etcd-peers"}}, Hostnames: []string{"localhost", cfg.Node.HostnameOverride, cfg.Node.NodeIP}, @@ -329,7 +344,7 @@ func certSetup(cfg *config.Config) (*certchains.CertificateChains, error) { &certchains.PeerCertificateSigningRequestInfo{ CSRMeta: certchains.CSRMeta{ Name: "etcd-serving", - Validity: cryptomaterial.LongLivedCertificateValidity, + Validity: alignValidity(cryptomaterial.LongLivedCertificateValidity), }, UserInfo: &user.DefaultInfo{Name: "system:etcd-server:etcd-client", Groups: []string{"system:etcd-servers"}}, Hostnames: []string{"localhost", cfg.Node.HostnameOverride, cfg.Node.NodeIP}, diff --git a/test/suites/standard2/validate-certificate-rotation.robot b/test/suites/standard2/validate-certificate-rotation.robot index 0d5bf6b529..6e93bc45d2 100644 --- a/test/suites/standard2/validate-certificate-rotation.robot +++ b/test/suites/standard2/validate-certificate-rotation.robot @@ -25,9 +25,14 @@ ${FUTURE_DAYS} 150 *** Test Cases *** Certificate Rotation [Documentation] Performs Certificate Expiration Rotation test + # Certificates expire at midnight of (tomorrow + validity). For short-lived certs, + # validity is 365 days, so expiry = tomorrow + 365 days. ${first_cert_date}= Compute Date After Days 365 ${OSSL_DATE_FORMAT} Certs Should Expire On ${KUBE_SCHEDULER_CLIENT_CERT} ${first_cert_date} - ${cert_should_expire_in_days}= Evaluate 365+${FUTURE_DAYS} + # After moving the clock forward by FUTURE_DAYS, regenerated certs will expire at + # (new tomorrow + 365). Since "new tomorrow" is (original tomorrow + FUTURE_DAYS + 1), + # the expected expiry from the original date is: tomorrow + 366 + FUTURE_DAYS. + ${cert_should_expire_in_days}= Evaluate 366+${FUTURE_DAYS} ${cert_expiry_date}= Compute Date After Days ${cert_should_expire_in_days} ${OSSL_DATE_FORMAT} ${future_date}= Compute Date After Days ${FUTURE_DAYS} ${TIMEDATECTL_DATE_FORMAT} Change System Date To ${future_date} @@ -67,10 +72,10 @@ Change System Date To Wait For MicroShift Compute Date After Days - [Documentation] return system date after number of days elapsed + [Documentation] return system date after number of days elapsed from midnight tomorrow [Arguments] ${number_of_days} ${date_format} - # date command is used here because we need to consider the remote vm timezone . - ${future_date}= Command Should Work TZ=UTC date "+${date_format}" -d "$(date) + ${number_of_days} day" + # Certificates are aligned to expire at midnight of the next day + validity + ${future_date}= Command Should Work TZ=UTC date "+${date_format}" -d "tomorrow + ${number_of_days} day" RETURN ${future_date} Certs Should Expire On