123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719 |
- /*
- Copyright 2017 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package auth
- import (
- "crypto/ecdsa"
- "encoding/base64"
- "encoding/json"
- "fmt"
- "reflect"
- "strings"
- "testing"
- "time"
- "gopkg.in/square/go-jose.v2/jwt"
- authenticationv1 "k8s.io/api/authentication/v1"
- v1 "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/types"
- "k8s.io/apiserver/pkg/authentication/authenticator"
- "k8s.io/apiserver/pkg/authentication/request/bearertoken"
- apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
- "k8s.io/apiserver/pkg/authorization/authorizerfactory"
- utilfeature "k8s.io/apiserver/pkg/util/feature"
- clientset "k8s.io/client-go/kubernetes"
- v1listers "k8s.io/client-go/listers/core/v1"
- "k8s.io/client-go/tools/cache"
- "k8s.io/client-go/util/keyutil"
- featuregatetesting "k8s.io/component-base/featuregate/testing"
- "k8s.io/kubernetes/pkg/apis/core"
- serviceaccountgetter "k8s.io/kubernetes/pkg/controller/serviceaccount"
- "k8s.io/kubernetes/pkg/features"
- "k8s.io/kubernetes/pkg/serviceaccount"
- "k8s.io/kubernetes/test/integration/framework"
- )
- const ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY-----
- MHcCAQEEIEZmTmUhuanLjPA2CLquXivuwBDHTt5XYwgIr/kA1LtRoAoGCCqGSM49
- AwEHoUQDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPLX2i8uIp/C/ASqiIGUeeKQtX0
- /IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg==
- -----END EC PRIVATE KEY-----`
- func TestServiceAccountTokenCreate(t *testing.T) {
- defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TokenRequest, true)()
- // Build client config, clientset, and informers
- sk, err := keyutil.ParsePrivateKeyPEM([]byte(ecdsaPrivateKey))
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- pk := sk.(*ecdsa.PrivateKey).PublicKey
- const iss = "https://foo.bar.example.com"
- aud := authenticator.Audiences{"api"}
- maxExpirationSeconds := int64(60 * 60)
- maxExpirationDuration, err := time.ParseDuration(fmt.Sprintf("%ds", maxExpirationSeconds))
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- gcs := &clientset.Clientset{}
- // Start the server
- masterConfig := framework.NewIntegrationTestMasterConfig()
- masterConfig.GenericConfig.Authorization.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
- masterConfig.GenericConfig.Authentication.APIAudiences = aud
- masterConfig.GenericConfig.Authentication.Authenticator = bearertoken.New(
- serviceaccount.JWTTokenAuthenticator(
- iss,
- []interface{}{&pk},
- aud,
- serviceaccount.NewValidator(serviceaccountgetter.NewGetterFromClient(
- gcs,
- v1listers.NewSecretLister(newIndexer(func(namespace, name string) (interface{}, error) {
- return gcs.CoreV1().Secrets(namespace).Get(name, metav1.GetOptions{})
- })),
- v1listers.NewServiceAccountLister(newIndexer(func(namespace, name string) (interface{}, error) {
- return gcs.CoreV1().ServiceAccounts(namespace).Get(name, metav1.GetOptions{})
- })),
- v1listers.NewPodLister(newIndexer(func(namespace, name string) (interface{}, error) {
- return gcs.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
- })),
- )),
- ),
- )
- tokenGenerator, err := serviceaccount.JWTTokenGenerator(iss, sk)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- masterConfig.ExtraConfig.ServiceAccountIssuer = tokenGenerator
- masterConfig.ExtraConfig.ServiceAccountMaxExpiration = maxExpirationDuration
- masterConfig.GenericConfig.Authentication.APIAudiences = aud
- master, _, closeFn := framework.RunAMaster(masterConfig)
- defer closeFn()
- cs, err := clientset.NewForConfig(master.GenericAPIServer.LoopbackClientConfig)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- *gcs = *cs
- var (
- sa = &v1.ServiceAccount{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-svcacct",
- Namespace: "myns",
- },
- }
- pod = &v1.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-pod",
- Namespace: sa.Namespace,
- },
- Spec: v1.PodSpec{
- ServiceAccountName: sa.Name,
- Containers: []v1.Container{{Name: "test-container", Image: "nginx"}},
- },
- }
- otherpod = &v1.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "other-test-pod",
- Namespace: sa.Namespace,
- },
- Spec: v1.PodSpec{
- ServiceAccountName: "other-" + sa.Name,
- Containers: []v1.Container{{Name: "test-container", Image: "nginx"}},
- },
- }
- secret = &v1.Secret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "test-secret",
- Namespace: sa.Namespace,
- },
- }
- wrongUID = types.UID("wrong")
- noUID = types.UID("")
- )
- t.Run("bound to service account", func(t *testing.T) {
- treq := &authenticationv1.TokenRequest{
- Spec: authenticationv1.TokenRequestSpec{
- Audiences: []string{"api"},
- },
- }
- if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
- t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
- }
- sa, delSvcAcct := createDeleteSvcAcct(t, cs, sa)
- defer delSvcAcct()
- treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
- checkPayload(t, treq.Status.Token, `["api"]`, "aud")
- checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "pod")
- checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
- checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
- checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
- info := doTokenReview(t, cs, treq, false)
- if info.Extra != nil {
- t.Fatalf("expected Extra to be nil but got: %#v", info.Extra)
- }
- delSvcAcct()
- doTokenReview(t, cs, treq, true)
- })
- t.Run("bound to service account and pod", func(t *testing.T) {
- treq := &authenticationv1.TokenRequest{
- Spec: authenticationv1.TokenRequestSpec{
- Audiences: []string{"api"},
- BoundObjectRef: &authenticationv1.BoundObjectReference{
- Kind: "Pod",
- APIVersion: "v1",
- Name: pod.Name,
- },
- },
- }
- if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
- t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
- }
- sa, del := createDeleteSvcAcct(t, cs, sa)
- defer del()
- if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
- t.Fatalf("expected err creating token bound to nonexistant pod but got: %#v", resp)
- }
- pod, delPod := createDeletePod(t, cs, pod)
- defer delPod()
- // right uid
- treq.Spec.BoundObjectRef.UID = pod.UID
- if _, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
- t.Fatalf("err: %v", err)
- }
- // wrong uid
- treq.Spec.BoundObjectRef.UID = wrongUID
- if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
- t.Fatalf("expected err creating token bound to pod with wrong uid but got: %#v", resp)
- }
- // no uid
- treq.Spec.BoundObjectRef.UID = noUID
- treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
- checkPayload(t, treq.Status.Token, `["api"]`, "aud")
- checkPayload(t, treq.Status.Token, `"test-pod"`, "kubernetes.io", "pod", "name")
- checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
- checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
- checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
- info := doTokenReview(t, cs, treq, false)
- if len(info.Extra) != 2 {
- t.Fatalf("expected Extra have length of 2 but was length %d: %#v", len(info.Extra), info.Extra)
- }
- if expected := map[string]authenticationv1.ExtraValue{
- "authentication.kubernetes.io/pod-name": {pod.ObjectMeta.Name},
- "authentication.kubernetes.io/pod-uid": {string(pod.ObjectMeta.UID)},
- }; !reflect.DeepEqual(info.Extra, expected) {
- t.Fatalf("unexpected Extra:\ngot:\t%#v\nwant:\t%#v", info.Extra, expected)
- }
- delPod()
- doTokenReview(t, cs, treq, true)
- })
- t.Run("bound to service account and secret", func(t *testing.T) {
- treq := &authenticationv1.TokenRequest{
- Spec: authenticationv1.TokenRequestSpec{
- Audiences: []string{"api"},
- BoundObjectRef: &authenticationv1.BoundObjectReference{
- Kind: "Secret",
- APIVersion: "v1",
- Name: secret.Name,
- UID: secret.UID,
- },
- },
- }
- if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
- t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
- }
- sa, del := createDeleteSvcAcct(t, cs, sa)
- defer del()
- if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
- t.Fatalf("expected err creating token bound to nonexistant secret but got: %#v", resp)
- }
- secret, delSecret := createDeleteSecret(t, cs, secret)
- defer delSecret()
- // right uid
- treq.Spec.BoundObjectRef.UID = secret.UID
- if _, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
- t.Fatalf("err: %v", err)
- }
- // wrong uid
- treq.Spec.BoundObjectRef.UID = wrongUID
- if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
- t.Fatalf("expected err creating token bound to secret with wrong uid but got: %#v", resp)
- }
- // no uid
- treq.Spec.BoundObjectRef.UID = noUID
- treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
- checkPayload(t, treq.Status.Token, `["api"]`, "aud")
- checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
- checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
- checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
- checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
- doTokenReview(t, cs, treq, false)
- delSecret()
- doTokenReview(t, cs, treq, true)
- })
- t.Run("bound to service account and pod running as different service account", func(t *testing.T) {
- treq := &authenticationv1.TokenRequest{
- Spec: authenticationv1.TokenRequestSpec{
- Audiences: []string{"api"},
- BoundObjectRef: &authenticationv1.BoundObjectReference{
- Kind: "Pod",
- APIVersion: "v1",
- Name: otherpod.Name,
- },
- },
- }
- sa, del := createDeleteSvcAcct(t, cs, sa)
- defer del()
- _, del = createDeletePod(t, cs, otherpod)
- defer del()
- if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
- t.Fatalf("expected err but got: %#v", resp)
- }
- })
- t.Run("expired token", func(t *testing.T) {
- treq := &authenticationv1.TokenRequest{
- Spec: authenticationv1.TokenRequestSpec{
- Audiences: []string{"api"},
- },
- }
- sa, del := createDeleteSvcAcct(t, cs, sa)
- defer del()
- treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- doTokenReview(t, cs, treq, false)
- // backdate the token
- then := time.Now().Add(-2 * time.Hour)
- sc := &jwt.Claims{
- Subject: apiserverserviceaccount.MakeUsername(sa.Namespace, sa.Name),
- Audience: jwt.Audience([]string{"api"}),
- IssuedAt: jwt.NewNumericDate(then),
- NotBefore: jwt.NewNumericDate(then),
- Expiry: jwt.NewNumericDate(then.Add(time.Duration(60*60) * time.Second)),
- }
- coresa := core.ServiceAccount{
- ObjectMeta: sa.ObjectMeta,
- }
- _, pc := serviceaccount.Claims(coresa, nil, nil, 0, nil)
- tok, err := masterConfig.ExtraConfig.ServiceAccountIssuer.GenerateToken(sc, pc)
- if err != nil {
- t.Fatalf("err signing expired token: %v", err)
- }
- treq.Status.Token = tok
- doTokenReview(t, cs, treq, true)
- })
- t.Run("a token without an api audience is invalid", func(t *testing.T) {
- treq := &authenticationv1.TokenRequest{
- Spec: authenticationv1.TokenRequestSpec{
- Audiences: []string{"not-the-api"},
- },
- }
- sa, del := createDeleteSvcAcct(t, cs, sa)
- defer del()
- treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- doTokenReview(t, cs, treq, true)
- })
- t.Run("a tokenrequest without an audience is valid against the api", func(t *testing.T) {
- treq := &authenticationv1.TokenRequest{
- Spec: authenticationv1.TokenRequestSpec{},
- }
- sa, del := createDeleteSvcAcct(t, cs, sa)
- defer del()
- treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- checkPayload(t, treq.Status.Token, `["api"]`, "aud")
- doTokenReview(t, cs, treq, false)
- })
- t.Run("a token should be invalid after recreating same name pod", func(t *testing.T) {
- treq := &authenticationv1.TokenRequest{
- Spec: authenticationv1.TokenRequestSpec{
- Audiences: []string{"api"},
- BoundObjectRef: &authenticationv1.BoundObjectReference{
- Kind: "Pod",
- APIVersion: "v1",
- Name: pod.Name,
- },
- },
- }
- sa, del := createDeleteSvcAcct(t, cs, sa)
- defer del()
- originalPod, originalDelPod := createDeletePod(t, cs, pod)
- defer originalDelPod()
- treq.Spec.BoundObjectRef.UID = originalPod.UID
- if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
- t.Fatalf("err: %v", err)
- }
- checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
- checkPayload(t, treq.Status.Token, `["api"]`, "aud")
- checkPayload(t, treq.Status.Token, `"test-pod"`, "kubernetes.io", "pod", "name")
- checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
- checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
- checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
- doTokenReview(t, cs, treq, false)
- originalDelPod()
- doTokenReview(t, cs, treq, true)
- _, recreateDelPod := createDeletePod(t, cs, pod)
- defer recreateDelPod()
- doTokenReview(t, cs, treq, true)
- })
- t.Run("a token should be invalid after recreating same name secret", func(t *testing.T) {
- treq := &authenticationv1.TokenRequest{
- Spec: authenticationv1.TokenRequestSpec{
- Audiences: []string{"api"},
- BoundObjectRef: &authenticationv1.BoundObjectReference{
- Kind: "Secret",
- APIVersion: "v1",
- Name: secret.Name,
- UID: secret.UID,
- },
- },
- }
- sa, del := createDeleteSvcAcct(t, cs, sa)
- defer del()
- originalSecret, originalDelSecret := createDeleteSecret(t, cs, secret)
- defer originalDelSecret()
- treq.Spec.BoundObjectRef.UID = originalSecret.UID
- if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
- t.Fatalf("err: %v", err)
- }
- checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
- checkPayload(t, treq.Status.Token, `["api"]`, "aud")
- checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
- checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
- checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
- checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
- doTokenReview(t, cs, treq, false)
- originalDelSecret()
- doTokenReview(t, cs, treq, true)
- _, recreateDelSecret := createDeleteSecret(t, cs, secret)
- defer recreateDelSecret()
- doTokenReview(t, cs, treq, true)
- })
- t.Run("a token request within expiration time", func(t *testing.T) {
- normalExpirationTime := maxExpirationSeconds - 10*60
- treq := &authenticationv1.TokenRequest{
- Spec: authenticationv1.TokenRequestSpec{
- Audiences: []string{"api"},
- ExpirationSeconds: &normalExpirationTime,
- BoundObjectRef: &authenticationv1.BoundObjectReference{
- Kind: "Secret",
- APIVersion: "v1",
- Name: secret.Name,
- UID: secret.UID,
- },
- },
- }
- sa, del := createDeleteSvcAcct(t, cs, sa)
- defer del()
- originalSecret, originalDelSecret := createDeleteSecret(t, cs, secret)
- defer originalDelSecret()
- treq.Spec.BoundObjectRef.UID = originalSecret.UID
- if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
- t.Fatalf("err: %v", err)
- }
- checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
- checkPayload(t, treq.Status.Token, `["api"]`, "aud")
- checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
- checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
- checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
- checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
- checkExpiration(t, treq, normalExpirationTime)
- doTokenReview(t, cs, treq, false)
- originalDelSecret()
- doTokenReview(t, cs, treq, true)
- _, recreateDelSecret := createDeleteSecret(t, cs, secret)
- defer recreateDelSecret()
- doTokenReview(t, cs, treq, true)
- })
- t.Run("a token request with out-of-range expiration", func(t *testing.T) {
- tooLongExpirationTime := maxExpirationSeconds + 10*60
- treq := &authenticationv1.TokenRequest{
- Spec: authenticationv1.TokenRequestSpec{
- Audiences: []string{"api"},
- ExpirationSeconds: &tooLongExpirationTime,
- BoundObjectRef: &authenticationv1.BoundObjectReference{
- Kind: "Secret",
- APIVersion: "v1",
- Name: secret.Name,
- UID: secret.UID,
- },
- },
- }
- sa, del := createDeleteSvcAcct(t, cs, sa)
- defer del()
- originalSecret, originalDelSecret := createDeleteSecret(t, cs, secret)
- defer originalDelSecret()
- treq.Spec.BoundObjectRef.UID = originalSecret.UID
- if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
- t.Fatalf("err: %v", err)
- }
- checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
- checkPayload(t, treq.Status.Token, `["api"]`, "aud")
- checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
- checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
- checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
- checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
- checkExpiration(t, treq, maxExpirationSeconds)
- doTokenReview(t, cs, treq, false)
- originalDelSecret()
- doTokenReview(t, cs, treq, true)
- _, recreateDelSecret := createDeleteSecret(t, cs, secret)
- defer recreateDelSecret()
- doTokenReview(t, cs, treq, true)
- })
- }
- func doTokenReview(t *testing.T, cs clientset.Interface, treq *authenticationv1.TokenRequest, expectErr bool) authenticationv1.UserInfo {
- t.Helper()
- trev, err := cs.AuthenticationV1().TokenReviews().Create(&authenticationv1.TokenReview{
- Spec: authenticationv1.TokenReviewSpec{
- Token: treq.Status.Token,
- },
- })
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- t.Logf("status: %+v", trev.Status)
- if (trev.Status.Error != "") && !expectErr {
- t.Fatalf("expected no error but got: %v", trev.Status.Error)
- }
- if (trev.Status.Error == "") && expectErr {
- t.Fatalf("expected error but got: %+v", trev.Status)
- }
- if !trev.Status.Authenticated && !expectErr {
- t.Fatal("expected token to be authenticated but it wasn't")
- }
- return trev.Status.User
- }
- func checkPayload(t *testing.T, tok string, want string, parts ...string) {
- t.Helper()
- got := getSubObject(t, getPayload(t, tok), parts...)
- if got != want {
- t.Errorf("unexpected payload.\nsaw:\t%v\nwant:\t%v", got, want)
- }
- }
- func checkExpiration(t *testing.T, treq *authenticationv1.TokenRequest, expectedExpiration int64) {
- t.Helper()
- if treq.Spec.ExpirationSeconds == nil {
- t.Errorf("unexpected nil expiration seconds.")
- }
- if *treq.Spec.ExpirationSeconds != expectedExpiration {
- t.Errorf("unexpected expiration seconds.\nsaw:\t%d\nwant:\t%d", treq.Spec.ExpirationSeconds, expectedExpiration)
- }
- }
- func getSubObject(t *testing.T, b string, parts ...string) string {
- t.Helper()
- var obj interface{}
- obj = make(map[string]interface{})
- if err := json.Unmarshal([]byte(b), &obj); err != nil {
- t.Fatalf("err: %v", err)
- }
- for _, part := range parts {
- obj = obj.(map[string]interface{})[part]
- }
- out, err := json.Marshal(obj)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- return string(out)
- }
- func getPayload(t *testing.T, b string) string {
- t.Helper()
- parts := strings.Split(b, ".")
- if len(parts) != 3 {
- t.Fatalf("token did not have three parts: %v", b)
- }
- payload, err := base64.RawURLEncoding.DecodeString(parts[1])
- if err != nil {
- t.Fatalf("failed to base64 decode token: %v", err)
- }
- return string(payload)
- }
- func createDeleteSvcAcct(t *testing.T, cs clientset.Interface, sa *v1.ServiceAccount) (*v1.ServiceAccount, func()) {
- t.Helper()
- sa, err := cs.CoreV1().ServiceAccounts(sa.Namespace).Create(sa)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- done := false
- return sa, func() {
- t.Helper()
- if done {
- return
- }
- done = true
- if err := cs.CoreV1().ServiceAccounts(sa.Namespace).Delete(sa.Name, nil); err != nil {
- t.Fatalf("err: %v", err)
- }
- }
- }
- func createDeletePod(t *testing.T, cs clientset.Interface, pod *v1.Pod) (*v1.Pod, func()) {
- t.Helper()
- pod, err := cs.CoreV1().Pods(pod.Namespace).Create(pod)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- done := false
- return pod, func() {
- t.Helper()
- if done {
- return
- }
- done = true
- if err := cs.CoreV1().Pods(pod.Namespace).Delete(pod.Name, nil); err != nil {
- t.Fatalf("err: %v", err)
- }
- }
- }
- func createDeleteSecret(t *testing.T, cs clientset.Interface, sec *v1.Secret) (*v1.Secret, func()) {
- t.Helper()
- sec, err := cs.CoreV1().Secrets(sec.Namespace).Create(sec)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- done := false
- return sec, func() {
- t.Helper()
- if done {
- return
- }
- done = true
- if err := cs.CoreV1().Secrets(sec.Namespace).Delete(sec.Name, nil); err != nil {
- t.Fatalf("err: %v", err)
- }
- }
- }
- func newIndexer(get func(namespace, name string) (interface{}, error)) cache.Indexer {
- return &fakeIndexer{get: get}
- }
- type fakeIndexer struct {
- cache.Indexer
- get func(namespace, name string) (interface{}, error)
- }
- func (f *fakeIndexer) GetByKey(key string) (interface{}, bool, error) {
- parts := strings.SplitN(key, "/", 2)
- namespace := parts[0]
- name := ""
- if len(parts) == 2 {
- name = parts[1]
- }
- obj, err := f.get(namespace, name)
- return obj, err == nil, err
- }
|