admission: do not leak admission config types outside of the plugins

pull/6/head
Dr. Stefan Schimanski 2017-11-27 14:44:04 +01:00
parent 2476aa410b
commit 1a552bbe14
14 changed files with 119 additions and 137 deletions

View File

@ -23,7 +23,6 @@ import (
"k8s.io/client-go/util/flowcontrol" "k8s.io/client-go/util/flowcontrol"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
eventratelimitapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit" eventratelimitapi "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit"
eventratelimitapiv1alpha1 "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1"
"k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation" "k8s.io/kubernetes/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation"
) )
@ -44,10 +43,6 @@ func Register(plugins *admission.Plugins) {
} }
return newEventRateLimit(configuration, realClock{}) return newEventRateLimit(configuration, realClock{})
}) })
// add our config types
eventratelimitapi.AddToScheme(plugins.ConfigScheme)
eventratelimitapiv1alpha1.AddToScheme(plugins.ConfigScheme)
} }
// Plugin implements an admission controller that can enforce event rate limits // Plugin implements an admission controller that can enforce event rate limits

View File

@ -38,7 +38,6 @@ import (
"k8s.io/kubernetes/pkg/scheduler/algorithm" "k8s.io/kubernetes/pkg/scheduler/algorithm"
"k8s.io/kubernetes/pkg/util/tolerations" "k8s.io/kubernetes/pkg/util/tolerations"
pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction" pluginapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
pluginapiv1alpha1 "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1"
) )
// Register registers a plugin // Register registers a plugin
@ -50,9 +49,6 @@ func Register(plugins *admission.Plugins) {
} }
return NewPodTolerationsPlugin(pluginConfig), nil return NewPodTolerationsPlugin(pluginConfig), nil
}) })
// add our config types
pluginapi.AddToScheme(plugins.ConfigScheme)
pluginapiv1alpha1.AddToScheme(plugins.ConfigScheme)
} }
// The annotation keys for default and whitelist of tolerations // The annotation keys for default and whitelist of tolerations

View File

@ -28,7 +28,6 @@ import (
kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission" kubeapiserveradmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
"k8s.io/kubernetes/pkg/quota" "k8s.io/kubernetes/pkg/quota"
resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota" resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
resourcequotaapiv1alpha1 "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1"
"k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/validation" "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/validation"
) )
@ -49,10 +48,6 @@ func Register(plugins *admission.Plugins) {
} }
return NewResourceQuota(configuration, 5, make(chan struct{})) return NewResourceQuota(configuration, 5, make(chan struct{}))
}) })
// add our config types
resourcequotaapi.AddToScheme(plugins.ConfigScheme)
resourcequotaapiv1alpha1.AddToScheme(plugins.ConfigScheme)
} }
// QuotaAdmission implements an admission controller that can enforce quota constraints // QuotaAdmission implements an admission controller that can enforce quota constraints

View File

@ -20,6 +20,7 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/apiserver:go_default_library", "//vendor/k8s.io/apiserver/pkg/apis/apiserver:go_default_library",
"//vendor/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1:go_default_library", "//vendor/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1:go_default_library",
], ],

View File

