k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/serviceaccount/service_account_test.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package serviceaccount 18 19 // This file tests authentication and (soon) authorization of HTTP requests to an API server object. 20 // It does not use the client in pkg/client/... because authentication and authorization needs 21 // to work for any client of the HTTP interface. 22 23 import ( 24 "context" 25 "fmt" 26 "sync" 27 "testing" 28 "time" 29 30 v1 "k8s.io/api/core/v1" 31 apierrors "k8s.io/apimachinery/pkg/api/errors" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/util/wait" 34 serviceaccountapiserver "k8s.io/apiserver/pkg/authentication/serviceaccount" 35 "k8s.io/apiserver/pkg/authorization/authorizer" 36 unionauthz "k8s.io/apiserver/pkg/authorization/union" 37 clientinformers "k8s.io/client-go/informers" 38 clientset "k8s.io/client-go/kubernetes" 39 restclient "k8s.io/client-go/rest" 40 "k8s.io/client-go/util/keyutil" 41 "k8s.io/kubernetes/cmd/kube-apiserver/app/options" 42 "k8s.io/kubernetes/pkg/controller" 43 serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount" 44 "k8s.io/kubernetes/pkg/controlplane" 45 "k8s.io/kubernetes/pkg/controlplane/controller/legacytokentracking" 46 "k8s.io/kubernetes/pkg/serviceaccount" 47 serviceaccountadmission "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount" 48 "k8s.io/kubernetes/test/integration/framework" 49 "k8s.io/kubernetes/test/utils/ktesting" 50 ) 51 52 const ( 53 readOnlyServiceAccountName = "ro" 54 readWriteServiceAccountName = "rw" 55 ) 56 57 func TestServiceAccountAutoCreate(t *testing.T) { 58 tCtx := ktesting.Init(t) 59 c, _, stopFunc, _, err := startServiceAccountTestServerAndWaitForCaches(tCtx, t) 60 defer stopFunc() 61 if err != nil { 62 t.Fatalf("failed to setup ServiceAccounts server: %v", err) 63 } 64 65 ns := "test-service-account-creation" 66 67 // Create namespace 68 _, err = c.CoreV1().Namespaces().Create(tCtx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}, metav1.CreateOptions{}) 69 if err != nil { 70 t.Fatalf("could not create namespace: %v", err) 71 } 72 73 // Get service account 74 defaultUser, err := getServiceAccount(c, ns, "default", true) 75 if err != nil { 76 t.Fatalf("Default serviceaccount not created: %v", err) 77 } 78 79 // Delete service account 80 err = c.CoreV1().ServiceAccounts(ns).Delete(tCtx, defaultUser.Name, metav1.DeleteOptions{}) 81 if err != nil { 82 t.Fatalf("Could not delete default serviceaccount: %v", err) 83 } 84 85 // Get recreated service account 86 defaultUser2, err := getServiceAccount(c, ns, "default", true) 87 if err != nil { 88 t.Fatalf("Default serviceaccount not created: %v", err) 89 } 90 if defaultUser2.UID == defaultUser.UID { 91 t.Fatalf("Expected different UID with recreated serviceaccount") 92 } 93 } 94 95 func TestServiceAccountTokenAutoMount(t *testing.T) { 96 tCtx := ktesting.Init(t) 97 c, _, stopFunc, _, err := startServiceAccountTestServerAndWaitForCaches(tCtx, t) 98 defer stopFunc() 99 if err != nil { 100 t.Fatalf("failed to setup ServiceAccounts server: %v", err) 101 } 102 103 ns := "auto-mount-ns" 104 105 // Create "my" namespace 106 _, err = c.CoreV1().Namespaces().Create(tCtx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: ns}}, metav1.CreateOptions{}) 107 if err != nil && !apierrors.IsAlreadyExists(err) { 108 t.Fatalf("could not create namespace: %v", err) 109 } 110 111 // Pod to create 112 protoPod := v1.Pod{ 113 ObjectMeta: metav1.ObjectMeta{Name: "protopod"}, 114 Spec: v1.PodSpec{ 115 Containers: []v1.Container{ 116 { 117 Name: "container", 118 Image: "container-image", 119 }, 120 }, 121 }, 122 } 123 124 createdPod, err := c.CoreV1().Pods(ns).Create(tCtx, &protoPod, metav1.CreateOptions{}) 125 if err != nil { 126 t.Fatal(err) 127 } 128 129 expectedServiceAccount := serviceaccountadmission.DefaultServiceAccountName 130 if createdPod.Spec.ServiceAccountName != expectedServiceAccount { 131 t.Fatalf("Expected %s, got %s", expectedServiceAccount, createdPod.Spec.ServiceAccountName) 132 } 133 if len(createdPod.Spec.Volumes) == 0 || createdPod.Spec.Volumes[0].Projected == nil { 134 t.Fatal("Expected projected volume for service account token inserted") 135 } 136 } 137 138 func TestServiceAccountTokenAuthentication(t *testing.T) { 139 tCtx := ktesting.Init(t) 140 c, config, stopFunc, _, err := startServiceAccountTestServerAndWaitForCaches(tCtx, t) 141 defer stopFunc() 142 if err != nil { 143 t.Fatalf("failed to setup ServiceAccounts server: %v", err) 144 } 145 146 myns := "auth-ns" 147 otherns := "other-ns" 148 149 // Create "my" namespace 150 _, err = c.CoreV1().Namespaces().Create(tCtx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: myns}}, metav1.CreateOptions{}) 151 if err != nil && !apierrors.IsAlreadyExists(err) { 152 t.Fatalf("could not create namespace: %v", err) 153 } 154 155 // Create "other" namespace 156 _, err = c.CoreV1().Namespaces().Create(tCtx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: otherns}}, metav1.CreateOptions{}) 157 if err != nil && !apierrors.IsAlreadyExists(err) { 158 t.Fatalf("could not create namespace: %v", err) 159 } 160 161 // Create "ro" user in myns 162 roSA, err := c.CoreV1().ServiceAccounts(myns).Create(tCtx, &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: readOnlyServiceAccountName}}, metav1.CreateOptions{}) 163 if err != nil { 164 t.Fatalf("Service Account not created: %v", err) 165 } 166 167 roTokenName := "ro-test-token" 168 secret, err := createServiceAccountToken(c, roSA, myns, roTokenName) 169 if err != nil { 170 t.Fatalf("Secret not created: %v", err) 171 } 172 roClientConfig := *config 173 roClientConfig.BearerToken = string(secret.Data[v1.ServiceAccountTokenKey]) 174 roClient := clientset.NewForConfigOrDie(&roClientConfig) 175 doServiceAccountAPIRequests(t, roClient, myns, true, true, false) 176 doServiceAccountAPIRequests(t, roClient, otherns, true, false, false) 177 err = c.CoreV1().Secrets(myns).Delete(tCtx, roTokenName, metav1.DeleteOptions{}) 178 if err != nil { 179 t.Fatalf("could not delete token: %v", err) 180 } 181 // wait for delete to be observed and reacted to via watch 182 err = wait.PollImmediate(100*time.Millisecond, 30*time.Second, func() (bool, error) { 183 _, err := roClient.CoreV1().Secrets(myns).List(tCtx, metav1.ListOptions{}) 184 if err == nil { 185 t.Logf("token is still valid, waiting") 186 return false, nil 187 } 188 if !apierrors.IsUnauthorized(err) { 189 t.Logf("expected unauthorized error, got %v", err) 190 return false, nil 191 } 192 return true, nil 193 }) 194 if err != nil { 195 t.Fatalf("waiting for token to be invalidated: %v", err) 196 } 197 doServiceAccountAPIRequests(t, roClient, myns, false, false, false) 198 199 // Create "rw" user in myns 200 rwSA, err := c.CoreV1().ServiceAccounts(myns).Create(tCtx, &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: readWriteServiceAccountName}}, metav1.CreateOptions{}) 201 if err != nil { 202 t.Fatalf("Service Account not created: %v", err) 203 } 204 rwTokenName := "rw-test-token" 205 secret, err = createServiceAccountToken(c, rwSA, myns, rwTokenName) 206 if err != nil { 207 t.Fatalf("Secret not created: %v", err) 208 } 209 rwClientConfig := *config 210 rwClientConfig.BearerToken = string(secret.Data[v1.ServiceAccountTokenKey]) 211 rwClient := clientset.NewForConfigOrDie(&rwClientConfig) 212 doServiceAccountAPIRequests(t, rwClient, myns, true, true, true) 213 doServiceAccountAPIRequests(t, rwClient, otherns, true, false, false) 214 } 215 216 func TestLegacyServiceAccountTokenTracking(t *testing.T) { 217 tCtx := ktesting.Init(t) 218 c, config, stopFunc, _, err := startServiceAccountTestServerAndWaitForCaches(tCtx, t) 219 defer stopFunc() 220 if err != nil { 221 t.Fatalf("failed to setup ServiceAccounts server: %v", err) 222 } 223 224 // create service account 225 myns := "auth-ns" 226 _, err = c.CoreV1().Namespaces().Create(tCtx, &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: myns}}, metav1.CreateOptions{}) 227 if err != nil && !apierrors.IsAlreadyExists(err) { 228 t.Fatalf("could not create namespace: %v", err) 229 } 230 mysa, err := c.CoreV1().ServiceAccounts(myns).Create(tCtx, &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: readOnlyServiceAccountName}}, metav1.CreateOptions{}) 231 if err != nil { 232 t.Fatalf("Service Account not created: %v", err) 233 } 234 manualSecretName := "manual-token" 235 manualSecret, err := createServiceAccountToken(c, mysa, myns, manualSecretName) 236 if err != nil { 237 t.Fatalf("Secret not created: %v", err) 238 } 239 240 // manually craft an auto created token 241 autoSecretName := "auto-token" 242 autoSecret, err := createServiceAccountToken(c, mysa, myns, autoSecretName) 243 if err != nil { 244 t.Fatalf("Secret not created: %v", err) 245 } 246 if err := addReferencedServiceAccountToken(c, myns, readOnlyServiceAccountName, autoSecret); err != nil { 247 t.Fatal(err) 248 } 249 250 tests := []struct { 251 name string 252 secretName string 253 secretTokenData string 254 255 expectWarning bool 256 }{ 257 { 258 name: "manually created legacy token", 259 secretName: manualSecretName, 260 secretTokenData: string(manualSecret.Data[v1.ServiceAccountTokenKey]), 261 }, 262 { 263 name: "auto created legacy token", 264 secretName: autoSecretName, 265 secretTokenData: string(autoSecret.Data[v1.ServiceAccountTokenKey]), 266 expectWarning: true, 267 }, 268 } 269 for _, test := range tests { 270 t.Run(test.name, func(t *testing.T) { 271 myConfig := *config 272 wh := &warningHandler{} 273 myConfig.WarningHandler = wh 274 myConfig.BearerToken = string(test.secretTokenData) 275 roClient := clientset.NewForConfigOrDie(&myConfig) 276 dateBefore := time.Now().UTC().Format(dateFormat) 277 278 var wg sync.WaitGroup 279 concurrency := 5 280 for i := 0; i < concurrency; i++ { 281 wg.Add(1) 282 go func() { 283 doServiceAccountAPIRequests(t, roClient, myns, true, true, false) 284 wg.Done() 285 }() 286 } 287 wg.Wait() 288 dateAfter := time.Now().UTC().Format(dateFormat) 289 liveSecret, err := c.CoreV1().Secrets(myns).Get(tCtx, test.secretName, metav1.GetOptions{}) 290 if err != nil { 291 t.Fatalf("Could not get secret: %v", err) 292 } 293 294 // doServiceAccountAPIRequests has 4 API requests 295 if test.expectWarning && len(wh.warnings) != 4*concurrency { 296 t.Fatalf("Expect %d warnings, got %d", 4*concurrency, len(wh.warnings)) 297 } 298 if !test.expectWarning && len(wh.warnings) != 0 { 299 t.Fatalf("Don't expect warnings, got %d", len(wh.warnings)) 300 } 301 302 // authenticated legacy token should have the expected annotation and label. 303 date, ok := liveSecret.GetLabels()[serviceaccount.LastUsedLabelKey] 304 if !ok { 305 t.Fatalf("Secret wasn't labeled with %q", serviceaccount.LastUsedLabelKey) 306 } 307 if date != dateBefore || date != dateAfter { 308 t.Fatalf("Secret was labeled with wrong date: %q", date) 309 } 310 }) 311 } 312 313 // configmap should exist with 'since' timestamp. 314 if err = wait.PollImmediate(time.Millisecond*10, wait.ForeverTestTimeout, func() (bool, error) { 315 dateBefore := time.Now().UTC().Format("2006-01-02") 316 configMap, err := c.CoreV1().ConfigMaps(metav1.NamespaceSystem).Get(tCtx, legacytokentracking.ConfigMapName, metav1.GetOptions{}) 317 if err != nil { 318 return false, fmt.Errorf("failed to get %q configmap, err %w", legacytokentracking.ConfigMapDataKey, err) 319 } 320 dateAfter := time.Now().UTC().Format("2006-01-02") 321 date, ok := configMap.Data[legacytokentracking.ConfigMapDataKey] 322 if !ok { 323 return false, fmt.Errorf("configMap doesn't contain key %q", legacytokentracking.ConfigMapDataKey) 324 } 325 if date != dateBefore || date != dateAfter { 326 return false, fmt.Errorf("configMap contains a wrong date %q", date) 327 } 328 return true, nil 329 }); err != nil { 330 t.Fatal(err) 331 } 332 } 333 334 // startServiceAccountTestServerAndWaitForCaches returns a started server 335 // It is the responsibility of the caller to ensure the returned stopFunc is called 336 func startServiceAccountTestServerAndWaitForCaches(ctx context.Context, t *testing.T) (clientset.Interface, *restclient.Config, func(), clientinformers.SharedInformerFactory, error) { 337 var serviceAccountKey interface{} 338 339 ctx, cancel := context.WithCancel(ctx) 340 341 // Set up a API server 342 rootClientset, clientConfig, tearDownFn := framework.StartTestServer(ctx, t, framework.TestServerSetup{ 343 ModifyServerRunOptions: func(opts *options.ServerRunOptions) { 344 var err error 345 serviceAccountKey, err = keyutil.PrivateKeyFromFile(opts.ServiceAccountSigningKeyFile) 346 if err != nil { 347 t.Fatal(err) 348 } 349 }, 350 ModifyServerConfig: func(config *controlplane.Config) { 351 // Set up a stub authorizer: 352 // 1. The "root" user is allowed to do anything 353 // 2. ServiceAccounts named "ro" are allowed read-only operations in their namespace 354 // 3. ServiceAccounts named "rw" are allowed any operation in their namespace 355 authorizer := authorizer.AuthorizerFunc(func(ctx context.Context, attrs authorizer.Attributes) (authorizer.Decision, string, error) { 356 username := "" 357 if user := attrs.GetUser(); user != nil { 358 username = user.GetName() 359 } 360 ns := attrs.GetNamespace() 361 362 // If the user is a service account... 363 if serviceAccountNamespace, serviceAccountName, err := serviceaccountapiserver.SplitUsername(username); err == nil { 364 // Limit them to their own namespace 365 if serviceAccountNamespace == ns { 366 switch serviceAccountName { 367 case readOnlyServiceAccountName: 368 if attrs.IsReadOnly() { 369 return authorizer.DecisionAllow, "", nil 370 } 371 case readWriteServiceAccountName: 372 return authorizer.DecisionAllow, "", nil 373 } 374 } 375 } 376 377 return authorizer.DecisionNoOpinion, fmt.Sprintf("User %s is denied (ns=%s, readonly=%v, resource=%s)", username, ns, attrs.IsReadOnly(), attrs.GetResource()), nil 378 }) 379 config.ControlPlane.Generic.Authorization.Authorizer = unionauthz.New(config.ControlPlane.Generic.Authorization.Authorizer, authorizer) 380 }, 381 }) 382 383 stop := func() { 384 cancel() 385 tearDownFn() 386 } 387 388 informers := clientinformers.NewSharedInformerFactory(rootClientset, controller.NoResyncPeriodFunc()) 389 390 // Start the service account and service account token controllers 391 tokenGenerator, err := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, serviceAccountKey) 392 if err != nil { 393 return rootClientset, clientConfig, stop, informers, err 394 } 395 tokenController, err := serviceaccountcontroller.NewTokensController( 396 informers.Core().V1().ServiceAccounts(), 397 informers.Core().V1().Secrets(), 398 rootClientset, 399 serviceaccountcontroller.TokensControllerOptions{ 400 TokenGenerator: tokenGenerator, 401 }, 402 ) 403 if err != nil { 404 return rootClientset, clientConfig, stop, informers, err 405 } 406 go tokenController.Run(ctx, 1) 407 408 serviceAccountController, err := serviceaccountcontroller.NewServiceAccountsController( 409 informers.Core().V1().ServiceAccounts(), 410 informers.Core().V1().Namespaces(), 411 rootClientset, 412 serviceaccountcontroller.DefaultServiceAccountsControllerOptions(), 413 ) 414 if err != nil { 415 return rootClientset, clientConfig, stop, informers, err 416 } 417 informers.Start(ctx.Done()) 418 go serviceAccountController.Run(ctx, 5) 419 420 // since this method starts the controllers in a separate goroutine 421 // and the tests don't check /readyz there is no way 422 // the tests can tell it is safe to call the server and requests won't be rejected 423 // thus we wait until caches have synced 424 informers.WaitForCacheSync(ctx.Done()) 425 426 return rootClientset, clientConfig, stop, informers, nil 427 } 428 429 func getServiceAccount(c clientset.Interface, ns string, name string, shouldWait bool) (*v1.ServiceAccount, error) { 430 if !shouldWait { 431 return c.CoreV1().ServiceAccounts(ns).Get(context.TODO(), name, metav1.GetOptions{}) 432 } 433 434 var user *v1.ServiceAccount 435 var err error 436 err = wait.Poll(time.Second, 10*time.Second, func() (bool, error) { 437 user, err = c.CoreV1().ServiceAccounts(ns).Get(context.TODO(), name, metav1.GetOptions{}) 438 if apierrors.IsNotFound(err) { 439 return false, nil 440 } 441 if err != nil { 442 return false, err 443 } 444 return true, nil 445 }) 446 return user, err 447 } 448 449 func createServiceAccountToken(c clientset.Interface, sa *v1.ServiceAccount, ns string, name string) (*v1.Secret, error) { 450 secret := &v1.Secret{ 451 ObjectMeta: metav1.ObjectMeta{ 452 Name: name, 453 Namespace: ns, 454 Annotations: map[string]string{ 455 v1.ServiceAccountNameKey: sa.GetName(), 456 v1.ServiceAccountUIDKey: string(sa.UID), 457 }, 458 }, 459 Type: v1.SecretTypeServiceAccountToken, 460 Data: map[string][]byte{}, 461 } 462 secret, err := c.CoreV1().Secrets(ns).Create(context.TODO(), secret, metav1.CreateOptions{}) 463 if err != nil { 464 return nil, fmt.Errorf("failed to create secret (%s:%s) %+v, err: %v", ns, secret.Name, *secret, err) 465 } 466 err = wait.Poll(time.Second, 10*time.Second, func() (bool, error) { 467 if len(secret.Data[v1.ServiceAccountTokenKey]) != 0 { 468 return false, nil 469 } 470 secret, err = c.CoreV1().Secrets(ns).Get(context.TODO(), name, metav1.GetOptions{}) 471 if err != nil { 472 return true, fmt.Errorf("failed to get secret (%s:%s) %+v, err: %v", ns, secret.Name, *secret, err) 473 } 474 return true, nil 475 }) 476 return secret, nil 477 } 478 479 func addReferencedServiceAccountToken(c clientset.Interface, ns string, name string, secret *v1.Secret) error { 480 sa, err := c.CoreV1().ServiceAccounts(ns).Get(context.TODO(), name, metav1.GetOptions{}) 481 if err != nil { 482 return err 483 } 484 sa.Secrets = append(sa.Secrets, v1.ObjectReference{ 485 APIVersion: secret.APIVersion, 486 Kind: secret.Kind, 487 Namespace: secret.Namespace, 488 Name: secret.Name, 489 ResourceVersion: secret.ResourceVersion, 490 }) 491 if _, err = c.CoreV1().ServiceAccounts(ns).Update(context.TODO(), sa, metav1.UpdateOptions{}); err != nil { 492 return err 493 } 494 return nil 495 } 496 497 type testOperation func() error 498 499 func doServiceAccountAPIRequests(t *testing.T, c clientset.Interface, ns string, authenticated bool, canRead bool, canWrite bool) { 500 testSecret := &v1.Secret{ 501 ObjectMeta: metav1.ObjectMeta{Name: "testSecret"}, 502 Data: map[string][]byte{"test": []byte("data")}, 503 } 504 505 readOps := []testOperation{ 506 func() error { 507 _, err := c.CoreV1().Secrets(ns).List(context.TODO(), metav1.ListOptions{}) 508 return err 509 }, 510 func() error { 511 _, err := c.CoreV1().Pods(ns).List(context.TODO(), metav1.ListOptions{}) 512 return err 513 }, 514 } 515 writeOps := []testOperation{ 516 func() error { 517 _, err := c.CoreV1().Secrets(ns).Create(context.TODO(), testSecret, metav1.CreateOptions{}) 518 return err 519 }, 520 func() error { 521 return c.CoreV1().Secrets(ns).Delete(context.TODO(), testSecret.Name, metav1.DeleteOptions{}) 522 }, 523 } 524 525 for _, op := range readOps { 526 err := op() 527 unauthorizedError := apierrors.IsUnauthorized(err) 528 forbiddenError := apierrors.IsForbidden(err) 529 530 switch { 531 case !authenticated && !unauthorizedError: 532 t.Fatalf("expected unauthorized error, got %v", err) 533 case authenticated && unauthorizedError: 534 t.Fatalf("unexpected unauthorized error: %v", err) 535 case authenticated && canRead && forbiddenError: 536 t.Fatalf("unexpected forbidden error: %v", err) 537 case authenticated && !canRead && !forbiddenError: 538 t.Fatalf("expected forbidden error, got: %v", err) 539 } 540 } 541 542 for _, op := range writeOps { 543 err := op() 544 unauthorizedError := apierrors.IsUnauthorized(err) 545 forbiddenError := apierrors.IsForbidden(err) 546 547 switch { 548 case !authenticated && !unauthorizedError: 549 t.Fatalf("expected unauthorized error, got %v", err) 550 case authenticated && unauthorizedError: 551 t.Fatalf("unexpected unauthorized error: %v", err) 552 case authenticated && canWrite && forbiddenError: 553 t.Fatalf("unexpected forbidden error: %v", err) 554 case authenticated && !canWrite && !forbiddenError: 555 t.Fatalf("expected forbidden error, got: %v", err) 556 } 557 } 558 } 559 560 type warningHandler struct { 561 mu sync.Mutex 562 warnings []string 563 } 564 565 func (r *warningHandler) HandleWarningHeader(code int, agent string, message string) { 566 r.mu.Lock() 567 defer r.mu.Unlock() 568 r.warnings = append(r.warnings, message) 569 }