Merge pull request #128396 from ritazh/deprecate-EnforceMountableSecretsAnnotation

deprecate EnforceMountableSecretsAnnotation in 1.32
This commit is contained in:
Kubernetes Prow Robot 2024-11-05 06:07:40 +00:00 committed by GitHub
commit bc79d3ba87
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 152 additions and 10 deletions

View File

@ -11303,7 +11303,7 @@
"description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"
},
"secrets": {
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". The \"kubernetes.io/enforce-mountable-secrets\" annotation is deprecated since v1.32. Prefer separate namespaces to isolate access to mounted secrets. This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference"
},

View File

@ -7436,7 +7436,7 @@
"description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"
},
"secrets": {
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". The \"kubernetes.io/enforce-mountable-secrets\" annotation is deprecated since v1.32. Prefer separate namespaces to isolate access to mounted secrets. This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"items": {
"allOf": [
{

View File

@ -4822,6 +4822,8 @@ type ServiceAccount struct {
// Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use.
// Pods are only limited to this list if this service account has a "kubernetes.io/enforce-mountable-secrets" annotation set to "true".
// The "kubernetes.io/enforce-mountable-secrets" annotation is deprecated since v1.32.
// Prefer separate namespaces to isolate access to mounted secrets.
// This field should not be used to find auto-generated service account token secrets for use outside of pods.
// Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created.
Secrets []ObjectReference

View File

@ -30736,7 +30736,7 @@ func schema_k8sio_api_core_v1_ServiceAccount(ref common.ReferenceCallback) commo
},
},
SchemaProps: spec.SchemaProps{
Description: "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
Description: "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". The \"kubernetes.io/enforce-mountable-secrets\" annotation is deprecated since v1.32. Prefer separate namespaces to isolate access to mounted secrets. This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{

View File

@ -18,6 +18,7 @@ package serviceaccount
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
@ -25,6 +26,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/validation"
sa "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
)
// strategy implements behavior for ServiceAccount objects
@ -50,7 +52,9 @@ func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorLis
}
// WarningsOnCreate returns warnings for the creation of the given object.
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return warnIfHasEnforceMountableSecretsAnnotation(obj.(*api.ServiceAccount), nil)
}
// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
@ -76,9 +80,24 @@ func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) fie
// WarningsOnUpdate returns warnings for the given update.
func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
return warnIfHasEnforceMountableSecretsAnnotation(obj.(*api.ServiceAccount), old.(*api.ServiceAccount))
}
func (strategy) AllowUnconditionalUpdate() bool {
return true
}
func warnIfHasEnforceMountableSecretsAnnotation(serviceAccount, oldServiceAccount *api.ServiceAccount) []string {
if oldServiceAccount != nil {
_, ok := oldServiceAccount.Annotations[sa.EnforceMountableSecretsAnnotation]
if ok {
// skip warning if request isn't newly setting the annotation
return nil
}
}
_, ok := serviceAccount.Annotations[sa.EnforceMountableSecretsAnnotation]
if ok {
return []string{fmt.Sprintf("metadata.annotations[%s]: deprecated in v1.32+; prefer separate namespaces to isolate access to mounted secrets", sa.EnforceMountableSecretsAnnotation)}
}
return nil
}

View File

