Merge pull request #60059 from fabriziopandini/kubeadm461

Automatic merge from submit-queue (batch tested with PRs 59674, 60059, 60220, 58916, 60336). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

kubeadm token create using config file

**What this PR does / why we need it**:
Extends `kubeadm token create` adding `--config` flag. Using a config file keeps the token off of bash history.

**Which issue(s) this PR fixes**:
Fixes [#461](https://github.com/kubernetes/kubeadm/issues/461)

**Special notes for your reviewer**:

**Release note**:
```release-note
NONE
```
This commit is contained in:
Kubernetes Submit Queue 2018-02-27 03:31:35 -08:00 committed by GitHub
commit edd7a48104
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 197 additions and 83 deletions

View File

@ -36,7 +36,6 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
c.FuzzNoCustom(obj) c.FuzzNoCustom(obj)
obj.KubernetesVersion = "v10" obj.KubernetesVersion = "v10"
obj.API.BindPort = 20 obj.API.BindPort = 20
obj.TokenTTL = &metav1.Duration{Duration: 1 * time.Hour}
obj.API.AdvertiseAddress = "foo" obj.API.AdvertiseAddress = "foo"
obj.Networking.ServiceSubnet = "foo" obj.Networking.ServiceSubnet = "foo"
obj.Networking.DNSDomain = "foo" obj.Networking.DNSDomain = "foo"
@ -47,6 +46,9 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
obj.Etcd.PeerCertSANs = []string{"foo"} obj.Etcd.PeerCertSANs = []string{"foo"}
obj.Token = "foo" obj.Token = "foo"
obj.CRISocket = "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.Image = "foo"
obj.Etcd.DataDir = "foo" obj.Etcd.DataDir = "foo"
obj.ImageRepository = "foo" obj.ImageRepository = "foo"

View File

@ -64,8 +64,12 @@ type MasterConfiguration struct {
// Token is used for establishing bidirectional trust between nodes and masters. // Token is used for establishing bidirectional trust between nodes and masters.
// Used for joining nodes in the cluster. // Used for joining nodes in the cluster.
Token string Token string
// TokenTTL is a ttl for Token. Defaults to 24h. // TokenTTL defines the ttl for Token. Defaults to 24h.
TokenTTL *metav1.Duration 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 is used to retrieve container runtime info.
CRISocket string CRISocket string

View File

@ -116,6 +116,14 @@ func SetDefaults_MasterConfiguration(obj *MasterConfiguration) {
obj.CRISocket = DefaultCRISocket obj.CRISocket = DefaultCRISocket
} }
if len(obj.TokenUsages) == 0 {
obj.TokenUsages = constants.DefaultTokenUsages
}
if len(obj.TokenGroups) == 0 {
obj.TokenGroups = constants.DefaultTokenGroups
}
if obj.ImageRepository == "" { if obj.ImageRepository == "" {
obj.ImageRepository = DefaultImageRepository obj.ImageRepository = DefaultImageRepository
} }

View File

@ -64,8 +64,12 @@ type MasterConfiguration struct {
// Token is used for establishing bidirectional trust between nodes and masters. // Token is used for establishing bidirectional trust between nodes and masters.
// Used for joining nodes in the cluster. // Used for joining nodes in the cluster.
Token string `json:"token"` 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"` 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 is used to retrieve container runtime info.
CRISocket string `json:"criSocket,omitempty"` CRISocket string `json:"criSocket,omitempty"`

View File

@ -239,6 +239,8 @@ func autoConvert_v1alpha1_MasterConfiguration_To_kubeadm_MasterConfiguration(in
out.PrivilegedPods = in.PrivilegedPods out.PrivilegedPods = in.PrivilegedPods
out.Token = in.Token out.Token = in.Token
out.TokenTTL = (*v1.Duration)(unsafe.Pointer(in.TokenTTL)) 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.CRISocket = in.CRISocket
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs)) out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs)) 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.PrivilegedPods = in.PrivilegedPods
out.Token = in.Token out.Token = in.Token
out.TokenTTL = (*v1.Duration)(unsafe.Pointer(in.TokenTTL)) 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.CRISocket = in.CRISocket
out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs)) out.APIServerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.APIServerExtraArgs))
out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs)) out.ControllerManagerExtraArgs = *(*map[string]string)(unsafe.Pointer(&in.ControllerManagerExtraArgs))

