diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 589cb97c52b..5b29dbd5c8b 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -179,6 +179,7 @@ const ( // owner: @mikedanese // alpha: v1.13 // beta: v1.21 + // ga: v1.22 // // Migrate ServiceAccount volumes to use a projected volume consisting of a // ServiceAccountTokenVolumeProjection. This feature adds new required flags @@ -764,7 +765,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS StorageObjectInUseProtection: {Default: true, PreRelease: featuregate.GA}, SupportPodPidsLimit: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.23 SupportNodePidsLimit: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.23 - BoundServiceAccountTokenVolume: {Default: true, PreRelease: featuregate.Beta}, + BoundServiceAccountTokenVolume: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.23 ServiceAccountIssuerDiscovery: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.22 CSIMigration: {Default: true, PreRelease: featuregate.Beta}, CSIMigrationGCE: {Default: false, PreRelease: featuregate.Beta}, // Off by default (requires GCE PD CSI Driver) diff --git a/pkg/security/podsecuritypolicy/provider_test.go b/pkg/security/podsecuritypolicy/provider_test.go index 76c5a753d75..14c7ab45782 100644 --- a/pkg/security/podsecuritypolicy/provider_test.go +++ b/pkg/security/podsecuritypolicy/provider_test.go @@ -1481,7 +1481,7 @@ func TestValidateProjectedVolume(t *testing.T) { psp.Spec.Volumes = test.allowedFSTypes errs := provider.ValidatePod(pod) if test.wantAllow { - assert.Empty(t, errs, "projected volumes are allowed if secret volumes is allowed and BoundServiceAccountTokenVolume is enabled") + assert.Empty(t, errs, "projected volumes are allowed") } else { assert.Contains(t, errs.ToAggregate().Error(), fmt.Sprintf("projected volumes are not allowed to be used"), "did not find the expected error") } diff --git a/plugin/pkg/admission/serviceaccount/admission.go b/plugin/pkg/admission/serviceaccount/admission.go index 6a003f75e0a..e84f9c037da 100644 --- a/plugin/pkg/admission/serviceaccount/admission.go +++ b/plugin/pkg/admission/serviceaccount/admission.go @@ -28,20 +28,15 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apiserver/pkg/admission" genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer" - apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount" "k8s.io/apiserver/pkg/storage/names" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" corev1listers "k8s.io/client-go/listers/core/v1" - "k8s.io/component-base/featuregate" podutil "k8s.io/kubernetes/pkg/api/pod" api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/serviceaccount" ) @@ -80,24 +75,18 @@ type Plugin struct { // LimitSecretReferences rejects pods that reference secrets their service accounts do not reference LimitSecretReferences bool - // RequireAPIToken determines whether pod creation attempts are rejected if no API token exists for the pod's service account - RequireAPIToken bool // MountServiceAccountToken creates Volume and VolumeMounts for the first referenced ServiceAccountToken for the pod's service account MountServiceAccountToken bool client kubernetes.Interface serviceAccountLister corev1listers.ServiceAccountLister - secretLister corev1listers.SecretLister generateName func(string) string - - boundServiceAccountTokenVolume bool } var _ admission.MutationInterface = &Plugin{} var _ admission.ValidationInterface = &Plugin{} -var _ genericadmissioninitializer.WantsFeatures = &Plugin{} var _ = genericadmissioninitializer.WantsExternalKubeClientSet(&Plugin{}) var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&Plugin{}) @@ -114,18 +103,11 @@ func NewServiceAccount() *Plugin { LimitSecretReferences: false, // Auto mount service account API token secrets MountServiceAccountToken: true, - // Reject pod creation until a service account token is available - RequireAPIToken: true, generateName: names.SimpleNameGenerator.GenerateName, } } -// InspectFeatureGates allows setting bools without taking a dep on a global variable -func (s *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) { - s.boundServiceAccountTokenVolume = featureGates.Enabled(features.BoundServiceAccountTokenVolume) -} - // SetExternalKubeClientSet sets the client for the plugin func (s *Plugin) SetExternalKubeClientSet(cl kubernetes.Interface) { s.client = cl @@ -135,12 +117,8 @@ func (s *Plugin) SetExternalKubeClientSet(cl kubernetes.Interface) { func (s *Plugin) SetExternalKubeInformerFactory(f informers.SharedInformerFactory) { serviceAccountInformer := f.Core().V1().ServiceAccounts() s.serviceAccountLister = serviceAccountInformer.Lister() - - secretInformer := f.Core().V1().Secrets() - s.secretLister = secretInformer.Lister() - s.SetReadyFunc(func() bool { - return serviceAccountInformer.Informer().HasSynced() && secretInformer.Informer().HasSynced() + return serviceAccountInformer.Informer().HasSynced() }) } @@ -149,9 +127,6 @@ func (s *Plugin) ValidateInitialization() error { if s.client == nil { return fmt.Errorf("missing client") } - if s.secretLister == nil { - return fmt.Errorf("missing secretLister") - } if s.serviceAccountLister == nil { return fmt.Errorf("missing serviceAccountLister") } @@ -183,12 +158,7 @@ func (s *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission. return admission.NewForbidden(a, fmt.Errorf("error looking up service account %s/%s: %v", a.GetNamespace(), pod.Spec.ServiceAccountName, err)) } if s.MountServiceAccountToken && shouldAutomount(serviceAccount, pod) { - if err := s.mountServiceAccountToken(serviceAccount, pod); err != nil { - if _, ok := err.(errors.APIStatus); ok { - return err - } - return admission.NewForbidden(a, err) - } + s.mountServiceAccountToken(serviceAccount, pod) } if len(pod.Spec.ImagePullSecrets) == 0 { pod.Spec.ImagePullSecrets = make([]api.LocalObjectReference, len(serviceAccount.ImagePullSecrets)) @@ -328,52 +298,6 @@ func (s *Plugin) getServiceAccount(namespace string, name string) (*corev1.Servi return nil, errors.NewNotFound(api.Resource("serviceaccount"), name) } -// getReferencedServiceAccountToken returns the name of the first referenced secret which is a ServiceAccountToken for the service account -func (s *Plugin) getReferencedServiceAccountToken(serviceAccount *corev1.ServiceAccount) (string, error) { - if len(serviceAccount.Secrets) == 0 { - return "", nil - } - - tokens, err := s.getServiceAccountTokens(serviceAccount) - if err != nil { - return "", err - } - - accountTokens := sets.NewString() - for _, token := range tokens { - accountTokens.Insert(token.Name) - } - // Prefer secrets in the order they're referenced. - for _, secret := range serviceAccount.Secrets { - if accountTokens.Has(secret.Name) { - return secret.Name, nil - } - } - - return "", nil -} - -// getServiceAccountTokens returns all ServiceAccountToken secrets for the given ServiceAccount -func (s *Plugin) getServiceAccountTokens(serviceAccount *corev1.ServiceAccount) ([]*corev1.Secret, error) { - secrets, err := s.secretLister.Secrets(serviceAccount.Namespace).List(labels.Everything()) - if err != nil { - return nil, err - } - - tokens := []*corev1.Secret{} - - for _, secret := range secrets { - if secret.Type != corev1.SecretTypeServiceAccountToken { - continue - } - - if apiserverserviceaccount.IsServiceAccountToken(secret, serviceAccount) { - tokens = append(tokens, secret) - } - } - return tokens, nil -} - func (s *Plugin) limitSecretReferences(serviceAccount *corev1.ServiceAccount, pod *api.Pod) error { // Ensure all secrets the pod references are allowed by the service account mountableSecrets := sets.NewString() @@ -424,38 +348,14 @@ func (s *Plugin) limitSecretReferences(serviceAccount *corev1.ServiceAccount, po return nil } -func (s *Plugin) mountServiceAccountToken(serviceAccount *corev1.ServiceAccount, pod *api.Pod) error { - var ( - // serviceAccountToken holds the name of a secret containing a legacy service account token - serviceAccountToken string - err error - ) - if !s.boundServiceAccountTokenVolume { - // Find the name of a referenced ServiceAccountToken secret we can mount - serviceAccountToken, err = s.getReferencedServiceAccountToken(serviceAccount) - if err != nil { - return fmt.Errorf("Error looking up service account token for %s/%s: %v", serviceAccount.Namespace, serviceAccount.Name, err) - } - } - if len(serviceAccountToken) == 0 && !s.boundServiceAccountTokenVolume { - // We don't have an API token to mount, so return - if s.RequireAPIToken { - // If a token is required, this is considered an error - err := errors.NewServerTimeout(schema.GroupResource{Resource: "serviceaccounts"}, "create pod", 1) - err.ErrStatus.Message = fmt.Sprintf("No API token found for service account %q, retry after the token is automatically created and added to the service account", serviceAccount.Name) - return err - } - return nil - } - +func (s *Plugin) mountServiceAccountToken(serviceAccount *corev1.ServiceAccount, pod *api.Pod) { // Find the volume and volume name for the ServiceAccountTokenSecret if it already exists tokenVolumeName := "" hasTokenVolume := false allVolumeNames := sets.NewString() for _, volume := range pod.Spec.Volumes { allVolumeNames.Insert(volume.Name) - if (!s.boundServiceAccountTokenVolume && volume.Secret != nil && volume.Secret.SecretName == serviceAccountToken) || - (s.boundServiceAccountTokenVolume && strings.HasPrefix(volume.Name, ServiceAccountVolumeName+"-")) { + if strings.HasPrefix(volume.Name, ServiceAccountVolumeName+"-") { tokenVolumeName = volume.Name hasTokenVolume = true break @@ -464,16 +364,7 @@ func (s *Plugin) mountServiceAccountToken(serviceAccount *corev1.ServiceAccount, // Determine a volume name for the ServiceAccountTokenSecret in case we need it if len(tokenVolumeName) == 0 { - if s.boundServiceAccountTokenVolume { - tokenVolumeName = s.generateName(ServiceAccountVolumeName + "-") - } else { - // Try naming the volume the same as the serviceAccountToken, and uniquify if needed - // Replace dots because volumeMountName can't contain it - tokenVolumeName = strings.Replace(serviceAccountToken, ".", "-", -1) - if allVolumeNames.Has(tokenVolumeName) { - tokenVolumeName = s.generateName(fmt.Sprintf("%s-", tokenVolumeName)) - } - } + tokenVolumeName = s.generateName(ServiceAccountVolumeName + "-") } // Create the prototypical VolumeMount @@ -516,27 +407,12 @@ func (s *Plugin) mountServiceAccountToken(serviceAccount *corev1.ServiceAccount, // Add the volume if a container needs it if !hasTokenVolume && needsTokenVolume { - pod.Spec.Volumes = append(pod.Spec.Volumes, s.createVolume(tokenVolumeName, serviceAccountToken)) - } - return nil -} - -func (s *Plugin) createVolume(tokenVolumeName, secretName string) api.Volume { - if s.boundServiceAccountTokenVolume { - return api.Volume{ + pod.Spec.Volumes = append(pod.Spec.Volumes, api.Volume{ Name: tokenVolumeName, VolumeSource: api.VolumeSource{ Projected: TokenVolumeSource(), }, - } - } - return api.Volume{ - Name: tokenVolumeName, - VolumeSource: api.VolumeSource{ - Secret: &api.SecretVolumeSource{ - SecretName: secretName, - }, - }, + }) } } diff --git a/plugin/pkg/admission/serviceaccount/admission_test.go b/plugin/pkg/admission/serviceaccount/admission_test.go index 14fe7f223b3..0ef21a935ee 100644 --- a/plugin/pkg/admission/serviceaccount/admission_test.go +++ b/plugin/pkg/admission/serviceaccount/admission_test.go @@ -24,7 +24,6 @@ import ( "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/diff" @@ -32,18 +31,11 @@ import ( admissiontesting "k8s.io/apiserver/pkg/admission/testing" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes/fake" - corev1listers "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/tools/cache" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/controller" kubelet "k8s.io/kubernetes/pkg/kubelet/types" ) -var ( - deprecationDisabledBoundTokenVolume = false - deprecationEnabledBoundTokenVolume = true -) - func TestIgnoresNonCreate(t *testing.T) { for _, op := range []admission.Operation{admission.Delete, admission.Connect} { handler := NewServiceAccount() @@ -165,59 +157,6 @@ func TestRejectsMirrorPodWithServiceAccountTokenVolumeProjections(t *testing.T) } } -func TestAssignsDefaultServiceAccountAndToleratesMissingAPIToken(t *testing.T) { - ns := "myns" - - admit := NewServiceAccount() - informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) - admit.SetExternalKubeInformerFactory(informerFactory) - admit.MountServiceAccountToken = true - admit.RequireAPIToken = false - - // Add the default service account for the ns into the cache - informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: DefaultServiceAccountName, - Namespace: ns, - }, - }) - - pod := &api.Pod{} - attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) - err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if pod.Spec.ServiceAccountName != DefaultServiceAccountName { - t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName) - } -} - -func TestAssignsDefaultServiceAccountAndRejectsMissingAPIToken(t *testing.T) { - ns := "myns" - - admit := NewServiceAccount() - informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) - admit.SetExternalKubeInformerFactory(informerFactory) - admit.MountServiceAccountToken = true - admit.RequireAPIToken = true - - // Add the default service account for the ns into the cache - informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: DefaultServiceAccountName, - Namespace: ns, - }, - }) - - pod := &api.Pod{} - attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) - err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil) - if err == nil || !errors.IsServerTimeout(err) { - t.Errorf("Expected server timeout error for missing API token: %v", err) - } -} - func TestAssignsDefaultServiceAccountAndBoundTokenWithNoSecretTokens(t *testing.T) { ns := "myns" @@ -225,8 +164,6 @@ func TestAssignsDefaultServiceAccountAndBoundTokenWithNoSecretTokens(t *testing. informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) admit.SetExternalKubeInformerFactory(informerFactory) admit.MountServiceAccountToken = true - admit.RequireAPIToken = true - admit.boundServiceAccountTokenVolume = true // Add the default service account for the ns into the cache informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ @@ -300,7 +237,6 @@ func TestFetchesUncachedServiceAccount(t *testing.T) { informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) admit.SetExternalKubeInformerFactory(informerFactory) admit.client = client - admit.RequireAPIToken = false pod := &api.Pod{} attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) @@ -333,222 +269,181 @@ func TestDeniesInvalidServiceAccount(t *testing.T) { } func TestAutomountsAPIToken(t *testing.T) { - testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*Plugin) *Plugin) { - admit := applyFeatures(NewServiceAccount()) - informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) - admit.SetExternalKubeInformerFactory(informerFactory) - admit.generateName = testGenerateName - admit.MountServiceAccountToken = true - admit.RequireAPIToken = true + admit := NewServiceAccount() + informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) + admit.SetExternalKubeInformerFactory(informerFactory) + admit.generateName = testGenerateName + admit.MountServiceAccountToken = true - ns := "myns" - serviceAccountName := DefaultServiceAccountName - serviceAccountUID := "12345" + ns := "myns" + serviceAccountName := DefaultServiceAccountName + serviceAccountUID := "12345" - tokenName := "token-name" - if admit.boundServiceAccountTokenVolume { - tokenName = generatedVolumeName - } + tokenName := generatedVolumeName - expectedVolume := admit.createVolume(tokenName, tokenName) - expectedVolumeMount := api.VolumeMount{ - Name: tokenName, - ReadOnly: true, - MountPath: DefaultAPITokenMountPath, - } - // Add the default service account for the ns with a token into the cache - informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: serviceAccountName, - Namespace: ns, - UID: types.UID(serviceAccountUID), - }, - Secrets: []corev1.ObjectReference{ - {Name: tokenName}, - }, - }) - // Add a token for the service account into the cache - informerFactory.Core().V1().Secrets().Informer().GetStore().Add(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: tokenName, - Namespace: ns, - Annotations: map[string]string{ - corev1.ServiceAccountNameKey: serviceAccountName, - corev1.ServiceAccountUIDKey: serviceAccountUID, - }, - }, - Type: corev1.SecretTypeServiceAccountToken, - Data: map[string][]byte{ - api.ServiceAccountTokenKey: []byte("token-data"), - }, - }) - - pod := &api.Pod{ - Spec: api.PodSpec{ - Containers: []api.Container{ - {}, - }, - }, - } - attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) - err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if pod.Spec.ServiceAccountName != DefaultServiceAccountName { - t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName) - } - if len(pod.Spec.Volumes) != 1 { - t.Fatalf("Expected 1 volume, got %d", len(pod.Spec.Volumes)) - } - if !reflect.DeepEqual(expectedVolume, pod.Spec.Volumes[0]) { - t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolume, pod.Spec.Volumes[0]) - } - if len(pod.Spec.Containers[0].VolumeMounts) != 1 { - t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.Containers[0].VolumeMounts)) - } - if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) { - t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) - } - - // testing InitContainers - pod = &api.Pod{ - Spec: api.PodSpec{ - InitContainers: []api.Container{ - {}, - }, - }, - } - attrs = admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) - if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { - t.Errorf("Unexpected error: %v", err) - } - if pod.Spec.ServiceAccountName != DefaultServiceAccountName { - t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName) - } - if len(pod.Spec.Volumes) != 1 { - t.Fatalf("Expected 1 volume, got %d", len(pod.Spec.Volumes)) - } - if !reflect.DeepEqual(expectedVolume, pod.Spec.Volumes[0]) { - t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolume, pod.Spec.Volumes[0]) - } - if len(pod.Spec.InitContainers[0].VolumeMounts) != 1 { - t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.InitContainers[0].VolumeMounts)) - } - if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) { - t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) - } + expectedVolume := api.Volume{ + Name: tokenName, + VolumeSource: api.VolumeSource{ + Projected: TokenVolumeSource(), + }, + } + expectedVolumeMount := api.VolumeMount{ + Name: tokenName, + ReadOnly: true, + MountPath: DefaultAPITokenMountPath, + } + // Add the default service account for the ns with a token into the cache + informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceAccountName, + Namespace: ns, + UID: types.UID(serviceAccountUID), + }, }) + + pod := &api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + {}, + }, + }, + } + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) + err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + if pod.Spec.ServiceAccountName != DefaultServiceAccountName { + t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName) + } + if len(pod.Spec.Volumes) != 1 { + t.Fatalf("Expected 1 volume, got %d", len(pod.Spec.Volumes)) + } + if !reflect.DeepEqual(expectedVolume, pod.Spec.Volumes[0]) { + t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolume, pod.Spec.Volumes[0]) + } + if len(pod.Spec.Containers[0].VolumeMounts) != 1 { + t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.Containers[0].VolumeMounts)) + } + if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) { + t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) + } + + // testing InitContainers + pod = &api.Pod{ + Spec: api.PodSpec{ + InitContainers: []api.Container{ + {}, + }, + }, + } + attrs = admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) + if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { + t.Errorf("Unexpected error: %v", err) + } + if pod.Spec.ServiceAccountName != DefaultServiceAccountName { + t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName) + } + if len(pod.Spec.Volumes) != 1 { + t.Fatalf("Expected 1 volume, got %d", len(pod.Spec.Volumes)) + } + if !reflect.DeepEqual(expectedVolume, pod.Spec.Volumes[0]) { + t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolume, pod.Spec.Volumes[0]) + } + if len(pod.Spec.InitContainers[0].VolumeMounts) != 1 { + t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.InitContainers[0].VolumeMounts)) + } + if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) { + t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) + } } func TestRespectsExistingMount(t *testing.T) { - testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*Plugin) *Plugin) { - ns := "myns" - tokenName := "token-name" - serviceAccountName := DefaultServiceAccountName - serviceAccountUID := "12345" + ns := "myns" + serviceAccountName := DefaultServiceAccountName + serviceAccountUID := "12345" - expectedVolumeMount := api.VolumeMount{ - Name: "my-custom-mount", - ReadOnly: false, - MountPath: DefaultAPITokenMountPath, - } + expectedVolumeMount := api.VolumeMount{ + Name: "my-custom-mount", + ReadOnly: false, + MountPath: DefaultAPITokenMountPath, + } - admit := applyFeatures(NewServiceAccount()) - informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) - admit.SetExternalKubeInformerFactory(informerFactory) - admit.MountServiceAccountToken = true - admit.RequireAPIToken = true + admit := NewServiceAccount() + informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) + admit.SetExternalKubeInformerFactory(informerFactory) + admit.MountServiceAccountToken = true - // Add the default service account for the ns with a token into the cache - informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: serviceAccountName, - Namespace: ns, - UID: types.UID(serviceAccountUID), - }, - Secrets: []corev1.ObjectReference{ - {Name: tokenName}, - }, - }) - // Add a token for the service account into the cache - informerFactory.Core().V1().Secrets().Informer().GetStore().Add(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: tokenName, - Namespace: ns, - Annotations: map[string]string{ - corev1.ServiceAccountNameKey: serviceAccountName, - corev1.ServiceAccountUIDKey: serviceAccountUID, - }, - }, - Type: corev1.SecretTypeServiceAccountToken, - Data: map[string][]byte{ - corev1.ServiceAccountTokenKey: []byte("token-data"), - }, - }) - - // Define a pod with a container that already mounts a volume at the API token path - // Admission should respect that - // Additionally, no volume should be created if no container is going to use it - pod := &api.Pod{ - Spec: api.PodSpec{ - Containers: []api.Container{ - { - VolumeMounts: []api.VolumeMount{ - expectedVolumeMount, - }, - }, - }, - }, - } - attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) - err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if pod.Spec.ServiceAccountName != DefaultServiceAccountName { - t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName) - } - if len(pod.Spec.Volumes) != 0 { - t.Fatalf("Expected 0 volumes (shouldn't create a volume for a secret we don't need), got %d", len(pod.Spec.Volumes)) - } - if len(pod.Spec.Containers[0].VolumeMounts) != 1 { - t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.Containers[0].VolumeMounts)) - } - if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) { - t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) - } - - // check init containers - pod = &api.Pod{ - Spec: api.PodSpec{ - InitContainers: []api.Container{ - { - VolumeMounts: []api.VolumeMount{ - expectedVolumeMount, - }, - }, - }, - }, - } - attrs = admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) - if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { - t.Errorf("Unexpected error: %v", err) - } - if pod.Spec.ServiceAccountName != DefaultServiceAccountName { - t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName) - } - if len(pod.Spec.Volumes) != 0 { - t.Fatalf("Expected 0 volumes (shouldn't create a volume for a secret we don't need), got %d", len(pod.Spec.Volumes)) - } - if len(pod.Spec.InitContainers[0].VolumeMounts) != 1 { - t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.InitContainers[0].VolumeMounts)) - } - if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) { - t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) - } + // Add the default service account for the ns with a token into the cache + informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: serviceAccountName, + Namespace: ns, + UID: types.UID(serviceAccountUID), + }, }) + + // Define a pod with a container that already mounts a volume at the API token path + // Admission should respect that + // Additionally, no volume should be created if no container is going to use it + pod := &api.Pod{ + Spec: api.PodSpec{ + Containers: []api.Container{ + { + VolumeMounts: []api.VolumeMount{ + expectedVolumeMount, + }, + }, + }, + }, + } + attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) + err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil) + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + if pod.Spec.ServiceAccountName != DefaultServiceAccountName { + t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName) + } + if len(pod.Spec.Volumes) != 0 { + t.Fatalf("Expected 0 volumes (shouldn't create a volume for a secret we don't need), got %d", len(pod.Spec.Volumes)) + } + if len(pod.Spec.Containers[0].VolumeMounts) != 1 { + t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.Containers[0].VolumeMounts)) + } + if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) { + t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) + } + + // check init containers + pod = &api.Pod{ + Spec: api.PodSpec{ + InitContainers: []api.Container{ + { + VolumeMounts: []api.VolumeMount{ + expectedVolumeMount, + }, + }, + }, + }, + } + attrs = admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) + if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { + t.Errorf("Unexpected error: %v", err) + } + if pod.Spec.ServiceAccountName != DefaultServiceAccountName { + t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName) + } + if len(pod.Spec.Volumes) != 0 { + t.Fatalf("Expected 0 volumes (shouldn't create a volume for a secret we don't need), got %d", len(pod.Spec.Volumes)) + } + if len(pod.Spec.InitContainers[0].VolumeMounts) != 1 { + t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.InitContainers[0].VolumeMounts)) + } + if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) { + t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.InitContainers[0].VolumeMounts[0]) + } } func TestAllowsReferencedSecret(t *testing.T) { @@ -558,7 +453,6 @@ func TestAllowsReferencedSecret(t *testing.T) { informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) admit.SetExternalKubeInformerFactory(informerFactory) admit.LimitSecretReferences = true - admit.RequireAPIToken = false // Add the default service account for the ns with a secret reference into the cache informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ @@ -639,7 +533,6 @@ func TestRejectsUnreferencedSecretVolumes(t *testing.T) { informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) admit.SetExternalKubeInformerFactory(informerFactory) admit.LimitSecretReferences = true - admit.RequireAPIToken = false // Add the default service account for the ns into the cache informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ @@ -717,7 +610,6 @@ func TestAllowUnreferencedSecretVolumesForPermissiveSAs(t *testing.T) { informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) admit.SetExternalKubeInformerFactory(informerFactory) admit.LimitSecretReferences = false - admit.RequireAPIToken = false // Add the default service account for the ns into the cache informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ @@ -749,7 +641,6 @@ func TestAllowsReferencedImagePullSecrets(t *testing.T) { informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) admit.SetExternalKubeInformerFactory(informerFactory) admit.LimitSecretReferences = true - admit.RequireAPIToken = false // Add the default service account for the ns with a secret reference into the cache informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ @@ -781,7 +672,6 @@ func TestRejectsUnreferencedImagePullSecrets(t *testing.T) { informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) admit.SetExternalKubeInformerFactory(informerFactory) admit.LimitSecretReferences = true - admit.RequireAPIToken = false // Add the default service account for the ns into the cache informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ @@ -810,7 +700,6 @@ func TestDoNotAddImagePullSecrets(t *testing.T) { informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) admit.SetExternalKubeInformerFactory(informerFactory) admit.LimitSecretReferences = true - admit.RequireAPIToken = false // Add the default service account for the ns with a secret reference into the cache informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ @@ -847,7 +736,6 @@ func TestAddImagePullSecrets(t *testing.T) { informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) admit.SetExternalKubeInformerFactory(informerFactory) admit.LimitSecretReferences = true - admit.RequireAPIToken = false sa := &corev1.ServiceAccount{ ObjectMeta: metav1.ObjectMeta{ @@ -877,348 +765,8 @@ func TestAddImagePullSecrets(t *testing.T) { } } -func TestMultipleReferencedSecrets(t *testing.T) { - var ( - ns = "myns" - serviceAccountName = "mysa" - serviceAccountUID = "mysauid" - token1 = "token1" - token2 = "token2" - ) - - admit := NewServiceAccount() - informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) - admit.SetExternalKubeInformerFactory(informerFactory) - admit.MountServiceAccountToken = true - admit.RequireAPIToken = true - - sa := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: serviceAccountName, - UID: types.UID(serviceAccountUID), - Namespace: ns, - }, - Secrets: []corev1.ObjectReference{ - {Name: token1}, - {Name: token2}, - }, - } - informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(sa) - - // Add two tokens for the service account into the cache. - informerFactory.Core().V1().Secrets().Informer().GetStore().Add(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: token2, - Namespace: ns, - Annotations: map[string]string{ - api.ServiceAccountNameKey: serviceAccountName, - api.ServiceAccountUIDKey: serviceAccountUID, - }, - }, - Type: corev1.SecretTypeServiceAccountToken, - Data: map[string][]byte{ - api.ServiceAccountTokenKey: []byte("token-data"), - }, - }) - informerFactory.Core().V1().Secrets().Informer().GetStore().Add(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: token1, - Namespace: ns, - Annotations: map[string]string{ - api.ServiceAccountNameKey: serviceAccountName, - api.ServiceAccountUIDKey: serviceAccountUID, - }, - }, - Type: corev1.SecretTypeServiceAccountToken, - Data: map[string][]byte{ - api.ServiceAccountTokenKey: []byte("token-data"), - }, - }) - - pod := &api.Pod{ - Spec: api.PodSpec{ - ServiceAccountName: serviceAccountName, - Containers: []api.Container{ - {Name: "container-1"}, - }, - }, - } - - attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) - if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { - t.Fatal(err) - } - - if n := len(pod.Spec.Volumes); n != 1 { - t.Fatalf("expected 1 volume mount, got %d", n) - } - if name := pod.Spec.Volumes[0].Name; name != token1 { - t.Errorf("expected first referenced secret to be mounted, got %q", name) - } -} - -func newSecret(secretType corev1.SecretType, namespace, name, serviceAccountName, serviceAccountUID string) *corev1.Secret { - return &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - Annotations: map[string]string{ - corev1.ServiceAccountNameKey: serviceAccountName, - corev1.ServiceAccountUIDKey: serviceAccountUID, - }, - }, - Type: secretType, - } -} - -func TestGetServiceAccountTokens(t *testing.T) { - testBoundServiceAccountTokenVolumePhases(t, func(t *testing.T, applyFeatures func(*Plugin) *Plugin) { - admit := applyFeatures(NewServiceAccount()) - indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) - admit.secretLister = corev1listers.NewSecretLister(indexer) - - ns := "namespace" - serviceAccountUID := "12345" - - sa := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: DefaultServiceAccountName, - Namespace: ns, - UID: types.UID(serviceAccountUID), - }, - } - - nonSATokenSecret := newSecret(corev1.SecretTypeDockercfg, ns, "nonSATokenSecret", DefaultServiceAccountName, serviceAccountUID) - indexer.Add(nonSATokenSecret) - - differentSAToken := newSecret(corev1.SecretTypeServiceAccountToken, ns, "differentSAToken", "someOtherSA", "someOtherUID") - indexer.Add(differentSAToken) - - matchingSAToken := newSecret(corev1.SecretTypeServiceAccountToken, ns, "matchingSAToken", DefaultServiceAccountName, serviceAccountUID) - indexer.Add(matchingSAToken) - - tokens, err := admit.getServiceAccountTokens(sa) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - - if len(tokens) != 1 { - names := make([]string, 0, len(tokens)) - for _, token := range tokens { - names = append(names, token.Name) - } - t.Fatalf("expected only 1 token, got %v", names) - } - if e, a := matchingSAToken.Name, tokens[0].Name; e != a { - t.Errorf("expected token %s, got %s", e, a) - } - }) -} - -func TestAutomountIsBackwardsCompatible(t *testing.T) { - ns := "myns" - tokenName := "token-name" - serviceAccountName := DefaultServiceAccountName - serviceAccountUID := "12345" - defaultTokenName := "default-token-abc123" - - expectedVolume := api.Volume{ - Name: defaultTokenName, - VolumeSource: api.VolumeSource{ - Secret: &api.SecretVolumeSource{ - SecretName: defaultTokenName, - }, - }, - } - expectedVolumeMount := api.VolumeMount{ - Name: defaultTokenName, - ReadOnly: true, - MountPath: DefaultAPITokenMountPath, - } - - admit := NewServiceAccount() - admit.generateName = testGenerateName - admit.boundServiceAccountTokenVolume = deprecationEnabledBoundTokenVolume - - informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) - admit.SetExternalKubeInformerFactory(informerFactory) - admit.MountServiceAccountToken = true - admit.RequireAPIToken = true - - // Add the default service account for the ns with a token into the cache - informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: serviceAccountName, - Namespace: ns, - UID: types.UID(serviceAccountUID), - }, - Secrets: []corev1.ObjectReference{ - {Name: tokenName}, - }, - }) - // Add a token for the service account into the cache - informerFactory.Core().V1().Secrets().Informer().GetStore().Add(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: tokenName, - Namespace: ns, - Annotations: map[string]string{ - corev1.ServiceAccountNameKey: serviceAccountName, - corev1.ServiceAccountUIDKey: serviceAccountUID, - }, - }, - Type: corev1.SecretTypeServiceAccountToken, - Data: map[string][]byte{ - api.ServiceAccountTokenKey: []byte("token-data"), - }, - }) - - pod := &api.Pod{ - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "c-1", - VolumeMounts: []api.VolumeMount{ - { - Name: defaultTokenName, - MountPath: DefaultAPITokenMountPath, - ReadOnly: true, - }, - }, - }, - }, - Volumes: []api.Volume{ - { - Name: defaultTokenName, - VolumeSource: api.VolumeSource{ - Secret: &api.SecretVolumeSource{ - SecretName: defaultTokenName, - }, - }, - }, - }, - }, - } - attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) - err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - if pod.Spec.ServiceAccountName != DefaultServiceAccountName { - t.Errorf("Expected service account %s assigned, got %s", DefaultServiceAccountName, pod.Spec.ServiceAccountName) - } - _ = expectedVolume - _ = expectedVolumeMount - if len(pod.Spec.Volumes) != 1 { - t.Fatalf("Expected 1 volume, got %d", len(pod.Spec.Volumes)) - } - if !reflect.DeepEqual(expectedVolume, pod.Spec.Volumes[0]) { - t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolume, pod.Spec.Volumes[0]) - } - if len(pod.Spec.Containers[0].VolumeMounts) != 1 { - t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.Containers[0].VolumeMounts)) - } - if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) { - t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) - } -} -func TestServiceAccountNameWithDotMount(t *testing.T) { - ns := "myns" - tokenName := "token.name-123" - serviceAccountName := "token.name" - serviceAccountUID := "12345" - - expectedVolume := api.Volume{ - Name: "token-name-123", - VolumeSource: api.VolumeSource{ - Secret: &api.SecretVolumeSource{ - SecretName: "token.name-123", - }, - }, - } - expectedVolumeMount := api.VolumeMount{ - Name: "token-name-123", - ReadOnly: true, - MountPath: DefaultAPITokenMountPath, - } - - admit := NewServiceAccount() - informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc()) - admit.SetExternalKubeInformerFactory(informerFactory) - admit.MountServiceAccountToken = true - admit.RequireAPIToken = true - - informerFactory.Core().V1().ServiceAccounts().Informer().GetStore().Add(&corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: serviceAccountName, - Namespace: ns, - UID: types.UID(serviceAccountUID), - }, - Secrets: []corev1.ObjectReference{ - {Name: tokenName}, - }, - }) - - informerFactory.Core().V1().Secrets().Informer().GetStore().Add(&corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: tokenName, - Namespace: ns, - Annotations: map[string]string{ - corev1.ServiceAccountNameKey: serviceAccountName, - corev1.ServiceAccountUIDKey: serviceAccountUID, - }, - }, - Type: corev1.SecretTypeServiceAccountToken, - Data: map[string][]byte{ - api.ServiceAccountTokenKey: []byte("token-data"), - }, - }) - - pod := &api.Pod{ - Spec: api.PodSpec{ - ServiceAccountName: serviceAccountName, - Containers: []api.Container{ - {Name: "container-1"}, - }, - }, - } - - attrs := admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) - if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { - t.Fatal(err) - } - - if len(pod.Spec.Volumes) != 1 { - t.Fatalf("Expected 1 volume, got %d", len(pod.Spec.Volumes)) - } - if !reflect.DeepEqual(expectedVolume, pod.Spec.Volumes[0]) { - t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolume, pod.Spec.Volumes[0]) - } - if len(pod.Spec.Containers[0].VolumeMounts) != 1 { - t.Fatalf("Expected 1 volume mount, got %d", len(pod.Spec.Containers[0].VolumeMounts)) - } - if !reflect.DeepEqual(expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) { - t.Fatalf("Expected\n\t%#v\ngot\n\t%#v", expectedVolumeMount, pod.Spec.Containers[0].VolumeMounts[0]) - } -} - func testGenerateName(n string) string { return n + "abc123" } var generatedVolumeName = testGenerateName(ServiceAccountVolumeName + "-") - -func testBoundServiceAccountTokenVolumePhases(t *testing.T, f func(*testing.T, func(*Plugin) *Plugin)) { - t.Run("BoundServiceAccountTokenVolume disabled", func(t *testing.T) { - f(t, func(s *Plugin) *Plugin { - s.boundServiceAccountTokenVolume = deprecationDisabledBoundTokenVolume - return s - }) - }) - - t.Run("BoundServiceAccountTokenVolume enabled", func(t *testing.T) { - f(t, func(s *Plugin) *Plugin { - s.boundServiceAccountTokenVolume = deprecationEnabledBoundTokenVolume - return s - }) - }) -} diff --git a/test/e2e/cloud/gcp/auth/service_account_admission_controller_migration.go b/test/e2e/cloud/gcp/auth/service_account_admission_controller_migration.go index 591eb16a2fc..e2a385c974b 100644 --- a/test/e2e/cloud/gcp/auth/service_account_admission_controller_migration.go +++ b/test/e2e/cloud/gcp/auth/service_account_admission_controller_migration.go @@ -46,8 +46,7 @@ var _ = SIGDescribe("ServiceAccount admission controller migration [Feature:Boun } testSuite.TestCases = append(testSuite.TestCases, serviceaccountAdmissionControllerMigrationTest) - extraEnvs := []string{"KUBE_FEATURE_GATES=BoundServiceAccountTokenVolume=true"} - upgradeFunc := common.ControlPlaneUpgradeFunc(f, upgCtx, serviceaccountAdmissionControllerMigrationTest, extraEnvs) + upgradeFunc := common.ControlPlaneUpgradeFunc(f, upgCtx, serviceaccountAdmissionControllerMigrationTest, nil) upgrades.RunUpgradeSuite(upgCtx, upgradeTests, testFrameworks, testSuite, upgrades.MasterUpgrade, upgradeFunc) }) }) diff --git a/test/integration/serviceaccount/service_account_test.go b/test/integration/serviceaccount/service_account_test.go index fd189d4bf03..08d5d3bebb3 100644 --- a/test/integration/serviceaccount/service_account_test.go +++ b/test/integration/serviceaccount/service_account_test.go @@ -31,7 +31,6 @@ import ( "time" v1 "k8s.io/api/core/v1" - apiequality "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" @@ -211,70 +210,30 @@ func TestServiceAccountTokenAutoMount(t *testing.T) { t.Fatalf("could not create namespace: %v", err) } - // Get default token - defaultTokenName, _, err := getReferencedServiceAccountToken(c, ns, serviceaccountadmission.DefaultServiceAccountName, true) - if err != nil { - t.Fatal(err) - } - // Pod to create protoPod := v1.Pod{ ObjectMeta: metav1.ObjectMeta{Name: "protopod"}, Spec: v1.PodSpec{ Containers: []v1.Container{ { - Name: "container-1", - Image: "container-1-image", - }, - { - Name: "container-2", - Image: "container-2-image", - VolumeMounts: []v1.VolumeMount{ - {Name: "empty-dir", MountPath: serviceaccountadmission.DefaultAPITokenMountPath}, - }, - }, - }, - Volumes: []v1.Volume{ - { - Name: "empty-dir", - VolumeSource: v1.VolumeSource{EmptyDir: &v1.EmptyDirVolumeSource{}}, + Name: "container", + Image: "container-image", }, }, }, } - // Pod we expect to get created - defaultMode := int32(0644) - expectedServiceAccount := serviceaccountadmission.DefaultServiceAccountName - expectedVolumes := append(protoPod.Spec.Volumes, v1.Volume{ - Name: defaultTokenName, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: defaultTokenName, - DefaultMode: &defaultMode, - }, - }, - }) - expectedContainer1VolumeMounts := []v1.VolumeMount{ - {Name: defaultTokenName, MountPath: serviceaccountadmission.DefaultAPITokenMountPath, ReadOnly: true}, - } - expectedContainer2VolumeMounts := protoPod.Spec.Containers[1].VolumeMounts - createdPod, err := c.CoreV1().Pods(ns).Create(context.TODO(), &protoPod, metav1.CreateOptions{}) if err != nil { t.Fatal(err) } + + expectedServiceAccount := serviceaccountadmission.DefaultServiceAccountName if createdPod.Spec.ServiceAccountName != expectedServiceAccount { t.Fatalf("Expected %s, got %s", expectedServiceAccount, createdPod.Spec.ServiceAccountName) } - if !apiequality.Semantic.DeepEqual(&expectedVolumes, &createdPod.Spec.Volumes) { - t.Fatalf("Expected\n\t%#v\n\tgot\n\t%#v", expectedVolumes, createdPod.Spec.Volumes) - } - if !apiequality.Semantic.DeepEqual(&expectedContainer1VolumeMounts, &createdPod.Spec.Containers[0].VolumeMounts) { - t.Fatalf("Expected\n\t%#v\n\tgot\n\t%#v", expectedContainer1VolumeMounts, createdPod.Spec.Containers[0].VolumeMounts) - } - if !apiequality.Semantic.DeepEqual(&expectedContainer2VolumeMounts, &createdPod.Spec.Containers[1].VolumeMounts) { - t.Fatalf("Expected\n\t%#v\n\tgot\n\t%#v", expectedContainer2VolumeMounts, createdPod.Spec.Containers[1].VolumeMounts) + if len(createdPod.Spec.Volumes) == 0 || createdPod.Spec.Volumes[0].Projected == nil { + t.Fatal("Expected projected volume for service account token inserted") } }