@ -126,16 +126,10 @@ type configProvider struct {
} }
// GetAdmissionPluginConfigurationFor returns a reader that holds the admission plugin configuration. // GetAdmissionPluginConfigurationFor returns a reader that holds the admission plugin configuration.
func GetAdmissionPluginConfigurationFor(pluginCfg apiserver.AdmissionPluginConfiguration, scheme *runtime.Scheme) (io.Reader, error) { func GetAdmissionPluginConfigurationFor(pluginCfg apiserver.AdmissionPluginConfiguration) (io.Reader, error) {
// if there is nothing nested in the object, we return the named location // if there is a nest object, return it directly
obj := pluginCfg.Configuration if pluginCfg.Configuration != nil {
if obj != nil { return bytes.NewBuffer(pluginCfg.Configuration.Raw), nil
// serialize the configuration and build a reader for it
content, err := writeYAML(obj, scheme)
if err != nil {
return nil, err
}
return bytes.NewBuffer(content), nil
} }
// there is nothing nested, so we delegate to path // there is nothing nested, so we delegate to path
if pluginCfg.Path != "" { if pluginCfg.Path != "" {
@ -162,7 +156,7 @@ func (p configProvider) ConfigFor(pluginName string) (io.Reader, error) {
if pluginName != pluginCfg.Name { if pluginName != pluginCfg.Name {
continue continue
} }
pluginConfig, err := GetAdmissionPluginConfigurationFor(pluginCfg, p.scheme) pluginConfig, err := GetAdmissionPluginConfigurationFor(pluginCfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -23,6 +23,7 @@ import (
"testing" "testing"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apiserver/pkg/apis/apiserver" "k8s.io/apiserver/pkg/apis/apiserver"
apiserverapi "k8s.io/apiserver/pkg/apis/apiserver" apiserverapi "k8s.io/apiserver/pkg/apis/apiserver"
apiserverapiv1alpha1 "k8s.io/apiserver/pkg/apis/apiserver/v1alpha1" apiserverapiv1alpha1 "k8s.io/apiserver/pkg/apis/apiserver/v1alpha1"
@ -49,7 +50,7 @@ func TestReadAdmissionConfiguration(t *testing.T) {
ExpectedAdmissionConfig *apiserver.AdmissionConfiguration ExpectedAdmissionConfig *apiserver.AdmissionConfiguration
PluginNames []string PluginNames []string
}{ }{
"v1Alpha1 configuration - path fixup": { "v1alpha1 configuration - path fixup": {
ConfigBody: `{ ConfigBody: `{
"apiVersion": "apiserver.k8s.io/v1alpha1", "apiVersion": "apiserver.k8s.io/v1alpha1",
"kind": "AdmissionConfiguration", "kind": "AdmissionConfiguration",
@ -70,7 +71,7 @@ func TestReadAdmissionConfiguration(t *testing.T) {
}, },
PluginNames: []string{}, PluginNames: []string{},
}, },
"v1Alpha1 configuration - abspath": { "v1alpha1 configuration - abspath": {
ConfigBody: `{ ConfigBody: `{
"apiVersion": "apiserver.k8s.io/v1alpha1", "apiVersion": "apiserver.k8s.io/v1alpha1",
"kind": "AdmissionConfiguration", "kind": "AdmissionConfiguration",
@ -153,3 +154,98 @@ func TestReadAdmissionConfiguration(t *testing.T) {
} }
} }
} }
func TestEmbeddedConfiguration(t *testing.T) {
// create a place holder file to hold per test config
configFile, err := ioutil.TempFile("", "admission-plugin-config")
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if err = configFile.Close(); err != nil {
t.Fatalf("unexpected err: %v", err)
}
configFileName := configFile.Name()
testCases := map[string]struct {
ConfigBody string
ExpectedConfig string
}{
"versioned configuration": {
ConfigBody: `{
"apiVersion": "apiserver.k8s.io/v1alpha1",
"kind": "AdmissionConfiguration",
"plugins": [
{
"name": "Foo",
"configuration": {
"apiVersion": "foo.admission.k8s.io/v1alpha1",
"kind": "Configuration",
"foo": "bar"
}
}
]}`,
ExpectedConfig: `{
"apiVersion": "foo.admission.k8s.io/v1alpha1",
"kind": "Configuration",
"foo": "bar"
}`,
},
"legacy configuration": {
ConfigBody: `{
"apiVersion": "apiserver.k8s.io/v1alpha1",
"kind": "AdmissionConfiguration",
"plugins": [
{
"name": "Foo",
"configuration": {
"foo": "bar"
}
}
]}`,
ExpectedConfig: `{
"foo": "bar"
}`,
},
}
for desc, test := range testCases {
scheme := runtime.NewScheme()
apiserverapi.AddToScheme(scheme)
apiserverapiv1alpha1.AddToScheme(scheme)
if err = ioutil.WriteFile(configFileName, []byte(test.ConfigBody), 0644); err != nil {
t.Errorf("[%s] unexpected err writing temp file: %v", desc, err)
continue
}
config, err := ReadAdmissionConfiguration([]string{"Foo"}, configFileName, scheme)
if err != nil {
t.Errorf("[%s] unexpected err: %v", desc, err)
continue
}
r, err := config.ConfigFor("Foo")
if err != nil {
t.Errorf("[%s] Failed to get Foo config: %v", desc, err)
continue
}
bs, err := ioutil.ReadAll(r)
if err != nil {
t.Errorf("[%s] Failed to read Foo config data: %v", desc, err)
continue
}
if !equalJSON(test.ExpectedConfig, string(bs)) {
t.Errorf("Unexpected config: expected=%q got=%q", test.ExpectedConfig, string(bs))
}
}
}
func equalJSON(a, b string) bool {
var x, y interface{}
if err := json.Unmarshal([]byte(a), &x); err != nil {
return false
}
if err := json.Unmarshal([]byte(b), &y); err != nil {
return false
}
return reflect.DeepEqual(x, y)
}

View File

@ -23,8 +23,6 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/admission/initializer:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/metrics:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission/metrics:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/errors:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/namespace:go_default_library",
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/request:go_default_library",

View File

@ -40,8 +40,6 @@ import (
genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer" genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer"
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics" admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
"k8s.io/apiserver/pkg/admission/plugin/webhook/config" "k8s.io/apiserver/pkg/admission/plugin/webhook/config"
webhookadmissionapi "k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission"
webhookadmissionapiv1alpha1 "k8s.io/apiserver/pkg/admission/plugin/webhook/config/apis/webhookadmission/v1alpha1"
webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors" webhookerrors "k8s.io/apiserver/pkg/admission/plugin/webhook/errors"
"k8s.io/apiserver/pkg/admission/plugin/webhook/namespace" "k8s.io/apiserver/pkg/admission/plugin/webhook/namespace"
"k8s.io/apiserver/pkg/admission/plugin/webhook/request" "k8s.io/apiserver/pkg/admission/plugin/webhook/request"
@ -66,9 +64,6 @@ func Register(plugins *admission.Plugins) {
return plugin, nil return plugin, nil
}) })
// add our config types
webhookadmissionapi.AddToScheme(plugins.ConfigScheme)
webhookadmissionapiv1alpha1.AddToScheme(plugins.ConfigScheme)
} }
// WebhookSource can list dynamic webhook plugins. // WebhookSource can list dynamic webhook plugins.

View File

@ -25,8 +25,6 @@ import (
"sort" "sort"
"sync" "sync"
"k8s.io/apimachinery/pkg/runtime"
"github.com/golang/glog" "github.com/golang/glog"
) )
@ -39,16 +37,10 @@ type Factory func(config io.Reader) (Interface, error)
type Plugins struct { type Plugins struct {
lock sync.Mutex lock sync.Mutex
registry map[string]Factory registry map[string]Factory
// ConfigScheme is used to parse the admission plugin config file.
// It is exposed to act as a hook for extending server providing their own config.
ConfigScheme *runtime.Scheme
} }
func NewPlugins() *Plugins { func NewPlugins() *Plugins {
return &Plugins{ return &Plugins{}
ConfigScheme: runtime.NewScheme(),
}
} }
// All registered admission options. // All registered admission options.

View File

@ -46,5 +46,5 @@ type AdmissionPluginConfiguration struct {
// Configuration is an embedded configuration object to be used as the plugin's // Configuration is an embedded configuration object to be used as the plugin's
// configuration. If present, it will be used instead of the path to the configuration file. // configuration. If present, it will be used instead of the path to the configuration file.
// +optional // +optional
Configuration runtime.Object Configuration *runtime.Unknown
} }