View File

@ -204,6 +204,16 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) {
**out = **in **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 { if in.APIServerExtraArgs != nil {
in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs
*out = make(map[string]string, len(*in)) *out = make(map[string]string, len(*in))

View File

@ -25,6 +25,8 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_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:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field: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",
], ],
) )

View File

@ -29,6 +29,8 @@ import (
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field" "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/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/constants" "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/features" "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, ValidateAbsolutePath(c.CertificatesDir, field.NewPath("certificates-dir"))...)
allErrs = append(allErrs, ValidateNodeName(c.NodeName, field.NewPath("node-name"))...) allErrs = append(allErrs, ValidateNodeName(c.NodeName, field.NewPath("node-name"))...)
allErrs = append(allErrs, ValidateToken(c.Token, field.NewPath("token"))...) 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, ValidateFeatureGates(c.FeatureGates, field.NewPath("feature-gates"))...)
allErrs = append(allErrs, ValidateAPIEndpoint(c, field.NewPath("api-endpoint"))...) allErrs = append(allErrs, ValidateAPIEndpoint(c, field.NewPath("api-endpoint"))...)
allErrs = append(allErrs, ValidateProxy(c, field.NewPath("kube-proxy"))...) allErrs = append(allErrs, ValidateProxy(c, field.NewPath("kube-proxy"))...)
@ -230,6 +234,39 @@ func ValidateToken(t string, fldPath *field.Path) field.ErrorList {
return allErrs 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 // ValidateCertSANs validates alternative names
func ValidateCertSANs(altnames []string, fldPath *field.Path) field.ErrorList { func ValidateCertSANs(altnames []string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}

View File

@ -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) { func TestValidateAuthorizationModes(t *testing.T) {
var tests = []struct { var tests = []struct {
s []string s []string

View File

@ -204,6 +204,16 @@ func (in *MasterConfiguration) DeepCopyInto(out *MasterConfiguration) {
**out = **in **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 { if in.APIServerExtraArgs != nil {
in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs in, out := &in.APIServerExtraArgs, &out.APIServerExtraArgs
*out = make(map[string]string, len(*in)) *out = make(map[string]string, len(*in))

View File

@ -72,7 +72,6 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_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/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/tools/bootstrap/token/api: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/client-go/util/cert:go_default_library",
"//vendor/k8s.io/utils/exec:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library",
], ],
@ -86,10 +85,12 @@ go_test(
], ],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library", "//cmd/kubeadm/app/preflight:go_default_library",
"//vendor/k8s.io/api/core/v1: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/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/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library", "//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library", "//vendor/k8s.io/client-go/testing:go_default_library",

View File

@ -413,7 +413,7 @@ func (i *Init) Run(out io.Writer) error {
// Create the default node bootstrap token // Create the default node bootstrap token
tokenDescription := "The default bootstrap token generated by 'kubeadm init'." 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) return fmt.Errorf("error updating or creating token: %v", err)
} }
// Create RBAC rules that makes the bootstrap tokens able to post CSRs // Create RBAC rules that makes the bootstrap tokens able to post CSRs

View File

@ -55,7 +55,6 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_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/kubernetes:go_default_library",
"//vendor/k8s.io/client-go/tools/bootstrap/token/api: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", "//vendor/k8s.io/utils/exec:go_default_library",
], ],
) )

View File

