Merge pull request #74671 from yagonobre/certificate-key

Add certificate-key to kubeadm upload-certs phase, and improve init output
pull/564/head
Kubernetes Prow Robot 2019-03-05 21:42:32 -08:00 committed by GitHub
commit 2b63efcd3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 40 deletions

View File

@ -75,16 +75,18 @@ var (
// Please note that this structure includes the public kubeadm config API, but only a subset of the options // Please note that this structure includes the public kubeadm config API, but only a subset of the options
// supported by this api will be exposed as a flag. // supported by this api will be exposed as a flag.
type initOptions struct { type initOptions struct {
cfgPath string cfgPath string
skipTokenPrint bool skipTokenPrint bool
dryRun bool dryRun bool
kubeconfigDir string kubeconfigDir string
kubeconfigPath string kubeconfigPath string
featureGatesString string featureGatesString string
ignorePreflightErrors []string ignorePreflightErrors []string
bto *options.BootstrapTokenOptions bto *options.BootstrapTokenOptions
externalcfg *kubeadmapiv1beta1.InitConfiguration externalcfg *kubeadmapiv1beta1.InitConfiguration
uploadCerts bool uploadCerts bool
certificateKey string
skipCertificateKeyPrint bool
} }
// compile-time assert that the local data object satisfies the phases data interface. // compile-time assert that the local data object satisfies the phases data interface.
@ -93,20 +95,21 @@ var _ phases.InitData = &initData{}
// initData defines all the runtime information used when running the kubeadm init worklow; // initData defines all the runtime information used when running the kubeadm init worklow;
// this data is shared across all the phases that are included in the workflow. // this data is shared across all the phases that are included in the workflow.
type initData struct { type initData struct {
cfg *kubeadmapi.InitConfiguration cfg *kubeadmapi.InitConfiguration
skipTokenPrint bool skipTokenPrint bool
dryRun bool dryRun bool
kubeconfigDir string kubeconfigDir string
kubeconfigPath string kubeconfigPath string
ignorePreflightErrors sets.String ignorePreflightErrors sets.String
certificatesDir string certificatesDir string
dryRunDir string dryRunDir string
externalCA bool externalCA bool
client clientset.Interface client clientset.Interface
waiter apiclient.Waiter waiter apiclient.Waiter
outputWriter io.Writer outputWriter io.Writer
uploadCerts bool uploadCerts bool
certificateKey string certificateKey string
skipCertificateKeyPrint bool
} }
// NewCmdInit returns "kubeadm init" command. // NewCmdInit returns "kubeadm init" command.
@ -241,7 +244,15 @@ func AddInitOtherFlags(flagSet *flag.FlagSet, initOptions *initOptions) {
) )
flagSet.BoolVar( flagSet.BoolVar(
&initOptions.uploadCerts, options.UploadCerts, initOptions.uploadCerts, &initOptions.uploadCerts, options.UploadCerts, initOptions.uploadCerts,
"Upload certfificates to kubeadm-certs secret.", "Upload control-plane certificates to the kubeadm-certs Secret.",
)
flagSet.StringVar(
&initOptions.certificateKey, options.CertificateKey, "",
"Key used to encrypt the control-plane certificates in the kubeadm-certs Secret.",
)
flagSet.BoolVar(
&initOptions.skipCertificateKeyPrint, options.SkipCertificateKeyPrint, initOptions.skipCertificateKeyPrint,
"Don't print the key used to encrypt the control-plane certificates.",
) )
} }
@ -337,17 +348,19 @@ func newInitData(cmd *cobra.Command, args []string, options *initOptions, out io
} }
return &initData{ return &initData{
cfg: cfg, cfg: cfg,
certificatesDir: cfg.CertificatesDir, certificatesDir: cfg.CertificatesDir,
skipTokenPrint: options.skipTokenPrint, skipTokenPrint: options.skipTokenPrint,
dryRun: options.dryRun, dryRun: options.dryRun,
dryRunDir: dryRunDir, dryRunDir: dryRunDir,
kubeconfigDir: options.kubeconfigDir, kubeconfigDir: options.kubeconfigDir,
kubeconfigPath: options.kubeconfigPath, kubeconfigPath: options.kubeconfigPath,
ignorePreflightErrors: ignorePreflightErrorsSet, ignorePreflightErrors: ignorePreflightErrorsSet,
externalCA: externalCA, externalCA: externalCA,
outputWriter: out, outputWriter: out,
uploadCerts: options.uploadCerts, uploadCerts: options.uploadCerts,
certificateKey: options.certificateKey,
skipCertificateKeyPrint: options.skipCertificateKeyPrint,
}, nil }, nil
} }
@ -472,7 +485,7 @@ func (d *initData) Tokens() []string {
} }
func printJoinCommand(out io.Writer, adminKubeConfigPath, token string, i *initData) error { func printJoinCommand(out io.Writer, adminKubeConfigPath, token string, i *initData) error {
joinCommand, err := cmdutil.GetJoinCommand(adminKubeConfigPath, token, i.certificateKey, i.skipTokenPrint, i.uploadCerts) joinCommand, err := cmdutil.GetJoinCommand(adminKubeConfigPath, token, i.certificateKey, i.skipTokenPrint, i.uploadCerts, i.skipCertificateKeyPrint)
if err != nil { if err != nil {
return err return err
} }

View File

@ -124,4 +124,7 @@ const (
// CertificateKey flag sets the key used to encrypt and decrypt certificate secrets // CertificateKey flag sets the key used to encrypt and decrypt certificate secrets
CertificateKey = "certificate-key" CertificateKey = "certificate-key"
// SkipCertificateKeyPrint flag instruct kubeadm to skip printing certificate key used to encrypt certs by 'kubeadm init'.
SkipCertificateKeyPrint = "skip-certificate-key-print"
) )

View File

@ -39,6 +39,7 @@ func NewUploadCertsPhase() workflow.Phase {
InheritFlags: []string{ InheritFlags: []string{
options.CfgPath, options.CfgPath,
options.UploadCerts, options.UploadCerts,
options.CertificateKey,
}, },
} }
} }