View File

@ -1,88 +0,0 @@
/*
Copyright 2017 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 v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
var _ runtime.NestedObjectDecoder = &AdmissionConfiguration{}
// DecodeNestedObjects handles encoding RawExtensions on the AdmissionConfiguration, ensuring the
// objects are decoded with the provided decoder.
func (c *AdmissionConfiguration) DecodeNestedObjects(d runtime.Decoder) error {
// decoding failures result in a runtime.Unknown object being created in Object and passed
// to conversion
for k, v := range c.Plugins {
decodeNestedRawExtensionOrUnknown(d, &v.Configuration)
c.Plugins[k] = v
}
return nil
}
var _ runtime.NestedObjectEncoder = &AdmissionConfiguration{}
// EncodeNestedObjects handles encoding RawExtensions on the AdmissionConfiguration, ensuring the
// objects are encoded with the provided encoder.
func (c *AdmissionConfiguration) EncodeNestedObjects(e runtime.Encoder) error {
for k, v := range c.Plugins {
if err := encodeNestedRawExtension(e, &v.Configuration); err != nil {
return err
}
c.Plugins[k] = v
}
return nil
}
// decodeNestedRawExtensionOrUnknown decodes the raw extension into an object once. If called
// On a RawExtension that has already been decoded (has an object), it will not run again.
func decodeNestedRawExtensionOrUnknown(d runtime.Decoder, ext *runtime.RawExtension) {
if ext.Raw == nil || ext.Object != nil {
return
}
obj, gvk, err := d.Decode(ext.Raw, nil, nil)
if err != nil {
unk := &runtime.Unknown{Raw: ext.Raw}
if runtime.IsNotRegisteredError(err) {
if _, gvk, err := d.Decode(ext.Raw, nil, unk); err == nil {
unk.APIVersion = gvk.GroupVersion().String()
unk.Kind = gvk.Kind
ext.Object = unk
return
}
}
// TODO: record mime-type with the object
if gvk != nil {
unk.APIVersion = gvk.GroupVersion().String()
unk.Kind = gvk.Kind
}
obj = unk
}
ext.Object = obj
}
func encodeNestedRawExtension(e runtime.Encoder, ext *runtime.RawExtension) error {
if ext.Raw != nil || ext.Object == nil {
return nil
}
data, err := runtime.Encode(e, ext.Object)
if err != nil {
return err
}
ext.Raw = data
return nil
}

View File

@ -46,5 +46,5 @@ type AdmissionPluginConfiguration struct {
// Configuration is an embedded configuration object to be used as the plugin's // Configuration is an embedded configuration object to be used as the plugin's
// configuration. If present, it will be used instead of the path to the configuration file. // configuration. If present, it will be used instead of the path to the configuration file.
// +optional // +optional
Configuration runtime.RawExtension `json:"configuration"` Configuration *runtime.Unknown `json:"configuration"`
} }

View File

@ -38,6 +38,13 @@ import (
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
) )
var scheme = runtime.NewScheme()
func init() {
apiserverapi.AddToScheme(scheme)
apiserverapiv1alpha1.AddToScheme(scheme)
}
// AdmissionOptions holds the admission options // AdmissionOptions holds the admission options
type AdmissionOptions struct { type AdmissionOptions struct {
// RecommendedPluginOrder holds an ordered list of plugin names we recommend to use by default // RecommendedPluginOrder holds an ordered list of plugin names we recommend to use by default
@ -69,8 +76,6 @@ func NewAdmissionOptions() *AdmissionOptions {
RecommendedPluginOrder: []string{lifecycle.PluginName, initialization.PluginName, mutatingwebhook.PluginName, validatingwebhook.PluginName}, RecommendedPluginOrder: []string{lifecycle.PluginName, initialization.PluginName, mutatingwebhook.PluginName, validatingwebhook.PluginName},
DefaultOffPlugins: []string{initialization.PluginName, mutatingwebhook.PluginName, validatingwebhook.PluginName}, DefaultOffPlugins: []string{initialization.PluginName, mutatingwebhook.PluginName, validatingwebhook.PluginName},
} }
apiserverapi.AddToScheme(options.Plugins.ConfigScheme)
apiserverapiv1alpha1.AddToScheme(options.Plugins.ConfigScheme)
server.RegisterAllAdmissionPlugins(options.Plugins) server.RegisterAllAdmissionPlugins(options.Plugins)
return options return options
} }
@ -120,7 +125,7 @@ func (a *AdmissionOptions) ApplyTo(
pluginNames = a.enabledPluginNames() pluginNames = a.enabledPluginNames()
} }
pluginsConfigProvider, err := admission.ReadAdmissionConfiguration(pluginNames, a.ConfigFile, a.Plugins.ConfigScheme) pluginsConfigProvider, err := admission.ReadAdmissionConfiguration(pluginNames, a.ConfigFile, scheme)
if err != nil { if err != nil {
return fmt.Errorf("failed to read plugin config: %v", err) return fmt.Errorf("failed to read plugin config: %v", err)
} }

View File

@ -25,7 +25,10 @@ filegroup(
filegroup( filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [":package-srcs"], srcs = [
":package-srcs",
"//vendor/github.com/jmespath/go-jmespath/cmd/jpgo:all-srcs",
],
tags = ["automanaged"], tags = ["automanaged"],
visibility = ["//visibility:public"], visibility = ["//visibility:public"],
) )