@ -24,10 +24,8 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api" 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" kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
@ -119,7 +117,6 @@ func NewSubCmdBootstrapTokenAll(kubeConfigFile *string) *cobra.Command {
legacyscheme.Scheme.Default(cfg) legacyscheme.Scheme.Default(cfg)
var cfgPath, description string var cfgPath, description string
var usages, extraGroups []string
var skipTokenPrint bool var skipTokenPrint bool
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -135,7 +132,7 @@ func NewSubCmdBootstrapTokenAll(kubeConfigFile *string) *cobra.Command {
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
// Creates the bootstap token // 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) kubeadmutil.CheckErr(err)
// Create the cluster-info ConfigMap or update if it already exists // 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 // Adds flags to the command
addBootstrapTokenFlags(cmd.Flags(), cfg, &cfgPath, &description, &usages, &extraGroups, &skipTokenPrint) addBootstrapTokenFlags(cmd.Flags(), cfg, &cfgPath, &description, &skipTokenPrint)
return cmd return cmd
} }
@ -178,7 +175,6 @@ func NewSubCmdBootstrapToken(kubeConfigFile *string) *cobra.Command {
legacyscheme.Scheme.Default(cfg) legacyscheme.Scheme.Default(cfg)
var cfgPath, description string var cfgPath, description string
var usages, extraGroups []string
var skipTokenPrint bool var skipTokenPrint bool
cmd := &cobra.Command{ cmd := &cobra.Command{
@ -192,13 +188,13 @@ func NewSubCmdBootstrapToken(kubeConfigFile *string) *cobra.Command {
client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile) client, err := kubeconfigutil.ClientSetFromFile(*kubeConfigFile)
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
err = createBootstrapToken(*kubeConfigFile, client, cfgPath, cfg, description, usages, extraGroups, skipTokenPrint) err = createBootstrapToken(*kubeConfigFile, client, cfgPath, cfg, description, skipTokenPrint)
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
}, },
} }
// Adds flags to the command // Adds flags to the command
addBootstrapTokenFlags(cmd.Flags(), cfg, &cfgPath, &description, &usages, &extraGroups, &skipTokenPrint) addBootstrapTokenFlags(cmd.Flags(), cfg, &cfgPath, &description, &skipTokenPrint)
return cmd return cmd
} }
@ -281,7 +277,7 @@ func NewSubCmdNodeBootstrapTokenAutoApprove(kubeConfigFile *string) *cobra.Comma
return cmd 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( flagSet.StringVar(
cfgPath, "config", *cfgPath, cfgPath, "config", *cfgPath,
"Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)", "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", "The token to use for establishing bidirectional trust between nodes and masters",
) )
flagSet.DurationVar( 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", "The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). If set to '0', the token will never expire",
) )
flagSet.StringSliceVar( 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, ",")), 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( 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), fmt.Sprintf("Extra groups that this token will authenticate as when used for authentication. Must match %q", bootstrapapi.BootstrapGroupPattern),
) )
flagSet.StringVar( 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 { func createBootstrapToken(kubeConfigFile string, client clientset.Interface, cfgPath string, cfg *kubeadmapiext.MasterConfiguration, description 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
}
}
// 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 // 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) internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
kubeadmutil.CheckErr(err) if err != nil {
return err
}
// Creates or updates the token // 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 return err
} }

View File