View File

@ -231,7 +231,8 @@ func RunCreateToken(out io.Writer, client clientset.Interface, cfgPath string, c
key := "" key := ""
skipTokenPrint := false skipTokenPrint := false
uploadCerts := false uploadCerts := false
joinCommand, err := cmdutil.GetJoinCommand(kubeConfigFile, internalcfg.BootstrapTokens[0].Token.String(), key, skipTokenPrint, uploadCerts) skipCertificateKeyPrint := false
joinCommand, err := cmdutil.GetJoinCommand(kubeConfigFile, internalcfg.BootstrapTokens[0].Token.String(), key, skipTokenPrint, uploadCerts, skipCertificateKeyPrint)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to get join command") return errors.Wrap(err, "failed to get join command")
} }

View File

@ -30,12 +30,19 @@ import (
) )
var joinCommandTemplate = template.Must(template.New("join").Parse(`` + var joinCommandTemplate = template.Must(template.New("join").Parse(`` +
`kubeadm join {{.ControlPlaneHostPort}} --token {{.Token}}{{range $h := .CAPubKeyPins}} --discovery-token-ca-cert-hash {{$h}}{{end}}{{if .UploadCerts}} --certificate-key {{.CertificateKey}}{{end}}`, `{{if .UploadCerts}}You can now join any number of control-plane node running the following command on each as a root:{{else}}You can now join any number of control-plane node by copying the required certificate authorities on each node and then running the following as root:{{end}}
kubeadm join {{.ControlPlaneHostPort}} --token {{.Token}}{{range $h := .CAPubKeyPins}} --discovery-token-ca-cert-hash {{$h}}{{end}} --experimental-control-plane {{if .UploadCerts}}--certificate-key {{.CertificateKey}}
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use kubeadm init phase upload-certs to reload certs afterward.{{end}}
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join {{.ControlPlaneHostPort}} --token {{.Token}}{{range $h := .CAPubKeyPins}} --discovery-token-ca-cert-hash {{$h}}{{end}}`,
)) ))
// GetJoinCommand returns the kubeadm join command for a given token and // GetJoinCommand returns the kubeadm join command for a given token and
// and Kubernetes cluster (the current cluster in the kubeconfig file) // and Kubernetes cluster (the current cluster in the kubeconfig file)
func GetJoinCommand(kubeConfigFile, token, key string, skipTokenPrint, uploadCerts bool) (string, error) { func GetJoinCommand(kubeConfigFile, token, key string, skipTokenPrint, uploadCerts, skipCertificateKeyPrint bool) (string, error) {
// load the kubeconfig file to get the CA certificate and endpoint // load the kubeconfig file to get the CA certificate and endpoint
config, err := clientcmd.LoadFromFile(kubeConfigFile) config, err := clientcmd.LoadFromFile(kubeConfigFile)
if err != nil { if err != nil {
@ -81,6 +88,9 @@ func GetJoinCommand(kubeConfigFile, token, key string, skipTokenPrint, uploadCer
if skipTokenPrint { if skipTokenPrint {
ctx["Token"] = template.HTML("<value withheld>") ctx["Token"] = template.HTML("<value withheld>")
} }
if skipCertificateKeyPrint {
ctx["CertificateKey"] = template.HTML("<value withheld>")
}
var out bytes.Buffer var out bytes.Buffer
err = joinCommandTemplate.Execute(&out, ctx) err = joinCommandTemplate.Execute(&out, ctx)