...

Source file src/k8s.io/kubernetes/test/integration/auth/svcaccttoken_test.go

Documentation: k8s.io/kubernetes/test/integration/auth

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

View as plain text