k8s.io/kubernetes@v1.29.3/test/integration/auth/svcaccttoken_test.go (about)

     1  /*
     2  Copyright 2017 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 auth
    18  
    19  import (
    20  	"bytes"
    21  	"context"
    22  	"encoding/base64"
    23  	"encoding/json"
    24  	"fmt"
    25  	"io"
    26  	"net/http"
    27  	"net/url"
    28  	"reflect"
    29  	"strconv"
    30  	"strings"
    31  	"sync"
    32  	"testing"
    33  	"time"
    34  
    35  	jose "gopkg.in/square/go-jose.v2"
    36  	"gopkg.in/square/go-jose.v2/jwt"
    37  
    38  	authenticationv1 "k8s.io/api/authentication/v1"
    39  	v1 "k8s.io/api/core/v1"
    40  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    41  	"k8s.io/apimachinery/pkg/types"
    42  	"k8s.io/apiserver/pkg/authentication/authenticator"
    43  	apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
    44  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    45  	clientset "k8s.io/client-go/kubernetes"
    46  	"k8s.io/client-go/kubernetes/scheme"
    47  	"k8s.io/client-go/rest"
    48  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    49  	"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
    50  	"k8s.io/kubernetes/pkg/apis/core"
    51  	"k8s.io/kubernetes/pkg/controlplane"
    52  	"k8s.io/kubernetes/pkg/features"
    53  	"k8s.io/kubernetes/pkg/serviceaccount"
    54  	"k8s.io/kubernetes/test/integration/framework"
    55  	"k8s.io/kubernetes/test/utils/ktesting"
    56  	"k8s.io/utils/ptr"
    57  )
    58  
    59  const (
    60  	// This key is for testing purposes only and is not considered secure.
    61  	ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY-----
    62  MHcCAQEEIEZmTmUhuanLjPA2CLquXivuwBDHTt5XYwgIr/kA1LtRoAoGCCqGSM49
    63  AwEHoUQDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPLX2i8uIp/C/ASqiIGUeeKQtX0
    64  /IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg==
    65  -----END EC PRIVATE KEY-----`
    66  
    67  	tokenExpirationSeconds = 60*60 + 7
    68  )
    69  
    70  func TestServiceAccountTokenCreate(t *testing.T) {
    71  	const iss = "https://foo.bar.example.com"
    72  	aud := authenticator.Audiences{"api"}
    73  
    74  	maxExpirationSeconds := int64(60 * 60 * 2)
    75  	maxExpirationDuration, err := time.ParseDuration(fmt.Sprintf("%ds", maxExpirationSeconds))
    76  	if err != nil {
    77  		t.Fatalf("err: %v", err)
    78  	}
    79  
    80  	var tokenGenerator serviceaccount.TokenGenerator
    81  
    82  	_, ctx := ktesting.NewTestContext(t)
    83  	ctx, cancel := context.WithCancel(ctx)
    84  	defer cancel()
    85  
    86  	// Enable the node token improvements feature gates prior to starting the apiserver, as the node getter is
    87  	// conditionally passed to the service account token generator based on feature enablement.
    88  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenNodeBinding, true)()
    89  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenPodNodeInfo, true)()
    90  	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenNodeBindingValidation, true)()
    91  
    92  	// Start the server
    93  	var serverAddress string
    94  	kubeClient, kubeConfig, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{
    95  		ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
    96  			// Disable ServiceAccount admission plugin as we don't have serviceaccount controller running.
    97  			opts.Admission.GenericAdmission.DisablePlugins = []string{"ServiceAccount"}
    98  			opts.Authorization.Modes = []string{"AlwaysAllow"}
    99  			// Disable token cache so we can check reaction to service account deletion quickly
   100  			opts.Authentication.TokenSuccessCacheTTL = 0
   101  			opts.Authentication.TokenFailureCacheTTL = 0
   102  			// Pin to fixed URLs for easier testing
   103  			opts.Authentication.ServiceAccounts.JWKSURI = "https:///openid/v1/jwks"
   104  			opts.Authentication.ServiceAccounts.Issuers = []string{iss}
   105  			opts.Authentication.APIAudiences = aud
   106  		},
   107  		ModifyServerConfig: func(config *controlplane.Config) {
   108  			// extract token generator
   109  			tokenGenerator = config.ExtraConfig.ServiceAccountIssuer
   110  
   111  			config.ExtraConfig.ServiceAccountMaxExpiration = maxExpirationDuration
   112  			config.ExtraConfig.ExtendExpiration = true
   113  		},
   114  	})
   115  	defer tearDownFn()
   116  
   117  	ns := framework.CreateNamespaceOrDie(kubeClient, "myns", t)
   118  	defer framework.DeleteNamespaceOrDie(kubeClient, ns, t)
   119  
   120  	warningHandler := &recordingWarningHandler{}
   121  
   122  	configWithWarningHandler := rest.CopyConfig(kubeConfig)
   123  	configWithWarningHandler.WarningHandler = warningHandler
   124  	cs, err := clientset.NewForConfig(configWithWarningHandler)
   125  	if err != nil {
   126  		t.Fatalf("err: %v", err)
   127  	}
   128  
   129  	kubeConfig.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
   130  	rc, err := rest.UnversionedRESTClientFor(kubeConfig)
   131  	if err != nil {
   132  		t.Fatal(err)
   133  	}
   134  
   135  	var (
   136  		sa = &v1.ServiceAccount{
   137  			ObjectMeta: metav1.ObjectMeta{
   138  				Name:      "test-svcacct",
   139  				Namespace: ns.Name,
   140  			},
   141  		}
   142  		node = &v1.Node{
   143  			ObjectMeta: metav1.ObjectMeta{
   144  				Name: "test-node",
   145  			},
   146  		}
   147  		pod = &v1.Pod{
   148  			ObjectMeta: metav1.ObjectMeta{
   149  				Name:      "test-pod",
   150  				Namespace: sa.Namespace,
   151  			},
   152  			Spec: v1.PodSpec{
   153  				ServiceAccountName: sa.Name,
   154  				Containers:         []v1.Container{{Name: "test-container", Image: "nginx"}},
   155  			},
   156  		}
   157  		scheduledpod = &v1.Pod{
   158  			ObjectMeta: metav1.ObjectMeta{
   159  				Name:      "test-pod",
   160  				Namespace: sa.Namespace,
   161  			},
   162  			Spec: v1.PodSpec{
   163  				ServiceAccountName: sa.Name,
   164  				NodeName:           node.Name,
   165  				Containers:         []v1.Container{{Name: "test-container", Image: "nginx"}},
   166  			},
   167  		}
   168  		otherpod = &v1.Pod{
   169  			ObjectMeta: metav1.ObjectMeta{
   170  				Name:      "other-test-pod",
   171  				Namespace: sa.Namespace,
   172  			},
   173  			Spec: v1.PodSpec{
   174  				ServiceAccountName: "other-" + sa.Name,
   175  				Containers:         []v1.Container{{Name: "test-container", Image: "nginx"}},
   176  			},
   177  		}
   178  		secret = &v1.Secret{
   179  			ObjectMeta: metav1.ObjectMeta{
   180  				Name:      "test-secret",
   181  				Namespace: sa.Namespace,
   182  			},
   183  		}
   184  		wrongUID = types.UID("wrong")
   185  		noUID    = types.UID("")
   186  	)
   187  
   188  	t.Run("bound to service account", func(t *testing.T) {
   189  		treq := &authenticationv1.TokenRequest{
   190  			Spec: authenticationv1.TokenRequestSpec{
   191  				Audiences: []string{"api"},
   192  			},
   193  		}
   194  
   195  		warningHandler.clear()
   196  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   197  			t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
   198  		}
   199  		warningHandler.assertEqual(t, nil)
   200  		sa, delSvcAcct := createDeleteSvcAcct(t, cs, sa)
   201  		defer delSvcAcct()
   202  
   203  		treqWithBadName := treq.DeepCopy()
   204  		treqWithBadName.Name = "invalid-name"
   205  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treqWithBadName, metav1.CreateOptions{}); err == nil || !strings.Contains(err.Error(), "must match the service account name") {
   206  			t.Fatalf("expected err creating token with mismatched name but got: %#v", resp)
   207  		}
   208  
   209  		treqWithBadNamespace := treq.DeepCopy()
   210  		treqWithBadNamespace.Namespace = "invalid-namespace"
   211  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treqWithBadNamespace, metav1.CreateOptions{}); err == nil || !strings.Contains(err.Error(), "does not match the namespace") {
   212  			t.Fatalf("expected err creating token with mismatched namespace but got: %#v, %v", resp, err)
   213  		}
   214  
   215  		warningHandler.clear()
   216  		treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{})
   217  		if err != nil {
   218  			t.Fatalf("err: %v", err)
   219  		}
   220  		warningHandler.assertEqual(t, nil)
   221  
   222  		if treq.Name != sa.Name {
   223  			t.Errorf("expected name=%s, got %s", sa.Name, treq.Name)
   224  		}
   225  		if treq.Namespace != sa.Namespace {
   226  			t.Errorf("expected namespace=%s, got %s", sa.Namespace, treq.Namespace)
   227  		}
   228  		if treq.CreationTimestamp.IsZero() {
   229  			t.Errorf("expected non-zero creation timestamp")
   230  		}
   231  
   232  		checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
   233  		checkPayload(t, treq.Status.Token, `["api"]`, "aud")
   234  		checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "pod")
   235  		checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
   236  		checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
   237  		checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
   238  
   239  		info := doTokenReview(t, cs, treq, false)
   240  		if info.Extra != nil {
   241  			t.Fatalf("expected Extra to be nil but got: %#v", info.Extra)
   242  		}
   243  		delSvcAcct()
   244  		doTokenReview(t, cs, treq, true)
   245  	})
   246  
   247  	t.Run("bound to service account and pod", func(t *testing.T) {
   248  		// Disable embedding pod's node info
   249  		defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenPodNodeInfo, false)()
   250  		treq := &authenticationv1.TokenRequest{
   251  			Spec: authenticationv1.TokenRequestSpec{
   252  				Audiences: []string{"api"},
   253  				BoundObjectRef: &authenticationv1.BoundObjectReference{
   254  					Kind:       "Pod",
   255  					APIVersion: "v1",
   256  					Name:       pod.Name,
   257  				},
   258  			},
   259  		}
   260  
   261  		warningHandler.clear()
   262  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   263  			t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
   264  		}
   265  		warningHandler.assertEqual(t, nil)
   266  		sa, del := createDeleteSvcAcct(t, cs, sa)
   267  		defer del()
   268  
   269  		warningHandler.clear()
   270  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   271  			t.Fatalf("expected err creating token bound to nonexistant pod but got: %#v", resp)
   272  		}
   273  		warningHandler.assertEqual(t, nil)
   274  		pod, delPod := createDeletePod(t, cs, pod)
   275  		defer delPod()
   276  
   277  		// right uid
   278  		treq.Spec.BoundObjectRef.UID = pod.UID
   279  		warningHandler.clear()
   280  		if _, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err != nil {
   281  			t.Fatalf("err: %v", err)
   282  		}
   283  		warningHandler.assertEqual(t, nil)
   284  		// wrong uid
   285  		treq.Spec.BoundObjectRef.UID = wrongUID
   286  		warningHandler.clear()
   287  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   288  			t.Fatalf("expected err creating token bound to pod with wrong uid but got: %#v", resp)
   289  		}
   290  		warningHandler.assertEqual(t, nil)
   291  		// no uid
   292  		treq.Spec.BoundObjectRef.UID = noUID
   293  		warningHandler.clear()
   294  		treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{})
   295  		if err != nil {
   296  			t.Fatalf("err: %v", err)
   297  		}
   298  		warningHandler.assertEqual(t, nil)
   299  
   300  		checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
   301  		checkPayload(t, treq.Status.Token, `["api"]`, "aud")
   302  		checkPayload(t, treq.Status.Token, `"test-pod"`, "kubernetes.io", "pod", "name")
   303  		checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
   304  		checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
   305  		checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
   306  		checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "node")
   307  
   308  		info := doTokenReview(t, cs, treq, false)
   309  		if len(info.Extra) != 2 {
   310  			t.Fatalf("expected Extra have length of 2 but was length %d: %#v", len(info.Extra), info.Extra)
   311  		}
   312  		if expected := map[string]authenticationv1.ExtraValue{
   313  			"authentication.kubernetes.io/pod-name": {pod.ObjectMeta.Name},
   314  			"authentication.kubernetes.io/pod-uid":  {string(pod.ObjectMeta.UID)},
   315  		}; !reflect.DeepEqual(info.Extra, expected) {
   316  			t.Fatalf("unexpected Extra:\ngot:\t%#v\nwant:\t%#v", info.Extra, expected)
   317  		}
   318  		delPod()
   319  		doTokenReview(t, cs, treq, true)
   320  	})
   321  
   322  	testPodWithAssignedNode := func(node *v1.Node) func(t *testing.T) {
   323  		return func(t *testing.T) {
   324  			treq := &authenticationv1.TokenRequest{
   325  				Spec: authenticationv1.TokenRequestSpec{
   326  					Audiences: []string{"api"},
   327  					BoundObjectRef: &authenticationv1.BoundObjectReference{
   328  						Kind:       "Pod",
   329  						APIVersion: "v1",
   330  						Name:       scheduledpod.Name,
   331  					},
   332  				},
   333  			}
   334  
   335  			warningHandler.clear()
   336  			if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   337  				t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
   338  			}
   339  			warningHandler.assertEqual(t, nil)
   340  			sa, del := createDeleteSvcAcct(t, cs, sa)
   341  			defer del()
   342  
   343  			warningHandler.clear()
   344  			if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   345  				t.Fatalf("expected err creating token bound to nonexistant pod but got: %#v", resp)
   346  			}
   347  			warningHandler.assertEqual(t, nil)
   348  			pod, delPod := createDeletePod(t, cs, scheduledpod)
   349  			defer delPod()
   350  
   351  			if node != nil {
   352  				var delNode func()
   353  				node, delNode = createDeleteNode(t, cs, node)
   354  				defer delNode()
   355  			}
   356  
   357  			// right uid
   358  			treq.Spec.BoundObjectRef.UID = pod.UID
   359  			warningHandler.clear()
   360  			if _, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err != nil {
   361  				t.Fatalf("err: %v", err)
   362  			}
   363  			warningHandler.assertEqual(t, nil)
   364  			// wrong uid
   365  			treq.Spec.BoundObjectRef.UID = wrongUID
   366  			warningHandler.clear()
   367  			if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   368  				t.Fatalf("expected err creating token bound to pod with wrong uid but got: %#v", resp)
   369  			}
   370  			warningHandler.assertEqual(t, nil)
   371  			// no uid
   372  			treq.Spec.BoundObjectRef.UID = noUID
   373  			warningHandler.clear()
   374  			treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{})
   375  			if err != nil {
   376  				t.Fatalf("err: %v", err)
   377  			}
   378  			warningHandler.assertEqual(t, nil)
   379  
   380  			checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
   381  			checkPayload(t, treq.Status.Token, `["api"]`, "aud")
   382  			checkPayload(t, treq.Status.Token, `"test-pod"`, "kubernetes.io", "pod", "name")
   383  			checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
   384  			checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
   385  			checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
   386  
   387  			expectedExtraValues := map[string]authenticationv1.ExtraValue{
   388  				"authentication.kubernetes.io/pod-name": {pod.ObjectMeta.Name},
   389  				"authentication.kubernetes.io/pod-uid":  {string(pod.ObjectMeta.UID)},
   390  			}
   391  			// If the NodeName is set at all, expect it to be included in the claims
   392  			if pod.Spec.NodeName != "" {
   393  				checkPayload(t, treq.Status.Token, fmt.Sprintf(`"%s"`, pod.Spec.NodeName), "kubernetes.io", "node", "name")
   394  				expectedExtraValues["authentication.kubernetes.io/node-name"] = authenticationv1.ExtraValue{pod.Spec.NodeName}
   395  			}
   396  			// If the node is non-nil, we expect the UID to be set too
   397  			if node != nil {
   398  				checkPayload(t, treq.Status.Token, fmt.Sprintf(`"%s"`, node.UID), "kubernetes.io", "node", "uid")
   399  				expectedExtraValues["authentication.kubernetes.io/node-uid"] = authenticationv1.ExtraValue{string(node.ObjectMeta.UID)}
   400  			}
   401  
   402  			info := doTokenReview(t, cs, treq, false)
   403  			if len(info.Extra) != len(expectedExtraValues) {
   404  				t.Fatalf("expected Extra have length of %d but was length %d: %#v", len(expectedExtraValues), len(info.Extra), info.Extra)
   405  			}
   406  			if !reflect.DeepEqual(info.Extra, expectedExtraValues) {
   407  				t.Fatalf("unexpected Extra:\ngot:\t%#v\nwant:\t%#v", info.Extra, expectedExtraValues)
   408  			}
   409  
   410  			delPod()
   411  			doTokenReview(t, cs, treq, true)
   412  		}
   413  	}
   414  
   415  	t.Run("bound to service account and a pod with an assigned nodeName that does not exist", testPodWithAssignedNode(nil))
   416  	t.Run("bound to service account and a pod with an assigned nodeName", testPodWithAssignedNode(node))
   417  
   418  	t.Run("fails to bind to a Node if the feature gate is disabled", func(t *testing.T) {
   419  		// Disable node binding
   420  		defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenNodeBinding, false)()
   421  
   422  		// Create ServiceAccount and Node objects
   423  		sa, del := createDeleteSvcAcct(t, cs, sa)
   424  		defer del()
   425  		node, delNode := createDeleteNode(t, cs, node)
   426  		defer delNode()
   427  
   428  		treq := &authenticationv1.TokenRequest{
   429  			Spec: authenticationv1.TokenRequestSpec{
   430  				Audiences: []string{"api"},
   431  				BoundObjectRef: &authenticationv1.BoundObjectReference{
   432  					Kind:       "Node",
   433  					APIVersion: "v1",
   434  					Name:       node.Name,
   435  					UID:        node.UID,
   436  				},
   437  			},
   438  		}
   439  		warningHandler.clear()
   440  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   441  			t.Fatalf("expected err creating token with featuregate disabled but got: %#v", resp)
   442  		} else if err.Error() != "cannot bind token to a Node object as the \"ServiceAccountTokenNodeBinding\" feature-gate is disabled" {
   443  			t.Fatalf("expected error due to feature gate being disabled, but got: %s", err.Error())
   444  		}
   445  		warningHandler.assertEqual(t, nil)
   446  	})
   447  
   448  	t.Run("bound to service account and node", func(t *testing.T) {
   449  		treq := &authenticationv1.TokenRequest{
   450  			Spec: authenticationv1.TokenRequestSpec{
   451  				Audiences: []string{"api"},
   452  				BoundObjectRef: &authenticationv1.BoundObjectReference{
   453  					Kind:       "Node",
   454  					APIVersion: "v1",
   455  					Name:       node.Name,
   456  					UID:        node.UID,
   457  				},
   458  			},
   459  		}
   460  
   461  		warningHandler.clear()
   462  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   463  			t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
   464  		}
   465  		warningHandler.assertEqual(t, nil)
   466  		sa, del := createDeleteSvcAcct(t, cs, sa)
   467  		defer del()
   468  
   469  		warningHandler.clear()
   470  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   471  			t.Fatalf("expected err creating token bound to nonexistant node but got: %#v", resp)
   472  		}
   473  		warningHandler.assertEqual(t, nil)
   474  		node, delNode := createDeleteNode(t, cs, node)
   475  		defer delNode()
   476  
   477  		// right uid
   478  		treq.Spec.BoundObjectRef.UID = node.UID
   479  		warningHandler.clear()
   480  		if _, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err != nil {
   481  			t.Fatalf("err: %v", err)
   482  		}
   483  		warningHandler.assertEqual(t, nil)
   484  		// wrong uid
   485  		treq.Spec.BoundObjectRef.UID = wrongUID
   486  		warningHandler.clear()
   487  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   488  			t.Fatalf("expected err creating token bound to node with wrong uid but got: %#v", resp)
   489  		}
   490  		warningHandler.assertEqual(t, nil)
   491  		// no uid
   492  		treq.Spec.BoundObjectRef.UID = noUID
   493  		warningHandler.clear()
   494  		treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{})
   495  		if err != nil {
   496  			t.Fatalf("err: %v", err)
   497  		}
   498  		warningHandler.assertEqual(t, nil)
   499  
   500  		checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
   501  		checkPayload(t, treq.Status.Token, `["api"]`, "aud")
   502  		checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
   503  		checkPayload(t, treq.Status.Token, `"test-node"`, "kubernetes.io", "node", "name")
   504  		checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
   505  		checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
   506  
   507  		doTokenReview(t, cs, treq, false)
   508  		delNode()
   509  		doTokenReview(t, cs, treq, true)
   510  	})
   511  
   512  	t.Run("bound to service account and secret", func(t *testing.T) {
   513  		treq := &authenticationv1.TokenRequest{
   514  			Spec: authenticationv1.TokenRequestSpec{
   515  				Audiences: []string{"api"},
   516  				BoundObjectRef: &authenticationv1.BoundObjectReference{
   517  					Kind:       "Secret",
   518  					APIVersion: "v1",
   519  					Name:       secret.Name,
   520  					UID:        secret.UID,
   521  				},
   522  			},
   523  		}
   524  
   525  		warningHandler.clear()
   526  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   527  			t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
   528  		}
   529  		warningHandler.assertEqual(t, nil)
   530  		sa, del := createDeleteSvcAcct(t, cs, sa)
   531  		defer del()
   532  
   533  		warningHandler.clear()
   534  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   535  			t.Fatalf("expected err creating token bound to nonexistant secret but got: %#v", resp)
   536  		}
   537  		warningHandler.assertEqual(t, nil)
   538  		secret, delSecret := createDeleteSecret(t, cs, secret)
   539  		defer delSecret()
   540  
   541  		// right uid
   542  		treq.Spec.BoundObjectRef.UID = secret.UID
   543  		warningHandler.clear()
   544  		if _, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err != nil {
   545  			t.Fatalf("err: %v", err)
   546  		}
   547  		warningHandler.assertEqual(t, nil)
   548  		// wrong uid
   549  		treq.Spec.BoundObjectRef.UID = wrongUID
   550  		warningHandler.clear()
   551  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   552  			t.Fatalf("expected err creating token bound to secret with wrong uid but got: %#v", resp)
   553  		}
   554  		warningHandler.assertEqual(t, nil)
   555  		// no uid
   556  		treq.Spec.BoundObjectRef.UID = noUID
   557  		warningHandler.clear()
   558  		treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{})
   559  		if err != nil {
   560  			t.Fatalf("err: %v", err)
   561  		}
   562  		warningHandler.assertEqual(t, nil)
   563  
   564  		checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
   565  		checkPayload(t, treq.Status.Token, `["api"]`, "aud")
   566  		checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
   567  		checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
   568  		checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
   569  		checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
   570  
   571  		doTokenReview(t, cs, treq, false)
   572  		delSecret()
   573  		doTokenReview(t, cs, treq, true)
   574  	})
   575  
   576  	t.Run("bound to service account and pod running as different service account", func(t *testing.T) {
   577  		treq := &authenticationv1.TokenRequest{
   578  			Spec: authenticationv1.TokenRequestSpec{
   579  				Audiences: []string{"api"},
   580  				BoundObjectRef: &authenticationv1.BoundObjectReference{
   581  					Kind:       "Pod",
   582  					APIVersion: "v1",
   583  					Name:       otherpod.Name,
   584  				},
   585  			},
   586  		}
   587  
   588  		sa, del := createDeleteSvcAcct(t, cs, sa)
   589  		defer del()
   590  		_, del = createDeletePod(t, cs, otherpod)
   591  		defer del()
   592  
   593  		warningHandler.clear()
   594  		if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err == nil {
   595  			t.Fatalf("expected err but got: %#v", resp)
   596  		}
   597  		warningHandler.assertEqual(t, nil)
   598  	})
   599  
   600  	t.Run("expired token", func(t *testing.T) {
   601  		treq := &authenticationv1.TokenRequest{
   602  			Spec: authenticationv1.TokenRequestSpec{
   603  				Audiences: []string{"api"},
   604  			},
   605  		}
   606  
   607  		sa, del := createDeleteSvcAcct(t, cs, sa)
   608  		defer del()
   609  
   610  		warningHandler.clear()
   611  		treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{})
   612  		if err != nil {
   613  			t.Fatalf("err: %v", err)
   614  		}
   615  		warningHandler.assertEqual(t, nil)
   616  
   617  		doTokenReview(t, cs, treq, false)
   618  
   619  		// backdate the token
   620  		then := time.Now().Add(-2 * time.Hour)
   621  		sc := &jwt.Claims{
   622  			Subject:   apiserverserviceaccount.MakeUsername(sa.Namespace, sa.Name),
   623  			Audience:  jwt.Audience([]string{"api"}),
   624  			IssuedAt:  jwt.NewNumericDate(then),
   625  			NotBefore: jwt.NewNumericDate(then),
   626  			Expiry:    jwt.NewNumericDate(then.Add(time.Duration(60*60) * time.Second)),
   627  		}
   628  		coresa := core.ServiceAccount{
   629  			ObjectMeta: sa.ObjectMeta,
   630  		}
   631  		_, pc, err := serviceaccount.Claims(coresa, nil, nil, nil, 0, 0, nil)
   632  		if err != nil {
   633  			t.Fatalf("err calling Claims: %v", err)
   634  		}
   635  		tok, err := tokenGenerator.GenerateToken(sc, pc)
   636  		if err != nil {
   637  			t.Fatalf("err signing expired token: %v", err)
   638  		}
   639  
   640  		treq.Status.Token = tok
   641  		doTokenReview(t, cs, treq, true)
   642  	})
   643  
   644  	t.Run("expiration extended token", func(t *testing.T) {
   645  		var requestExp int64 = tokenExpirationSeconds
   646  		treq := &authenticationv1.TokenRequest{
   647  			Spec: authenticationv1.TokenRequestSpec{
   648  				Audiences:         []string{"api"},
   649  				ExpirationSeconds: &requestExp,
   650  				BoundObjectRef: &authenticationv1.BoundObjectReference{
   651  					Kind:       "Pod",
   652  					APIVersion: "v1",
   653  					Name:       pod.Name,
   654  				},
   655  			},
   656  		}
   657  
   658  		sa, del := createDeleteSvcAcct(t, cs, sa)
   659  		defer del()
   660  		pod, delPod := createDeletePod(t, cs, pod)
   661  		defer delPod()
   662  		treq.Spec.BoundObjectRef.UID = pod.UID
   663  
   664  		warningHandler.clear()
   665  		treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{})
   666  		if err != nil {
   667  			t.Fatalf("err: %v", err)
   668  		}
   669  		warningHandler.assertEqual(t, nil)
   670  
   671  		doTokenReview(t, cs, treq, false)
   672  
   673  		// Give some tolerance to avoid flakiness since we are using real time.
   674  		var leeway int64 = 2
   675  		actualExpiry := *jwt.NewNumericDate(time.Now().Add(time.Duration(24*365) * time.Hour))
   676  		assumedExpiry := *jwt.NewNumericDate(time.Now().Add(time.Duration(requestExp) * time.Second))
   677  		exp, err := strconv.ParseInt(getSubObject(t, getPayload(t, treq.Status.Token), "exp"), 10, 64)
   678  		if err != nil {
   679  			t.Fatalf("error parsing exp: %v", err)
   680  		}
   681  		warnafter, err := strconv.ParseInt(getSubObject(t, getPayload(t, treq.Status.Token), "kubernetes.io", "warnafter"), 10, 64)
   682  		if err != nil {
   683  			t.Fatalf("error parsing warnafter: %v", err)
   684  		}
   685  
   686  		if exp < int64(actualExpiry)-leeway || exp > int64(actualExpiry)+leeway {
   687  			t.Errorf("unexpected token exp %d, should within range of %d +- %d seconds", exp, actualExpiry, leeway)
   688  		}
   689  		if warnafter < int64(assumedExpiry)-leeway || warnafter > int64(assumedExpiry)+leeway {
   690  			t.Errorf("unexpected token warnafter %d, should within range of %d +- %d seconds", warnafter, assumedExpiry, leeway)
   691  		}
   692  
   693  		checkExpiration(t, treq, requestExp)
   694  		expStatus := treq.Status.ExpirationTimestamp.Time.Unix()
   695  		if expStatus < int64(assumedExpiry)-leeway || warnafter > int64(assumedExpiry)+leeway {
   696  			t.Errorf("unexpected expiration returned in tokenrequest status %d, should within range of %d +- %d seconds", expStatus, assumedExpiry, leeway)
   697  		}
   698  	})
   699  
   700  	t.Run("extended expiration extended does not apply for other audiences", func(t *testing.T) {
   701  		var requestExp int64 = tokenExpirationSeconds
   702  		treq := &authenticationv1.TokenRequest{
   703  			Spec: authenticationv1.TokenRequestSpec{
   704  				Audiences:         []string{"not-the-api", "api"},
   705  				ExpirationSeconds: &requestExp,
   706  				BoundObjectRef: &authenticationv1.BoundObjectReference{
   707  					Kind:       "Pod",
   708  					APIVersion: "v1",
   709  					Name:       pod.Name,
   710  				},
   711  			},
   712  		}
   713  
   714  		sa, del := createDeleteSvcAcct(t, cs, sa)
   715  		defer del()
   716  		pod, delPod := createDeletePod(t, cs, pod)
   717  		defer delPod()
   718  		treq.Spec.BoundObjectRef.UID = pod.UID
   719  
   720  		warningHandler.clear()
   721  		treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{})
   722  		if err != nil {
   723  			t.Fatalf("err: %v", err)
   724  		}
   725  		warningHandler.assertEqual(t, nil)
   726  
   727  		// Give some tolerance to avoid flakiness since we are using real time.
   728  		var leeway int64 = 10
   729  		actualExpiry := *jwt.NewNumericDate(time.Now().Add(time.Duration(60*60) * time.Second))
   730  		assumedExpiry := *jwt.NewNumericDate(time.Now().Add(time.Duration(requestExp) * time.Second))
   731  
   732  		warnAfter := getSubObject(t, getPayload(t, treq.Status.Token), "kubernetes.io", "warnafter")
   733  		if warnAfter != "null" {
   734  			t.Errorf("warn after should be empty.Found: %s", warnAfter)
   735  		}
   736  
   737  		exp, err := strconv.ParseInt(getSubObject(t, getPayload(t, treq.Status.Token), "exp"), 10, 64)
   738  		if err != nil {
   739  			t.Fatalf("error parsing exp: %v", err)
   740  		}
   741  		if exp < int64(actualExpiry)-leeway || exp > int64(actualExpiry)+leeway {
   742  			t.Errorf("unexpected token exp %d, should within range of %d +- %d seconds", exp, actualExpiry, leeway)
   743  		}
   744  
   745  		checkExpiration(t, treq, requestExp)
   746  		expStatus := treq.Status.ExpirationTimestamp.Time.Unix()
   747  		if expStatus < int64(assumedExpiry)-leeway {
   748  			t.Errorf("unexpected expiration returned in tokenrequest status %d, should within range of %d +- %d seconds", expStatus, assumedExpiry, leeway)
   749  		}
   750  	})
   751  
   752  	t.Run("a token without an api audience is invalid", func(t *testing.T) {
   753  		treq := &authenticationv1.TokenRequest{
   754  			Spec: authenticationv1.TokenRequestSpec{
   755  				Audiences: []string{"not-the-api"},
   756  			},
   757  		}
   758  
   759  		sa, del := createDeleteSvcAcct(t, cs, sa)
   760  		defer del()
   761  
   762  		warningHandler.clear()
   763  		treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{})
   764  		if err != nil {
   765  			t.Fatalf("err: %v", err)
   766  		}
   767  		warningHandler.assertEqual(t, nil)
   768  
   769  		doTokenReview(t, cs, treq, true)
   770  	})
   771  
   772  	t.Run("a tokenrequest without an audience is valid against the api", func(t *testing.T) {
   773  		treq := &authenticationv1.TokenRequest{
   774  			Spec: authenticationv1.TokenRequestSpec{},
   775  		}
   776  
   777  		sa, del := createDeleteSvcAcct(t, cs, sa)
   778  		defer del()
   779  
   780  		warningHandler.clear()
   781  		treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{})
   782  		if err != nil {
   783  			t.Fatalf("err: %v", err)
   784  		}
   785  		warningHandler.assertEqual(t, nil)
   786  
   787  		checkPayload(t, treq.Status.Token, `["api"]`, "aud")
   788  
   789  		doTokenReview(t, cs, treq, false)
   790  	})
   791  
   792  	t.Run("a token should be invalid after recreating same name pod", func(t *testing.T) {
   793  		treq := &authenticationv1.TokenRequest{
   794  			Spec: authenticationv1.TokenRequestSpec{
   795  				Audiences: []string{"api"},
   796  				BoundObjectRef: &authenticationv1.BoundObjectReference{
   797  					Kind:       "Pod",
   798  					APIVersion: "v1",
   799  					Name:       pod.Name,
   800  				},
   801  			},
   802  		}
   803  
   804  		sa, del := createDeleteSvcAcct(t, cs, sa)
   805  		defer del()
   806  		originalPod, originalDelPod := createDeletePod(t, cs, pod)
   807  		defer originalDelPod()
   808  
   809  		treq.Spec.BoundObjectRef.UID = originalPod.UID
   810  		warningHandler.clear()
   811  		if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err != nil {
   812  			t.Fatalf("err: %v", err)
   813  		}
   814  		warningHandler.assertEqual(t, nil)
   815  
   816  		checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
   817  		checkPayload(t, treq.Status.Token, `["api"]`, "aud")
   818  		checkPayload(t, treq.Status.Token, `"test-pod"`, "kubernetes.io", "pod", "name")
   819  		checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
   820  		checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
   821  		checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
   822  
   823  		doTokenReview(t, cs, treq, false)
   824  		originalDelPod()
   825  		doTokenReview(t, cs, treq, true)
   826  
   827  		_, recreateDelPod := createDeletePod(t, cs, pod)
   828  		defer recreateDelPod()
   829  
   830  		doTokenReview(t, cs, treq, true)
   831  	})
   832  
   833  	t.Run("a token should be invalid after recreating same name secret", func(t *testing.T) {
   834  		treq := &authenticationv1.TokenRequest{
   835  			Spec: authenticationv1.TokenRequestSpec{
   836  				Audiences: []string{"api"},
   837  				BoundObjectRef: &authenticationv1.BoundObjectReference{
   838  					Kind:       "Secret",
   839  					APIVersion: "v1",
   840  					Name:       secret.Name,
   841  					UID:        secret.UID,
   842  				},
   843  			},
   844  		}
   845  
   846  		sa, del := createDeleteSvcAcct(t, cs, sa)
   847  		defer del()
   848  
   849  		originalSecret, originalDelSecret := createDeleteSecret(t, cs, secret)
   850  		defer originalDelSecret()
   851  
   852  		treq.Spec.BoundObjectRef.UID = originalSecret.UID
   853  		warningHandler.clear()
   854  		if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err != nil {
   855  			t.Fatalf("err: %v", err)
   856  		}
   857  		warningHandler.assertEqual(t, nil)
   858  
   859  		checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
   860  		checkPayload(t, treq.Status.Token, `["api"]`, "aud")
   861  		checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
   862  		checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
   863  		checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
   864  		checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
   865  
   866  		doTokenReview(t, cs, treq, false)
   867  		originalDelSecret()
   868  		doTokenReview(t, cs, treq, true)
   869  
   870  		_, recreateDelSecret := createDeleteSecret(t, cs, secret)
   871  		defer recreateDelSecret()
   872  
   873  		doTokenReview(t, cs, treq, true)
   874  	})
   875  
   876  	t.Run("a token request within expiration time", func(t *testing.T) {
   877  		normalExpirationTime := maxExpirationSeconds - 10*60
   878  		treq := &authenticationv1.TokenRequest{
   879  			Spec: authenticationv1.TokenRequestSpec{
   880  				Audiences:         []string{"api"},
   881  				ExpirationSeconds: &normalExpirationTime,
   882  				BoundObjectRef: &authenticationv1.BoundObjectReference{
   883  					Kind:       "Secret",
   884  					APIVersion: "v1",
   885  					Name:       secret.Name,
   886  					UID:        secret.UID,
   887  				},
   888  			},
   889  		}
   890  
   891  		sa, del := createDeleteSvcAcct(t, cs, sa)
   892  		defer del()
   893  
   894  		originalSecret, originalDelSecret := createDeleteSecret(t, cs, secret)
   895  		defer originalDelSecret()
   896  
   897  		treq.Spec.BoundObjectRef.UID = originalSecret.UID
   898  		warningHandler.clear()
   899  		if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err != nil {
   900  			t.Fatalf("err: %v", err)
   901  		}
   902  		warningHandler.assertEqual(t, nil)
   903  
   904  		checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
   905  		checkPayload(t, treq.Status.Token, `["api"]`, "aud")
   906  		checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
   907  		checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
   908  		checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
   909  		checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
   910  		checkExpiration(t, treq, normalExpirationTime)
   911  
   912  		doTokenReview(t, cs, treq, false)
   913  		originalDelSecret()
   914  		doTokenReview(t, cs, treq, true)
   915  
   916  		_, recreateDelSecret := createDeleteSecret(t, cs, secret)
   917  		defer recreateDelSecret()
   918  
   919  		doTokenReview(t, cs, treq, true)
   920  	})
   921  
   922  	t.Run("a token request with out-of-range expiration", func(t *testing.T) {
   923  		tooLongExpirationTime := maxExpirationSeconds + 10*60
   924  		treq := &authenticationv1.TokenRequest{
   925  			Spec: authenticationv1.TokenRequestSpec{
   926  				Audiences:         []string{"api"},
   927  				ExpirationSeconds: &tooLongExpirationTime,
   928  				BoundObjectRef: &authenticationv1.BoundObjectReference{
   929  					Kind:       "Secret",
   930  					APIVersion: "v1",
   931  					Name:       secret.Name,
   932  					UID:        secret.UID,
   933  				},
   934  			},
   935  		}
   936  
   937  		sa, del := createDeleteSvcAcct(t, cs, sa)
   938  		defer del()
   939  
   940  		originalSecret, originalDelSecret := createDeleteSecret(t, cs, secret)
   941  		defer originalDelSecret()
   942  
   943  		treq.Spec.BoundObjectRef.UID = originalSecret.UID
   944  		warningHandler.clear()
   945  		if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name, treq, metav1.CreateOptions{}); err != nil {
   946  			t.Fatalf("err: %v", err)
   947  		}
   948  		warningHandler.assertEqual(t, []string{fmt.Sprintf("requested expiration of %d seconds shortened to %d seconds", tooLongExpirationTime, maxExpirationSeconds)})
   949  
   950  		checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
   951  		checkPayload(t, treq.Status.Token, `["api"]`, "aud")
   952  		checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
   953  		checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
   954  		checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
   955  		checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
   956  		checkExpiration(t, treq, maxExpirationSeconds)
   957  
   958  		doTokenReview(t, cs, treq, false)
   959  		originalDelSecret()
   960  		doTokenReview(t, cs, treq, true)
   961  
   962  		_, recreateDelSecret := createDeleteSecret(t, cs, secret)
   963  		defer recreateDelSecret()
   964  
   965  		doTokenReview(t, cs, treq, true)
   966  	})
   967  
   968  	t.Run("a token is valid against the HTTP-provided service account issuer metadata", func(t *testing.T) {
   969  		sa, del := createDeleteSvcAcct(t, cs, sa)
   970  		defer del()
   971  
   972  		t.Log("get token")
   973  		warningHandler.clear()
   974  		tokenRequest, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(ctx, sa.Name,
   975  			&authenticationv1.TokenRequest{
   976  				Spec: authenticationv1.TokenRequestSpec{
   977  					Audiences: []string{"api"},
   978  				},
   979  			}, metav1.CreateOptions{})
   980  		if err != nil {
   981  			t.Fatalf("unexpected error creating token: %v", err)
   982  		}
   983  		warningHandler.assertEqual(t, nil)
   984  		token := tokenRequest.Status.Token
   985  		if token == "" {
   986  			t.Fatal("no token")
   987  		}
   988  
   989  		t.Log("get discovery doc")
   990  		discoveryDoc := struct {
   991  			Issuer string `json:"issuer"`
   992  			JWKS   string `json:"jwks_uri"`
   993  		}{}
   994  
   995  		// A little convoluted, but the base path is hidden inside the RESTClient.
   996  		// We can't just use the RESTClient, because it throws away the headers
   997  		// before returning a result, and we need to check the headers.
   998  		discoveryURL := rc.Get().AbsPath("/.well-known/openid-configuration").URL().String()
   999  		resp, err := rc.Client.Get(discoveryURL)
  1000  		if err != nil {
  1001  			t.Fatalf("error getting metadata: %v", err)
  1002  		}
  1003  		defer resp.Body.Close()
  1004  
  1005  		if resp.StatusCode != http.StatusOK {
  1006  			t.Errorf("got status: %v, want: %v", resp.StatusCode, http.StatusOK)
  1007  		}
  1008  		if got, want := resp.Header.Get("Content-Type"), "application/json"; got != want {
  1009  			t.Errorf("got Content-Type: %v, want: %v", got, want)
  1010  		}
  1011  		if got, want := resp.Header.Get("Cache-Control"), "public, max-age=3600"; got != want {
  1012  			t.Errorf("got Cache-Control: %v, want: %v", got, want)
  1013  		}
  1014  
  1015  		b, err := io.ReadAll(resp.Body)
  1016  		if err != nil {
  1017  			t.Fatal(err)
  1018  		}
  1019  		md := bytes.NewBuffer(b)
  1020  		t.Logf("raw discovery doc response:\n---%s\n---", md.String())
  1021  		if md.Len() == 0 {
  1022  			t.Fatal("empty response for discovery doc")
  1023  		}
  1024  
  1025  		if err := json.NewDecoder(md).Decode(&discoveryDoc); err != nil {
  1026  			t.Fatalf("could not decode metadata: %v", err)
  1027  		}
  1028  		if discoveryDoc.Issuer != iss {
  1029  			t.Fatalf("invalid issuer in discovery doc: got %s, want %s",
  1030  				discoveryDoc.Issuer, iss)
  1031  		}
  1032  		expectJWKSURI := (&url.URL{
  1033  			Scheme: "https",
  1034  			Host:   serverAddress,
  1035  			Path:   serviceaccount.JWKSPath,
  1036  		}).String()
  1037  		if discoveryDoc.JWKS != expectJWKSURI {
  1038  			t.Fatalf("unexpected jwks_uri in discovery doc: got %s, want %s",
  1039  				discoveryDoc.JWKS, expectJWKSURI)
  1040  		}
  1041  
  1042  		// Since the test framework hardcodes the host, we combine our client's
  1043  		// scheme and host with serviceaccount.JWKSPath. We know that this is what was
  1044  		// in the discovery doc because we checked that it matched above.
  1045  		jwksURI := rc.Get().AbsPath(serviceaccount.JWKSPath).URL().String()
  1046  		t.Log("get jwks from", jwksURI)
  1047  		resp, err = rc.Client.Get(jwksURI)
  1048  		if err != nil {
  1049  			t.Fatalf("error getting jwks: %v", err)
  1050  		}
  1051  		defer resp.Body.Close()
  1052  
  1053  		if resp.StatusCode != http.StatusOK {
  1054  			t.Errorf("got status: %v, want: %v", resp.StatusCode, http.StatusOK)
  1055  		}
  1056  		if got, want := resp.Header.Get("Content-Type"), "application/jwk-set+json"; got != want {
  1057  			t.Errorf("got Content-Type: %v, want: %v", got, want)
  1058  		}
  1059  		if got, want := resp.Header.Get("Cache-Control"), "public, max-age=3600"; got != want {
  1060  			t.Errorf("got Cache-Control: %v, want: %v", got, want)
  1061  		}
  1062  
  1063  		b, err = io.ReadAll(resp.Body)
  1064  		if err != nil {
  1065  			t.Fatal(err)
  1066  		}
  1067  		ks := bytes.NewBuffer(b)
  1068  		if ks.Len() == 0 {
  1069  			t.Fatal("empty jwks")
  1070  		}
  1071  		t.Logf("raw JWKS: \n---\n%s\n---", ks.String())
  1072  
  1073  		jwks := jose.JSONWebKeySet{}
  1074  		if err := json.NewDecoder(ks).Decode(&jwks); err != nil {
  1075  			t.Fatalf("could not decode JWKS: %v", err)
  1076  		}
  1077  		if len(jwks.Keys) != 1 {
  1078  			t.Fatalf("len(jwks.Keys) = %d, want 1", len(jwks.Keys))
  1079  		}
  1080  		key := jwks.Keys[0]
  1081  		tok, err := jwt.ParseSigned(token)
  1082  		if err != nil {
  1083  			t.Fatalf("could not parse token %q: %v", token, err)
  1084  		}
  1085  		var claims jwt.Claims
  1086  		if err := tok.Claims(key, &claims); err != nil {
  1087  			t.Fatalf("could not validate claims on token: %v", err)
  1088  		}
  1089  		if err := claims.Validate(jwt.Expected{Issuer: discoveryDoc.Issuer}); err != nil {
  1090  			t.Fatalf("invalid claims: %v", err)
  1091  		}
  1092  	})
  1093  }
  1094  
  1095  func doTokenReview(t *testing.T, cs clientset.Interface, treq *authenticationv1.TokenRequest, expectErr bool) authenticationv1.UserInfo {
  1096  	t.Helper()
  1097  	tries := 0
  1098  	for {
  1099  		trev, err := cs.AuthenticationV1().TokenReviews().Create(context.TODO(), &authenticationv1.TokenReview{
  1100  			Spec: authenticationv1.TokenReviewSpec{
  1101  				Token: treq.Status.Token,
  1102  			},
  1103  		}, metav1.CreateOptions{})
  1104  		if err != nil {
  1105  			t.Fatalf("err: %v", err)
  1106  		}
  1107  		t.Logf("status: %+v", trev.Status)
  1108  		if (trev.Status.Error != "") && !expectErr {
  1109  			t.Fatalf("expected no error but got: %v", trev.Status.Error)
  1110  		}
  1111  		if (trev.Status.Error == "") && expectErr {
  1112  			// if we expected an error and didn't get one, retry
  1113  			// to let changes that invalidate the token percolate through informers
  1114  			if tries < 10 {
  1115  				tries++
  1116  				time.Sleep(100 * time.Millisecond)
  1117  				t.Logf("expected error but got: %+v, retrying", trev.Status)
  1118  				continue
  1119  			}
  1120  			t.Fatalf("expected error but got: %+v", trev.Status)
  1121  		}
  1122  		if !trev.Status.Authenticated && !expectErr {
  1123  			t.Fatal("expected token to be authenticated but it wasn't")
  1124  		}
  1125  		return trev.Status.User
  1126  	}
  1127  }
  1128  
  1129  func checkPayload(t *testing.T, tok string, want string, parts ...string) {
  1130  	t.Helper()
  1131  	got := getSubObject(t, getPayload(t, tok), parts...)
  1132  	if got != want {
  1133  		t.Errorf("unexpected payload.\nsaw:\t%v\nwant:\t%v", got, want)
  1134  	}
  1135  }
  1136  
  1137  func checkExpiration(t *testing.T, treq *authenticationv1.TokenRequest, expectedExpiration int64) {
  1138  	t.Helper()
  1139  	if treq.Spec.ExpirationSeconds == nil {
  1140  		t.Errorf("unexpected nil expiration seconds.")
  1141  	}
  1142  	if *treq.Spec.ExpirationSeconds != expectedExpiration {
  1143  		t.Errorf("unexpected expiration seconds.\nsaw:\t%d\nwant:\t%d", treq.Spec.ExpirationSeconds, expectedExpiration)
  1144  	}
  1145  }
  1146  
  1147  func getSubObject(t *testing.T, b string, parts ...string) string {
  1148  	t.Helper()
  1149  	var obj interface{}
  1150  	obj = make(map[string]interface{})
  1151  	if err := json.Unmarshal([]byte(b), &obj); err != nil {
  1152  		t.Fatalf("err: %v", err)
  1153  	}
  1154  	for _, part := range parts {
  1155  		obj = obj.(map[string]interface{})[part]
  1156  	}
  1157  	out, err := json.Marshal(obj)
  1158  	if err != nil {
  1159  		t.Fatalf("err: %v", err)
  1160  	}
  1161  	return string(out)
  1162  }
  1163  
  1164  func getPayload(t *testing.T, b string) string {
  1165  	t.Helper()
  1166  	parts := strings.Split(b, ".")
  1167  	if len(parts) != 3 {
  1168  		t.Fatalf("token did not have three parts: %v", b)
  1169  	}
  1170  	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
  1171  	if err != nil {
  1172  		t.Fatalf("failed to base64 decode token: %v", err)
  1173  	}
  1174  	return string(payload)
  1175  }
  1176  
  1177  func createDeleteSvcAcct(t *testing.T, cs clientset.Interface, sa *v1.ServiceAccount) (*v1.ServiceAccount, func()) {
  1178  	t.Helper()
  1179  	sa, err := cs.CoreV1().ServiceAccounts(sa.Namespace).Create(context.TODO(), sa, metav1.CreateOptions{})
  1180  	if err != nil {
  1181  		t.Fatalf("err: %v", err)
  1182  	}
  1183  	done := false
  1184  	return sa, func() {
  1185  		t.Helper()
  1186  		if done {
  1187  			return
  1188  		}
  1189  		done = true
  1190  		if err := cs.CoreV1().ServiceAccounts(sa.Namespace).Delete(context.TODO(), sa.Name, metav1.DeleteOptions{}); err != nil {
  1191  			t.Fatalf("err: %v", err)
  1192  		}
  1193  	}
  1194  }
  1195  
  1196  func createDeletePod(t *testing.T, cs clientset.Interface, pod *v1.Pod) (*v1.Pod, func()) {
  1197  	t.Helper()
  1198  	pod, err := cs.CoreV1().Pods(pod.Namespace).Create(context.TODO(), pod, metav1.CreateOptions{})
  1199  	if err != nil {
  1200  		t.Fatalf("err: %v", err)
  1201  	}
  1202  	done := false
  1203  	return pod, func() {
  1204  		t.Helper()
  1205  		if done {
  1206  			return
  1207  		}
  1208  		done = true
  1209  		if err := cs.CoreV1().Pods(pod.Namespace).Delete(context.TODO(), pod.Name, metav1.DeleteOptions{
  1210  			GracePeriodSeconds: ptr.To(int64(0)),
  1211  		}); err != nil {
  1212  			t.Fatalf("err: %v", err)
  1213  		}
  1214  	}
  1215  }
  1216  
  1217  func createDeleteSecret(t *testing.T, cs clientset.Interface, sec *v1.Secret) (*v1.Secret, func()) {
  1218  	t.Helper()
  1219  	sec, err := cs.CoreV1().Secrets(sec.Namespace).Create(context.TODO(), sec, metav1.CreateOptions{})
  1220  	if err != nil {
  1221  		t.Fatalf("err: %v", err)
  1222  	}
  1223  	done := false
  1224  	return sec, func() {
  1225  		t.Helper()
  1226  		if done {
  1227  			return
  1228  		}
  1229  		done = true
  1230  		if err := cs.CoreV1().Secrets(sec.Namespace).Delete(context.TODO(), sec.Name, metav1.DeleteOptions{}); err != nil {
  1231  			t.Fatalf("err: %v", err)
  1232  		}
  1233  	}
  1234  }
  1235  
  1236  func createDeleteNode(t *testing.T, cs clientset.Interface, node *v1.Node) (*v1.Node, func()) {
  1237  	t.Helper()
  1238  	node, err := cs.CoreV1().Nodes().Create(context.TODO(), node, metav1.CreateOptions{})
  1239  	if err != nil {
  1240  		t.Fatalf("err: %v", err)
  1241  	}
  1242  	done := false
  1243  	return node, func() {
  1244  		t.Helper()
  1245  		if done {
  1246  			return
  1247  		}
  1248  		done = true
  1249  		if err := cs.CoreV1().Nodes().Delete(context.TODO(), node.Name, metav1.DeleteOptions{}); err != nil {
  1250  			t.Fatalf("err: %v", err)
  1251  		}
  1252  	}
  1253  }
  1254  
  1255  type recordingWarningHandler struct {
  1256  	warnings []string
  1257  
  1258  	sync.Mutex
  1259  }
  1260  
  1261  func (r *recordingWarningHandler) HandleWarningHeader(code int, agent string, message string) {
  1262  	r.Lock()
  1263  	defer r.Unlock()
  1264  	r.warnings = append(r.warnings, message)
  1265  }
  1266  
  1267  func (r *recordingWarningHandler) clear() {
  1268  	r.Lock()
  1269  	defer r.Unlock()
  1270  	r.warnings = nil
  1271  }
  1272  func (r *recordingWarningHandler) assertEqual(t *testing.T, expected []string) {
  1273  	t.Helper()
  1274  	r.Lock()
  1275  	defer r.Unlock()
  1276  	if !reflect.DeepEqual(r.warnings, expected) {
  1277  		t.Errorf("expected\n\t%v\ngot\n\t%v", expected, r.warnings)
  1278  	}
  1279  }