mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 05:40:42 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			141 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| Copyright 2018 The Kubernetes Authors.
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
|     http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| */
 | |
| 
 | |
| package serviceaccount
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 
 | |
| 	"gopkg.in/square/go-jose.v2/jwt"
 | |
| 	"k8s.io/klog/v2"
 | |
| 
 | |
| 	"k8s.io/api/core/v1"
 | |
| 	apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
 | |
| )
 | |
| 
 | |
| func LegacyClaims(serviceAccount v1.ServiceAccount, secret v1.Secret) (*jwt.Claims, interface{}) {
 | |
| 	return &jwt.Claims{
 | |
| 			Subject: apiserverserviceaccount.MakeUsername(serviceAccount.Namespace, serviceAccount.Name),
 | |
| 		}, &legacyPrivateClaims{
 | |
| 			Namespace:          serviceAccount.Namespace,
 | |
| 			ServiceAccountName: serviceAccount.Name,
 | |
| 			ServiceAccountUID:  string(serviceAccount.UID),
 | |
| 			SecretName:         secret.Name,
 | |
| 		}
 | |
| }
 | |
| 
 | |
| const LegacyIssuer = "kubernetes/serviceaccount"
 | |
| 
 | |
| type legacyPrivateClaims struct {
 | |
| 	ServiceAccountName string `json:"kubernetes.io/serviceaccount/service-account.name"`
 | |
| 	ServiceAccountUID  string `json:"kubernetes.io/serviceaccount/service-account.uid"`
 | |
| 	SecretName         string `json:"kubernetes.io/serviceaccount/secret.name"`
 | |
| 	Namespace          string `json:"kubernetes.io/serviceaccount/namespace"`
 | |
| }
 | |
| 
 | |
| func NewLegacyValidator(lookup bool, getter ServiceAccountTokenGetter) Validator {
 | |
| 	return &legacyValidator{
 | |
| 		lookup: lookup,
 | |
| 		getter: getter,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type legacyValidator struct {
 | |
| 	lookup bool
 | |
| 	getter ServiceAccountTokenGetter
 | |
| }
 | |
| 
 | |
| var _ = Validator(&legacyValidator{})
 | |
| 
 | |
| func (v *legacyValidator) Validate(ctx context.Context, tokenData string, public *jwt.Claims, privateObj interface{}) (*ServiceAccountInfo, error) {
 | |
| 	private, ok := privateObj.(*legacyPrivateClaims)
 | |
| 	if !ok {
 | |
| 		klog.Errorf("jwt validator expected private claim of type *legacyPrivateClaims but got: %T", privateObj)
 | |
| 		return nil, errors.New("Token could not be validated.")
 | |
| 	}
 | |
| 
 | |
| 	// Make sure the claims we need exist
 | |
| 	if len(public.Subject) == 0 {
 | |
| 		return nil, errors.New("sub claim is missing")
 | |
| 	}
 | |
| 	namespace := private.Namespace
 | |
| 	if len(namespace) == 0 {
 | |
| 		return nil, errors.New("namespace claim is missing")
 | |
| 	}
 | |
| 	secretName := private.SecretName
 | |
| 	if len(secretName) == 0 {
 | |
| 		return nil, errors.New("secretName claim is missing")
 | |
| 	}
 | |
| 	serviceAccountName := private.ServiceAccountName
 | |
| 	if len(serviceAccountName) == 0 {
 | |
| 		return nil, errors.New("serviceAccountName claim is missing")
 | |
| 	}
 | |
| 	serviceAccountUID := private.ServiceAccountUID
 | |
| 	if len(serviceAccountUID) == 0 {
 | |
| 		return nil, errors.New("serviceAccountUID claim is missing")
 | |
| 	}
 | |
| 
 | |
| 	subjectNamespace, subjectName, err := apiserverserviceaccount.SplitUsername(public.Subject)
 | |
| 	if err != nil || subjectNamespace != namespace || subjectName != serviceAccountName {
 | |
| 		return nil, errors.New("sub claim is invalid")
 | |
| 	}
 | |
| 
 | |
| 	if v.lookup {
 | |
| 		// Make sure token hasn't been invalidated by deletion of the secret
 | |
| 		secret, err := v.getter.GetSecret(namespace, secretName)
 | |
| 		if err != nil {
 | |
| 			klog.V(4).Infof("Could not retrieve token %s/%s for service account %s/%s: %v", namespace, secretName, namespace, serviceAccountName, err)
 | |
| 			return nil, errors.New("Token has been invalidated")
 | |
| 		}
 | |
| 		if secret.DeletionTimestamp != nil {
 | |
| 			klog.V(4).Infof("Token is deleted and awaiting removal: %s/%s for service account %s/%s", namespace, secretName, namespace, serviceAccountName)
 | |
| 			return nil, errors.New("Token has been invalidated")
 | |
| 		}
 | |
| 		if !bytes.Equal(secret.Data[v1.ServiceAccountTokenKey], []byte(tokenData)) {
 | |
| 			klog.V(4).Infof("Token contents no longer matches %s/%s for service account %s/%s", namespace, secretName, namespace, serviceAccountName)
 | |
| 			return nil, errors.New("Token does not match server's copy")
 | |
| 		}
 | |
| 
 | |
| 		// Make sure service account still exists (name and UID)
 | |
| 		serviceAccount, err := v.getter.GetServiceAccount(namespace, serviceAccountName)
 | |
| 		if err != nil {
 | |
| 			klog.V(4).Infof("Could not retrieve service account %s/%s: %v", namespace, serviceAccountName, err)
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		if serviceAccount.DeletionTimestamp != nil {
 | |
| 			klog.V(4).Infof("Service account has been deleted %s/%s", namespace, serviceAccountName)
 | |
| 			return nil, fmt.Errorf("ServiceAccount %s/%s has been deleted", namespace, serviceAccountName)
 | |
| 		}
 | |
| 		if string(serviceAccount.UID) != serviceAccountUID {
 | |
| 			klog.V(4).Infof("Service account UID no longer matches %s/%s: %q != %q", namespace, serviceAccountName, string(serviceAccount.UID), serviceAccountUID)
 | |
| 			return nil, fmt.Errorf("ServiceAccount UID (%s) does not match claim (%s)", serviceAccount.UID, serviceAccountUID)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return &ServiceAccountInfo{
 | |
| 		Namespace: private.Namespace,
 | |
| 		Name:      private.ServiceAccountName,
 | |
| 		UID:       private.ServiceAccountUID,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (v *legacyValidator) NewPrivateClaims() interface{} {
 | |
| 	return &legacyPrivateClaims{}
 | |
| }
 |