mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 12:18:16 +00:00 
			
		
		
		
	jwt: strictly support compact serialization only
Signed-off-by: Monis Khan <mok@microsoft.com>
This commit is contained in:
		@@ -291,6 +291,11 @@ func (j *jwtTokenAuthenticator) AuthenticateToken(ctx context.Context, tokenData
 | 
			
		||||
		return nil, false, utilerrors.NewAggregate(errlist)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// sanity check issuer since we parsed it out before signature validation
 | 
			
		||||
	if !j.issuers[public.Issuer] {
 | 
			
		||||
		return nil, false, fmt.Errorf("token issuer %q is invalid", public.Issuer)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tokenAudiences := authenticator.Audiences(public.Audience)
 | 
			
		||||
	if len(tokenAudiences) == 0 {
 | 
			
		||||
		// only apiserver audiences are allowed for legacy tokens
 | 
			
		||||
@@ -330,6 +335,9 @@ func (j *jwtTokenAuthenticator) AuthenticateToken(ctx context.Context, tokenData
 | 
			
		||||
// Note: go-jose currently does not allow access to unverified JWS payloads.
 | 
			
		||||
// See https://github.com/square/go-jose/issues/169
 | 
			
		||||
func (j *jwtTokenAuthenticator) hasCorrectIssuer(tokenData string) bool {
 | 
			
		||||
	if strings.HasPrefix(strings.TrimSpace(tokenData), "{") {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	parts := strings.Split(tokenData, ".")
 | 
			
		||||
	if len(parts) != 3 {
 | 
			
		||||
		return false
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@ package serviceaccount_test
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/base64"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
@@ -200,23 +202,16 @@ func TestTokenGenerateAndValidate(t *testing.T) {
 | 
			
		||||
	checkJSONWebSignatureHasKeyID(t, invalidAutoSecretToken, rsaKeyID)
 | 
			
		||||
 | 
			
		||||
	// Generate the ECDSA token
 | 
			
		||||
	ecdsaGenerator, err := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, getPrivateKey(ecdsaPrivateKey))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error making generator: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	ecdsaToken, err := ecdsaGenerator.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *ecdsaSecret))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error generating token: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(ecdsaToken) == 0 {
 | 
			
		||||
		t.Fatalf("no token generated")
 | 
			
		||||
	}
 | 
			
		||||
	ecdsaToken := generateECDSAToken(t, serviceaccount.LegacyIssuer, serviceAccount, ecdsaSecret)
 | 
			
		||||
 | 
			
		||||
	ecdsaSecret.Data = map[string][]byte{
 | 
			
		||||
		"token": []byte(ecdsaToken),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	checkJSONWebSignatureHasKeyID(t, ecdsaToken, ecdsaKeyID)
 | 
			
		||||
 | 
			
		||||
	ecdsaTokenMalformedIss := generateECDSATokenWithMalformedIss(t, serviceAccount, ecdsaSecret)
 | 
			
		||||
 | 
			
		||||
	// Generate signer with same keys as RSA signer but different unrecognized issuer
 | 
			
		||||
	badIssuerGenerator, err := serviceaccount.JWTTokenGenerator("foo", getPrivateKey(rsaPrivateKey))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -356,6 +351,13 @@ func TestTokenGenerateAndValidate(t *testing.T) {
 | 
			
		||||
			Keys:        []interface{}{getPublicKey(rsaPublicKey)},
 | 
			
		||||
			ExpectedErr: true,
 | 
			
		||||
		},
 | 
			
		||||
		"malformed iss": {
 | 
			
		||||
			Token:       ecdsaTokenMalformedIss,
 | 
			
		||||
			Client:      nil,
 | 
			
		||||
			Keys:        []interface{}{getPublicKey(ecdsaPublicKey)},
 | 
			
		||||
			ExpectedErr: false,
 | 
			
		||||
			ExpectedOK:  false,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, tc := range testCases {
 | 
			
		||||
@@ -457,3 +459,46 @@ func (f *fakeIndexer) GetByKey(key string) (interface{}, bool, error) {
 | 
			
		||||
	obj, err := f.get(namespace, name)
 | 
			
		||||
	return obj, err == nil, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateECDSAToken(t *testing.T, iss string, serviceAccount *v1.ServiceAccount, ecdsaSecret *v1.Secret) string {
 | 
			
		||||
	t.Helper()
 | 
			
		||||
 | 
			
		||||
	ecdsaGenerator, err := serviceaccount.JWTTokenGenerator(iss, getPrivateKey(ecdsaPrivateKey))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error making generator: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	ecdsaToken, err := ecdsaGenerator.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *ecdsaSecret))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error generating token: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(ecdsaToken) == 0 {
 | 
			
		||||
		t.Fatalf("no token generated")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ecdsaToken
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateECDSATokenWithMalformedIss(t *testing.T, serviceAccount *v1.ServiceAccount, ecdsaSecret *v1.Secret) string {
 | 
			
		||||
	t.Helper()
 | 
			
		||||
 | 
			
		||||
	ecdsaToken := generateECDSAToken(t, "panda", serviceAccount, ecdsaSecret)
 | 
			
		||||
 | 
			
		||||
	ecdsaTokenJWS, err := jose.ParseSigned(ecdsaToken)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dataFullSerialize := map[string]any{}
 | 
			
		||||
	if err := json.Unmarshal([]byte(ecdsaTokenJWS.FullSerialize()), &dataFullSerialize); err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dataFullSerialize["malformed_iss"] = "." + base64.RawURLEncoding.EncodeToString([]byte(`{"iss":"bar"}`)) + "."
 | 
			
		||||
 | 
			
		||||
	out, err := json.Marshal(dataFullSerialize)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return string(out)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -342,6 +342,9 @@ func New(opts Options) (*Authenticator, error) {
 | 
			
		||||
// or returns an error if the token can not be parsed.  Since the JWT is not
 | 
			
		||||
// verified, the returned issuer should not be trusted.
 | 
			
		||||
func untrustedIssuer(token string) (string, error) {
 | 
			
		||||
	if strings.HasPrefix(strings.TrimSpace(token), "{") {
 | 
			
		||||
		return "", fmt.Errorf("token is not compact JWT")
 | 
			
		||||
	}
 | 
			
		||||
	parts := strings.Split(token, ".")
 | 
			
		||||
	if len(parts) != 3 {
 | 
			
		||||
		return "", fmt.Errorf("malformed token")
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user