From 46dc023f93ee7905170c4009523d3fc3fd39d2d8 Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Sun, 25 Feb 2018 11:01:40 +0100 Subject: [PATCH 1/2] autogenerated files --- .../apis/kubeadm/v1alpha1/zz_generated.conversion.go | 4 ++++ .../app/apis/kubeadm/v1alpha1/zz_generated.deepcopy.go | 10 ++++++++++ cmd/kubeadm/app/apis/kubeadm/validation/BUILD | 2 ++ cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go | 10 ++++++++++ cmd/kubeadm/app/cmd/BUILD | 3 ++- cmd/kubeadm/app/cmd/phases/BUILD | 1 - 6 files changed, 28 insertions(+), 2 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.conversion.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.conversion.go index 1f5b4d2d0a0..51bffac145d 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.conversion.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.conversion.go @@ -239,6 +239,8 @@ func autoConvert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in out.PrivilegedPods = in.PrivilegedPods out.Token = in.Token out.TokenTTL = (*v1.Duration)(unsafe.Pointer(in.TokenTTL)) + out.TokenUsages = *(*[]string)(unsafe.Pointer(&in.TokenUsages)) + out.TokenGroups = *(*[]string)(unsafe.Pointer(&in.TokenGroups)) out.CRISocket = in.CRISocket out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs)) out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs)) @@ -287,6 +289,8 @@ func autoConvert_kubeadm_MasterConfiguration_To_v1alpha1_MasterConfiguration(in out.PrivilegedPods = in.PrivilegedPods out.Token = in.Token out.TokenTTL = (*v1.Duration)(unsafe.Pointer(in.TokenTTL)) + out.TokenUsages = *(*[]string)(unsafe.Pointer(&in.TokenUsages)) + out.TokenGroups = *(*[]string)(unsafe.Pointer(&in.TokenGroups)) out.CRISocket = in.CRISocket out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs)) out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs)) diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.deepcopy.go index 578c431af99..67a5a935be7 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/zz_generated.deepcopy.go @@ -204,6 +204,16 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) { **out = **in } } + if in.TokenUsages != nil { + in, out := &in.TokenUsages, &out.TokenUsages + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.TokenGroups != nil { + in, out := &in.TokenGroups, &out.TokenGroups + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.APIServerExtraArgs != nil { in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs *out = make(map[string]string, len(*in)) diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/BUILD b/cmd/kubeadm/app/apis/kubeadm/validation/BUILD index ccd33aa3986..022e9c7a34c 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/BUILD +++ b/cmd/kubeadm/app/apis/kubeadm/validation/BUILD @@ -25,6 +25,8 @@ go_library( "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", + "//vendor/k8s.io/client-go/tools/bootstrap/token/api:go_default_library", + "//vendor/k8s.io/client-go/tools/bootstrap/token/util:go_default_library", ], ) diff --git a/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go index 30c6c6c5d92..52421b0ed3f 100644 --- a/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go +++ b/cmd/kubeadm/app/apis/kubeadm/zz_generated.deepcopy.go @@ -204,6 +204,16 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) { **out = **in } } + if in.TokenUsages != nil { + in, out := &in.TokenUsages, &out.TokenUsages + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.TokenGroups != nil { + in, out := &in.TokenGroups, &out.TokenGroups + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.APIServerExtraArgs != nil { in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs *out = make(map[string]string, len(*in)) diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index ddc77a00694..7c9174a09d5 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -72,7 +72,6 @@ go_library( "//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/tools/bootstrap/token/api:go_default_library", - "//vendor/k8s.io/client-go/tools/bootstrap/token/util:go_default_library", "//vendor/k8s.io/client-go/util/cert:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library", ], @@ -86,10 +85,12 @@ go_test( ], embed = [":go_default_library"], deps = [ + "//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/preflight:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/client-go/kubernetes/fake:go_default_library", "//vendor/k8s.io/client-go/testing:go_default_library", diff --git a/cmd/kubeadm/app/cmd/phases/BUILD b/cmd/kubeadm/app/cmd/phases/BUILD index 17354d3b0e4..da53a82dedd 100644 --- a/cmd/kubeadm/app/cmd/phases/BUILD +++ b/cmd/kubeadm/app/cmd/phases/BUILD @@ -55,7 +55,6 @@ go_library( "//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/tools/bootstrap/token/api:go_default_library", - "//vendor/k8s.io/client-go/tools/bootstrap/token/util:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library", ], ) From e247752ef9c1026dbd3d3c561515736bd36be6a6 Mon Sep 17 00:00:00 2001 From: fabriziopandini Date: Sun, 25 Feb 2018 11:02:04 +0100 Subject: [PATCH 2/2] kubeadm create token using config file --- cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go | 4 +- cmd/kubeadm/app/apis/kubeadm/types.go | 6 +- .../app/apis/kubeadm/v1alpha1/defaults.go | 8 ++ .../app/apis/kubeadm/v1alpha1/types.go | 6 +- .../app/apis/kubeadm/validation/validation.go | 37 +++++++++ .../kubeadm/validation/validation_test.go | 45 ++++++++++ cmd/kubeadm/app/cmd/init.go | 2 +- cmd/kubeadm/app/cmd/phases/bootstraptoken.go | 41 +++------ cmd/kubeadm/app/cmd/token.go | 83 ++++++++----------- cmd/kubeadm/app/cmd/token_test.go | 15 +++- cmd/kubeadm/app/constants/constants.go | 3 + 11 files changed, 169 insertions(+), 81 deletions(-) diff --git a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go index 2709df23e18..850f41b0066 100644 --- a/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go +++ b/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go @@ -36,7 +36,6 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { c.FuzzNoCustom(obj) obj.KubernetesVersion = "v10" obj.API.BindPort = 20 - obj.TokenTTL = &metav1.Duration{Duration: 1 * time.Hour} obj.API.AdvertiseAddress = "foo" obj.Networking.ServiceSubnet = "foo" obj.Networking.DNSDomain = "foo" @@ -47,6 +46,9 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { obj.Etcd.PeerCertSANs = []string{"foo"} obj.Token = "foo" obj.CRISocket = "foo" + obj.TokenTTL = &metav1.Duration{Duration: 1 * time.Hour} + obj.TokenUsages = []string{"foo"} + obj.TokenGroups = []string{"foo"} obj.Etcd.Image = "foo" obj.Etcd.DataDir = "foo" obj.ImageRepository = "foo" diff --git a/cmd/kubeadm/app/apis/kubeadm/types.go b/cmd/kubeadm/app/apis/kubeadm/types.go index cf6c9db3a1f..5f6c6c1b183 100644 --- a/cmd/kubeadm/app/apis/kubeadm/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/types.go @@ -64,8 +64,12 @@ type MasterConfiguration struct { // Token is used for establishing bidirectional trust between nodes and masters. // Used for joining nodes in the cluster. Token string - // TokenTTL is a ttl for Token. Defaults to 24h. + // TokenTTL defines the ttl for Token. Defaults to 24h. TokenTTL *metav1.Duration + // TokenUsages describes the ways in which this token can be used. + TokenUsages []string + // Extra groups that this token will authenticate as when used for authentication + TokenGroups []string // CRISocket is used to retrieve container runtime info. CRISocket string diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go index 5fabfe4d577..c806d4463a4 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go @@ -116,6 +116,14 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) { obj.CRISocket = DefaultCRISocket } + if len(obj.TokenUsages) == 0 { + obj.TokenUsages = constants.DefaultTokenUsages + } + + if len(obj.TokenGroups) == 0 { + obj.TokenGroups = constants.DefaultTokenGroups + } + if obj.ImageRepository == "" { obj.ImageRepository = DefaultImageRepository } diff --git a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go index 4d94ad7f208..76911cf399c 100644 --- a/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go +++ b/cmd/kubeadm/app/apis/kubeadm/v1alpha1/types.go @@ -64,8 +64,12 @@ type MasterConfiguration struct { // Token is used for establishing bidirectional trust between nodes and masters. // Used for joining nodes in the cluster. Token string `json:"token"` - // TokenTTL is a ttl for Token. Defaults to 24h. + // TokenTTL defines the ttl for Token. Defaults to 24h. TokenTTL *metav1.Duration `json:"tokenTTL,omitempty"` + // TokenUsages describes the ways in which this token can be used. + TokenUsages []string `json:"tokenUsages,omitempty"` + // Extra groups that this token will authenticate as when used for authentication + TokenGroups []string `json:"tokenGroups,omitempty"` // CRISocket is used to retrieve container runtime info. CRISocket string `json:"criSocket,omitempty"` diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index 3e8aa557515..dbef2b7a5f0 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -29,6 +29,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation/field" + bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api" + bootstraputil "k8s.io/client-go/tools/bootstrap/token/util" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" "k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/features" @@ -78,6 +80,8 @@ func ValidateMasterConfiguration(c *kubeadm.MasterConfiguration) field.ErrorList allErrs = append(allErrs, ValidateAbsolutePath(c.CertificatesDir, field.NewPath("certificates-dir"))...) allErrs = append(allErrs, ValidateNodeName(c.NodeName, field.NewPath("node-name"))...) allErrs = append(allErrs, ValidateToken(c.Token, field.NewPath("token"))...) + allErrs = append(allErrs, ValidateTokenUsages(c.TokenUsages, field.NewPath("tokenUsages"))...) + allErrs = append(allErrs, ValidateTokenGroups(c.TokenUsages, c.TokenGroups, field.NewPath("tokenGroups"))...) allErrs = append(allErrs, ValidateFeatureGates(c.FeatureGates, field.NewPath("feature-gates"))...) allErrs = append(allErrs, ValidateAPIEndpoint(c, field.NewPath("api-endpoint"))...) allErrs = append(allErrs, ValidateProxy(c, field.NewPath("kube-proxy"))...) @@ -230,6 +234,39 @@ func ValidateToken(t string, fldPath *field.Path) field.ErrorList { return allErrs } +// ValidateTokenGroups validates token groups +func ValidateTokenGroups(usages []string, groups []string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + // adding groups only makes sense for authentication + usagesSet := sets.NewString(usages...) + usageAuthentication := strings.TrimPrefix(bootstrapapi.BootstrapTokenUsageAuthentication, bootstrapapi.BootstrapTokenUsagePrefix) + if len(groups) > 0 && !usagesSet.Has(usageAuthentication) { + allErrs = append(allErrs, field.Invalid(fldPath, groups, fmt.Sprintf("token groups cannot be specified unless --usages includes %q", usageAuthentication))) + } + + // validate any extra group names + for _, group := range groups { + if err := bootstraputil.ValidateBootstrapGroupName(group); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath, groups, err.Error())) + } + } + + return allErrs +} + +// ValidateTokenUsages validates token usages +func ValidateTokenUsages(usages []string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + // validate usages + if err := bootstraputil.ValidateUsages(usages); err != nil { + allErrs = append(allErrs, field.Invalid(fldPath, usages, err.Error())) + } + + return allErrs +} + // ValidateCertSANs validates alternative names func ValidateCertSANs(altnames []string, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index 29262a998c1..a1aca11cbb6 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -59,6 +59,51 @@ func TestValidateTokenDiscovery(t *testing.T) { } } +func TestValidateValidateTokenUsages(t *testing.T) { + var tests = []struct { + u []string + f *field.Path + expected bool + }{ + {[]string{}, nil, true}, // supported (no usages) + {[]string{"signing", "authentication"}, nil, true}, // supported + {[]string{"something else"}, nil, false}, // usage not supported + } + for _, rt := range tests { + actual := ValidateTokenUsages(rt.u, rt.f) + if (len(actual) == 0) != rt.expected { + t.Errorf( + "failed ValidateTokenUsages:\n\texpected: %t\n\t actual: %t", + rt.expected, + (len(actual) == 0), + ) + } + } +} + +func TestValidateTokenGroups(t *testing.T) { + var tests = []struct { + u []string + g []string + f *field.Path + expected bool + }{ + {[]string{"some usage"}, []string{"some group"}, nil, false}, // groups doesn't makes sense if usage authentication + {[]string{"authentication"}, []string{"some group"}, nil, false}, // group not supported + {[]string{"authentication"}, []string{"system:bootstrappers:anygroup"}, nil, true}, // supported + } + for _, rt := range tests { + actual := ValidateTokenGroups(rt.u, rt.g, rt.f) + if (len(actual) == 0) != rt.expected { + t.Errorf( + "failed ValidateTokenGroups:\n\texpected: %t\n\t actual: %t", + rt.expected, + (len(actual) == 0), + ) + } + } +} + func TestValidateAuthorizationModes(t *testing.T) { var tests = []struct { s []string diff --git a/cmd/kubeadm/app/cmd/init.go b/cmd/kubeadm/app/cmd/init.go index 9471b761a26..b5ce3d86ffa 100644 --- a/cmd/kubeadm/app/cmd/init.go +++ b/cmd/kubeadm/app/cmd/init.go @@ -413,7 +413,7 @@ func (i *Init) Run(out io.Writer) error { // Create the default node bootstrap token tokenDescription := "The default bootstrap token generated by 'kubeadm init'." - if err := nodebootstraptokenphase.UpdateOrCreateToken(client, i.cfg.Token, false, i.cfg.TokenTTL.Duration, kubeadmconstants.DefaultTokenUsages, []string{kubeadmconstants.NodeBootstrapTokenAuthGroup}, tokenDescription); err != nil { + if err := nodebootstraptokenphase.UpdateOrCreateToken(client, i.cfg.Token, false, i.cfg.TokenTTL.Duration, i.cfg.TokenUsages, i.cfg.TokenGroups, tokenDescription); err != nil { return fmt.Errorf("error updating or creating token: %v", err) } // Create RBAC rules that makes the bootstrap tokens able to post CSRs diff --git a/cmd/kubeadm/app/cmd/phases/bootstraptoken.go b/cmd/kubeadm/app/cmd/phases/bootstraptoken.go index d4f65017167..e9c237e1519 100644 --- a/cmd/kubeadm/app/cmd/phases/bootstraptoken.go +++ b/cmd/kubeadm/app/cmd/phases/bootstraptoken.go @@ -24,10 +24,8 @@ import ( "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/sets" clientset "k8s.io/client-go/kubernetes" bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api" - bootstraputil "k8s.io/client-go/tools/bootstrap/token/util" kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" @@ -119,7 +117,6 @@ func NewSubCmdBootstrapTokenAll(kubeConfigFile *string) *cobra.Command { legacyscheme.Scheme.Default(cfg) var cfgPath, description string - var usages, extraGroups []string var skipTokenPrint bool cmd := &cobra.Command{ @@ -135,7 +132,7 @@ func NewSubCmdBootstrapTokenAll(kubeConfigFile *string) *cobra.Command { kubeadmutil.CheckErr(err) // Creates the bootstap token - err = createBootstrapToken(*kubeConfigFile, client, cfgPath, cfg, description, usages, extraGroups, skipTokenPrint) + err = createBootstrapToken(*kubeConfigFile, client, cfgPath, cfg, description, skipTokenPrint) kubeadmutil.CheckErr(err) // Create the cluster-info ConfigMap or update if it already exists @@ -161,7 +158,7 @@ func NewSubCmdBootstrapTokenAll(kubeConfigFile *string) *cobra.Command { } // Adds flags to the command - addBootstrapTokenFlags(cmd.Flags(), cfg, &cfgPath, &description, &usages, &extraGroups, &skipTokenPrint) + addBootstrapTokenFlags(cmd.Flags(), cfg, &cfgPath, &description, &skipTokenPrint) return cmd } @@ -178,7 +175,6 @@ func NewSubCmdBootstrapToken(kubeConfigFile *string) *cobra.Command { legacyscheme.Scheme.Default(cfg) var cfgPath, description string - var usages, extraGroups []string var skipTokenPrint bool cmd := &cobra.Command{ @@ -192,13 +188,13 @@ func NewSubCmdBootstrapToken(kubeConfigFile *string) *cobra.Command { client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile) kubeadmutil.CheckErr(err) - err = createBootstrapToken(*kubeConfigFile, client, cfgPath, cfg, description, usages, extraGroups, skipTokenPrint) + err = createBootstrapToken(*kubeConfigFile, client, cfgPath, cfg, description, skipTokenPrint) kubeadmutil.CheckErr(err) }, } // Adds flags to the command - addBootstrapTokenFlags(cmd.Flags(), cfg, &cfgPath, &description, &usages, &extraGroups, &skipTokenPrint) + addBootstrapTokenFlags(cmd.Flags(), cfg, &cfgPath, &description, &skipTokenPrint) return cmd } @@ -281,7 +277,7 @@ func NewSubCmdNodeBootstrapTokenAutoApprove(kubeConfigFile *string) *cobra.Comma return cmd } -func addBootstrapTokenFlags(flagSet *pflag.FlagSet, cfg *kubeadmapiext.MasterConfiguration, cfgPath, description *string, usages, extraGroups *[]string, skipTokenPrint *bool) { +func addBootstrapTokenFlags(flagSet *pflag.FlagSet, cfg *kubeadmapiext.MasterConfiguration, cfgPath, description *string, skipTokenPrint *bool) { flagSet.StringVar( cfgPath, "config", *cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)", @@ -291,15 +287,15 @@ func addBootstrapTokenFlags(flagSet *pflag.FlagSet, cfg *kubeadmapiext.MasterCon "The token to use for establishing bidirectional trust between nodes and masters", ) flagSet.DurationVar( - &cfg.TokenTTL.Duration, "ttl", kubeadmconstants.DefaultTokenDuration, + &cfg.TokenTTL.Duration, "ttl", cfg.TokenTTL.Duration, "The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). If set to '0', the token will never expire", ) flagSet.StringSliceVar( - usages, "usages", kubeadmconstants.DefaultTokenUsages, + &cfg.TokenUsages, "usages", cfg.TokenUsages, fmt.Sprintf("Describes the ways in which this token can be used. You can pass --usages multiple times or provide a comma separated list of options. Valid options: [%s]", strings.Join(kubeadmconstants.DefaultTokenUsages, ",")), ) flagSet.StringSliceVar( - extraGroups, "groups", []string{kubeadmconstants.NodeBootstrapTokenAuthGroup}, + &cfg.TokenGroups, "groups", cfg.TokenGroups, fmt.Sprintf("Extra groups that this token will authenticate as when used for authentication. Must match %q", bootstrapapi.BootstrapGroupPattern), ) flagSet.StringVar( @@ -312,27 +308,16 @@ func addBootstrapTokenFlags(flagSet *pflag.FlagSet, cfg *kubeadmapiext.MasterCon ) } -func createBootstrapToken(kubeConfigFile string, client clientset.Interface, cfgPath string, cfg *kubeadmapiext.MasterConfiguration, description string, usages, extraGroups []string, skipTokenPrint bool) error { - // adding groups only makes sense for authentication - usagesSet := sets.NewString(usages...) - usageAuthentication := strings.TrimPrefix(bootstrapapi.BootstrapTokenUsageAuthentication, bootstrapapi.BootstrapTokenUsagePrefix) - if len(extraGroups) > 0 && !usagesSet.Has(usageAuthentication) { - return fmt.Errorf("--groups cannot be specified unless --usages includes %q", usageAuthentication) - } - - // validate any extra group names - for _, group := range extraGroups { - if err := bootstraputil.ValidateBootstrapGroupName(group); err != nil { - return err - } - } +func createBootstrapToken(kubeConfigFile string, client clientset.Interface, cfgPath string, cfg *kubeadmapiext.MasterConfiguration, description string, skipTokenPrint bool) error { // This call returns the ready-to-use configuration based on the configuration file that might or might not exist and the default cfg populated by flags internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg) - kubeadmutil.CheckErr(err) + if err != nil { + return err + } // Creates or updates the token - if err := node.UpdateOrCreateToken(client, internalcfg.Token, false, internalcfg.TokenTTL.Duration, usages, extraGroups, description); err != nil { + if err := node.UpdateOrCreateToken(client, internalcfg.Token, false, internalcfg.TokenTTL.Duration, internalcfg.TokenUsages, internalcfg.TokenGroups, description); err != nil { return err } diff --git a/cmd/kubeadm/app/cmd/token.go b/cmd/kubeadm/app/cmd/token.go index 84058eea4f3..7781357aefe 100644 --- a/cmd/kubeadm/app/cmd/token.go +++ b/cmd/kubeadm/app/cmd/token.go @@ -32,18 +32,20 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/util/duration" - "k8s.io/apimachinery/pkg/util/sets" clientset "k8s.io/client-go/kubernetes" bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api" - bootstraputil "k8s.io/client-go/tools/bootstrap/token/util" kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" + "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" + configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token" + "k8s.io/kubernetes/pkg/api/legacyscheme" api "k8s.io/kubernetes/pkg/apis/core" ) @@ -86,10 +88,16 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command { tokenCmd.PersistentFlags().BoolVar(&dryRun, "dry-run", dryRun, "Whether to enable dry-run mode or not") - var usages []string - var extraGroups []string - var tokenDuration time.Duration - var description string + cfg := &kubeadmapiext.MasterConfiguration{ + // KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid + // the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig + KubernetesVersion: "v1.9.0", + } + + // Default values for the cobra help text + legacyscheme.Scheme.Default(cfg) + + var cfgPath, description string var printJoinCommand bool createCmd := &cobra.Command{ Use: "create [token]", @@ -104,23 +112,28 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command { If no [token] is given, kubeadm will generate a random token instead. `), Run: func(tokenCmd *cobra.Command, args []string) { - token := "" if len(args) != 0 { - token = args[0] + cfg.Token = args[0] } + + err := validation.ValidateMixedArguments(tokenCmd.Flags()) + kubeadmutil.CheckErr(err) + client, err := getClientset(kubeConfigFile, dryRun) kubeadmutil.CheckErr(err) - err = RunCreateToken(out, client, token, tokenDuration, usages, extraGroups, description, printJoinCommand, kubeConfigFile) + err = RunCreateToken(out, client, cfgPath, cfg, description, printJoinCommand, kubeConfigFile) kubeadmutil.CheckErr(err) }, } - createCmd.Flags().DurationVar(&tokenDuration, - "ttl", kubeadmconstants.DefaultTokenDuration, "The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). If set to '0', the token will never expire.") - createCmd.Flags().StringSliceVar(&usages, - "usages", kubeadmconstants.DefaultTokenUsages, fmt.Sprintf("Describes the ways in which this token can be used. You can pass --usages multiple times or provide a comma separated list of options. Valid options: [%s].", strings.Join(kubeadmconstants.DefaultTokenUsages, ","))) - createCmd.Flags().StringSliceVar(&extraGroups, - "groups", []string{kubeadmconstants.NodeBootstrapTokenAuthGroup}, + createCmd.Flags().StringVar(&cfgPath, + "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)") + createCmd.Flags().DurationVar(&cfg.TokenTTL.Duration, + "ttl", cfg.TokenTTL.Duration, "The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). If set to '0', the token will never expire.") + createCmd.Flags().StringSliceVar(&cfg.TokenUsages, + "usages", cfg.TokenUsages, fmt.Sprintf("Describes the ways in which this token can be used. You can pass --usages multiple times or provide a comma separated list of options. Valid options: [%s].", strings.Join(kubeadmconstants.DefaultTokenUsages, ","))) + createCmd.Flags().StringSliceVar(&cfg.TokenGroups, + "groups", cfg.TokenGroups, fmt.Sprintf("Extra groups that this token will authenticate as when used for authentication. Must match %q.", bootstrapapi.BootstrapGroupPattern)) createCmd.Flags().StringVar(&description, "description", "", "A human friendly description of how this token is used.") @@ -196,40 +209,14 @@ func NewCmdTokenGenerate(out io.Writer) *cobra.Command { } // RunCreateToken generates a new bootstrap token and stores it as a secret on the server. -func RunCreateToken(out io.Writer, client clientset.Interface, token string, tokenDuration time.Duration, usages []string, extraGroups []string, description string, printJoinCommand bool, kubeConfigFile string) error { - if len(token) == 0 { - var err error - token, err = tokenutil.GenerateToken() - if err != nil { - return err - } - } else { - _, _, err := tokenutil.ParseToken(token) - if err != nil { - return err - } - } - - // adding groups only makes sense for authentication - usagesSet := sets.NewString(usages...) - usageAuthentication := strings.TrimPrefix(bootstrapapi.BootstrapTokenUsageAuthentication, bootstrapapi.BootstrapTokenUsagePrefix) - if len(extraGroups) > 0 && !usagesSet.Has(usageAuthentication) { - return fmt.Errorf("--groups cannot be specified unless --usages includes %q", usageAuthentication) - } - - // validate any extra group names - for _, group := range extraGroups { - if err := bootstraputil.ValidateBootstrapGroupName(group); err != nil { - return err - } - } - - // validate usages - if err := bootstraputil.ValidateUsages(usages); err != nil { +func RunCreateToken(out io.Writer, client clientset.Interface, cfgPath string, cfg *kubeadmapiext.MasterConfiguration, description string, printJoinCommand bool, kubeConfigFile string) error { + // This call returns the ready-to-use configuration based on the configuration file that might or might not exist and the default cfg populated by flags + internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg) + if err != nil { return err } - err := tokenphase.CreateNewToken(client, token, tokenDuration, usages, extraGroups, description) + err = tokenphase.CreateNewToken(client, internalcfg.Token, internalcfg.TokenTTL.Duration, internalcfg.TokenUsages, internalcfg.TokenGroups, description) if err != nil { return err } @@ -237,13 +224,13 @@ func RunCreateToken(out io.Writer, client clientset.Interface, token string, tok // if --print-join-command was specified, print the full `kubeadm join` command // otherwise, just print the token if printJoinCommand { - joinCommand, err := cmdutil.GetJoinCommand(kubeConfigFile, token, false) + joinCommand, err := cmdutil.GetJoinCommand(kubeConfigFile, internalcfg.Token, false) if err != nil { return fmt.Errorf("failed to get join command: %v", err) } fmt.Fprintln(out, joinCommand) } else { - fmt.Fprintln(out, token) + fmt.Fprintln(out, internalcfg.Token) } return nil diff --git a/cmd/kubeadm/app/cmd/token_test.go b/cmd/kubeadm/app/cmd/token_test.go index c3a344d1691..10588af479a 100644 --- a/cmd/kubeadm/app/cmd/token_test.go +++ b/cmd/kubeadm/app/cmd/token_test.go @@ -23,9 +23,11 @@ import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/fake" core "k8s.io/client-go/testing" + kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1" ) const ( @@ -123,7 +125,18 @@ func TestRunCreateToken(t *testing.T) { }, } for _, tc := range testCases { - err := RunCreateToken(&buf, fakeClient, tc.token, 0, tc.usages, tc.extraGroups, "", false, "") + + cfg := &kubeadmapiext.MasterConfiguration{ + // KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid + // the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig + KubernetesVersion: "v1.9.0", + Token: tc.token, + TokenTTL: &metav1.Duration{Duration: 0}, + TokenUsages: tc.usages, + TokenGroups: tc.extraGroups, + } + + err := RunCreateToken(&buf, fakeClient, "", cfg, "", false, "") if (err != nil) != tc.expectedError { t.Errorf("Test case %s: RunCreateToken expected error: %v, saw: %v", tc.name, tc.expectedError, (err != nil)) } diff --git a/cmd/kubeadm/app/constants/constants.go b/cmd/kubeadm/app/constants/constants.go index c2986240744..7efce5b8b81 100644 --- a/cmd/kubeadm/app/constants/constants.go +++ b/cmd/kubeadm/app/constants/constants.go @@ -266,6 +266,9 @@ var ( // DefaultTokenUsages specifies the default functions a token will get DefaultTokenUsages = bootstrapapi.KnownTokenUsages + // DefaultTokenGroups specifies the default groups that this token will authenticate as when used for authentication + DefaultTokenGroups = []string{NodeBootstrapTokenAuthGroup} + // MasterComponents defines the master component names MasterComponents = []string{KubeAPIServer, KubeControllerManager, KubeScheduler}