diff --git a/test/e2e/auth/BUILD b/test/e2e/auth/BUILD index c7f31b02098..7ae174611a3 100644 --- a/test/e2e/auth/BUILD +++ b/test/e2e/auth/BUILD @@ -25,6 +25,7 @@ go_library( "//pkg/security/podsecuritypolicy/util:go_default_library", "//plugin/pkg/admission/serviceaccount:go_default_library", "//staging/src/k8s.io/api/apps/v1:go_default_library", + "//staging/src/k8s.io/api/authentication/v1:go_default_library", "//staging/src/k8s.io/api/batch/v1:go_default_library", "//staging/src/k8s.io/api/certificates/v1beta1:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", @@ -37,6 +38,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/types:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/uuid:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/apiserver/pkg/apis/audit:go_default_library", diff --git a/test/e2e/auth/service_accounts.go b/test/e2e/auth/service_accounts.go index 2941a65320d..5ba059475e1 100644 --- a/test/e2e/auth/service_accounts.go +++ b/test/e2e/auth/service_accounts.go @@ -18,11 +18,14 @@ package auth import ( "fmt" + "path" "time" + authenticationv1 "k8s.io/api/authentication/v1" "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/uuid" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount" @@ -163,23 +166,25 @@ var _ = SIGDescribe("ServiceAccounts", func() { Account mount path MUST be auto mounted to the Container. */ framework.ConformanceIt("should mount an API token into pods ", func() { - var tokenContent string var rootCAContent string + sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Create(&v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "mount-test"}}) + framework.ExpectNoError(err) + // Standard get, update retry loop framework.ExpectNoError(wait.Poll(time.Millisecond*500, framework.ServiceAccountProvisionTimeout, func() (bool, error) { By("getting the auto-created API token") - sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get("default", metav1.GetOptions{}) + sa, err := f.ClientSet.CoreV1().ServiceAccounts(f.Namespace.Name).Get("mount-test", metav1.GetOptions{}) if apierrors.IsNotFound(err) { - framework.Logf("default service account was not found") + framework.Logf("mount-test service account was not found") return false, nil } if err != nil { - framework.Logf("error getting default service account: %v", err) + framework.Logf("error getting mount-test service account: %v", err) return false, err } if len(sa.Secrets) == 0 { - framework.Logf("default service account has no secret references") + framework.Logf("mount-test service account has no secret references") return false, nil } for _, secretRef := range sa.Secrets { @@ -189,7 +194,6 @@ var _ = SIGDescribe("ServiceAccounts", func() { continue } if secret.Type == v1.SecretTypeServiceAccountToken { - tokenContent = string(secret.Data[v1.ServiceAccountTokenKey]) rootCAContent = string(secret.Data[v1.ServiceAccountRootCAKey]) return true, nil } @@ -199,49 +203,46 @@ var _ = SIGDescribe("ServiceAccounts", func() { return false, nil })) - pod := &v1.Pod{ + zero := int64(0) + pod, err := f.ClientSet.CoreV1().Pods(f.Namespace.Name).Create(&v1.Pod{ ObjectMeta: metav1.ObjectMeta{ - GenerateName: "pod-service-account-" + string(uuid.NewUUID()) + "-", + Name: "pod-service-account-" + string(uuid.NewUUID()), }, Spec: v1.PodSpec{ - Containers: []v1.Container{ - { - Name: "token-test", - Image: mountImage, - Args: []string{ - fmt.Sprintf("--file_content=%s/%s", serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountTokenKey), - }, - }, - { - Name: "root-ca-test", - Image: mountImage, - Args: []string{ - fmt.Sprintf("--file_content=%s/%s", serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountRootCAKey), - }, - }, - }, - RestartPolicy: v1.RestartPolicyNever, - }, - } - - pod.Spec.Containers = append(pod.Spec.Containers, v1.Container{ - Name: "namespace-test", - Image: mountImage, - Args: []string{ - fmt.Sprintf("--file_content=%s/%s", serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountNamespaceKey), + ServiceAccountName: sa.Name, + Containers: []v1.Container{{ + Name: "test", + Image: imageutils.GetE2EImage(imageutils.BusyBox), + Command: []string{"sleep", "100000"}, // run and pause + }}, + TerminationGracePeriodSeconds: &zero, // terminate quickly when deleted + RestartPolicy: v1.RestartPolicyNever, // never restart }, }) + framework.ExpectNoError(err) + framework.ExpectNoError(framework.WaitForPodRunningInNamespace(f.ClientSet, pod)) - f.TestContainerOutput("consume service account token", pod, 0, []string{ - fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountTokenKey, tokenContent), - }) - f.TestContainerOutput("consume service account root CA", pod, 1, []string{ - fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountRootCAKey, rootCAContent), - }) + mountedToken, err := f.ReadFileViaContainer(pod.Name, pod.Spec.Containers[0].Name, path.Join(serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountTokenKey)) + framework.ExpectNoError(err) + mountedCA, err := f.ReadFileViaContainer(pod.Name, pod.Spec.Containers[0].Name, path.Join(serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountRootCAKey)) + framework.ExpectNoError(err) + mountedNamespace, err := f.ReadFileViaContainer(pod.Name, pod.Spec.Containers[0].Name, path.Join(serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountNamespaceKey)) + framework.ExpectNoError(err) - f.TestContainerOutput("consume service account namespace", pod, 2, []string{ - fmt.Sprintf(`content of file "%s/%s": %s`, serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountNamespaceKey, f.Namespace.Name), - }) + // CA and namespace should be identical + Expect(mountedCA).To(Equal(rootCAContent)) + Expect(mountedNamespace).To(Equal(f.Namespace.Name)) + // Token should be a valid credential that identifies the pod's service account + tokenReview := &authenticationv1.TokenReview{Spec: authenticationv1.TokenReviewSpec{Token: mountedToken}} + tokenReview, err = f.ClientSet.AuthenticationV1().TokenReviews().Create(tokenReview) + framework.ExpectNoError(err) + Expect(tokenReview.Status.Authenticated).To(Equal(true)) + Expect(tokenReview.Status.Error).To(Equal("")) + Expect(tokenReview.Status.User.Username).To(Equal("system:serviceaccount:" + f.Namespace.Name + ":" + sa.Name)) + groups := sets.NewString(tokenReview.Status.User.Groups...) + Expect(groups.Has("system:authenticated")).To(Equal(true), fmt.Sprintf("expected system:authenticated group, had %v", groups.List())) + Expect(groups.Has("system:serviceaccounts")).To(Equal(true), fmt.Sprintf("expected system:serviceaccounts group, had %v", groups.List())) + Expect(groups.Has("system:serviceaccounts:"+f.Namespace.Name)).To(Equal(true), fmt.Sprintf("expected system:serviceaccounts:"+f.Namespace.Name+" group, had %v", groups.List())) }) /*