Skip to content

make admission and pod-security-admission checks be informed by emulation version #133178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion pkg/controlplane/apiserver/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,17 @@ import (
"k8s.io/apiserver/pkg/server/egressselector"
"k8s.io/apiserver/pkg/server/filters"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/apiserver/pkg/util/compatibility"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/pkg/util/openapi"
utilpeerproxy "k8s.io/apiserver/pkg/util/peerproxy"
"k8s.io/client-go/dynamic"
clientgoinformers "k8s.io/client-go/informers"
clientgoclientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/util/keyutil"
basecompatibility "k8s.io/component-base/compatibility"
aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver"
openapicommon "k8s.io/kube-openapi/pkg/common"

"k8s.io/kubernetes/pkg/api/legacyscheme"
controlplaneadmission "k8s.io/kubernetes/pkg/controlplane/apiserver/admission"
"k8s.io/kubernetes/pkg/controlplane/apiserver/options"
Expand Down Expand Up @@ -379,6 +380,7 @@ func CreateConfig(
clientgoExternalClient,
dynamicExternalClient,
utilfeature.DefaultFeatureGate,
compatibility.DefaultComponentGlobalsRegistry.EffectiveVersionFor(basecompatibility.DefaultKubeComponent),
append(genericInitializers, additionalInitializers...)...,
)
if err != nil {
Expand Down
11 changes: 0 additions & 11 deletions pkg/features/kube_features.go
Original file line number Diff line number Diff line change
Expand Up @@ -719,12 +719,6 @@ const (
// Denies pod admission if static pods reference other API objects.
PreventStaticPodAPIReferences featuregate.Feature = "PreventStaticPodAPIReferences"

// owner: @tssurya
// kep: https://kep.k8s.io/4559
//
// Enables probe host enforcement for Pod Security Standards.
ProbeHostPodSecurityStandards featuregate.Feature = "ProbeHostPodSecurityStandards"

// owner: @jessfraz
//
// Enables control over ProcMountType for containers.
Expand Down Expand Up @@ -1552,11 +1546,6 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.Beta},
},

// Policy is GA in first release, this gate only exists to disable the enforcement when emulating older minors
ProbeHostPodSecurityStandards: {
{Version: version.MustParse("1.34"), Default: true, PreRelease: featuregate.GA, LockToDefault: true},
},