@ -0,0 +1,63 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package serviceaccount
import (
"context"
"fmt"
"testing"
api "k8s.io/kubernetes/pkg/apis/core"
sa "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
)
func TestWarningsOnCreate(t *testing.T) {
ctx := context.TODO()
serviceAccount := &api.ServiceAccount{}
warnings := Strategy.WarningsOnCreate(ctx, serviceAccount)
if len(warnings) != 0 {
t.Errorf("expected no warnings, got %v", warnings)
}
serviceAccount.Annotations = map[string]string{sa.EnforceMountableSecretsAnnotation: "true"}
warnings = Strategy.WarningsOnCreate(ctx, serviceAccount)
if len(warnings) != 1 || warnings[0] != fmt.Sprintf("metadata.annotations[%s]: deprecated in v1.32+; prefer separate namespaces to isolate access to mounted secrets", sa.EnforceMountableSecretsAnnotation) {
t.Errorf("expected warnings, got %v", warnings)
}
}
func TestWarningsOnUpdate(t *testing.T) {
ctx := context.TODO()
serviceAccount := &api.ServiceAccount{}
oldServiceAccount := &api.ServiceAccount{}
warnings := Strategy.WarningsOnUpdate(ctx, serviceAccount, oldServiceAccount)
if len(warnings) != 0 {
t.Errorf("expected no warnings, got %v", warnings)
}
serviceAccount.Annotations = map[string]string{sa.EnforceMountableSecretsAnnotation: "true"}
warnings = Strategy.WarningsOnUpdate(ctx, serviceAccount, oldServiceAccount)
if len(warnings) != 1 || warnings[0] != fmt.Sprintf("metadata.annotations[%s]: deprecated in v1.32+; prefer separate namespaces to isolate access to mounted secrets", sa.EnforceMountableSecretsAnnotation) {
t.Errorf("expected warnings, got %v", warnings)
}
oldServiceAccount.Annotations = map[string]string{sa.EnforceMountableSecretsAnnotation: "true"}
warnings = Strategy.WarningsOnUpdate(ctx, serviceAccount, oldServiceAccount)
if len(warnings) != 0 {
t.Errorf("expected no warnings if request isn't newly setting the annotation, got %v", warnings)
}
}

View File

