mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 19:31:44 +00:00
Merge pull request #122612 from mtardy/remove-scdeny
auth: remove SecurityContextDeny admission plugin
This commit is contained in:
commit
7bea14097b
@ -30,7 +30,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:-""}
|
||||
@ -509,14 +508,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}")
|
||||
|
@ -677,14 +677,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
|
||||
@ -1098,8 +1090,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},
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
@ -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{
|
||||
{},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user