@ -32,18 +32,20 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/duration" "k8s.io/apimachinery/pkg/util/duration"
"k8s.io/apimachinery/pkg/util/sets"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
bootstrapapi "k8s.io/client-go/tools/bootstrap/token/api" 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" 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" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node" tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/bootstraptoken/node"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util" kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" "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" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token" tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core" 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, tokenCmd.PersistentFlags().BoolVar(&dryRun,
"dry-run", dryRun, "Whether to enable dry-run mode or not") "dry-run", dryRun, "Whether to enable dry-run mode or not")
var usages []string cfg := &kubeadmapiext.MasterConfiguration{
var extraGroups []string // KubernetesVersion is not used by bootstrap-token, but we set this explicitly to avoid
var tokenDuration time.Duration // the lookup of the version from the internet when executing ConfigFileAndDefaultsToInternalConfig
var description string KubernetesVersion: "v1.9.0",
}
// Default values for the cobra help text
legacyscheme.Scheme.Default(cfg)
var cfgPath, description string
var printJoinCommand bool var printJoinCommand bool
createCmd := &cobra.Command{ createCmd := &cobra.Command{
Use: "create [token]", 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. If no [token] is given, kubeadm will generate a random token instead.
`), `),
Run: func(tokenCmd *cobra.Command, args []string) { Run: func(tokenCmd *cobra.Command, args []string) {
token := ""
if len(args) != 0 { if len(args) != 0 {
token = args[0] cfg.Token = args[0]
} }
err := validation.ValidateMixedArguments(tokenCmd.Flags())
kubeadmutil.CheckErr(err)
client, err := getClientset(kubeConfigFile, dryRun) client, err := getClientset(kubeConfigFile, dryRun)
kubeadmutil.CheckErr(err) 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) kubeadmutil.CheckErr(err)
}, },
} }
createCmd.Flags().DurationVar(&tokenDuration, createCmd.Flags().StringVar(&cfgPath,
"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.") "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
createCmd.Flags().StringSliceVar(&usages, createCmd.Flags().DurationVar(&cfg.TokenTTL.Duration,
"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, ","))) "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(&extraGroups, createCmd.Flags().StringSliceVar(&cfg.TokenUsages,
"groups", []string{kubeadmconstants.NodeBootstrapTokenAuthGroup}, "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)) fmt.Sprintf("Extra groups that this token will authenticate as when used for authentication. Must match %q.", bootstrapapi.BootstrapGroupPattern))
createCmd.Flags().StringVar(&description, createCmd.Flags().StringVar(&description,
"description", "", "A human friendly description of how this token is used.") "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. // 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 { func RunCreateToken(out io.Writer, client clientset.Interface, cfgPath string, cfg *kubeadmapiext.MasterConfiguration, description string, printJoinCommand bool, kubeConfigFile string) error {
if len(token) == 0 { // 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
var err error internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig(cfgPath, cfg)
token, err = tokenutil.GenerateToken()
if err != nil { if err != nil {
return err return err
} }
} else {
_, _, err := tokenutil.ParseToken(token)
if err != nil {
return err
}
}
// adding groups only makes sense for authentication err = tokenphase.CreateNewToken(client, internalcfg.Token, internalcfg.TokenTTL.Duration, internalcfg.TokenUsages, internalcfg.TokenGroups, description)
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 {
return err
}
err := tokenphase.CreateNewToken(client, token, tokenDuration, usages, extraGroups, description)
if err != nil { if err != nil {
return err 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 // if --print-join-command was specified, print the full `kubeadm join` command
// otherwise, just print the token // otherwise, just print the token
if printJoinCommand { if printJoinCommand {
joinCommand, err := cmdutil.GetJoinCommand(kubeConfigFile, token, false) joinCommand, err := cmdutil.GetJoinCommand(kubeConfigFile, internalcfg.Token, false)
if err != nil { if err != nil {
return fmt.Errorf("failed to get join command: %v", err) return fmt.Errorf("failed to get join command: %v", err)
} }
fmt.Fprintln(out, joinCommand) fmt.Fprintln(out, joinCommand)
} else { } else {
fmt.Fprintln(out, token) fmt.Fprintln(out, internalcfg.Token)
} }
return nil return nil

View File

@ -23,9 +23,11 @@ import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
core "k8s.io/client-go/testing" core "k8s.io/client-go/testing"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
) )
const ( const (
@ -123,7 +125,18 @@ func TestRunCreateToken(t *testing.T) {
}, },
} }
for _, tc := range testCases { 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 { if (err != nil) != tc.expectedError {
t.Errorf("Test case %s: RunCreateToken expected error: %v, saw: %v", tc.name, tc.expectedError, (err != nil)) t.Errorf("Test case %s: RunCreateToken expected error: %v, saw: %v", tc.name, tc.expectedError, (err != nil))
} }

View File

@ -266,6 +266,9 @@ var (
// DefaultTokenUsages specifies the default functions a token will get // DefaultTokenUsages specifies the default functions a token will get
DefaultTokenUsages = bootstrapapi.KnownTokenUsages 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 defines the master component names
MasterComponents = []string{KubeAPIServer, KubeControllerManager, KubeScheduler} MasterComponents = []string{KubeAPIServer, KubeControllerManager, KubeScheduler}