diff --git a/hack/local-up-cluster.sh b/hack/local-up-cluster.sh index 0200ef0e969..33a0ba15905 100755 --- a/hack/local-up-cluster.sh +++ b/hack/local-up-cluster.sh @@ -27,7 +27,6 @@ DOCKER_OPTS=${DOCKER_OPTS:-""} export DOCKER=(docker "${DOCKER_OPTS[@]}") DOCKER_ROOT=${DOCKER_ROOT:-""} ALLOW_PRIVILEGED=${ALLOW_PRIVILEGED:-""} -DENY_SECURITY_CONTEXT_ADMISSION=${DENY_SECURITY_CONTEXT_ADMISSION:-""} RUNTIME_CONFIG=${RUNTIME_CONFIG:-""} KUBELET_AUTHORIZATION_WEBHOOK=${KUBELET_AUTHORIZATION_WEBHOOK:-""} KUBELET_AUTHENTICATION_WEBHOOK=${KUBELET_AUTHENTICATION_WEBHOOK:-""} @@ -486,14 +485,6 @@ function generate_kubelet_certs { } function start_apiserver { - security_admission="" - if [[ -n "${DENY_SECURITY_CONTEXT_ADMISSION}" ]]; then - security_admission=",SecurityContextDeny" - fi - - # Append security_admission plugin - ENABLE_ADMISSION_PLUGINS="${ENABLE_ADMISSION_PLUGINS}${security_admission}" - authorizer_args=() if [[ -n "${AUTHORIZATION_CONFIG:-}" ]]; then authorizer_args+=("--authorization-config=${AUTHORIZATION_CONFIG}") diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index 980c75cd5e9..4f0bb99c3d7 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -673,14 +673,6 @@ const ( // which benefits to reduce the useless requeueing. SchedulerQueueingHints featuregate.Feature = "SchedulerQueueingHints" - // owner: @mtardy - // alpha: v1.0 - // - // Putting this admission plugin behind a feature gate is part of the - // deprecation process. For details about the removal see: - // https://github.com/kubernetes/kubernetes/issues/111516 - SecurityContextDeny featuregate.Feature = "SecurityContextDeny" - // owner: @atosatto @yuanchen8911 // kep: http://kep.k8s.io/3902 // beta: v1.29 @@ -1084,8 +1076,6 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS SchedulerQueueingHints: {Default: false, PreRelease: featuregate.Beta}, - SecurityContextDeny: {Default: false, PreRelease: featuregate.Alpha}, - SeparateTaintEvictionController: {Default: true, PreRelease: featuregate.Beta}, ServiceAccountTokenJTI: {Default: false, PreRelease: featuregate.Alpha}, diff --git a/pkg/kubeapiserver/options/plugins.go b/pkg/kubeapiserver/options/plugins.go index 542da72b9b8..0d4556c318f 100644 --- a/pkg/kubeapiserver/options/plugins.go +++ b/pkg/kubeapiserver/options/plugins.go @@ -47,7 +47,6 @@ import ( podpriority "k8s.io/kubernetes/plugin/pkg/admission/priority" "k8s.io/kubernetes/plugin/pkg/admission/runtimeclass" "k8s.io/kubernetes/plugin/pkg/admission/security/podsecurity" - "k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny" "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount" "k8s.io/kubernetes/plugin/pkg/admission/storage/persistentvolume/label" "k8s.io/kubernetes/plugin/pkg/admission/storage/persistentvolume/resize" @@ -68,7 +67,6 @@ var AllOrderedPlugins = []string{ autoprovision.PluginName, // NamespaceAutoProvision lifecycle.PluginName, // NamespaceLifecycle exists.PluginName, // NamespaceExists - scdeny.PluginName, // SecurityContextDeny antiaffinity.PluginName, // LimitPodHardAntiAffinityTopology limitranger.PluginName, // LimitRanger serviceaccount.PluginName, // ServiceAccount @@ -132,7 +130,6 @@ func RegisterAllAdmissionPlugins(plugins *admission.Plugins) { resourcequota.Register(plugins) podsecurity.Register(plugins) podpriority.Register(plugins) - scdeny.Register(plugins) serviceaccount.Register(plugins) setdefault.Register(plugins) resize.Register(plugins) diff --git a/plugin/pkg/admission/securitycontext/scdeny/admission.go b/plugin/pkg/admission/securitycontext/scdeny/admission.go deleted file mode 100644 index 160724f7649..00000000000 --- a/plugin/pkg/admission/securitycontext/scdeny/admission.go +++ /dev/null @@ -1,115 +0,0 @@ -/* -Copyright 2014 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 scdeny - -import ( - "context" - "fmt" - "io" - - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apiserver/pkg/admission" - utilfeature "k8s.io/apiserver/pkg/util/feature" - "k8s.io/klog/v2" - api "k8s.io/kubernetes/pkg/apis/core" - "k8s.io/kubernetes/pkg/features" -) - -// PluginName indicates name of admission plugin. -const PluginName = "SecurityContextDeny" - -const docLink = "https://k8s.io/docs/reference/access-authn-authz/admission-controllers/#securitycontextdeny" - -// Register registers a plugin -func Register(plugins *admission.Plugins) { - plugins.Register(PluginName, func(config io.Reader) (admission.Interface, error) { - if utilfeature.DefaultFeatureGate.Enabled(features.SecurityContextDeny) { - return NewSecurityContextDeny(), nil - } else { - return nil, fmt.Errorf("%s admission controller is an alpha feature, planned to be removed, and requires the SecurityContextDeny feature gate to be enabled, see %s for more information", PluginName, docLink) - } - }) -} - -// Plugin implements admission.Interface. -type Plugin struct { - *admission.Handler -} - -var _ admission.ValidationInterface = &Plugin{} - -// NewSecurityContextDeny creates a new instance of the SecurityContextDeny admission controller -func NewSecurityContextDeny() *Plugin { - // DEPRECATED: SecurityContextDeny will be removed in favor of PodSecurity admission. - klog.Warningf("%s admission controller is deprecated. "+ - "Please remove this controller from your configuration files and scripts. "+ - "See %s for more information.", - PluginName, docLink) - return &Plugin{ - Handler: admission.NewHandler(admission.Create, admission.Update), - } -} - -// Validate will deny any pod that defines SupplementalGroups, SELinuxOptions, RunAsUser or FSGroup -func (p *Plugin) Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error) { - if a.GetSubresource() != "" || a.GetResource().GroupResource() != api.Resource("pods") { - return nil - } - - pod, ok := a.GetObject().(*api.Pod) - if !ok { - return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted") - } - - if pod.Spec.SecurityContext != nil { - if pod.Spec.SecurityContext.SupplementalGroups != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.SupplementalGroups is forbidden")) - } - if pod.Spec.SecurityContext.SELinuxOptions != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.SELinuxOptions is forbidden")) - } - if pod.Spec.SecurityContext.RunAsUser != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.RunAsUser is forbidden")) - } - if pod.Spec.SecurityContext.FSGroup != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.FSGroup is forbidden")) - } - } - - for _, v := range pod.Spec.InitContainers { - if v.SecurityContext != nil { - if v.SecurityContext.SELinuxOptions != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("SecurityContext.SELinuxOptions is forbidden")) - } - if v.SecurityContext.RunAsUser != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("SecurityContext.RunAsUser is forbidden")) - } - } - } - - for _, v := range pod.Spec.Containers { - if v.SecurityContext != nil { - if v.SecurityContext.SELinuxOptions != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("SecurityContext.SELinuxOptions is forbidden")) - } - if v.SecurityContext.RunAsUser != nil { - return apierrors.NewForbidden(a.GetResource().GroupResource(), pod.Name, fmt.Errorf("SecurityContext.RunAsUser is forbidden")) - } - } - } - return nil -} diff --git a/plugin/pkg/admission/securitycontext/scdeny/admission_test.go b/plugin/pkg/admission/securitycontext/scdeny/admission_test.go deleted file mode 100644 index 210d2844705..00000000000 --- a/plugin/pkg/admission/securitycontext/scdeny/admission_test.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright 2014 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 scdeny - -import ( - "context" - "testing" - - "k8s.io/apiserver/pkg/admission" - api "k8s.io/kubernetes/pkg/apis/core" -) - -// ensures the SecurityContext is denied if it defines anything more than Caps or Privileged -func TestAdmission(t *testing.T) { - handler := NewSecurityContextDeny() - - runAsUser := int64(1) - priv := true - - cases := []struct { - name string - sc *api.SecurityContext - podSc *api.PodSecurityContext - expectError bool - }{ - { - name: "unset", - }, - { - name: "empty container.SecurityContext", - sc: &api.SecurityContext{}, - }, - { - name: "empty pod.Spec.SecurityContext", - podSc: &api.PodSecurityContext{}, - }, - { - name: "valid container.SecurityContext", - sc: &api.SecurityContext{Privileged: &priv, Capabilities: &api.Capabilities{}}, - }, - { - name: "valid pod.Spec.SecurityContext", - podSc: &api.PodSecurityContext{}, - }, - { - name: "container.SecurityContext.RunAsUser", - sc: &api.SecurityContext{RunAsUser: &runAsUser}, - expectError: true, - }, - { - name: "container.SecurityContext.SELinuxOptions", - sc: &api.SecurityContext{SELinuxOptions: &api.SELinuxOptions{}}, - expectError: true, - }, - { - name: "pod.Spec.SecurityContext.RunAsUser", - podSc: &api.PodSecurityContext{RunAsUser: &runAsUser}, - expectError: true, - }, - { - name: "pod.Spec.SecurityContext.SELinuxOptions", - podSc: &api.PodSecurityContext{SELinuxOptions: &api.SELinuxOptions{}}, - expectError: true, - }, - } - - for _, tc := range cases { - p := pod() - p.Spec.SecurityContext = tc.podSc - p.Spec.Containers[0].SecurityContext = tc.sc - - err := handler.Validate(context.TODO(), admission.NewAttributesRecord(p, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil, false, nil), nil) - if err != nil && !tc.expectError { - t.Errorf("%v: unexpected error: %v", tc.name, err) - } else if err == nil && tc.expectError { - t.Errorf("%v: expected error", tc.name) - } - - // verify init containers are also checked - p = pod() - p.Spec.SecurityContext = tc.podSc - p.Spec.Containers[0].SecurityContext = tc.sc - p.Spec.InitContainers = p.Spec.Containers - p.Spec.Containers = nil - - err = handler.Validate(context.TODO(), admission.NewAttributesRecord(p, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil, false, nil), nil) - if err != nil && !tc.expectError { - t.Errorf("%v: unexpected error: %v", tc.name, err) - } else if err == nil && tc.expectError { - t.Errorf("%v: expected error", tc.name) - } - } -} - -func TestPodSecurityContextAdmission(t *testing.T) { - handler := NewSecurityContextDeny() - pod := api.Pod{ - Spec: api.PodSpec{ - Containers: []api.Container{ - {}, - }, - }, - } - - fsGroup := int64(1001) - - tests := []struct { - securityContext api.PodSecurityContext - errorExpected bool - }{ - { - securityContext: api.PodSecurityContext{}, - errorExpected: false, - }, - { - securityContext: api.PodSecurityContext{ - SupplementalGroups: []int64{int64(1234)}, - }, - errorExpected: true, - }, - { - securityContext: api.PodSecurityContext{ - FSGroup: &fsGroup, - }, - errorExpected: true, - }, - } - for _, test := range tests { - pod.Spec.SecurityContext = &test.securityContext - err := handler.Validate(context.TODO(), admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil, false, nil), nil) - - if test.errorExpected && err == nil { - t.Errorf("Expected error for security context %+v but did not get an error", test.securityContext) - } - - if !test.errorExpected && err != nil { - t.Errorf("Unexpected error %v for security context %+v", err, test.securityContext) - } - } -} - -func TestHandles(t *testing.T) { - handler := NewSecurityContextDeny() - tests := map[admission.Operation]bool{ - admission.Update: true, - admission.Create: true, - admission.Delete: false, - admission.Connect: false, - } - for op, expected := range tests { - result := handler.Handles(op) - if result != expected { - t.Errorf("Unexpected result for operation %s: %v\n", op, result) - } - } -} - -func pod() *api.Pod { - return &api.Pod{ - Spec: api.PodSpec{ - Containers: []api.Container{ - {}, - }, - }, - } -} diff --git a/test/fixtures/doc-yaml/admin/high-availability/kube-apiserver.yaml b/test/fixtures/doc-yaml/admin/high-availability/kube-apiserver.yaml index 472677b4f4c..dbc5ea7a0dd 100644 --- a/test/fixtures/doc-yaml/admin/high-availability/kube-apiserver.yaml +++ b/test/fixtures/doc-yaml/admin/high-availability/kube-apiserver.yaml @@ -11,7 +11,7 @@ spec: - /bin/sh - -c - /usr/local/bin/kube-apiserver --address=127.0.0.1 --etcd-servers=http://127.0.0.1:4001 - --cloud-provider=gce --admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota + --cloud-provider=gce --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota --service-cluster-ip-range=10.0.0.0/16 --client-ca-file=/srv/kubernetes/ca.crt --cluster-name=e2e-test-bburns --tls-cert-file=/srv/kubernetes/server.cert --tls-private-key-file=/srv/kubernetes/server.key