mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	refactor bootstrap token utils
This commit is contained in:
		| @@ -23,20 +23,18 @@ import ( | ||||
| 	"context" | ||||
| 	"crypto/subtle" | ||||
| 	"fmt" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"k8s.io/klog" | ||||
|  | ||||
| 	corev1 "k8s.io/api/core/v1" | ||||
| 	"k8s.io/apimachinery/pkg/api/errors" | ||||
| 	"k8s.io/apimachinery/pkg/util/sets" | ||||
| 	"k8s.io/apiserver/pkg/authentication/authenticator" | ||||
| 	"k8s.io/apiserver/pkg/authentication/user" | ||||
| 	corev1listers "k8s.io/client-go/listers/core/v1" | ||||
| 	bootstrapapi "k8s.io/cluster-bootstrap/token/api" | ||||
| 	bootstraputil "k8s.io/cluster-bootstrap/token/util" | ||||
| 	bootstrapsecretutil "k8s.io/cluster-bootstrap/util/secrets" | ||||
| 	bootstraptokenutil "k8s.io/cluster-bootstrap/util/tokens" | ||||
| ) | ||||
|  | ||||
| // TODO: A few methods in this package is copied from other sources. Either | ||||
| @@ -92,7 +90,7 @@ func tokenErrorf(s *corev1.Secret, format string, i ...interface{}) { | ||||
| //     ( token-id ).( token-secret ) | ||||
| // | ||||
| func (t *TokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) { | ||||
| 	tokenID, tokenSecret, err := parseToken(token) | ||||
| 	tokenID, tokenSecret, err := bootstraptokenutil.ParseToken(token) | ||||
| 	if err != nil { | ||||
| 		// Token isn't of the correct form, ignore it. | ||||
| 		return nil, false, nil | ||||
| @@ -118,29 +116,29 @@ func (t *TokenAuthenticator) AuthenticateToken(ctx context.Context, token string | ||||
| 		return nil, false, nil | ||||
| 	} | ||||
|  | ||||
| 	ts := getSecretString(secret, bootstrapapi.BootstrapTokenSecretKey) | ||||
| 	ts := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenSecretKey) | ||||
| 	if subtle.ConstantTimeCompare([]byte(ts), []byte(tokenSecret)) != 1 { | ||||
| 		tokenErrorf(secret, "has invalid value for key %s, expected %s.", bootstrapapi.BootstrapTokenSecretKey, tokenSecret) | ||||
| 		return nil, false, nil | ||||
| 	} | ||||
|  | ||||
| 	id := getSecretString(secret, bootstrapapi.BootstrapTokenIDKey) | ||||
| 	id := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenIDKey) | ||||
| 	if id != tokenID { | ||||
| 		tokenErrorf(secret, "has invalid value for key %s, expected %s.", bootstrapapi.BootstrapTokenIDKey, tokenID) | ||||
| 		return nil, false, nil | ||||
| 	} | ||||
|  | ||||
| 	if isSecretExpired(secret) { | ||||
| 	if bootstrapsecretutil.HasExpired(secret, time.Now()) { | ||||
| 		// logging done in isSecretExpired method. | ||||
| 		return nil, false, nil | ||||
| 	} | ||||
|  | ||||
| 	if getSecretString(secret, bootstrapapi.BootstrapTokenUsageAuthentication) != "true" { | ||||
| 	if bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenUsageAuthentication) != "true" { | ||||
| 		tokenErrorf(secret, "not marked %s=true.", bootstrapapi.BootstrapTokenUsageAuthentication) | ||||
| 		return nil, false, nil | ||||
| 	} | ||||
|  | ||||
| 	groups, err := getGroups(secret) | ||||
| 	groups, err := bootstrapsecretutil.GetGroups(secret) | ||||
| 	if err != nil { | ||||
| 		tokenErrorf(secret, "has invalid value for key %s: %v.", bootstrapapi.BootstrapTokenExtraGroupsKey, err) | ||||
| 		return nil, false, nil | ||||
| @@ -153,76 +151,3 @@ func (t *TokenAuthenticator) AuthenticateToken(ctx context.Context, token string | ||||
| 		}, | ||||
| 	}, true, nil | ||||
| } | ||||
|  | ||||
| // Copied from k8s.io/cluster-bootstrap/token/api | ||||
| func getSecretString(secret *corev1.Secret, key string) string { | ||||
| 	data, ok := secret.Data[key] | ||||
| 	if !ok { | ||||
| 		return "" | ||||
| 	} | ||||
|  | ||||
| 	return string(data) | ||||
| } | ||||
|  | ||||
| // Copied from k8s.io/cluster-bootstrap/token/api | ||||
| func isSecretExpired(secret *corev1.Secret) bool { | ||||
| 	expiration := getSecretString(secret, bootstrapapi.BootstrapTokenExpirationKey) | ||||
| 	if len(expiration) > 0 { | ||||
| 		expTime, err2 := time.Parse(time.RFC3339, expiration) | ||||
| 		if err2 != nil { | ||||
| 			klog.V(3).Infof("Unparseable expiration time (%s) in %s/%s Secret: %v. Treating as expired.", | ||||
| 				expiration, secret.Namespace, secret.Name, err2) | ||||
| 			return true | ||||
| 		} | ||||
| 		if time.Now().After(expTime) { | ||||
| 			klog.V(3).Infof("Expired bootstrap token in %s/%s Secret: %v", | ||||
| 				secret.Namespace, secret.Name, expiration) | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // Copied from kubernetes/cmd/kubeadm/app/util/token | ||||
|  | ||||
| var ( | ||||
| 	// tokenRegexpString defines id.secret regular expression pattern | ||||
| 	tokenRegexpString = "^([a-z0-9]{6})\\.([a-z0-9]{16})$" | ||||
| 	// tokenRegexp is a compiled regular expression of TokenRegexpString | ||||
| 	tokenRegexp = regexp.MustCompile(tokenRegexpString) | ||||
| ) | ||||
|  | ||||
| // parseToken tries and parse a valid token from a string. | ||||
| // A token ID and token secret are returned in case of success, an error otherwise. | ||||
| func parseToken(s string) (string, string, error) { | ||||
| 	split := tokenRegexp.FindStringSubmatch(s) | ||||
| 	if len(split) != 3 { | ||||
| 		return "", "", fmt.Errorf("token [%q] was not of form [%q]", s, tokenRegexpString) | ||||
| 	} | ||||
| 	return split[1], split[2], nil | ||||
| } | ||||
|  | ||||
| // getGroups loads and validates the bootstrapapi.BootstrapTokenExtraGroupsKey | ||||
| // key from the bootstrap token secret, returning a list of group names or an | ||||
| // error if any of the group names are invalid. | ||||
| func getGroups(secret *corev1.Secret) ([]string, error) { | ||||
| 	// always include the default group | ||||
| 	groups := sets.NewString(bootstrapapi.BootstrapDefaultGroup) | ||||
|  | ||||
| 	// grab any extra groups and if there are none, return just the default | ||||
| 	extraGroupsString := getSecretString(secret, bootstrapapi.BootstrapTokenExtraGroupsKey) | ||||
| 	if extraGroupsString == "" { | ||||
| 		return groups.List(), nil | ||||
| 	} | ||||
|  | ||||
| 	// validate the names of the extra groups | ||||
| 	for _, group := range strings.Split(extraGroupsString, ",") { | ||||
| 		if err := bootstraputil.ValidateBootstrapGroupName(group); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		groups.Insert(group) | ||||
| 	} | ||||
|  | ||||
| 	// return the result as a deduplicated, sorted list | ||||
| 	return groups.List(), nil | ||||
| } | ||||
|   | ||||
| @@ -288,72 +288,3 @@ func TestTokenAuthenticator(t *testing.T) { | ||||
| 		}() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestGetGroups(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name         string | ||||
| 		secret       *corev1.Secret | ||||
| 		expectResult []string | ||||
| 		expectError  bool | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name: "not set", | ||||
| 			secret: &corev1.Secret{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "test"}, | ||||
| 				Data:       map[string][]byte{}, | ||||
| 			}, | ||||
| 			expectResult: []string{"system:bootstrappers"}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "set to empty value", | ||||
| 			secret: &corev1.Secret{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "test"}, | ||||
| 				Data: map[string][]byte{ | ||||
| 					bootstrapapi.BootstrapTokenExtraGroupsKey: []byte(""), | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectResult: []string{"system:bootstrappers"}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "invalid prefix", | ||||
| 			secret: &corev1.Secret{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "test"}, | ||||
| 				Data: map[string][]byte{ | ||||
| 					bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("foo"), | ||||
| 				}, | ||||
| 			}, | ||||
| 			expectError: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "valid", | ||||
| 			secret: &corev1.Secret{ | ||||
| 				ObjectMeta: metav1.ObjectMeta{Name: "test"}, | ||||
| 				Data: map[string][]byte{ | ||||
| 					bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("system:bootstrappers:foo,system:bootstrappers:bar,system:bootstrappers:bar"), | ||||
| 				}, | ||||
| 			}, | ||||
| 			// expect the results in deduplicated, sorted order | ||||
| 			expectResult: []string{ | ||||
| 				"system:bootstrappers", | ||||
| 				"system:bootstrappers:bar", | ||||
| 				"system:bootstrappers:foo", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, test := range tests { | ||||
| 		result, err := getGroups(test.secret) | ||||
| 		if test.expectError { | ||||
| 			if err == nil { | ||||
| 				t.Errorf("test %q expected an error, but didn't get one (result: %#v)", test.name, result) | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			t.Errorf("test %q return an unexpected error: %v", test.name, err) | ||||
| 			continue | ||||
| 		} | ||||
| 		if !reflect.DeepEqual(result, test.expectResult) { | ||||
| 			t.Errorf("test %q expected %#v, got %#v", test.name, test.expectResult, result) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user