1
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
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
85
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
91 var serverAddress string
92 kubeClient, kubeConfig, tearDownFn := framework.StartTestServer(tCtx, t, framework.TestServerSetup{
93 ModifyServerRunOptions: func(opts *options.ServerRunOptions) {
94
95 opts.Admission.GenericAdmission.DisablePlugins = []string{"ServiceAccount"}
96 opts.Authorization.Modes = []string{"AlwaysAllow"}
97
98 opts.Authentication.TokenSuccessCacheTTL = 0
99 opts.Authentication.TokenFailureCacheTTL = 0
100
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
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
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
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
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
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
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
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
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
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
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
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
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
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
426 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenNodeBinding, false)()
427
428
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
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
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
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
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
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
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
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
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
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
1002
1003
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
1049
1050
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
1119
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