kubeadm: add --validity-period flag for 'kubeadm kubeconfig user' command

This commit is contained in:
SataQiu 2021-04-09 14:11:02 +08:00
parent 11f95dc047
commit dc154e412e
4 changed files with 40 additions and 20 deletions

View File

@ -18,12 +18,15 @@ package alpha
import ( import (
"io" "io"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/klog/v2"
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" 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" kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
) )
@ -64,6 +67,7 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command {
var ( var (
token, clientName, cfgPath string token, clientName, cfgPath string
organizations []string organizations []string
validityPeriod time.Duration
) )
// Creates the UX Command // Creates the UX Command
@ -79,13 +83,20 @@ func newCmdUserKubeConfig(out io.Writer) *cobra.Command {
return err 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 the kubeconfig file for an additional user has to use a token, use it
if token != "" { if token != "" {
return kubeconfigphase.WriteKubeConfigWithToken(out, internalCfg, clientName, token) return kubeconfigphase.WriteKubeConfigWithToken(out, internalCfg, clientName, token, &notAfter)
} }
// Otherwise, write a kubeconfig file with a generate client cert // Otherwise, write a kubeconfig file with a generate client cert
return kubeconfigphase.WriteKubeConfigWithClientCert(out, internalCfg, clientName, organizations) return kubeconfigphase.WriteKubeConfigWithClientCert(out, internalCfg, clientName, organizations, &notAfter)
}, },
Args: cobra.NoArgs, 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(&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().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().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(options.CfgPath)
cmd.MarkFlagRequired("client-name") cmd.MarkFlagRequired("client-name")

View File

@ -24,6 +24,7 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"time"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -120,7 +121,7 @@ func createKubeConfigFiles(outDir string, cfg *kubeadmapi.InitConfiguration, kub
} }
// builds the KubeConfig object // builds the KubeConfig object
config, err := buildKubeConfigFromSpec(spec, cfg.ClusterName) config, err := buildKubeConfigFromSpec(spec, cfg.ClusterName, nil)
if err != nil { if err != nil {
return err return err
} }
@ -156,7 +157,7 @@ func getKubeConfigSpecs(cfg *kubeadmapi.InitConfiguration) (map[string]*kubeConf
} }
// buildKubeConfigFromSpec creates a kubeconfig object for the given kubeConfigSpec // 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 this kubeconfig should use token
if spec.TokenAuth != nil { if spec.TokenAuth != nil {
@ -171,7 +172,7 @@ func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string) (*clientc
} }
// otherwise, create a client certs // otherwise, create a client certs
clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec) clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec, notAfter)
clientCert, clientKey, err := pkiutil.NewCertAndKey(spec.CACert, spec.ClientCertAuth.CAKey, &clientCertConfig) clientCert, clientKey, err := pkiutil.NewCertAndKey(spec.CACert, spec.ClientCertAuth.CAKey, &clientCertConfig)
if err != nil { if err != nil {
@ -193,13 +194,14 @@ func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string) (*clientc
), nil ), nil
} }
func newClientCertConfigFromKubeConfigSpec(spec *kubeConfigSpec) pkiutil.CertConfig { func newClientCertConfigFromKubeConfigSpec(spec *kubeConfigSpec, notAfter *time.Time) pkiutil.CertConfig {
return pkiutil.CertConfig{ return pkiutil.CertConfig{
Config: certutil.Config{ Config: certutil.Config{
CommonName: spec.ClientName, CommonName: spec.ClientName,
Organization: spec.ClientCertAuth.Organizations, Organization: spec.ClientCertAuth.Organizations,
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, 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. // 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 // creates the KubeConfigSpecs, actualized for the current InitConfiguration
caCert, caKey, err := pkiutil.TryLoadCertAndKeyFromDisk(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName) 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. // 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 // creates the KubeConfigSpecs, actualized for the current InitConfiguration
caCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName) 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. // 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 // builds the KubeConfig object
config, err := buildKubeConfigFromSpec(spec, clustername) config, err := buildKubeConfigFromSpec(spec, clustername, notAfter)
if err != nil { if err != nil {
return err return err
} }
@ -465,7 +467,7 @@ func createKubeConfigAndCSR(kubeConfigDir string, kubeadmConfig *kubeadmapi.Init
return errors.Errorf("%s: csr: %s", errExist, kubeConfigPath) return errors.Errorf("%s: csr: %s", errExist, kubeConfigPath)
} }
clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec) clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec, nil)
clientKey, err := pkiutil.NewPrivateKey(clientCertConfig.PublicKeyAlgorithm) clientKey, err := pkiutil.NewPrivateKey(clientCertConfig.PublicKeyAlgorithm)
if err != nil { if err != nil {

View File

@ -374,13 +374,13 @@ func TestWriteKubeConfigFailsIfCADoesntExists(t *testing.T) {
{ {
name: "WriteKubeConfigWithClientCert", name: "WriteKubeConfigWithClientCert",
writeKubeConfigFunction: func(out io.Writer) error { writeKubeConfigFunction: func(out io.Writer) error {
return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}) return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}, nil)
}, },
}, },
{ {
name: "WriteKubeConfigWithToken", name: "WriteKubeConfigWithToken",
writeKubeConfigFunction: func(out io.Writer) error { 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", name: "WriteKubeConfigWithClientCert",
writeKubeConfigFunction: func(out io.Writer) error { writeKubeConfigFunction: func(out io.Writer) error {
return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}) return WriteKubeConfigWithClientCert(out, cfg, "myUser", []string{"myOrg"}, nil)
}, },
withClientCert: true, withClientCert: true,
}, },
{ {
name: "WriteKubeConfigWithToken", name: "WriteKubeConfigWithToken",
writeKubeConfigFunction: func(out io.Writer) error { writeKubeConfigFunction: func(out io.Writer) error {
return WriteKubeConfigWithToken(out, cfg, "myUser", "12345") return WriteKubeConfigWithToken(out, cfg, "myUser", "12345", nil)
}, },
withToken: true, 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 { if err != nil {
t.Fatal("buildKubeConfigFromSpec failed!") 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 { if err != nil {
t.Fatal("buildKubeConfigFromSpec failed!") t.Fatal("buildKubeConfigFromSpec failed!")
} }

View File

@ -62,6 +62,7 @@ const (
// CertConfig is a wrapper around certutil.Config extending it with PublicKeyAlgorithm. // CertConfig is a wrapper around certutil.Config extending it with PublicKeyAlgorithm.
type CertConfig struct { type CertConfig struct {
certutil.Config certutil.Config
NotAfter *time.Time
PublicKeyAlgorithm x509.PublicKeyAlgorithm PublicKeyAlgorithm x509.PublicKeyAlgorithm
} }
@ -647,6 +648,11 @@ func NewSignedCert(cfg *CertConfig, key crypto.Signer, caCert *x509.Certificate,
RemoveDuplicateAltNames(&cfg.AltNames) RemoveDuplicateAltNames(&cfg.AltNames)
notAfter := time.Now().Add(kubeadmconstants.CertificateValidity).UTC()
if cfg.NotAfter != nil {
notAfter = *cfg.NotAfter
}
certTmpl := x509.Certificate{ certTmpl := x509.Certificate{
Subject: pkix.Name{ Subject: pkix.Name{
CommonName: cfg.CommonName, CommonName: cfg.CommonName,
@ -656,7 +662,7 @@ func NewSignedCert(cfg *CertConfig, key crypto.Signer, caCert *x509.Certificate,
IPAddresses: cfg.AltNames.IPs, IPAddresses: cfg.AltNames.IPs,
SerialNumber: serial, SerialNumber: serial,
NotBefore: caCert.NotBefore, NotBefore: caCert.NotBefore,
NotAfter: time.Now().Add(kubeadmconstants.CertificateValidity).UTC(), NotAfter: notAfter,
KeyUsage: keyUsage, KeyUsage: keyUsage,
ExtKeyUsage: cfg.Usages, ExtKeyUsage: cfg.Usages,
BasicConstraintsValid: true, BasicConstraintsValid: true,