k8s.io/kubernetes@v1.29.3/pkg/serviceaccount/jwt.go (about) 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 tokenAudiences := authenticator.Audiences(public.Audience) 295 if len(tokenAudiences) == 0 { 296 // only apiserver audiences are allowed for legacy tokens 297 audit.AddAuditAnnotation(ctx, "authentication.k8s.io/legacy-token", public.Subject) 298 legacyTokensTotal.WithContext(ctx).Inc() 299 tokenAudiences = j.implicitAuds 300 } 301 302 requestedAudiences, ok := authenticator.AudiencesFrom(ctx) 303 if !ok { 304 // default to apiserver audiences 305 requestedAudiences = j.implicitAuds 306 } 307 308 auds := authenticator.Audiences(tokenAudiences).Intersect(requestedAudiences) 309 if len(auds) == 0 && len(j.implicitAuds) != 0 { 310 return nil, false, fmt.Errorf("token audiences %q is invalid for the target audiences %q", tokenAudiences, requestedAudiences) 311 } 312 313 // If we get here, we have a token with a recognized signature and 314 // issuer string. 315 sa, err := j.validator.Validate(ctx, tokenData, public, private) 316 if err != nil { 317 return nil, false, err 318 } 319 320 return &authenticator.Response{ 321 User: sa.UserInfo(), 322 Audiences: auds, 323 }, true, nil 324 } 325 326 // hasCorrectIssuer returns true if tokenData is a valid JWT in compact 327 // serialization format and the "iss" claim matches the iss field of this token 328 // authenticator, and otherwise returns false. 329 // 330 // Note: go-jose currently does not allow access to unverified JWS payloads. 331 // See https://github.com/square/go-jose/issues/169 332 func (j *jwtTokenAuthenticator) hasCorrectIssuer(tokenData string) bool { 333 parts := strings.Split(tokenData, ".") 334 if len(parts) != 3 { 335 return false 336 } 337 payload, err := base64.RawURLEncoding.DecodeString(parts[1]) 338 if err != nil { 339 return false 340 } 341 claims := struct { 342 // WARNING: this JWT is not verified. Do not trust these claims. 343 Issuer string `json:"iss"` 344 }{} 345 if err := json.Unmarshal(payload, &claims); err != nil { 346 return false 347 } 348 return j.issuers[claims.Issuer] 349 }