svcaccttoken_test.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package auth
  14. import (
  15. "crypto/ecdsa"
  16. "encoding/base64"
  17. "encoding/json"
  18. "fmt"
  19. "reflect"
  20. "strings"
  21. "testing"
  22. "time"
  23. "gopkg.in/square/go-jose.v2/jwt"
  24. authenticationv1 "k8s.io/api/authentication/v1"
  25. v1 "k8s.io/api/core/v1"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. "k8s.io/apimachinery/pkg/types"
  28. "k8s.io/apiserver/pkg/authentication/authenticator"
  29. "k8s.io/apiserver/pkg/authentication/request/bearertoken"
  30. apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
  31. "k8s.io/apiserver/pkg/authorization/authorizerfactory"
  32. utilfeature "k8s.io/apiserver/pkg/util/feature"
  33. clientset "k8s.io/client-go/kubernetes"
  34. v1listers "k8s.io/client-go/listers/core/v1"
  35. "k8s.io/client-go/tools/cache"
  36. "k8s.io/client-go/util/keyutil"
  37. featuregatetesting "k8s.io/component-base/featuregate/testing"
  38. "k8s.io/kubernetes/pkg/apis/core"
  39. serviceaccountgetter "k8s.io/kubernetes/pkg/controller/serviceaccount"
  40. "k8s.io/kubernetes/pkg/features"
  41. "k8s.io/kubernetes/pkg/serviceaccount"
  42. "k8s.io/kubernetes/test/integration/framework"
  43. )
  44. const ecdsaPrivateKey = `-----BEGIN EC PRIVATE KEY-----
  45. MHcCAQEEIEZmTmUhuanLjPA2CLquXivuwBDHTt5XYwgIr/kA1LtRoAoGCCqGSM49
  46. AwEHoUQDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPLX2i8uIp/C/ASqiIGUeeKQtX0
  47. /IR3qCXyThP/dbCiHrF3v1cuhBOHY8CLVg==
  48. -----END EC PRIVATE KEY-----`
  49. func TestServiceAccountTokenCreate(t *testing.T) {
  50. defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.TokenRequest, true)()
  51. // Build client config, clientset, and informers
  52. sk, err := keyutil.ParsePrivateKeyPEM([]byte(ecdsaPrivateKey))
  53. if err != nil {
  54. t.Fatalf("err: %v", err)
  55. }
  56. pk := sk.(*ecdsa.PrivateKey).PublicKey
  57. const iss = "https://foo.bar.example.com"
  58. aud := authenticator.Audiences{"api"}
  59. maxExpirationSeconds := int64(60 * 60)
  60. maxExpirationDuration, err := time.ParseDuration(fmt.Sprintf("%ds", maxExpirationSeconds))
  61. if err != nil {
  62. t.Fatalf("err: %v", err)
  63. }
  64. gcs := &clientset.Clientset{}
  65. // Start the server
  66. masterConfig := framework.NewIntegrationTestMasterConfig()
  67. masterConfig.GenericConfig.Authorization.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
  68. masterConfig.GenericConfig.Authentication.APIAudiences = aud
  69. masterConfig.GenericConfig.Authentication.Authenticator = bearertoken.New(
  70. serviceaccount.JWTTokenAuthenticator(
  71. iss,
  72. []interface{}{&pk},
  73. aud,
  74. serviceaccount.NewValidator(serviceaccountgetter.NewGetterFromClient(
  75. gcs,
  76. v1listers.NewSecretLister(newIndexer(func(namespace, name string) (interface{}, error) {
  77. return gcs.CoreV1().Secrets(namespace).Get(name, metav1.GetOptions{})
  78. })),
  79. v1listers.NewServiceAccountLister(newIndexer(func(namespace, name string) (interface{}, error) {
  80. return gcs.CoreV1().ServiceAccounts(namespace).Get(name, metav1.GetOptions{})
  81. })),
  82. v1listers.NewPodLister(newIndexer(func(namespace, name string) (interface{}, error) {
  83. return gcs.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
  84. })),
  85. )),
  86. ),
  87. )
  88. tokenGenerator, err := serviceaccount.JWTTokenGenerator(iss, sk)
  89. if err != nil {
  90. t.Fatalf("err: %v", err)
  91. }
  92. masterConfig.ExtraConfig.ServiceAccountIssuer = tokenGenerator
  93. masterConfig.ExtraConfig.ServiceAccountMaxExpiration = maxExpirationDuration
  94. masterConfig.GenericConfig.Authentication.APIAudiences = aud
  95. master, _, closeFn := framework.RunAMaster(masterConfig)
  96. defer closeFn()
  97. cs, err := clientset.NewForConfig(master.GenericAPIServer.LoopbackClientConfig)
  98. if err != nil {
  99. t.Fatalf("err: %v", err)
  100. }
  101. *gcs = *cs
  102. var (
  103. sa = &v1.ServiceAccount{
  104. ObjectMeta: metav1.ObjectMeta{
  105. Name: "test-svcacct",
  106. Namespace: "myns",
  107. },
  108. }
  109. pod = &v1.Pod{
  110. ObjectMeta: metav1.ObjectMeta{
  111. Name: "test-pod",
  112. Namespace: sa.Namespace,
  113. },
  114. Spec: v1.PodSpec{
  115. ServiceAccountName: sa.Name,
  116. Containers: []v1.Container{{Name: "test-container", Image: "nginx"}},
  117. },
  118. }
  119. otherpod = &v1.Pod{
  120. ObjectMeta: metav1.ObjectMeta{
  121. Name: "other-test-pod",
  122. Namespace: sa.Namespace,
  123. },
  124. Spec: v1.PodSpec{
  125. ServiceAccountName: "other-" + sa.Name,
  126. Containers: []v1.Container{{Name: "test-container", Image: "nginx"}},
  127. },
  128. }
  129. secret = &v1.Secret{
  130. ObjectMeta: metav1.ObjectMeta{
  131. Name: "test-secret",
  132. Namespace: sa.Namespace,
  133. },
  134. }
  135. wrongUID = types.UID("wrong")
  136. noUID = types.UID("")
  137. )
  138. t.Run("bound to service account", func(t *testing.T) {
  139. treq := &authenticationv1.TokenRequest{
  140. Spec: authenticationv1.TokenRequestSpec{
  141. Audiences: []string{"api"},
  142. },
  143. }
  144. if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
  145. t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
  146. }
  147. sa, delSvcAcct := createDeleteSvcAcct(t, cs, sa)
  148. defer delSvcAcct()
  149. treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
  150. if err != nil {
  151. t.Fatalf("err: %v", err)
  152. }
  153. checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
  154. checkPayload(t, treq.Status.Token, `["api"]`, "aud")
  155. checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "pod")
  156. checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
  157. checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
  158. checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
  159. info := doTokenReview(t, cs, treq, false)
  160. if info.Extra != nil {
  161. t.Fatalf("expected Extra to be nil but got: %#v", info.Extra)
  162. }
  163. delSvcAcct()
  164. doTokenReview(t, cs, treq, true)
  165. })
  166. t.Run("bound to service account and pod", func(t *testing.T) {
  167. treq := &authenticationv1.TokenRequest{
  168. Spec: authenticationv1.TokenRequestSpec{
  169. Audiences: []string{"api"},
  170. BoundObjectRef: &authenticationv1.BoundObjectReference{
  171. Kind: "Pod",
  172. APIVersion: "v1",
  173. Name: pod.Name,
  174. },
  175. },
  176. }
  177. if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
  178. t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
  179. }
  180. sa, del := createDeleteSvcAcct(t, cs, sa)
  181. defer del()
  182. if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
  183. t.Fatalf("expected err creating token bound to nonexistant pod but got: %#v", resp)
  184. }
  185. pod, delPod := createDeletePod(t, cs, pod)
  186. defer delPod()
  187. // right uid
  188. treq.Spec.BoundObjectRef.UID = pod.UID
  189. if _, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
  190. t.Fatalf("err: %v", err)
  191. }
  192. // wrong uid
  193. treq.Spec.BoundObjectRef.UID = wrongUID
  194. if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
  195. t.Fatalf("expected err creating token bound to pod with wrong uid but got: %#v", resp)
  196. }
  197. // no uid
  198. treq.Spec.BoundObjectRef.UID = noUID
  199. treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
  200. if err != nil {
  201. t.Fatalf("err: %v", err)
  202. }
  203. checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
  204. checkPayload(t, treq.Status.Token, `["api"]`, "aud")
  205. checkPayload(t, treq.Status.Token, `"test-pod"`, "kubernetes.io", "pod", "name")
  206. checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
  207. checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
  208. checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
  209. info := doTokenReview(t, cs, treq, false)
  210. if len(info.Extra) != 2 {
  211. t.Fatalf("expected Extra have length of 2 but was length %d: %#v", len(info.Extra), info.Extra)
  212. }
  213. if expected := map[string]authenticationv1.ExtraValue{
  214. "authentication.kubernetes.io/pod-name": {pod.ObjectMeta.Name},
  215. "authentication.kubernetes.io/pod-uid": {string(pod.ObjectMeta.UID)},
  216. }; !reflect.DeepEqual(info.Extra, expected) {
  217. t.Fatalf("unexpected Extra:\ngot:\t%#v\nwant:\t%#v", info.Extra, expected)
  218. }
  219. delPod()
  220. doTokenReview(t, cs, treq, true)
  221. })
  222. t.Run("bound to service account and secret", func(t *testing.T) {
  223. treq := &authenticationv1.TokenRequest{
  224. Spec: authenticationv1.TokenRequestSpec{
  225. Audiences: []string{"api"},
  226. BoundObjectRef: &authenticationv1.BoundObjectReference{
  227. Kind: "Secret",
  228. APIVersion: "v1",
  229. Name: secret.Name,
  230. UID: secret.UID,
  231. },
  232. },
  233. }
  234. if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
  235. t.Fatalf("expected err creating token for nonexistant svcacct but got: %#v", resp)
  236. }
  237. sa, del := createDeleteSvcAcct(t, cs, sa)
  238. defer del()
  239. if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
  240. t.Fatalf("expected err creating token bound to nonexistant secret but got: %#v", resp)
  241. }
  242. secret, delSecret := createDeleteSecret(t, cs, secret)
  243. defer delSecret()
  244. // right uid
  245. treq.Spec.BoundObjectRef.UID = secret.UID
  246. if _, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
  247. t.Fatalf("err: %v", err)
  248. }
  249. // wrong uid
  250. treq.Spec.BoundObjectRef.UID = wrongUID
  251. if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
  252. t.Fatalf("expected err creating token bound to secret with wrong uid but got: %#v", resp)
  253. }
  254. // no uid
  255. treq.Spec.BoundObjectRef.UID = noUID
  256. treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
  257. if err != nil {
  258. t.Fatalf("err: %v", err)
  259. }
  260. checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
  261. checkPayload(t, treq.Status.Token, `["api"]`, "aud")
  262. checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
  263. checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
  264. checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
  265. checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
  266. doTokenReview(t, cs, treq, false)
  267. delSecret()
  268. doTokenReview(t, cs, treq, true)
  269. })
  270. t.Run("bound to service account and pod running as different service account", func(t *testing.T) {
  271. treq := &authenticationv1.TokenRequest{
  272. Spec: authenticationv1.TokenRequestSpec{
  273. Audiences: []string{"api"},
  274. BoundObjectRef: &authenticationv1.BoundObjectReference{
  275. Kind: "Pod",
  276. APIVersion: "v1",
  277. Name: otherpod.Name,
  278. },
  279. },
  280. }
  281. sa, del := createDeleteSvcAcct(t, cs, sa)
  282. defer del()
  283. _, del = createDeletePod(t, cs, otherpod)
  284. defer del()
  285. if resp, err := cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err == nil {
  286. t.Fatalf("expected err but got: %#v", resp)
  287. }
  288. })
  289. t.Run("expired token", func(t *testing.T) {
  290. treq := &authenticationv1.TokenRequest{
  291. Spec: authenticationv1.TokenRequestSpec{
  292. Audiences: []string{"api"},
  293. },
  294. }
  295. sa, del := createDeleteSvcAcct(t, cs, sa)
  296. defer del()
  297. treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
  298. if err != nil {
  299. t.Fatalf("err: %v", err)
  300. }
  301. doTokenReview(t, cs, treq, false)
  302. // backdate the token
  303. then := time.Now().Add(-2 * time.Hour)
  304. sc := &jwt.Claims{
  305. Subject: apiserverserviceaccount.MakeUsername(sa.Namespace, sa.Name),
  306. Audience: jwt.Audience([]string{"api"}),
  307. IssuedAt: jwt.NewNumericDate(then),
  308. NotBefore: jwt.NewNumericDate(then),
  309. Expiry: jwt.NewNumericDate(then.Add(time.Duration(60*60) * time.Second)),
  310. }
  311. coresa := core.ServiceAccount{
  312. ObjectMeta: sa.ObjectMeta,
  313. }
  314. _, pc := serviceaccount.Claims(coresa, nil, nil, 0, nil)
  315. tok, err := masterConfig.ExtraConfig.ServiceAccountIssuer.GenerateToken(sc, pc)
  316. if err != nil {
  317. t.Fatalf("err signing expired token: %v", err)
  318. }
  319. treq.Status.Token = tok
  320. doTokenReview(t, cs, treq, true)
  321. })
  322. t.Run("a token without an api audience is invalid", func(t *testing.T) {
  323. treq := &authenticationv1.TokenRequest{
  324. Spec: authenticationv1.TokenRequestSpec{
  325. Audiences: []string{"not-the-api"},
  326. },
  327. }
  328. sa, del := createDeleteSvcAcct(t, cs, sa)
  329. defer del()
  330. treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
  331. if err != nil {
  332. t.Fatalf("err: %v", err)
  333. }
  334. doTokenReview(t, cs, treq, true)
  335. })
  336. t.Run("a tokenrequest without an audience is valid against the api", func(t *testing.T) {
  337. treq := &authenticationv1.TokenRequest{
  338. Spec: authenticationv1.TokenRequestSpec{},
  339. }
  340. sa, del := createDeleteSvcAcct(t, cs, sa)
  341. defer del()
  342. treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq)
  343. if err != nil {
  344. t.Fatalf("err: %v", err)
  345. }
  346. checkPayload(t, treq.Status.Token, `["api"]`, "aud")
  347. doTokenReview(t, cs, treq, false)
  348. })
  349. t.Run("a token should be invalid after recreating same name pod", func(t *testing.T) {
  350. treq := &authenticationv1.TokenRequest{
  351. Spec: authenticationv1.TokenRequestSpec{
  352. Audiences: []string{"api"},
  353. BoundObjectRef: &authenticationv1.BoundObjectReference{
  354. Kind: "Pod",
  355. APIVersion: "v1",
  356. Name: pod.Name,
  357. },
  358. },
  359. }
  360. sa, del := createDeleteSvcAcct(t, cs, sa)
  361. defer del()
  362. originalPod, originalDelPod := createDeletePod(t, cs, pod)
  363. defer originalDelPod()
  364. treq.Spec.BoundObjectRef.UID = originalPod.UID
  365. if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
  366. t.Fatalf("err: %v", err)
  367. }
  368. checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
  369. checkPayload(t, treq.Status.Token, `["api"]`, "aud")
  370. checkPayload(t, treq.Status.Token, `"test-pod"`, "kubernetes.io", "pod", "name")
  371. checkPayload(t, treq.Status.Token, "null", "kubernetes.io", "secret")
  372. checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
  373. checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
  374. doTokenReview(t, cs, treq, false)
  375. originalDelPod()
  376. doTokenReview(t, cs, treq, true)
  377. _, recreateDelPod := createDeletePod(t, cs, pod)
  378. defer recreateDelPod()
  379. doTokenReview(t, cs, treq, true)
  380. })
  381. t.Run("a token should be invalid after recreating same name secret", func(t *testing.T) {
  382. treq := &authenticationv1.TokenRequest{
  383. Spec: authenticationv1.TokenRequestSpec{
  384. Audiences: []string{"api"},
  385. BoundObjectRef: &authenticationv1.BoundObjectReference{
  386. Kind: "Secret",
  387. APIVersion: "v1",
  388. Name: secret.Name,
  389. UID: secret.UID,
  390. },
  391. },
  392. }
  393. sa, del := createDeleteSvcAcct(t, cs, sa)
  394. defer del()
  395. originalSecret, originalDelSecret := createDeleteSecret(t, cs, secret)
  396. defer originalDelSecret()
  397. treq.Spec.BoundObjectRef.UID = originalSecret.UID
  398. if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
  399. t.Fatalf("err: %v", err)
  400. }
  401. checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
  402. checkPayload(t, treq.Status.Token, `["api"]`, "aud")
  403. checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
  404. checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
  405. checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
  406. checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
  407. doTokenReview(t, cs, treq, false)
  408. originalDelSecret()
  409. doTokenReview(t, cs, treq, true)
  410. _, recreateDelSecret := createDeleteSecret(t, cs, secret)
  411. defer recreateDelSecret()
  412. doTokenReview(t, cs, treq, true)
  413. })
  414. t.Run("a token request within expiration time", func(t *testing.T) {
  415. normalExpirationTime := maxExpirationSeconds - 10*60
  416. treq := &authenticationv1.TokenRequest{
  417. Spec: authenticationv1.TokenRequestSpec{
  418. Audiences: []string{"api"},
  419. ExpirationSeconds: &normalExpirationTime,
  420. BoundObjectRef: &authenticationv1.BoundObjectReference{
  421. Kind: "Secret",
  422. APIVersion: "v1",
  423. Name: secret.Name,
  424. UID: secret.UID,
  425. },
  426. },
  427. }
  428. sa, del := createDeleteSvcAcct(t, cs, sa)
  429. defer del()
  430. originalSecret, originalDelSecret := createDeleteSecret(t, cs, secret)
  431. defer originalDelSecret()
  432. treq.Spec.BoundObjectRef.UID = originalSecret.UID
  433. if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
  434. t.Fatalf("err: %v", err)
  435. }
  436. checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
  437. checkPayload(t, treq.Status.Token, `["api"]`, "aud")
  438. checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
  439. checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
  440. checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
  441. checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
  442. checkExpiration(t, treq, normalExpirationTime)
  443. doTokenReview(t, cs, treq, false)
  444. originalDelSecret()
  445. doTokenReview(t, cs, treq, true)
  446. _, recreateDelSecret := createDeleteSecret(t, cs, secret)
  447. defer recreateDelSecret()
  448. doTokenReview(t, cs, treq, true)
  449. })
  450. t.Run("a token request with out-of-range expiration", func(t *testing.T) {
  451. tooLongExpirationTime := maxExpirationSeconds + 10*60
  452. treq := &authenticationv1.TokenRequest{
  453. Spec: authenticationv1.TokenRequestSpec{
  454. Audiences: []string{"api"},
  455. ExpirationSeconds: &tooLongExpirationTime,
  456. BoundObjectRef: &authenticationv1.BoundObjectReference{
  457. Kind: "Secret",
  458. APIVersion: "v1",
  459. Name: secret.Name,
  460. UID: secret.UID,
  461. },
  462. },
  463. }
  464. sa, del := createDeleteSvcAcct(t, cs, sa)
  465. defer del()
  466. originalSecret, originalDelSecret := createDeleteSecret(t, cs, secret)
  467. defer originalDelSecret()
  468. treq.Spec.BoundObjectRef.UID = originalSecret.UID
  469. if treq, err = cs.CoreV1().ServiceAccounts(sa.Namespace).CreateToken(sa.Name, treq); err != nil {
  470. t.Fatalf("err: %v", err)
  471. }
  472. checkPayload(t, treq.Status.Token, `"system:serviceaccount:myns:test-svcacct"`, "sub")
  473. checkPayload(t, treq.Status.Token, `["api"]`, "aud")
  474. checkPayload(t, treq.Status.Token, `null`, "kubernetes.io", "pod")
  475. checkPayload(t, treq.Status.Token, `"test-secret"`, "kubernetes.io", "secret", "name")
  476. checkPayload(t, treq.Status.Token, `"myns"`, "kubernetes.io", "namespace")
  477. checkPayload(t, treq.Status.Token, `"test-svcacct"`, "kubernetes.io", "serviceaccount", "name")
  478. checkExpiration(t, treq, maxExpirationSeconds)
  479. doTokenReview(t, cs, treq, false)
  480. originalDelSecret()
  481. doTokenReview(t, cs, treq, true)
  482. _, recreateDelSecret := createDeleteSecret(t, cs, secret)
  483. defer recreateDelSecret()
  484. doTokenReview(t, cs, treq, true)
  485. })
  486. }
  487. func doTokenReview(t *testing.T, cs clientset.Interface, treq *authenticationv1.TokenRequest, expectErr bool) authenticationv1.UserInfo {
  488. t.Helper()
  489. trev, err := cs.AuthenticationV1().TokenReviews().Create(&authenticationv1.TokenReview{
  490. Spec: authenticationv1.TokenReviewSpec{
  491. Token: treq.Status.Token,
  492. },
  493. })
  494. if err != nil {
  495. t.Fatalf("err: %v", err)
  496. }
  497. t.Logf("status: %+v", trev.Status)
  498. if (trev.Status.Error != "") && !expectErr {
  499. t.Fatalf("expected no error but got: %v", trev.Status.Error)
  500. }
  501. if (trev.Status.Error == "") && expectErr {
  502. t.Fatalf("expected error but got: %+v", trev.Status)
  503. }
  504. if !trev.Status.Authenticated && !expectErr {
  505. t.Fatal("expected token to be authenticated but it wasn't")
  506. }
  507. return trev.Status.User
  508. }
  509. func checkPayload(t *testing.T, tok string, want string, parts ...string) {
  510. t.Helper()
  511. got := getSubObject(t, getPayload(t, tok), parts...)
  512. if got != want {
  513. t.Errorf("unexpected payload.\nsaw:\t%v\nwant:\t%v", got, want)
  514. }
  515. }
  516. func checkExpiration(t *testing.T, treq *authenticationv1.TokenRequest, expectedExpiration int64) {
  517. t.Helper()
  518. if treq.Spec.ExpirationSeconds == nil {
  519. t.Errorf("unexpected nil expiration seconds.")
  520. }
  521. if *treq.Spec.ExpirationSeconds != expectedExpiration {
  522. t.Errorf("unexpected expiration seconds.\nsaw:\t%d\nwant:\t%d", treq.Spec.ExpirationSeconds, expectedExpiration)
  523. }
  524. }
  525. func getSubObject(t *testing.T, b string, parts ...string) string {
  526. t.Helper()
  527. var obj interface{}
  528. obj = make(map[string]interface{})
  529. if err := json.Unmarshal([]byte(b), &obj); err != nil {
  530. t.Fatalf("err: %v", err)
  531. }
  532. for _, part := range parts {
  533. obj = obj.(map[string]interface{})[part]
  534. }
  535. out, err := json.Marshal(obj)
  536. if err != nil {
  537. t.Fatalf("err: %v", err)
  538. }
  539. return string(out)
  540. }
  541. func getPayload(t *testing.T, b string) string {
  542. t.Helper()
  543. parts := strings.Split(b, ".")
  544. if len(parts) != 3 {
  545. t.Fatalf("token did not have three parts: %v", b)
  546. }
  547. payload, err := base64.RawURLEncoding.DecodeString(parts[1])
  548. if err != nil {
  549. t.Fatalf("failed to base64 decode token: %v", err)
  550. }
  551. return string(payload)
  552. }
  553. func createDeleteSvcAcct(t *testing.T, cs clientset.Interface, sa *v1.ServiceAccount) (*v1.ServiceAccount, func()) {
  554. t.Helper()
  555. sa, err := cs.CoreV1().ServiceAccounts(sa.Namespace).Create(sa)
  556. if err != nil {
  557. t.Fatalf("err: %v", err)
  558. }
  559. done := false
  560. return sa, func() {
  561. t.Helper()
  562. if done {
  563. return
  564. }
  565. done = true
  566. if err := cs.CoreV1().ServiceAccounts(sa.Namespace).Delete(sa.Name, nil); err != nil {
  567. t.Fatalf("err: %v", err)
  568. }
  569. }
  570. }
  571. func createDeletePod(t *testing.T, cs clientset.Interface, pod *v1.Pod) (*v1.Pod, func()) {
  572. t.Helper()
  573. pod, err := cs.CoreV1().Pods(pod.Namespace).Create(pod)
  574. if err != nil {
  575. t.Fatalf("err: %v", err)
  576. }
  577. done := false
  578. return pod, func() {
  579. t.Helper()
  580. if done {
  581. return
  582. }
  583. done = true
  584. if err := cs.CoreV1().Pods(pod.Namespace).Delete(pod.Name, nil); err != nil {
  585. t.Fatalf("err: %v", err)
  586. }
  587. }
  588. }
  589. func createDeleteSecret(t *testing.T, cs clientset.Interface, sec *v1.Secret) (*v1.Secret, func()) {
  590. t.Helper()
  591. sec, err := cs.CoreV1().Secrets(sec.Namespace).Create(sec)
  592. if err != nil {
  593. t.Fatalf("err: %v", err)
  594. }
  595. done := false
  596. return sec, func() {
  597. t.Helper()
  598. if done {
  599. return
  600. }
  601. done = true
  602. if err := cs.CoreV1().Secrets(sec.Namespace).Delete(sec.Name, nil); err != nil {
  603. t.Fatalf("err: %v", err)
  604. }
  605. }
  606. }
  607. func newIndexer(get func(namespace, name string) (interface{}, error)) cache.Indexer {
  608. return &fakeIndexer{get: get}
  609. }
  610. type fakeIndexer struct {
  611. cache.Indexer
  612. get func(namespace, name string) (interface{}, error)
  613. }
  614. func (f *fakeIndexer) GetByKey(key string) (interface{}, bool, error) {
  615. parts := strings.SplitN(key, "/", 2)
  616. namespace := parts[0]
  617. name := ""
  618. if len(parts) == 2 {
  619. name = parts[1]
  620. }
  621. obj, err := f.get(namespace, name)
  622. return obj, err == nil, err
  623. }