@ -5649,6 +5649,8 @@ message ServiceAccount {
// Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use.
// Pods are only limited to this list if this service account has a "kubernetes.io/enforce-mountable-secrets" annotation set to "true".
// The "kubernetes.io/enforce-mountable-secrets" annotation is deprecated since v1.32.
// Prefer separate namespaces to isolate access to mounted secrets.
// This field should not be used to find auto-generated service account token secrets for use outside of pods.
// Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created.
// More info: https://kubernetes.io/docs/concepts/configuration/secret

View File

@ -5736,6 +5736,8 @@ type ServiceAccount struct {
// Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use.
// Pods are only limited to this list if this service account has a "kubernetes.io/enforce-mountable-secrets" annotation set to "true".
// The "kubernetes.io/enforce-mountable-secrets" annotation is deprecated since v1.32.
// Prefer separate namespaces to isolate access to mounted secrets.
// This field should not be used to find auto-generated service account token secrets for use outside of pods.
// Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created.
// More info: https://kubernetes.io/docs/concepts/configuration/secret

View File

@ -2392,7 +2392,7 @@ func (Service) SwaggerDoc() map[string]string {
var map_ServiceAccount = map[string]string{
"": "ServiceAccount binds together: * a name, understood by users, and perhaps by peripheral systems, for an identity * a principal that can be authenticated and authorized * a set of secrets",
"metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata",
"secrets": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"secrets": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". The \"kubernetes.io/enforce-mountable-secrets\" annotation is deprecated since v1.32. Prefer separate namespaces to isolate access to mounted secrets. This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"imagePullSecrets": "ImagePullSecrets is a list of references to secrets in the same namespace to use for pulling any images in pods that reference this ServiceAccount. ImagePullSecrets are distinct from Secrets because Secrets can be mounted in the pod, but ImagePullSecrets are only accessed by the kubelet. More info: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod",
"automountServiceAccountToken": "AutomountServiceAccountToken indicates whether pods running as this service account should have an API token automatically mounted. Can be overridden at the pod level.",
}

View File

@ -9630,7 +9630,7 @@
"description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"
},
"secrets": {
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". The \"kubernetes.io/enforce-mountable-secrets\" annotation is deprecated since v1.32. Prefer separate namespaces to isolate access to mounted secrets. This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference"
},

View File

@ -10019,7 +10019,7 @@
"description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"
},
"secrets": {
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". The \"kubernetes.io/enforce-mountable-secrets\" annotation is deprecated since v1.32. Prefer separate namespaces to isolate access to mounted secrets. This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"items": {
"$ref": "#/definitions/io.k8s.api.core.v1.ObjectReference"
},

View File

@ -6851,7 +6851,7 @@
"description": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata"
},
"secrets": {
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". The \"kubernetes.io/enforce-mountable-secrets\" annotation is deprecated since v1.32. Prefer separate namespaces to isolate access to mounted secrets. This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"items": {
"allOf": [
{

View File

@ -26671,7 +26671,7 @@
]
},
"secrets": {
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"description": "Secrets is a list of the secrets in the same namespace that pods running using this ServiceAccount are allowed to use. Pods are only limited to this list if this service account has a \"kubernetes.io/enforce-mountable-secrets\" annotation set to \"true\". The \"kubernetes.io/enforce-mountable-secrets\" annotation is deprecated since v1.32. Prefer separate namespaces to isolate access to mounted secrets. This field should not be used to find auto-generated service account token secrets for use outside of pods. Instead, tokens can be requested directly using the TokenRequest API, or service account token secrets can be manually created. More info: https://kubernetes.io/docs/concepts/configuration/secret",
"type": "array",
"items": {
"default": {},

View File

@ -22,6 +22,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
@ -52,6 +53,7 @@ import (
"k8s.io/kubernetes/pkg/controlplane"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/serviceaccount"
svcacct "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
"k8s.io/kubernetes/test/integration/framework"
"k8s.io/kubernetes/test/utils/ktesting"
"k8s.io/utils/ptr"
@ -68,6 +70,58 @@ AwEHoUQDQgAEH6cuzP8XuD5wal6wf9M6xDljTOPLX2i8uIp/C/ASqiIGUeeKQtX0
tokenExpirationSeconds = 60*60 + 7
)
func TestServiceAccountAnnotationDeprecation(t *testing.T) {
tCtx := ktesting.Init(t)
// Start the server
kubeClient, kubeConfig, tearDownFn := framework.StartTestServer(tCtx, t, framework.TestServerSetup{})
defer tearDownFn()
ns := framework.CreateNamespaceOrDie(kubeClient, "myns", t)
defer framework.DeleteNamespaceOrDie(kubeClient, ns, t)
warningHandler := &recordingWarningHandler{}
configWithWarningHandler := rest.CopyConfig(kubeConfig)
configWithWarningHandler.WarningHandler = warningHandler
cs, err := clientset.NewForConfig(configWithWarningHandler)
if err != nil {
t.Fatalf("err: %v", err)
}
var (
saWithAnnotation = &v1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Name: "test-svcacct",
Namespace: ns.Name,
},
}
pod = &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: saWithAnnotation.Namespace,
},
Spec: v1.PodSpec{
ServiceAccountName: saWithAnnotation.Name,
Containers: []v1.Container{{Name: "test-container", Image: "nginx"}},
},
}
)
t.Run("service account with deprecated annotation", func(t *testing.T) {
warningHandler.clear()
// test warning is emitted when the service account has this deprecated annotation
saWithAnnotation.Annotations = map[string]string{svcacct.EnforceMountableSecretsAnnotation: "true"}
_, delSvcAcct := createDeleteSvcAcct(t, cs, saWithAnnotation)
defer delSvcAcct()
warningHandler.assertEqual(t, []string{fmt.Sprintf("metadata.annotations[%s]: deprecated in v1.32+; prefer separate namespaces to isolate access to mounted secrets", svcacct.EnforceMountableSecretsAnnotation)})
warningHandler.clear()
// no warning when a pod is created with a service account that has this deprecated annotation
_, delPod := createDeletePod(t, cs, pod)
defer delPod()
warningHandler.assertEqual(t, nil)
})
}
func TestServiceAccountTokenCreate(t *testing.T) {
const iss = "https://foo.bar.example.com"
aud := authenticator.Audiences{"api"}