Skip to content

Commit 0e1a747

Browse files
committed
Validate usage strings when creating bootstrap tokens via kubeadm
1 parent 338ee7f commit 0e1a747

File tree

6 files changed

+70
-7
lines changed

6 files changed

+70
-7
lines changed

cmd/kubeadm/app/cmd/token.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,11 @@ func RunCreateToken(out io.Writer, client clientset.Interface, token string, tok
220220
}
221221
}
222222

223+
// validate usages
224+
if err := bootstrapapi.ValidateUsages(usages); err != nil {
225+
return err
226+
}
227+
223228
err := tokenphase.CreateNewToken(client, token, tokenDuration, usages, extraGroups, description)
224229
if err != nil {
225230
return err

cmd/kubeadm/app/phases/bootstraptoken/node/token.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists
5353
return fmt.Errorf("a token with id %q already exists", tokenID)
5454
}
5555
// Secret with this ID already exists, update it:
56-
secret.Data = encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description)
56+
tokenSecretData, err := encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description)
57+
if err != nil {
58+
return err
59+
}
60+
secret.Data = tokenSecretData
5761
if _, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Update(secret); err == nil {
5862
return nil
5963
}
@@ -63,12 +67,17 @@ func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists
6367

6468
// Secret does not already exist:
6569
if apierrors.IsNotFound(err) {
70+
tokenSecretData, err := encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description)
71+
if err != nil {
72+
return err
73+
}
74+
6675
secret = &v1.Secret{
6776
ObjectMeta: metav1.ObjectMeta{
6877
Name: secretName,
6978
},
7079
Type: v1.SecretType(bootstrapapi.SecretTypeBootstrapToken),
71-
Data: encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description),
80+
Data: tokenSecretData,
7281
}
7382
if _, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Create(secret); err == nil {
7483
return nil
@@ -86,7 +95,7 @@ func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists
8695
}
8796

8897
// encodeTokenSecretData takes the token discovery object and an optional duration and returns the .Data for the Secret
89-
func encodeTokenSecretData(tokenID, tokenSecret string, duration time.Duration, usages []string, extraGroups []string, description string) map[string][]byte {
98+
func encodeTokenSecretData(tokenID, tokenSecret string, duration time.Duration, usages []string, extraGroups []string, description string) (map[string][]byte, error) {
9099
data := map[string][]byte{
91100
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
92101
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
@@ -104,9 +113,13 @@ func encodeTokenSecretData(tokenID, tokenSecret string, duration time.Duration,
104113
if len(description) > 0 {
105114
data[bootstrapapi.BootstrapTokenDescriptionKey] = []byte(description)
106115
}
116+
117+
// validate usages
118+
if err := bootstrapapi.ValidateUsages(usages); err != nil {
119+
return nil, err
120+
}
107121
for _, usage := range usages {
108-
// TODO: Validate the usage string here before
109122
data[bootstrapapi.BootstrapTokenUsagePrefix+usage] = []byte("true")
110123
}
111-
return data
124+
return data, nil
112125
}

cmd/kubeadm/app/phases/bootstraptoken/node/token_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ func TestEncodeTokenSecretData(t *testing.T) {
3333
{token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}, t: time.Second}, // should use default
3434
}
3535
for _, rt := range tests {
36-
actual := encodeTokenSecretData(rt.token.ID, rt.token.Secret, rt.t, []string{}, []string{}, "")
36+
actual, _ := encodeTokenSecretData(rt.token.ID, rt.token.Secret, rt.t, []string{}, []string{}, "")
3737
if !bytes.Equal(actual["token-id"], []byte(rt.token.ID)) {
3838
t.Errorf(
3939
"failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s",

pkg/bootstrap/api/BUILD

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ go_library(
1414
"types.go",
1515
],
1616
importpath = "k8s.io/kubernetes/pkg/bootstrap/api",
17-
deps = ["//vendor/k8s.io/api/core/v1:go_default_library"],
17+
deps = [
18+
"//vendor/k8s.io/api/core/v1:go_default_library",
19+
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
20+
],
1821
)
1922

2023
filegroup(

pkg/bootstrap/api/helpers.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ package api
1818

1919
import (
2020
"fmt"
21+
"k8s.io/apimachinery/pkg/util/sets"
2122
"regexp"
23+
"strings"
2224
)
2325

2426
var bootstrapGroupRegexp = regexp.MustCompile(`\A` + BootstrapGroupPattern + `\z`)
@@ -32,3 +34,19 @@ func ValidateBootstrapGroupName(name string) error {
3234
}
3335
return fmt.Errorf("bootstrap group %q is invalid (must match %s)", name, BootstrapGroupPattern)
3436
}
37+
38+
// ValidateUsages validates that the passed in string are valid usage strings for bootstrap tokens.
39+
func ValidateUsages(usages []string) error {
40+
usageAuthentication := strings.TrimPrefix(BootstrapTokenUsageAuthentication, BootstrapTokenUsagePrefix)
41+
usageSigning := strings.TrimPrefix(BootstrapTokenUsageSigningKey, BootstrapTokenUsagePrefix)
42+
invalidUsages := sets.NewString()
43+
for _, usage := range usages {
44+
if usage != usageAuthentication && usage != usageSigning {
45+
invalidUsages.Insert(usage)
46+
}
47+
}
48+
if len(invalidUsages) > 0 {
49+
return fmt.Errorf("invalide bootstrap token usage string: %s, valid usage option: %s, %s", strings.Join(invalidUsages.List(), ","), usageAuthentication, usageSigning)
50+
}
51+
return nil
52+
}

pkg/bootstrap/api/helpers_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,27 @@ func TestValidateBootstrapGroupName(t *testing.T) {
5050
}
5151
}
5252
}
53+
54+
func TestValidateUsages(t *testing.T) {
55+
tests := []struct {
56+
name string
57+
input []string
58+
valid bool
59+
}{
60+
{"valid of signing", []string{"signing"}, true},
61+
{"valid of authentication", []string{"authentication"}, true},
62+
{"all valid", []string{"authentication", "signing"}, true},
63+
{"single invalid", []string{"authentication", "foo"}, false},
64+
{"all invalid", []string{"foo", "bar"}, false},
65+
}
66+
67+
for _, test := range tests {
68+
err := ValidateUsages(test.input)
69+
if err != nil && test.valid {
70+
t.Errorf("test %q: ValidateUsages(%v) returned unexpected error: %v", test.name, test.input, err)
71+
}
72+
if err == nil && !test.valid {
73+
t.Errorf("test %q: ValidateUsages(%v) was supposed to return an error but didn't", test.name, test.input)
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)