diff --git a/pkg/features/versioned_kube_features.go b/pkg/features/versioned_kube_features.go index 24f9438fa5d..b76f55ad9c7 100644 --- a/pkg/features/versioned_kube_features.go +++ b/pkg/features/versioned_kube_features.go @@ -639,6 +639,7 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate ServiceAccountTokenJTI: { {Version: version.MustParse("1.29"), Default: false, PreRelease: featuregate.Alpha}, {Version: version.MustParse("1.30"), Default: true, PreRelease: featuregate.Beta}, + {Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.34 }, ServiceAccountTokenNodeBinding: { @@ -649,11 +650,13 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate ServiceAccountTokenNodeBindingValidation: { {Version: version.MustParse("1.29"), Default: false, PreRelease: featuregate.Alpha}, {Version: version.MustParse("1.30"), Default: true, PreRelease: featuregate.Beta}, + {Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.34 }, ServiceAccountTokenPodNodeInfo: { {Version: version.MustParse("1.29"), Default: false, PreRelease: featuregate.Alpha}, {Version: version.MustParse("1.30"), Default: true, PreRelease: featuregate.Beta}, + {Version: version.MustParse("1.32"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.34 }, ServiceTrafficDistribution: { diff --git a/pkg/kubeapiserver/options/authentication_test.go b/pkg/kubeapiserver/options/authentication_test.go index beedd013d8e..842f4dea414 100644 --- a/pkg/kubeapiserver/options/authentication_test.go +++ b/pkg/kubeapiserver/options/authentication_test.go @@ -43,7 +43,6 @@ import ( "k8s.io/component-base/featuregate" featuregatetesting "k8s.io/component-base/featuregate/testing" openapicommon "k8s.io/kube-openapi/pkg/common" - kubefeatures "k8s.io/kubernetes/pkg/features" kubeauthenticator "k8s.io/kubernetes/pkg/kubeapiserver/authenticator" "k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/utils/pointer" @@ -237,12 +236,6 @@ func TestAuthenticationValidate(t *testing.T) { }, expectErr: "authentication-config file and oidc-* flags are mutually exclusive", }, - { - name: "fails to validate if ServiceAccountTokenNodeBindingValidation is disabled and ServiceAccountTokenNodeBinding is enabled", - enabledFeatures: []featuregate.Feature{kubefeatures.ServiceAccountTokenNodeBinding}, - disabledFeatures: []featuregate.Feature{kubefeatures.ServiceAccountTokenNodeBindingValidation}, - expectErr: "the \"ServiceAccountTokenNodeBinding\" feature gate can only be enabled if the \"ServiceAccountTokenNodeBindingValidation\" feature gate is also enabled", - }, { name: "test when authentication config file and anonymous-auth flags are set AnonymousAuthConfigurableEndpoints disabled", disabledFeatures: []featuregate.Feature{features.AnonymousAuthConfigurableEndpoints}, @@ -489,7 +482,6 @@ func TestBuiltInAuthenticationOptionsAddFlags(t *testing.T) { } func TestWithTokenGetterFunction(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, kubefeatures.ServiceAccountTokenNodeBindingValidation, false) fakeClientset := fake.NewSimpleClientset() versionedInformer := informers.NewSharedInformerFactory(fakeClientset, 0) { diff --git a/pkg/serviceaccount/claims_test.go b/pkg/serviceaccount/claims_test.go index c85e0c334bd..3cc869f4ff0 100644 --- a/pkg/serviceaccount/claims_test.go +++ b/pkg/serviceaccount/claims_test.go @@ -89,7 +89,7 @@ func TestClaims(t *testing.T) { sc *jwt.Claims pc *privateClaims - featureJTI, featurePodNodeInfo, featureNodeBinding bool + featureNodeBinding bool }{ { // pod and secret @@ -115,6 +115,7 @@ func TestClaims(t *testing.T) { IssuedAt: jwt.NewNumericDate(time.Unix(1514764800, 0)), NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)), Expiry: jwt.NewNumericDate(time.Unix(1514764800+100, 0)), + ID: "fixed", }, pc: &privateClaims{ Kubernetes: kubernetes{ @@ -138,6 +139,7 @@ func TestClaims(t *testing.T) { IssuedAt: jwt.NewNumericDate(time.Unix(1514764800, 0)), NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)), Expiry: jwt.NewNumericDate(time.Unix(1514764800+100, 0)), + ID: "fixed", }, pc: &privateClaims{ Kubernetes: kubernetes{ @@ -160,6 +162,7 @@ func TestClaims(t *testing.T) { IssuedAt: jwt.NewNumericDate(time.Unix(1514764800, 0)), NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)), Expiry: jwt.NewNumericDate(time.Unix(1514764800+100, 0)), + ID: "fixed", }, pc: &privateClaims{ Kubernetes: kubernetes{ @@ -182,6 +185,7 @@ func TestClaims(t *testing.T) { IssuedAt: jwt.NewNumericDate(time.Unix(1514764800, 0)), NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)), Expiry: jwt.NewNumericDate(time.Unix(1514764800+60*60*24, 0)), + ID: "fixed", }, pc: &privateClaims{ Kubernetes: kubernetes{ @@ -202,30 +206,6 @@ func TestClaims(t *testing.T) { aud: nil, err: "token bound to Node object requested, but \"ServiceAccountTokenNodeBinding\" feature gate is disabled", }, - { - // node & pod with feature gate disabled - sa: sa, - node: node, - pod: pod, - // really fast - exp: 0, - // nil audience - aud: nil, - - sc: &jwt.Claims{ - Subject: "system:serviceaccount:myns:mysvcacct", - IssuedAt: jwt.NewNumericDate(time.Unix(1514764800, 0)), - NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)), - Expiry: jwt.NewNumericDate(time.Unix(1514764800, 0)), - }, - pc: &privateClaims{ - Kubernetes: kubernetes{ - Namespace: "myns", - Pod: &ref{Name: "mypod", UID: "mypod-uid"}, - Svcacct: ref{Name: "mysvcacct", UID: "mysvcacct-uid"}, - }, - }, - }, { // node alone sa: sa, @@ -242,6 +222,7 @@ func TestClaims(t *testing.T) { IssuedAt: jwt.NewNumericDate(time.Unix(1514764800, 0)), NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)), Expiry: jwt.NewNumericDate(time.Unix(1514764800, 0)), + ID: "fixed", }, pc: &privateClaims{ Kubernetes: kubernetes{ @@ -256,8 +237,6 @@ func TestClaims(t *testing.T) { sa: sa, pod: pod, node: node, - // enable embedding pod node info feature - featurePodNodeInfo: true, // really fast exp: 0, // nil audience @@ -268,6 +247,7 @@ func TestClaims(t *testing.T) { IssuedAt: jwt.NewNumericDate(time.Unix(1514764800, 0)), NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)), Expiry: jwt.NewNumericDate(time.Unix(1514764800, 0)), + ID: "fixed", }, pc: &privateClaims{ Kubernetes: kubernetes{ @@ -294,8 +274,6 @@ func TestClaims(t *testing.T) { { // ensure JTI is set sa: sa, - // enable setting JTI feature - featureJTI: true, // really fast exp: 0, // nil audience @@ -342,9 +320,7 @@ func TestClaims(t *testing.T) { } // set feature flags for the duration of the test case - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenJTI, c.featureJTI) featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenNodeBinding, c.featureNodeBinding) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenPodNodeInfo, c.featurePodNodeInfo) sc, pc, err := Claims(c.sa, c.pod, c.sec, c.node, c.exp, c.warnafter, c.aud) if err != nil && err.Error() != c.err { @@ -376,8 +352,6 @@ type claimTestCase struct { expiry jwt.NumericDate notBefore jwt.NumericDate expectErr string - - featureNodeBindingValidation bool } func TestValidatePrivateClaims(t *testing.T) { @@ -458,11 +432,10 @@ func TestValidatePrivateClaims(t *testing.T) { expectErr: "service account token has been invalidated", }, { - name: "missing node", - getter: fakeGetter{serviceAccount, nil, nil, nil}, - private: &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Node: &ref{Name: "nodename", UID: "nodeuid"}, Namespace: "ns"}}, - expectErr: "service account token has been invalidated", - featureNodeBindingValidation: true, + name: "missing node", + getter: fakeGetter{serviceAccount, nil, nil, nil}, + private: &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Node: &ref{Name: "nodename", UID: "nodeuid"}, Namespace: "ns"}}, + expectErr: "service account token has been invalidated", }, { name: "different uid serviceaccount", @@ -522,11 +495,10 @@ func TestValidatePrivateClaims(t *testing.T) { expectErr: deletedErr, }, claimTestCase{ - name: deletionTestCase.name + " node", - getter: fakeGetter{serviceAccount, nil, nil, deletedNode}, - private: &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Node: &ref{Name: "nodename", UID: "nodeuid"}, Namespace: "ns"}}, - expectErr: deletedErr, - featureNodeBindingValidation: true, + name: deletionTestCase.name + " node", + getter: fakeGetter{serviceAccount, nil, nil, deletedNode}, + private: &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Node: &ref{Name: "nodename", UID: "nodeuid"}, Namespace: "ns"}}, + expectErr: deletedErr, }, ) } @@ -539,8 +511,6 @@ func TestValidatePrivateClaims(t *testing.T) { expiry = tc.expiry } - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenNodeBindingValidation, tc.featureNodeBindingValidation) - _, err := v.Validate(context.Background(), "", &jwt.Claims{Expiry: &expiry, NotBefore: &tc.notBefore}, tc.private) if len(tc.expectErr) > 0 { if errStr := errString(err); tc.expectErr != errStr { diff --git a/test/e2e/auth/per_node_update.go b/test/e2e/auth/per_node_update.go index 34df998c1bc..4d3ddf5487d 100644 --- a/test/e2e/auth/per_node_update.go +++ b/test/e2e/auth/per_node_update.go @@ -24,6 +24,7 @@ import ( g "github.com/onsi/ginkgo/v2" o "github.com/onsi/gomega" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" authenticationv1 "k8s.io/api/authentication/v1" v1 "k8s.io/api/core/v1" @@ -34,7 +35,6 @@ import ( "k8s.io/client-go/kubernetes" cgoscheme "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" - "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/test/e2e/framework" e2epod "k8s.io/kubernetes/test/e2e/framework/pod" imageutils "k8s.io/kubernetes/test/utils/image" @@ -52,7 +52,7 @@ var ( perNodeCheckValidatingAdmissionPolicyBinding string ) -var _ = SIGDescribe("ValidatingAdmissionPolicy", framework.WithFeatureGate(features.ServiceAccountTokenNodeBindingValidation), func() { +var _ = SIGDescribe("ValidatingAdmissionPolicy", func() { defer g.GinkgoRecover() f := framework.NewDefaultFramework("node-authn") f.NamespacePodSecurityLevel = admissionapi.LevelRestricted diff --git a/test/e2e/auth/service_accounts.go b/test/e2e/auth/service_accounts.go index 3b7c3f9ba58..f517fc6cfcc 100644 --- a/test/e2e/auth/service_accounts.go +++ b/test/e2e/auth/service_accounts.go @@ -100,6 +100,12 @@ var _ = SIGDescribe("ServiceAccounts", func() { framework.ExpectNoError(err) framework.ExpectNoError(e2epod.WaitForPodRunningInNamespace(ctx, f.ClientSet, pod)) + // Read the running pod to get the current node name + pod, err = f.ClientSet.CoreV1().Pods(f.Namespace.Name).Get(ctx, pod.Name, metav1.GetOptions{}) + framework.ExpectNoError(err) + node, err := f.ClientSet.CoreV1().Nodes().Get(ctx, pod.Spec.NodeName, metav1.GetOptions{}) + framework.ExpectNoError(err) + tk := e2ekubectl.NewTestKubeconfig(framework.TestContext.CertDir, framework.TestContext.Host, framework.TestContext.KubeConfig, framework.TestContext.KubeContext, framework.TestContext.KubectlPath, f.Namespace.Name) mountedToken, err := tk.ReadFileViaContainer(pod.Name, pod.Spec.Containers[0].Name, path.Join(serviceaccount.DefaultAPITokenMountPath, v1.ServiceAccountTokenKey)) framework.ExpectNoError(err) @@ -133,6 +139,29 @@ var _ = SIGDescribe("ServiceAccounts", func() { if !groups.Has("system:serviceaccounts:" + f.Namespace.Name) { framework.Failf("expected system:serviceaccounts:%s group, had %v", f.Namespace.Name, groups.List()) } + + credentialID, ok := tokenReview.Status.User.Extra["authentication.kubernetes.io/credential-id"] + if !ok || len(credentialID) != 1 || !strings.HasPrefix(credentialID[0], "JTI=") { + framework.Failf("expected single authentication.kubernetes.io/credential-id extra info item starting with 'JTI=', got %v", credentialID) + } + + podName, ok := tokenReview.Status.User.Extra["authentication.kubernetes.io/pod-name"] + if !ok || len(podName) != 1 || podName[0] != pod.Name { + framework.Failf("expected single authentication.kubernetes.io/pod-name extra info item matching %v, got %v", pod.Name, podName) + } + podUID, ok := tokenReview.Status.User.Extra["authentication.kubernetes.io/pod-uid"] + if !ok || len(podUID) != 1 || podUID[0] != string(pod.UID) { + framework.Failf("expected single authentication.kubernetes.io/pod-uid extra info item matching %v, got %v", pod.UID, podUID) + } + + nodeName, ok := tokenReview.Status.User.Extra["authentication.kubernetes.io/node-name"] + if !ok || len(nodeName) != 1 || nodeName[0] != node.Name { + framework.Failf("expected single authentication.kubernetes.io/node-name extra info item matching %v, got %v", node.Name, nodeName) + } + nodeUID, ok := tokenReview.Status.User.Extra["authentication.kubernetes.io/node-uid"] + if !ok || len(nodeUID) != 1 || nodeUID[0] != string(node.UID) { + framework.Failf("expected single authentication.kubernetes.io/node-uid extra info item matching %v, got %v", node.UID, nodeUID) + } }) /* diff --git a/test/featuregates_linter/test_data/versioned_feature_list.yaml b/test/featuregates_linter/test_data/versioned_feature_list.yaml index b247156a595..7c12992c335 100644 --- a/test/featuregates_linter/test_data/versioned_feature_list.yaml +++ b/test/featuregates_linter/test_data/versioned_feature_list.yaml @@ -1036,6 +1036,10 @@ lockToDefault: false preRelease: Beta version: "1.30" + - default: true + lockToDefault: true + preRelease: GA + version: "1.32" - name: ServiceAccountTokenNodeBinding versionedSpecs: - default: false @@ -1056,6 +1060,10 @@ lockToDefault: false preRelease: Beta version: "1.30" + - default: true + lockToDefault: true + preRelease: GA + version: "1.32" - name: ServiceAccountTokenPodNodeInfo versionedSpecs: - default: false @@ -1066,6 +1074,10 @@ lockToDefault: false preRelease: Beta version: "1.30" + - default: true + lockToDefault: true + preRelease: GA + version: "1.32" - name: ServiceTrafficDistribution versionedSpecs: - default: false diff --git a/test/integration/auth/svcaccttoken_test.go b/test/integration/auth/svcaccttoken_test.go index f05a9dfe34a..0c31f285a60 100644 --- a/test/integration/auth/svcaccttoken_test.go +++ b/test/integration/auth/svcaccttoken_test.go @@ -248,8 +248,6 @@ func TestServiceAccountTokenCreate(t *testing.T) { }) t.Run("bound to service account and pod", func(t *testing.T) { - // Disable embedding pod's node info - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenPodNodeInfo, false) treq := &authenticationv1.TokenRequest{ Spec: authenticationv1.TokenRequestSpec{ Audiences: []string{"api"},