mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
jwt: strictly support compact serialization only
Signed-off-by: Monis Khan <mok@microsoft.com>
This commit is contained in:
parent
12217672a3
commit
e89dddd4af
@ -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")
|
||||
|
Loading…
Reference in New Issue
Block a user