From 0e1a74731d726376f6bb50e000d3780b78ee2b43 Mon Sep 17 00:00:00 2001 From: wackxu Date: Sat, 14 Oct 2017 20:44:57 +0800 Subject: [PATCH] Validate usage strings when creating bootstrap tokens via kubeadm --- cmd/kubeadm/app/cmd/token.go | 5 ++++ .../app/phases/bootstraptoken/node/token.go | 23 ++++++++++++++---- .../phases/bootstraptoken/node/token_test.go | 2 +- pkg/bootstrap/api/BUILD | 5 +++- pkg/bootstrap/api/helpers.go | 18 ++++++++++++++ pkg/bootstrap/api/helpers_test.go | 24 +++++++++++++++++++ 6 files changed, 70 insertions(+), 7 deletions(-) diff --git a/cmd/kubeadm/app/cmd/token.go b/cmd/kubeadm/app/cmd/token.go index 68bf760f868..115683da5de 100644 --- a/cmd/kubeadm/app/cmd/token.go +++ b/cmd/kubeadm/app/cmd/token.go @@ -220,6 +220,11 @@ func RunCreateToken(out io.Writer, client clientset.Interface, token string, tok } } + // validate usages + if err := bootstrapapi.ValidateUsages(usages); err != nil { + return err + } + err := tokenphase.CreateNewToken(client, token, tokenDuration, usages, extraGroups, description) if err != nil { return err diff --git a/cmd/kubeadm/app/phases/bootstraptoken/node/token.go b/cmd/kubeadm/app/phases/bootstraptoken/node/token.go index 4b59d81fcc0..b033702c468 100644 --- a/cmd/kubeadm/app/phases/bootstraptoken/node/token.go +++ b/cmd/kubeadm/app/phases/bootstraptoken/node/token.go @@ -53,7 +53,11 @@ func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists return fmt.Errorf("a token with id %q already exists", tokenID) } // Secret with this ID already exists, update it: - secret.Data = encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description) + tokenSecretData, err := encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description) + if err != nil { + return err + } + secret.Data = tokenSecretData if _, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Update(secret); err == nil { return nil } @@ -63,12 +67,17 @@ func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists // Secret does not already exist: if apierrors.IsNotFound(err) { + tokenSecretData, err := encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description) + if err != nil { + return err + } + secret = &v1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, }, Type: v1.SecretType(bootstrapapi.SecretTypeBootstrapToken), - Data: encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description), + Data: tokenSecretData, } if _, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Create(secret); err == nil { return nil @@ -86,7 +95,7 @@ func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists } // encodeTokenSecretData takes the token discovery object and an optional duration and returns the .Data for the Secret -func encodeTokenSecretData(tokenID, tokenSecret string, duration time.Duration, usages []string, extraGroups []string, description string) map[string][]byte { +func encodeTokenSecretData(tokenID, tokenSecret string, duration time.Duration, usages []string, extraGroups []string, description string) (map[string][]byte, error) { data := map[string][]byte{ bootstrapapi.BootstrapTokenIDKey: []byte(tokenID), bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret), @@ -104,9 +113,13 @@ func encodeTokenSecretData(tokenID, tokenSecret string, duration time.Duration, if len(description) > 0 { data[bootstrapapi.BootstrapTokenDescriptionKey] = []byte(description) } + + // validate usages + if err := bootstrapapi.ValidateUsages(usages); err != nil { + return nil, err + } for _, usage := range usages { - // TODO: Validate the usage string here before data[bootstrapapi.BootstrapTokenUsagePrefix+usage] = []byte("true") } - return data + return data, nil } diff --git a/cmd/kubeadm/app/phases/bootstraptoken/node/token_test.go b/cmd/kubeadm/app/phases/bootstraptoken/node/token_test.go index 7af575e01d1..48bc8c1f168 100644 --- a/cmd/kubeadm/app/phases/bootstraptoken/node/token_test.go +++ b/cmd/kubeadm/app/phases/bootstraptoken/node/token_test.go @@ -33,7 +33,7 @@ func TestEncodeTokenSecretData(t *testing.T) { {token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}, t: time.Second}, // should use default } for _, rt := range tests { - actual := encodeTokenSecretData(rt.token.ID, rt.token.Secret, rt.t, []string{}, []string{}, "") + actual, _ := encodeTokenSecretData(rt.token.ID, rt.token.Secret, rt.t, []string{}, []string{}, "") if !bytes.Equal(actual["token-id"], []byte(rt.token.ID)) { t.Errorf( "failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s", diff --git a/pkg/bootstrap/api/BUILD b/pkg/bootstrap/api/BUILD index f38e43841d2..0beb8e73331 100644 --- a/pkg/bootstrap/api/BUILD +++ b/pkg/bootstrap/api/BUILD @@ -14,7 +14,10 @@ go_library( "types.go", ], importpath = "k8s.io/kubernetes/pkg/bootstrap/api", - deps = ["//vendor/k8s.io/api/core/v1:go_default_library"], + deps = [ + "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", + ], ) filegroup( diff --git a/pkg/bootstrap/api/helpers.go b/pkg/bootstrap/api/helpers.go index 639d61a33c3..01859bc37fe 100644 --- a/pkg/bootstrap/api/helpers.go +++ b/pkg/bootstrap/api/helpers.go @@ -18,7 +18,9 @@ package api import ( "fmt" + "k8s.io/apimachinery/pkg/util/sets" "regexp" + "strings" ) var bootstrapGroupRegexp = regexp.MustCompile(`\A` + BootstrapGroupPattern + `\z`) @@ -32,3 +34,19 @@ func ValidateBootstrapGroupName(name string) error { } return fmt.Errorf("bootstrap group %q is invalid (must match %s)", name, BootstrapGroupPattern) } + +// ValidateUsages validates that the passed in string are valid usage strings for bootstrap tokens. +func ValidateUsages(usages []string) error { + usageAuthentication := strings.TrimPrefix(BootstrapTokenUsageAuthentication, BootstrapTokenUsagePrefix) + usageSigning := strings.TrimPrefix(BootstrapTokenUsageSigningKey, BootstrapTokenUsagePrefix) + invalidUsages := sets.NewString() + for _, usage := range usages { + if usage != usageAuthentication && usage != usageSigning { + invalidUsages.Insert(usage) + } + } + if len(invalidUsages) > 0 { + return fmt.Errorf("invalide bootstrap token usage string: %s, valid usage option: %s, %s", strings.Join(invalidUsages.List(), ","), usageAuthentication, usageSigning) + } + return nil +} diff --git a/pkg/bootstrap/api/helpers_test.go b/pkg/bootstrap/api/helpers_test.go index 177687150c5..d1575f60840 100644 --- a/pkg/bootstrap/api/helpers_test.go +++ b/pkg/bootstrap/api/helpers_test.go @@ -50,3 +50,27 @@ func TestValidateBootstrapGroupName(t *testing.T) { } } } + +func TestValidateUsages(t *testing.T) { + tests := []struct { + name string + input []string + valid bool + }{ + {"valid of signing", []string{"signing"}, true}, + {"valid of authentication", []string{"authentication"}, true}, + {"all valid", []string{"authentication", "signing"}, true}, + {"single invalid", []string{"authentication", "foo"}, false}, + {"all invalid", []string{"foo", "bar"}, false}, + } + + for _, test := range tests { + err := ValidateUsages(test.input) + if err != nil && test.valid { + t.Errorf("test %q: ValidateUsages(%v) returned unexpected error: %v", test.name, test.input, err) + } + if err == nil && !test.valid { + t.Errorf("test %q: ValidateUsages(%v) was supposed to return an error but didn't", test.name, test.input) + } + } +}