ProcMountType: {
{Version: version.MustParse("1.12"), Default: false, PreRelease: featuregate.Alpha},
{Version: version.MustParse("1.31"), Default: false, PreRelease: featuregate.Beta},
Expand Down
5 changes: 4 additions & 1 deletion pkg/kubeapiserver/options/admission.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"strings"

"github.com/spf13/pflag"

"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"

Expand All @@ -29,6 +30,7 @@ import (
"k8s.io/apiserver/pkg/server"
genericoptions "k8s.io/apiserver/pkg/server/options"
"k8s.io/client-go/informers"
"k8s.io/component-base/compatibility"
"k8s.io/component-base/featuregate"
)

Expand Down Expand Up @@ -116,6 +118,7 @@ func (a *AdmissionOptions) ApplyTo(
kubeClient kubernetes.Interface,
dynamicClient dynamic.Interface,
features featuregate.FeatureGate,
effectiveVersion compatibility.EffectiveVersion,
pluginInitializers ...admission.PluginInitializer,
) error {
if a == nil {
Expand All @@ -127,7 +130,7 @@ func (a *AdmissionOptions) ApplyTo(
a.GenericAdmission.EnablePlugins, a.GenericAdmission.DisablePlugins = computePluginNames(a.PluginNames, a.GenericAdmission.RecommendedPluginOrder)
}

return a.GenericAdmission.ApplyTo(c, informers, kubeClient, dynamicClient, features, pluginInitializers...)
return a.GenericAdmission.ApplyTo(c, informers, kubeClient, dynamicClient, features, effectiveVersion, pluginInitializers...)
}

// explicitly disable all plugins that are not in the enabled list
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ func TestHandles(t *testing.T) {
// newHandlerForTest returns a handler configured for testing.
func newHandlerForTest() (*Plugin, error) {
handler := NewDefaultTolerationSeconds()
pluginInitializer := initializer.New(nil, nil, nil, nil, nil, nil, nil)
pluginInitializer := initializer.New(nil, nil, nil, nil, nil, nil, nil, nil)
pluginInitializer.Initialize(handler)
return handler, admission.ValidateInitialization(handler)
}
2 changes: 1 addition & 1 deletion plugin/pkg/admission/gc/gc_admission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) {
return nil, fmt.Errorf("unexpected error while constructing resource list from fake discovery client: %v", err)
}
restMapper := restmapper.NewDiscoveryRESTMapper(restMapperRes)
genericPluginInitializer := initializer.New(nil, nil, nil, fakeAuthorizer{}, nil, nil, restMapper)
genericPluginInitializer := initializer.New(nil, nil, nil, fakeAuthorizer{}, nil, nil, nil, restMapper)
pluginInitializer := controlplaneadmission.NewPluginInitializer(nil, nil)
initializersChain := apiserveradmission.PluginInitializers{}
initializersChain = append(initializersChain, genericPluginInitializer)
Expand Down
2 changes: 1 addition & 1 deletion plugin/pkg/admission/limitranger/admission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -891,7 +891,7 @@ func newHandlerForTest(c clientset.Interface) (*LimitRanger, informers.SharedInf
if err != nil {
return nil, f, err
}
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil)
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil, nil)
pluginInitializer.Initialize(handler)
err = admission.ValidateInitialization(handler)
return handler, f, err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import (
func newHandlerForTest(c clientset.Interface) (admission.MutationInterface, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
handler := NewProvision()
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil)
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil, nil)
pluginInitializer.Initialize(handler)
err := admission.ValidateInitialization(handler)
return handler, f, err
Expand Down
2 changes: 1 addition & 1 deletion plugin/pkg/admission/namespace/exists/admission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import (
func newHandlerForTest(c kubernetes.Interface) (admission.ValidationInterface, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
handler := NewExists()
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil)
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil, nil)
pluginInitializer.Initialize(handler)
err := admission.ValidateInitialization(handler)
return handler, f, err
Expand Down
2 changes: 1 addition & 1 deletion plugin/pkg/admission/podnodeselector/admission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ func TestHandles(t *testing.T) {
func newHandlerForTest(c kubernetes.Interface) (*Plugin, informers.SharedInformerFactory, error) {
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
handler := NewPodNodeSelector(nil)
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil)
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil, nil)
pluginInitializer.Initialize(handler)
err := admission.ValidateInitialization(handler)
return handler, f, err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"time"

"github.com/stretchr/testify/assert"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -355,7 +356,7 @@ func newHandlerForTest(c kubernetes.Interface) (*Plugin, informers.SharedInforme
return nil, nil, err
}
handler := NewPodTolerationsPlugin(pluginConfig)
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil)
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil, nil)
pluginInitializer.Initialize(handler)
err = admission.ValidateInitialization(handler)
return handler, f, err
Expand Down
2 changes: 1 addition & 1 deletion plugin/pkg/admission/podtopologylabels/admission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ func TestPodTopology(t *testing.T) {
func newHandlerForTest(c kubernetes.Interface) (*Plugin, informers.SharedInformerFactory, error) {
factory := informers.NewSharedInformerFactory(c, 5*time.Minute)
handler := NewPodTopologyPlugin(defaultConfig) // todo: write additional test cases with non-default config.
pluginInitializer := genericadmissioninitializer.New(c, nil, factory, nil, feature.DefaultFeatureGate, nil, nil)
pluginInitializer := genericadmissioninitializer.New(c, nil, factory, nil, feature.DefaultFeatureGate, nil, nil, nil)
pluginInitializer.Initialize(handler)
return handler, factory, admission.ValidateInitialization(handler)
}
2 changes: 1 addition & 1 deletion plugin/pkg/admission/resourcequota/admission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func createHandlerWithConfig(kubeClient kubernetes.Interface, informerFactory in
}

initializers := admission.PluginInitializers{
genericadmissioninitializer.New(kubeClient, nil, informerFactory, nil, nil, stopCh, nil),
genericadmissioninitializer.New(kubeClient, nil, informerFactory, nil, nil, nil, stopCh, nil),
controlplaneadmission.NewPluginInitializer(quotaConfiguration, nil),
}
initializers.Initialize(handler)
Expand Down
47 changes: 33 additions & 14 deletions plugin/pkg/admission/security/podsecurity/admission.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
corev1listers "k8s.io/client-go/listers/core/v1"
"k8s.io/component-base/compatibility"
"k8s.io/component-base/featuregate"
"k8s.io/component-base/metrics/legacyregistry"
"k8s.io/kubernetes/pkg/api/legacyscheme"
Expand Down Expand Up @@ -72,6 +73,9 @@ type Plugin struct {

inspectedFeatureGates bool

inspectedEffectiveVersion bool
emulationVersion *podsecurityadmissionapi.Version

client kubernetes.Interface
namespaceLister corev1listers.NamespaceLister
podLister corev1listers.PodLister
Expand Down Expand Up @@ -104,16 +108,10 @@ func newPlugin(reader io.Reader) (*Plugin, error) {
return nil, err
}

evaluator, err := policy.NewEvaluator(policy.DefaultChecks())
if err != nil {
return nil, fmt.Errorf("could not create PodSecurityRegistry: %w", err)
}

return &Plugin{
Handler: admission.NewHandler(admission.Create, admission.Update),
delegate: &podsecurityadmission.Admission{
Configuration: config,
Evaluator: evaluator,
Metrics: getDefaultRecorder(),
PodSpecExtractor: podsecurityadmission.DefaultPodSpecExtractor{},
},
Expand Down Expand Up @@ -146,24 +144,45 @@ func (p *Plugin) updateDelegate() {
if p.client == nil {
return
}
p.delegate.PodLister = podsecurityadmission.PodListerFromInformer(p.podLister)
p.delegate.NamespaceGetter = podsecurityadmission.NamespaceGetterFromListerAndClient(p.namespaceLister, p.client)
if !p.inspectedEffectiveVersion {
return
}
if p.delegate.PodLister == nil {
p.delegate.PodLister = podsecurityadmission.PodListerFromInformer(p.podLister)
}
if p.delegate.NamespaceGetter == nil {
p.delegate.NamespaceGetter = podsecurityadmission.NamespaceGetterFromListerAndClient(p.namespaceLister, p.client)
}
if p.delegate.Evaluator == nil {
evaluator, err := policy.NewEvaluator(policy.DefaultChecks(), p.emulationVersion)
if err != nil {
panic(fmt.Errorf("could not create PodSecurityRegistry: %w", err))
}
p.delegate.Evaluator = evaluator
}
}

func (c *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
c.inspectedFeatureGates = true
policy.RelaxPolicyForUserNamespacePods(featureGates.Enabled(features.UserNamespacesPodSecurityStandards))

if !featureGates.Enabled(features.ProbeHostPodSecurityStandards) {
policy.SkipProbeHostEnforcement()
func (p *Plugin) InspectEffectiveVersion(version compatibility.EffectiveVersion) {
p.inspectedEffectiveVersion = true
if !version.EmulationVersion().EqualTo(version.BinaryVersion()) {
emulationVersion := podsecurityadmissionapi.MajorMinorVersion(int(version.EmulationVersion().Major()), int(version.EmulationVersion().Minor()))
p.emulationVersion = &emulationVersion
}
}

func (p *Plugin) InspectFeatureGates(featureGates featuregate.FeatureGate) {
p.inspectedFeatureGates = true
policy.RelaxPolicyForUserNamespacePods(featureGates.Enabled(features.UserNamespacesPodSecurityStandards))
}

// ValidateInitialization ensures all required options are set
func (p *Plugin) ValidateInitialization() error {
if !p.inspectedFeatureGates {
return fmt.Errorf("%s did not see feature gates", PluginName)
}
if !p.inspectedEffectiveVersion {
return fmt.Errorf("%s did not see effective version", PluginName)
}
if err := p.delegate.CompleteConfiguration(); err != nil {
return fmt.Errorf("%s configuration error: %w", PluginName, err)
}
Expand Down
6 changes: 5 additions & 1 deletion plugin/pkg/admission/security/podsecurity/admission_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ import (
"strings"
"testing"

"sigs.k8s.io/yaml"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/util/compatibility"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/pkg/warning"
"k8s.io/client-go/informers"
Expand All @@ -40,7 +43,6 @@ import (
v1 "k8s.io/kubernetes/pkg/apis/core/v1"
podsecurityadmission "k8s.io/pod-security-admission/admission"
"k8s.io/utils/ptr"
"sigs.k8s.io/yaml"
)

func TestConvert(t *testing.T) {
Expand Down Expand Up @@ -81,6 +83,7 @@ func BenchmarkVerifyPod(b *testing.B) {
b.Fatal(err)
}

p.InspectEffectiveVersion(compatibility.DefaultBuildEffectiveVersion())
p.InspectFeatureGates(utilfeature.DefaultFeatureGate)

enforceImplicitPrivilegedNamespace := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "enforce-implicit", Labels: map[string]string{}}}
Expand Down Expand Up @@ -189,6 +192,7 @@ func BenchmarkVerifyNamespace(b *testing.B) {
b.Fatal(err)
}

p.InspectEffectiveVersion(compatibility.DefaultBuildEffectiveVersion())
p.InspectFeatureGates(utilfeature.DefaultFeatureGate)

namespace := "enforce"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"k8s.io/client-go/dynamic"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/component-base/compatibility"
"k8s.io/component-base/featuregate"
)

Expand All @@ -32,6 +33,7 @@ type pluginInitializer struct {
externalInformers informers.SharedInformerFactory
authorizer authorizer.Authorizer
featureGates featuregate.FeatureGate
effectiveVersion compatibility.EffectiveVersion
stopCh <-chan struct{}
restMapper meta.RESTMapper
}
Expand All @@ -45,6 +47,7 @@ func New(
extInformers informers.SharedInformerFactory,
authz authorizer.Authorizer,
featureGates featuregate.FeatureGate,
effectiveVersion compatibility.EffectiveVersion,
stopCh <-chan struct{},
restMapper meta.RESTMapper,
) pluginInitializer {
Expand All @@ -54,6 +57,7 @@ func New(
externalInformers: extInformers,
authorizer: authz,
featureGates: featureGates,
effectiveVersion: effectiveVersion,
stopCh: stopCh,
restMapper: restMapper,
}
Expand All @@ -68,6 +72,9 @@ func (i pluginInitializer) Initialize(plugin admission.Interface) {
}

// Second tell the plugin about enabled features, so it can decide whether to start informers or not
if wants, ok := plugin.(WantsEffectiveVersion); ok {
wants.InspectEffectiveVersion(i.effectiveVersion)
}
if wants, ok := plugin.(WantsFeatures); ok {
wants.InspectFeatureGates(i.featureGates)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import (
// TestWantsAuthorizer ensures that the authorizer is injected
// when the WantsAuthorizer interface is implemented by a plugin.
func TestWantsAuthorizer(t *testing.T) {
target := initializer.New(nil, nil, nil, &TestAuthorizer{}, nil, nil, nil)
target := initializer.New(nil, nil, nil, &TestAuthorizer{}, nil, nil, nil, nil)
wantAuthorizerAdmission := &WantAuthorizerAdmission{}
target.Initialize(wantAuthorizerAdmission)
if wantAuthorizerAdmission.auth == nil {
Expand All @@ -46,7 +46,7 @@ func TestWantsAuthorizer(t *testing.T) {
// when the WantsExternalKubeClientSet interface is implemented by a plugin.
func TestWantsExternalKubeClientSet(t *testing.T) {
cs := &fake.Clientset{}
target := initializer.New(cs, nil, nil, &TestAuthorizer{}, nil, nil, nil)
target := initializer.New(cs, nil, nil, &TestAuthorizer{}, nil, nil, nil, nil)
wantExternalKubeClientSet := &WantExternalKubeClientSet{}
target.Initialize(wantExternalKubeClientSet)
if wantExternalKubeClientSet.cs != cs {
Expand All @@ -59,7 +59,7 @@ func TestWantsExternalKubeClientSet(t *testing.T) {
func TestWantsExternalKubeInformerFactory(t *testing.T) {
cs := &fake.Clientset{}
sf := informers.NewSharedInformerFactory(cs, time.Duration(1)*time.Second)
target := initializer.New(cs, nil, sf, &TestAuthorizer{}, nil, nil, nil)
target := initializer.New(cs, nil, sf, &TestAuthorizer{}, nil, nil, nil, nil)
wantExternalKubeInformerFactory := &WantExternalKubeInformerFactory{}
target.Initialize(wantExternalKubeInformerFactory)
if wantExternalKubeInformerFactory.sf != sf {
Expand All @@ -71,7 +71,7 @@ func TestWantsExternalKubeInformerFactory(t *testing.T) {
// when the WantsShutdownSignal interface is implemented by a plugin.
func TestWantsShutdownNotification(t *testing.T) {
stopCh := make(chan struct{})
target := initializer.New(nil, nil, nil, &TestAuthorizer{}, nil, stopCh, nil)
target := initializer.New(nil, nil, nil, &TestAuthorizer{}, nil, nil, stopCh, nil)
wantDrainedNotification := &WantDrainedNotification{}
target.Initialize(wantDrainedNotification)
if wantDrainedNotification.stopCh == nil {
Expand Down Expand Up @@ -153,7 +153,7 @@ func (t *TestAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes)
}

func TestRESTMapperAdmissionPlugin(t *testing.T) {
initializer := initializer.New(nil, nil, nil, &TestAuthorizer{}, nil, nil, &doNothingRESTMapper{})
initializer := initializer.New(nil, nil, nil, &TestAuthorizer{}, nil, nil, nil, &doNothingRESTMapper{})
wantsRESTMapperAdmission := &WantsRESTMapperAdmissionPlugin{}
initializer.Initialize(wantsRESTMapperAdmission)

Expand Down
Loading