...

Source file src/k8s.io/kubernetes/pkg/serviceaccount/jwt.go

Documentation: k8s.io/kubernetes/pkg/serviceaccount

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package serviceaccount
    18  
    19  import (
    20  	"context"
    21  	"crypto"
    22  	"crypto/ecdsa"
    23  	"crypto/elliptic"
    24  	"crypto/rsa"
    25  	"crypto/x509"
    26  	"encoding/base64"
    27  	"encoding/json"
    28  	"fmt"
    29  	"strings"
    30  
    31  	jose "gopkg.in/square/go-jose.v2"
    32  	"gopkg.in/square/go-jose.v2/jwt"
    33  
    34  	v1 "k8s.io/api/core/v1"
    35  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    36  	"k8s.io/apiserver/pkg/audit"
    37  	"k8s.io/apiserver/pkg/authentication/authenticator"
    38  	apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
    39  )
    40  
    41  // ServiceAccountTokenGetter defines functions to retrieve a named service account and secret
    42  type ServiceAccountTokenGetter interface {
    43  	GetServiceAccount(namespace, name string) (*v1.ServiceAccount, error)
    44  	GetPod(namespace, name string) (*v1.Pod, error)
    45  	GetSecret(namespace, name string) (*v1.Secret, error)
    46  	GetNode(name string) (*v1.Node, error)
    47  }
    48  
    49  type TokenGenerator interface {
    50  	// GenerateToken generates a token which will identify the given
    51  	// ServiceAccount. privateClaims is an interface that will be
    52  	// serialized into the JWT payload JSON encoding at the root level of
    53  	// the payload object. Public claims take precedent over private
    54  	// claims i.e. if both claims and privateClaims have an "exp" field,
    55  	// the value in claims will be used.
    56  	GenerateToken(claims *jwt.Claims, privateClaims interface{}) (string, error)
    57  }
    58  
    59  // JWTTokenGenerator returns a TokenGenerator that generates signed JWT tokens, using the given privateKey.
    60  // privateKey is a PEM-encoded byte array of a private RSA key.
    61  func JWTTokenGenerator(iss string, privateKey interface{}) (TokenGenerator, error) {
    62  	var signer jose.Signer
    63  	var err error
    64  	switch pk := privateKey.(type) {
    65  	case *rsa.PrivateKey:
    66  		signer, err = signerFromRSAPrivateKey(pk)
    67  		if err != nil {
    68  			return nil, fmt.Errorf("could not generate signer for RSA keypair: %v", err)
    69  		}
    70  	case *ecdsa.PrivateKey:
    71  		signer, err = signerFromECDSAPrivateKey(pk)
    72  		if err != nil {
    73  			return nil, fmt.Errorf("could not generate signer for ECDSA keypair: %v", err)
    74  		}
    75  	case jose.OpaqueSigner:
    76  		signer, err = signerFromOpaqueSigner(pk)
    77  		if err != nil {
    78  			return nil, fmt.Errorf("could not generate signer for OpaqueSigner: %v", err)
    79  		}
    80  	default:
    81  		return nil, fmt.Errorf("unknown private key type %T, must be *rsa.PrivateKey, *ecdsa.PrivateKey, or jose.OpaqueSigner", privateKey)
    82  	}
    83  
    84  	return &jwtTokenGenerator{
    85  		iss:    iss,
    86  		signer: signer,
    87  	}, nil
    88  }
    89  
    90  // keyIDFromPublicKey derives a key ID non-reversibly from a public key.
    91  //
    92  // The Key ID is field on a given on JWTs and JWKs that help relying parties
    93  // pick the correct key for verification when the identity party advertises
    94  // multiple keys.
    95  //
    96  // Making the derivation non-reversible makes it impossible for someone to
    97  // accidentally obtain the real key from the key ID and use it for token
    98  // validation.
    99  func keyIDFromPublicKey(publicKey interface{}) (string, error) {
   100  	publicKeyDERBytes, err := x509.MarshalPKIXPublicKey(publicKey)
   101  	if err != nil {
   102  		return "", fmt.Errorf("failed to serialize public key to DER format: %v", err)
   103  	}
   104  
   105  	hasher := crypto.SHA256.New()
   106  	hasher.Write(publicKeyDERBytes)
   107  	publicKeyDERHash := hasher.Sum(nil)
   108  
   109  	keyID := base64.RawURLEncoding.EncodeToString(publicKeyDERHash)
   110  
   111  	return keyID, nil
   112  }
   113  
   114  func signerFromRSAPrivateKey(keyPair *rsa.PrivateKey) (jose.Signer, error) {
   115  	keyID, err := keyIDFromPublicKey(&keyPair.PublicKey)
   116  	if err != nil {
   117  		return nil, fmt.Errorf("failed to derive keyID: %v", err)
   118  	}
   119  
   120  	// IMPORTANT: If this function is updated to support additional key sizes,
   121  	// algorithmForPublicKey in serviceaccount/openidmetadata.go must also be
   122  	// updated to support the same key sizes. Today we only support RS256.
   123  
   124  	// Wrap the RSA keypair in a JOSE JWK with the designated key ID.
   125  	privateJWK := &jose.JSONWebKey{
   126  		Algorithm: string(jose.RS256),
   127  		Key:       keyPair,
   128  		KeyID:     keyID,
   129  		Use:       "sig",
   130  	}
   131  
   132  	signer, err := jose.NewSigner(
   133  		jose.SigningKey{
   134  			Algorithm: jose.RS256,
   135  			Key:       privateJWK,
   136  		},
   137  		nil,
   138  	)
   139  
   140  	if err != nil {
   141  		return nil, fmt.Errorf("failed to create signer: %v", err)
   142  	}
   143  
   144  	return signer, nil
   145  }
   146  
   147  func signerFromECDSAPrivateKey(keyPair *ecdsa.PrivateKey) (jose.Signer, error) {
   148  	var alg jose.SignatureAlgorithm
   149  	switch keyPair.Curve {
   150  	case elliptic.P256():
   151  		alg = jose.ES256
   152  	case elliptic.P384():
   153  		alg = jose.ES384
   154  	case elliptic.P521():
   155  		alg = jose.ES512
   156  	default:
   157  		return nil, fmt.Errorf("unknown private key curve, must be 256, 384, or 521")
   158  	}
   159  
   160  	keyID, err := keyIDFromPublicKey(&keyPair.PublicKey)
   161  	if err != nil {
   162  		return nil, fmt.Errorf("failed to derive keyID: %v", err)
   163  	}
   164  
   165  	// Wrap the ECDSA keypair in a JOSE JWK with the designated key ID.
   166  	privateJWK := &jose.JSONWebKey{
   167  		Algorithm: string(alg),
   168  		Key:       keyPair,
   169  		KeyID:     keyID,
   170  		Use:       "sig",
   171  	}
   172  
   173  	signer, err := jose.NewSigner(
   174  		jose.SigningKey{
   175  			Algorithm: alg,
   176  			Key:       privateJWK,
   177  		},
   178  		nil,
   179  	)
   180  	if err != nil {
   181  		return nil, fmt.Errorf("failed to create signer: %v", err)
   182  	}
   183  
   184  	return signer, nil
   185  }
   186  
   187  func signerFromOpaqueSigner(opaqueSigner jose.OpaqueSigner) (jose.Signer, error) {
   188  	alg := jose.SignatureAlgorithm(opaqueSigner.Public().Algorithm)
   189  
   190  	signer, err := jose.NewSigner(
   191  		jose.SigningKey{
   192  			Algorithm: alg,
   193  			Key: &jose.JSONWebKey{
   194  				Algorithm: string(alg),
   195  				Key:       opaqueSigner,
   196  				KeyID:     opaqueSigner.Public().KeyID,
   197  				Use:       "sig",
   198  			},
   199  		},
   200  		nil,
   201  	)
   202  	if err != nil {
   203  		return nil, fmt.Errorf("failed to create signer: %v", err)
   204  	}
   205  
   206  	return signer, nil
   207  }
   208  
   209  type jwtTokenGenerator struct {
   210  	iss    string
   211  	signer jose.Signer
   212  }
   213  
   214  func (j *jwtTokenGenerator) GenerateToken(claims *jwt.Claims, privateClaims interface{}) (string, error) {
   215  	// claims are applied in reverse precedence
   216  	return jwt.Signed(j.signer).
   217  		Claims(privateClaims).
   218  		Claims(claims).
   219  		Claims(&jwt.Claims{
   220  			Issuer: j.iss,
   221  		}).
   222  		CompactSerialize()
   223  }
   224  
   225  // JWTTokenAuthenticator authenticates tokens as JWT tokens produced by JWTTokenGenerator
   226  // Token signatures are verified using each of the given public keys until one works (allowing key rotation)
   227  // If lookup is true, the service account and secret referenced as claims inside the token are retrieved and verified with the provided ServiceAccountTokenGetter
   228  func JWTTokenAuthenticator(issuers []string, keys []interface{}, implicitAuds authenticator.Audiences, validator Validator) authenticator.Token {
   229  	issuersMap := make(map[string]bool)
   230  	for _, issuer := range issuers {
   231  		issuersMap[issuer] = true
   232  	}
   233  	return &jwtTokenAuthenticator{
   234  		issuers:      issuersMap,
   235  		keys:         keys,
   236  		implicitAuds: implicitAuds,
   237  		validator:    validator,
   238  	}
   239  }
   240  
   241  type jwtTokenAuthenticator struct {
   242  	issuers      map[string]bool
   243  	keys         []interface{}
   244  	validator    Validator
   245  	implicitAuds authenticator.Audiences
   246  }
   247  
   248  // Validator is called by the JWT token authenticator to apply domain specific
   249  // validation to a token and extract user information.
   250  type Validator interface {
   251  	// Validate validates a token and returns user information or an error.
   252  	// Validator can assume that the issuer and signature of a token are already
   253  	// verified when this function is called.
   254  	Validate(ctx context.Context, tokenData string, public *jwt.Claims, private interface{}) (*apiserverserviceaccount.ServiceAccountInfo, error)
   255  	// NewPrivateClaims returns a struct that the authenticator should
   256  	// deserialize the JWT payload into. The authenticator may then pass this
   257  	// struct back to the Validator as the 'private' argument to a Validate()
   258  	// call. This struct should contain fields for any private claims that the
   259  	// Validator requires to validate the JWT.
   260  	NewPrivateClaims() interface{}
   261  }
   262  
   263  func (j *jwtTokenAuthenticator) AuthenticateToken(ctx context.Context, tokenData string) (*authenticator.Response, bool, error) {
   264  	if !j.hasCorrectIssuer(tokenData) {
   265  		return nil, false, nil
   266  	}
   267  
   268  	tok, err := jwt.ParseSigned(tokenData)
   269  	if err != nil {
   270  		return nil, false, nil
   271  	}
   272  
   273  	public := &jwt.Claims{}
   274  	private := j.validator.NewPrivateClaims()
   275  
   276  	// TODO: Pick the key that has the same key ID as `tok`, if one exists.
   277  	var (
   278  		found   bool
   279  		errlist []error
   280  	)
   281  	for _, key := range j.keys {
   282  		if err := tok.Claims(key, public, private); err != nil {
   283  			errlist = append(errlist, err)
   284  			continue
   285  		}
   286  		found = true
   287  		break
   288  	}
   289  
   290  	if !found {
   291  		return nil, false, utilerrors.NewAggregate(errlist)
   292  	}
   293  
   294  	// sanity check issuer since we parsed it out before signature validation
   295  	if !j.issuers[public.Issuer] {
   296  		return nil, false, fmt.Errorf("token issuer %q is invalid", public.Issuer)
   297  	}
   298  
   299  	tokenAudiences := authenticator.Audiences(public.Audience)
   300  	if len(tokenAudiences) == 0 {
   301  		// only apiserver audiences are allowed for legacy tokens
   302  		audit.AddAuditAnnotation(ctx, "authentication.k8s.io/legacy-token", public.Subject)
   303  		legacyTokensTotal.WithContext(ctx).Inc()
   304  		tokenAudiences = j.implicitAuds
   305  	}
   306  
   307  	requestedAudiences, ok := authenticator.AudiencesFrom(ctx)
   308  	if !ok {
   309  		// default to apiserver audiences
   310  		requestedAudiences = j.implicitAuds
   311  	}
   312  
   313  	auds := authenticator.Audiences(tokenAudiences).Intersect(requestedAudiences)
   314  	if len(auds) == 0 && len(j.implicitAuds) != 0 {
   315  		return nil, false, fmt.Errorf("token audiences %q is invalid for the target audiences %q", tokenAudiences, requestedAudiences)
   316  	}
   317  
   318  	// If we get here, we have a token with a recognized signature and
   319  	// issuer string.
   320  	sa, err := j.validator.Validate(ctx, tokenData, public, private)
   321  	if err != nil {
   322  		return nil, false, err
   323  	}
   324  
   325  	return &authenticator.Response{
   326  		User:      sa.UserInfo(),
   327  		Audiences: auds,
   328  	}, true, nil
   329  }
   330  
   331  // hasCorrectIssuer returns true if tokenData is a valid JWT in compact
   332  // serialization format and the "iss" claim matches the iss field of this token
   333  // authenticator, and otherwise returns false.
   334  //
   335  // Note: go-jose currently does not allow access to unverified JWS payloads.
   336  // See https://github.com/square/go-jose/issues/169
   337  func (j *jwtTokenAuthenticator) hasCorrectIssuer(tokenData string) bool {
   338  	if strings.HasPrefix(strings.TrimSpace(tokenData), "{") {
   339  		return false
   340  	}
   341  	parts := strings.Split(tokenData, ".")
   342  	if len(parts) != 3 {
   343  		return false
   344  	}
   345  	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
   346  	if err != nil {
   347  		return false
   348  	}
   349  	claims := struct {
   350  		// WARNING: this JWT is not verified. Do not trust these claims.
   351  		Issuer string `json:"iss"`
   352  	}{}
   353  	if err := json.Unmarshal(payload, &claims); err != nil {
   354  		return false
   355  	}
   356  	return j.issuers[claims.Issuer]
   357  }
   358  

View as plain text