From dc154e412e2c978ff0ff3cc0664546ee9b433ca5 Mon Sep 17 00:00:00 2001 From: SataQiu Date: Fri, 9 Apr 2021 14:11:02 +0800 Subject: [PATCH] kubeadm: add --validity-period flag for 'kubeadm kubeconfig user' command --- cmd/kubeadm/app/cmd/alpha/kubeconfig.go | 16 +++++++++++-- .../app/phases/kubeconfig/kubeconfig.go | 24 ++++++++++--------- .../app/phases/kubeconfig/kubeconfig_test.go | 12 +++++----- cmd/kubeadm/app/util/pkiutil/pki_helpers.go | 8 ++++++- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/cmd/kubeadm/app/cmd/alpha/kubeconfig.go b/cmd/kubeadm/app/cmd/alpha/kubeconfig.go index bb1dc25d426..7e75907f244 100644 --- a/cmd/kubeadm/app/cmd/alpha/kubeconfig.go +++ b/cmd/kubeadm/app/cmd/alpha/kubeconfig.go @@ -18,12 +18,15 @@ package alpha import ( "io" + "time" "github.com/spf13/cobra" + "k8s.io/klog/v2" kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" 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" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" ) @@ -64,6 +67,7 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command { var ( token, clientName, cfgPath string organizations []string + validityPeriod time.Duration ) // Creates the UX Command @@ -79,13 +83,20 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command { return err } + if validityPeriod > kubeadmconstants.CertificateValidity { + klog.Warningf("WARNING: the specified certificate validity period %v is longer than the default duration %v, this may increase security risks.", + validityPeriod, kubeadmconstants.CertificateValidity) + } + + notAfter := time.Now().Add(validityPeriod).UTC() + // if the kubeconfig file for an additional user has to use a token, use it if token != "" { - return kubeconfigphase.WriteKubeConfigWithToken(out, internalCfg, clientName, token) + return kubeconfigphase.WriteKubeConfigWithToken(out, internalCfg, clientName, token, ¬After) } // Otherwise, write a kubeconfig file with a generate client cert - return kubeconfigphase.WriteKubeConfigWithClientCert(out, internalCfg, clientName, organizations) + return kubeconfigphase.WriteKubeConfigWithClientCert(out, internalCfg, clientName, organizations, ¬After) }, Args: cobra.NoArgs, } @@ -96,6 +107,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 orgnizations 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.MarkFlagRequired(options.CfgPath) cmd.MarkFlagRequired("client-name") diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go index 48c5af8ef1b..72b9890f6d0 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go @@ -24,6 +24,7 @@ import ( "io" "os" "path/filepath" + "time" "github.com/pkg/errors" @@ -120,7 +121,7 @@ func createKubeConfigFiles(outDir string, cfg *kubeadmapi.InitConfiguration, kub } // builds the KubeConfig object - config, err := buildKubeConfigFromSpec(spec, cfg.ClusterName) + config, err := buildKubeConfigFromSpec(spec, cfg.ClusterName, nil) if err != nil { return err } @@ -156,7 +157,7 @@ func getKubeConfigSpecs(cfg *kubeadmapi.InitConfiguration) (map[string]*kubeConf } // buildKubeConfigFromSpec creates a kubeconfig object for the given kubeConfigSpec -func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string) (*clientcmdapi.Config, error) { +func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string, notAfter *time.Time) (*clientcmdapi.Config, error) { // If this kubeconfig should use token if spec.TokenAuth != nil { @@ -171,7 +172,7 @@ func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string) (*clientc } // otherwise, create a client certs - clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec) + clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec, notAfter) clientCert, clientKey, err := pkiutil.NewCertAndKey(spec.CACert, spec.ClientCertAuth.CAKey, &clientCertConfig) if err != nil { @@ -193,13 +194,14 @@ func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string) (*clientc ), nil } -func newClientCertConfigFromKubeConfigSpec(spec *kubeConfigSpec) pkiutil.CertConfig { +func newClientCertConfigFromKubeConfigSpec(spec *kubeConfigSpec, notAfter *time.Time) pkiutil.CertConfig { return pkiutil.CertConfig{ Config: certutil.Config{ CommonName: spec.ClientName, Organization: spec.ClientCertAuth.Organizations, Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, }, + NotAfter: notAfter, } } @@ -279,7 +281,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) 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) @@ -304,11 +306,11 @@ func WriteKubeConfigWithClientCert(out io.Writer, cfg *kubeadmapi.InitConfigurat }, } - return writeKubeConfigFromSpec(out, spec, cfg.ClusterName) + return writeKubeConfigFromSpec(out, spec, cfg.ClusterName, notAfter) } // 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) 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) @@ -332,14 +334,14 @@ func WriteKubeConfigWithToken(out io.Writer, cfg *kubeadmapi.InitConfiguration, }, } - return writeKubeConfigFromSpec(out, spec, cfg.ClusterName) + return writeKubeConfigFromSpec(out, spec, cfg.ClusterName, notAfter) } // writeKubeConfigFromSpec creates a kubeconfig object from a kubeConfigSpec and writes it to the given writer. -func writeKubeConfigFromSpec(out io.Writer, spec *kubeConfigSpec, clustername string) error { +func writeKubeConfigFromSpec(out io.Writer, spec *kubeConfigSpec, clustername string, notAfter *time.Time) error { // builds the KubeConfig object - config, err := buildKubeConfigFromSpec(spec, clustername) + config, err := buildKubeConfigFromSpec(spec, clustername, notAfter) if err != nil { return err } @@ -465,7 +467,7 @@ func createKubeConfigAndCSR(kubeConfigDir string, kubeadmConfig *kubeadmapi.Init return errors.Errorf("%s: csr: %s", errExist, kubeConfigPath) } - clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec) + clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec, nil) clientKey, err := pkiutil.NewPrivateKey(clientCertConfig.PublicKeyAlgorithm) if err != nil { diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go index df5d47b64e1..bdd3a80c8ab 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig_test.go @@ -374,13 +374,13 @@ func TestWriteKubeConfigFailsIfCADoesntExists(t *testing.T) { { name: "WriteKubeConfigWithClientCert", writeKubeConfigFunction: func(out io.Writer) error { - return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}) + return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}, nil) }, }, { name: "WriteKubeConfigWithToken", writeKubeConfigFunction: func(out io.Writer) error { - return WriteKubeConfigWithToken(out, cfg, "myUser", "12345") + return WriteKubeConfigWithToken(out, cfg, "myUser", "12345", nil) }, }, } @@ -428,14 +428,14 @@ func TestWriteKubeConfig(t *testing.T) { { name: "WriteKubeConfigWithClientCert", writeKubeConfigFunction: func(out io.Writer) error { - return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}) + return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}, nil) }, withClientCert: true, }, { name: "WriteKubeConfigWithToken", writeKubeConfigFunction: func(out io.Writer) error { - return WriteKubeConfigWithToken(out, cfg, "myUser", "12345") + return WriteKubeConfigWithToken(out, cfg, "myUser", "12345", nil) }, withToken: true, }, @@ -664,7 +664,7 @@ func setupdKubeConfigWithClientAuth(t *testing.T, caCert *x509.Certificate, caKe }, } - config, err := buildKubeConfigFromSpec(spec, clustername) + config, err := buildKubeConfigFromSpec(spec, clustername, nil) if err != nil { t.Fatal("buildKubeConfigFromSpec failed!") } @@ -683,7 +683,7 @@ func setupdKubeConfigWithTokenAuth(t *testing.T, caCert *x509.Certificate, APISe }, } - config, err := buildKubeConfigFromSpec(spec, clustername) + config, err := buildKubeConfigFromSpec(spec, clustername, nil) if err != nil { t.Fatal("buildKubeConfigFromSpec failed!") } diff --git a/cmd/kubeadm/app/util/pkiutil/pki_helpers.go b/cmd/kubeadm/app/util/pkiutil/pki_helpers.go index 9983a837620..9be4fa09e1c 100644 --- a/cmd/kubeadm/app/util/pkiutil/pki_helpers.go +++ b/cmd/kubeadm/app/util/pkiutil/pki_helpers.go @@ -62,6 +62,7 @@ const ( // CertConfig is a wrapper around certutil.Config extending it with PublicKeyAlgorithm. type CertConfig struct { certutil.Config + NotAfter *time.Time PublicKeyAlgorithm x509.PublicKeyAlgorithm } @@ -647,6 +648,11 @@ 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 + } + certTmpl := x509.Certificate{ Subject: pkix.Name{ CommonName: cfg.CommonName, @@ -656,7 +662,7 @@ func NewSignedCert(cfg *CertConfig, key crypto.Signer, caCert *x509.Certificate, IPAddresses: cfg.AltNames.IPs, SerialNumber: serial, NotBefore: caCert.NotBefore, - NotAfter: time.Now().Add(kubeadmconstants.CertificateValidity).UTC(), + NotAfter: notAfter, KeyUsage: keyUsage, ExtKeyUsage: cfg.Usages, BasicConstraintsValid: true,