From 0859798e8e278ec382dcbeb77914f40bf2c78a2c Mon Sep 17 00:00:00 2001 From: David Eads Date: Wed, 18 Oct 2017 12:57:59 -0400 Subject: [PATCH] update admission webhook to accept client config --- cmd/kube-apiserver/app/BUILD | 1 + cmd/kube-apiserver/app/server.go | 16 ++- federation/cmd/federation-apiserver/app/BUILD | 1 + .../cmd/federation-apiserver/app/server.go | 4 +- pkg/kubeapiserver/admission/initializer.go | 20 ---- plugin/pkg/admission/gc/gc_admission_test.go | 2 +- plugin/pkg/admission/webhook/BUILD | 1 + plugin/pkg/admission/webhook/admission.go | 50 +++------ .../pkg/admission/webhook/admission_test.go | 7 +- .../apiserver/pkg/admission/initializer/BUILD | 1 + .../pkg/admission/initializer/initializer.go | 27 +++-- .../admission/initializer/initializer_test.go | 22 +--- .../pkg/admission/initializer/interfaces.go | 7 +- .../namespace/lifecycle/admission_test.go | 2 +- .../apiserver/pkg/server/options/admission.go | 9 +- staging/src/k8s.io/client-go/rest/config.go | 40 +++++++ .../src/k8s.io/client-go/rest/config_test.go | 100 ++++++++++++++++++ .../sample-apiserver/pkg/cmd/server/start.go | 2 +- 18 files changed, 202 insertions(+), 110 deletions(-) diff --git a/cmd/kube-apiserver/app/BUILD b/cmd/kube-apiserver/app/BUILD index 497e3bae6e5..494a9b6dab7 100644 --- a/cmd/kube-apiserver/app/BUILD +++ b/cmd/kube-apiserver/app/BUILD @@ -70,6 +70,7 @@ go_library( "//vendor/k8s.io/apiserver/pkg/storage/etcd3/preflight:go_default_library", "//vendor/k8s.io/client-go/informers:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/tools/cache:go_default_library", "//vendor/k8s.io/kube-aggregator/pkg/apis/apiregistration:go_default_library", "//vendor/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1:go_default_library", diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 9c14fa45829..16329486b1e 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -84,6 +84,7 @@ import ( "k8s.io/kubernetes/pkg/version" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap" + "k8s.io/client-go/rest" "k8s.io/kubernetes/pkg/api" _ "k8s.io/kubernetes/pkg/util/reflector/prometheus" // for reflector metric registration _ "k8s.io/kubernetes/pkg/util/workqueue/prometheus" // for workqueue metric registration @@ -456,12 +457,15 @@ func BuildGenericConfig(s *options.ServerRunOptions, proxyTransport *http.Transp client, sharedInformers, serviceResolver, - proxyTransport, ) if err != nil { return nil, nil, nil, nil, nil, fmt.Errorf("failed to create admission plugin initializer: %v", err) } + webhookClientConfig := rest.AnonymousClientConfig(genericConfig.LoopbackClientConfig) + if proxyTransport != nil && proxyTransport.Dial != nil { + webhookClientConfig.Dial = proxyTransport.Dial + } // TODO: this is the wrong cert/key pair. // Given the generic case of webhook admission from a generic apiserver, // this key pair should be signed by the the API server's client CA. @@ -477,14 +481,17 @@ func BuildGenericConfig(s *options.ServerRunOptions, proxyTransport *http.Transp if err != nil { return nil, nil, nil, nil, nil, fmt.Errorf("failed to read proxy client key file from: %s, err: %v", s.ProxyClientKeyFile, err) } + webhookClientConfig.TLSClientConfig.CertData = certBytes + webhookClientConfig.TLSClientConfig.KeyData = keyBytes } + webhookClientConfig.UserAgent = "kube-apiserver-admission" + webhookClientConfig.Timeout = 30 * time.Second err = s.Admission.ApplyTo( genericConfig, versionedInformers, - certBytes, - keyBytes, kubeClientConfig, + webhookClientConfig, legacyscheme.Scheme, pluginInitializer) if err != nil { @@ -494,7 +501,7 @@ func BuildGenericConfig(s *options.ServerRunOptions, proxyTransport *http.Transp } // BuildAdmissionPluginInitializer constructs the admission plugin initializer -func BuildAdmissionPluginInitializer(s *options.ServerRunOptions, client internalclientset.Interface, sharedInformers informers.SharedInformerFactory, serviceResolver aggregatorapiserver.ServiceResolver, proxyTransport *http.Transport) (admission.PluginInitializer, error) { +func BuildAdmissionPluginInitializer(s *options.ServerRunOptions, client internalclientset.Interface, sharedInformers informers.SharedInformerFactory, serviceResolver aggregatorapiserver.ServiceResolver) (admission.PluginInitializer, error) { var cloudConfig []byte if s.CloudProvider.CloudConfigFile != "" { @@ -515,7 +522,6 @@ func BuildAdmissionPluginInitializer(s *options.ServerRunOptions, client interna pluginInitializer := kubeapiserveradmission.NewPluginInitializer(client, sharedInformers, cloudConfig, restMapper, quotaRegistry) pluginInitializer = pluginInitializer.SetServiceResolver(serviceResolver) - pluginInitializer = pluginInitializer.SetProxyTransport(proxyTransport) return pluginInitializer, nil } diff --git a/federation/cmd/federation-apiserver/app/BUILD b/federation/cmd/federation-apiserver/app/BUILD index 17b50f446c1..94dca3aff96 100644 --- a/federation/cmd/federation-apiserver/app/BUILD +++ b/federation/cmd/federation-apiserver/app/BUILD @@ -84,6 +84,7 @@ go_library( "//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library", "//vendor/k8s.io/client-go/informers:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library", ], ) diff --git a/federation/cmd/federation-apiserver/app/server.go b/federation/cmd/federation-apiserver/app/server.go index 7e1504533bc..e8f7a092b80 100644 --- a/federation/cmd/federation-apiserver/app/server.go +++ b/federation/cmd/federation-apiserver/app/server.go @@ -42,6 +42,7 @@ import ( serverstorage "k8s.io/apiserver/pkg/server/storage" clientgoinformers "k8s.io/client-go/informers" clientgoclientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" openapicommon "k8s.io/kube-openapi/pkg/common" federationv1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" "k8s.io/kubernetes/federation/cmd/federation-apiserver/app/options" @@ -215,9 +216,8 @@ func NonBlockingRun(s *options.ServerRunOptions, stopCh <-chan struct{}) error { err = s.Admission.ApplyTo( genericConfig, versionedInformers, - nil, - nil, kubeClientConfig, + rest.AnonymousClientConfig(kubeClientConfig), legacyscheme.Scheme, pluginInitializer, ) diff --git a/pkg/kubeapiserver/admission/initializer.go b/pkg/kubeapiserver/admission/initializer.go index d90e9bc63b3..27b10036fd6 100644 --- a/pkg/kubeapiserver/admission/initializer.go +++ b/pkg/kubeapiserver/admission/initializer.go @@ -17,7 +17,6 @@ limitations under the License. package admission import ( - "net/http" "net/url" "k8s.io/apimachinery/pkg/api/meta" @@ -71,12 +70,6 @@ type ServiceResolver interface { ResolveEndpoint(namespace, name string) (*url.URL, error) } -// WantsProxyTransport defines a fuction that accepts a proxy transport for admission -// plugins that need to make calls to pods. -type WantsProxyTransport interface { - SetProxyTransport(proxyTransport *http.Transport) -} - type PluginInitializer struct { internalClient internalclientset.Interface externalClient clientset.Interface @@ -86,9 +79,6 @@ type PluginInitializer struct { restMapper meta.RESTMapper quotaRegistry quota.Registry serviceResolver ServiceResolver - - // for proving we are apiserver in call-outs - proxyTransport *http.Transport } var _ admission.PluginInitializer = &PluginInitializer{} @@ -118,12 +108,6 @@ func (i *PluginInitializer) SetServiceResolver(s ServiceResolver) *PluginInitial return i } -// SetProxyTransport sets the proxyTransport which is needed by some plugins. -func (i *PluginInitializer) SetProxyTransport(proxyTransport *http.Transport) *PluginInitializer { - i.proxyTransport = proxyTransport - return i -} - // Initialize checks the initialization interfaces implemented by each plugin // and provide the appropriate initialization data func (i *PluginInitializer) Initialize(plugin admission.Interface) { @@ -150,8 +134,4 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) { if wants, ok := plugin.(WantsServiceResolver); ok { wants.SetServiceResolver(i.serviceResolver) } - - if wants, ok := plugin.(WantsProxyTransport); ok { - wants.SetProxyTransport(i.proxyTransport) - } } diff --git a/plugin/pkg/admission/gc/gc_admission_test.go b/plugin/pkg/admission/gc/gc_admission_test.go index 99fdf606202..d5c31f64572 100644 --- a/plugin/pkg/admission/gc/gc_admission_test.go +++ b/plugin/pkg/admission/gc/gc_admission_test.go @@ -86,7 +86,7 @@ func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) { whiteList: whiteList, } - genericPluginInitializer, err := initializer.New(nil, nil, fakeAuthorizer{}, nil, nil, nil) + genericPluginInitializer, err := initializer.New(nil, nil, fakeAuthorizer{}, nil, nil) if err != nil { return nil, err } diff --git a/plugin/pkg/admission/webhook/BUILD b/plugin/pkg/admission/webhook/BUILD index 6495af040ce..ef77878e899 100644 --- a/plugin/pkg/admission/webhook/BUILD +++ b/plugin/pkg/admission/webhook/BUILD @@ -53,6 +53,7 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apiserver/pkg/admission:go_default_library", "//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", ], ) diff --git a/plugin/pkg/admission/webhook/admission.go b/plugin/pkg/admission/webhook/admission.go index 4cb9828dd04..a0b222b413b 100644 --- a/plugin/pkg/admission/webhook/admission.go +++ b/plugin/pkg/admission/webhook/admission.go @@ -21,10 +21,7 @@ import ( "context" "fmt" "io" - "net" - "net/http" "sync" - "time" "github.com/golang/glog" @@ -105,19 +102,18 @@ type GenericAdmissionWebhook struct { hookSource WebhookSource serviceResolver admissioninit.ServiceResolver negotiatedSerializer runtime.NegotiatedSerializer - clientCert []byte - clientKey []byte - proxyTransport *http.Transport + + restClientConfig *rest.Config } var ( _ = admissioninit.WantsServiceResolver(&GenericAdmissionWebhook{}) - _ = genericadmissioninit.WantsClientCert(&GenericAdmissionWebhook{}) + _ = genericadmissioninit.WantsWebhookRESTClientConfig(&GenericAdmissionWebhook{}) _ = genericadmissioninit.WantsExternalKubeClientSet(&GenericAdmissionWebhook{}) ) -func (a *GenericAdmissionWebhook) SetProxyTransport(pt *http.Transport) { - a.proxyTransport = pt +func (a *GenericAdmissionWebhook) SetWebhookRESTClientConfig(in *rest.Config) { + a.restClientConfig = in } // SetServiceResolver sets a service resolver for the webhook admission plugin. @@ -137,18 +133,13 @@ func (a *GenericAdmissionWebhook) SetScheme(scheme *runtime.Scheme) { } } -func (a *GenericAdmissionWebhook) SetClientCert(cert, key []byte) { - a.clientCert = cert - a.clientKey = key -} - func (a *GenericAdmissionWebhook) SetExternalKubeClientSet(client clientset.Interface) { a.hookSource = configuration.NewExternalAdmissionHookConfigurationManager(client.Admissionregistration().ExternalAdmissionHookConfigurations()) } func (a *GenericAdmissionWebhook) Validate() error { - if a.clientCert == nil || a.clientKey == nil { - return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a client certificate and the private key to be provided") + if a.restClientConfig == nil { + return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a restClientConfig to be provided") } if a.hookSource == nil { return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a Kubernetes client to be provided") @@ -280,27 +271,12 @@ func (a *GenericAdmissionWebhook) hookClient(h *v1alpha1.ExternalAdmissionHook) return nil, err } - var dial func(network, addr string) (net.Conn, error) - if a.proxyTransport != nil && a.proxyTransport.Dial != nil { - dial = a.proxyTransport.Dial - } - // TODO: cache these instead of constructing one each time - cfg := &rest.Config{ - Host: u.Host, - APIPath: path.Join(u.Path, h.ClientConfig.URLPath), - TLSClientConfig: rest.TLSClientConfig{ - ServerName: h.ClientConfig.Service.Name + "." + h.ClientConfig.Service.Namespace + ".svc", - CAData: h.ClientConfig.CABundle, - CertData: a.clientCert, - KeyData: a.clientKey, - }, - UserAgent: "kube-apiserver-admission", - Timeout: 30 * time.Second, - ContentConfig: rest.ContentConfig{ - NegotiatedSerializer: a.negotiatedSerializer, - }, - Dial: dial, - } + cfg := rest.CopyConfig(a.restClientConfig) + cfg.Host = u.Host + cfg.APIPath = path.Join(u.Path, h.ClientConfig.URLPath) + cfg.TLSClientConfig.ServerName = h.ClientConfig.Service.Name + "." + h.ClientConfig.Service.Namespace + ".svc" + cfg.TLSClientConfig.CAData = h.ClientConfig.CABundle + cfg.ContentConfig.NegotiatedSerializer = a.negotiatedSerializer return rest.UnversionedRESTClientFor(cfg) } diff --git a/plugin/pkg/admission/webhook/admission_test.go b/plugin/pkg/admission/webhook/admission_test.go index b4e13c3e6b7..ee5888676d2 100644 --- a/plugin/pkg/admission/webhook/admission_test.go +++ b/plugin/pkg/admission/webhook/admission_test.go @@ -32,6 +32,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/client-go/rest" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" @@ -89,8 +90,10 @@ func TestAdmit(t *testing.T) { if err != nil { t.Fatal(err) } - wh.clientCert = clientCert - wh.clientKey = clientKey + wh.restClientConfig = &rest.Config{} + wh.restClientConfig.TLSClientConfig.CAData = caCert + wh.restClientConfig.TLSClientConfig.CertData = clientCert + wh.restClientConfig.TLSClientConfig.KeyData = clientKey // Set up a test object for the call kind := api.Kind("Pod").WithVersion("v1") diff --git a/staging/src/k8s.io/apiserver/pkg/admission/initializer/BUILD b/staging/src/k8s.io/apiserver/pkg/admission/initializer/BUILD index 6884d643a4e..98a3573b968 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/initializer/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/admission/initializer/BUILD @@ -19,6 +19,7 @@ go_library( "//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library", "//vendor/k8s.io/client-go/informers:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", ], ) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer.go b/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer.go index bce5433a34b..d41b305d964 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer.go @@ -22,17 +22,16 @@ import ( "k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" ) type pluginInitializer struct { externalClient kubernetes.Interface externalInformers informers.SharedInformerFactory authorizer authorizer.Authorizer - // serverIdentifyingClientCert used to provide identity when calling out to admission plugins - serverIdentifyingClientCert []byte - // serverIdentifyingClientKey private key for the client certificate used when calling out to admission plugins - serverIdentifyingClientKey []byte - scheme *runtime.Scheme + // webhookRESTClientConfig provies a client used to contact webhooks + webhookRESTClientConfig *rest.Config + scheme *runtime.Scheme } // New creates an instance of admission plugins initializer. @@ -41,17 +40,15 @@ func New( extClientset kubernetes.Interface, extInformers informers.SharedInformerFactory, authz authorizer.Authorizer, - serverIdentifyingClientCert, - serverIdentifyingClientKey []byte, + webhookRESTClientConfig *rest.Config, scheme *runtime.Scheme, ) (pluginInitializer, error) { return pluginInitializer{ - externalClient: extClientset, - externalInformers: extInformers, - authorizer: authz, - serverIdentifyingClientCert: serverIdentifyingClientCert, - serverIdentifyingClientKey: serverIdentifyingClientKey, - scheme: scheme, + externalClient: extClientset, + externalInformers: extInformers, + authorizer: authz, + webhookRESTClientConfig: webhookRESTClientConfig, + scheme: scheme, }, nil } @@ -70,8 +67,8 @@ func (i pluginInitializer) Initialize(plugin admission.Interface) { wants.SetAuthorizer(i.authorizer) } - if wants, ok := plugin.(WantsClientCert); ok { - wants.SetClientCert(i.serverIdentifyingClientCert, i.serverIdentifyingClientKey) + if wants, ok := plugin.(WantsWebhookRESTClientConfig); ok { + wants.SetWebhookRESTClientConfig(i.webhookRESTClientConfig) } if wants, ok := plugin.(WantsScheme); ok { diff --git a/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go b/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go index 0550bd17cc8..9a5236316d3 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer_test.go @@ -33,7 +33,7 @@ import ( // the WantsScheme interface is implemented by a plugin. func TestWantsScheme(t *testing.T) { scheme := runtime.NewScheme() - target, err := initializer.New(nil, nil, nil, nil, nil, scheme) + target, err := initializer.New(nil, nil, nil, nil, scheme) if err != nil { t.Fatal(err) } @@ -47,7 +47,7 @@ func TestWantsScheme(t *testing.T) { // TestWantsAuthorizer ensures that the authorizer is injected // when the WantsAuthorizer interface is implemented by a plugin. func TestWantsAuthorizer(t *testing.T) { - target, err := initializer.New(nil, nil, &TestAuthorizer{}, nil, nil, nil) + target, err := initializer.New(nil, nil, &TestAuthorizer{}, nil, nil) if err != nil { t.Fatal(err) } @@ -62,7 +62,7 @@ func TestWantsAuthorizer(t *testing.T) { // when the WantsExternalKubeClientSet interface is implemented by a plugin. func TestWantsExternalKubeClientSet(t *testing.T) { cs := &fake.Clientset{} - target, err := initializer.New(cs, nil, &TestAuthorizer{}, nil, nil, nil) + target, err := initializer.New(cs, nil, &TestAuthorizer{}, nil, nil) if err != nil { t.Fatal(err) } @@ -78,7 +78,7 @@ func TestWantsExternalKubeClientSet(t *testing.T) { func TestWantsExternalKubeInformerFactory(t *testing.T) { cs := &fake.Clientset{} sf := informers.NewSharedInformerFactory(cs, time.Duration(1)*time.Second) - target, err := initializer.New(cs, sf, &TestAuthorizer{}, nil, nil, nil) + target, err := initializer.New(cs, sf, &TestAuthorizer{}, nil, nil) if err != nil { t.Fatal(err) } @@ -89,20 +89,6 @@ func TestWantsExternalKubeInformerFactory(t *testing.T) { } } -// TestWantsClientCert ensures that the client certificate and key are injected -// when the WantsClientCert interface is implemented by a plugin. -func TestWantsClientCert(t *testing.T) { - target, err := initializer.New(nil, nil, nil, []byte("cert"), []byte("key"), nil) - if err != nil { - t.Fatal(err) - } - wantClientCert := &clientCertWanter{} - target.Initialize(wantClientCert) - if string(wantClientCert.gotCert) != "cert" || string(wantClientCert.gotKey) != "key" { - t.Errorf("expected client cert to be initialized, clientCert = %v, clientKey = %v", wantClientCert.gotCert, wantClientCert.gotKey) - } -} - // WantExternalKubeInformerFactory is a test stub that fulfills the WantsExternalKubeInformerFactory interface type WantExternalKubeInformerFactory struct { sf informers.SharedInformerFactory diff --git a/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go b/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go index 067b4a1da23..7571252169f 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/initializer/interfaces.go @@ -22,6 +22,7 @@ import ( "k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" ) // WantsExternalKubeClientSet defines a function which sets external ClientSet for admission plugins that need it @@ -42,10 +43,10 @@ type WantsAuthorizer interface { admission.Validator } -// WantsClientCert defines a fuction that accepts a cert & key for admission +// WantsWebhookRESTClientConfig defines a function that accepts client config for admission // plugins that need to make calls and prove their identity. -type WantsClientCert interface { - SetClientCert(cert, key []byte) +type WantsWebhookRESTClientConfig interface { + SetWebhookRESTClientConfig(*rest.Config) admission.Validator } diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go index b0cad52f824..c3e8162e72c 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission_test.go @@ -48,7 +48,7 @@ func newHandlerForTestWithClock(c clientset.Interface, cacheClock clock.Clock) ( if err != nil { return nil, f, err } - pluginInitializer, err := kubeadmission.New(c, f, nil, nil, nil, nil) + pluginInitializer, err := kubeadmission.New(c, f, nil, nil, nil) if err != nil { return handler, f, err } diff --git a/staging/src/k8s.io/apiserver/pkg/server/options/admission.go b/staging/src/k8s.io/apiserver/pkg/server/options/admission.go index 1c23d3e2f96..d784b767246 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/admission.go +++ b/staging/src/k8s.io/apiserver/pkg/server/options/admission.go @@ -80,9 +80,8 @@ func (a *AdmissionOptions) AddFlags(fs *pflag.FlagSet) { func (a *AdmissionOptions) ApplyTo( c *server.Config, informers informers.SharedInformerFactory, - serverIdentifyingClientCert []byte, - serverIdentifyingClientKey []byte, - clientConfig *rest.Config, + kubeAPIServerClientConfig *rest.Config, + webhookClientConfig *rest.Config, scheme *runtime.Scheme, pluginInitializers ...admission.PluginInitializer, ) error { @@ -96,11 +95,11 @@ func (a *AdmissionOptions) ApplyTo( return fmt.Errorf("failed to read plugin config: %v", err) } - clientset, err := kubernetes.NewForConfig(clientConfig) + clientset, err := kubernetes.NewForConfig(kubeAPIServerClientConfig) if err != nil { return err } - genericInitializer, err := initializer.New(clientset, informers, c.Authorizer, serverIdentifyingClientCert, serverIdentifyingClientKey, scheme) + genericInitializer, err := initializer.New(clientset, informers, c.Authorizer, webhookClientConfig, scheme) if err != nil { return err } diff --git a/staging/src/k8s.io/client-go/rest/config.go b/staging/src/k8s.io/client-go/rest/config.go index 57848c8a74f..038fee94537 100644 --- a/staging/src/k8s.io/client-go/rest/config.go +++ b/staging/src/k8s.io/client-go/rest/config.go @@ -420,5 +420,45 @@ func AnonymousClientConfig(config *Config) *Config { QPS: config.QPS, Burst: config.Burst, Timeout: config.Timeout, + Dial: config.Dial, + } +} + +// CopyConfig returns a copy of the given config +func CopyConfig(config *Config) *Config { + return &Config{ + Host: config.Host, + APIPath: config.APIPath, + Prefix: config.Prefix, + ContentConfig: config.ContentConfig, + Username: config.Username, + Password: config.Password, + BearerToken: config.BearerToken, + CacheDir: config.CacheDir, + Impersonate: ImpersonationConfig{ + Groups: config.Impersonate.Groups, + Extra: config.Impersonate.Extra, + UserName: config.Impersonate.UserName, + }, + AuthProvider: config.AuthProvider, + AuthConfigPersister: config.AuthConfigPersister, + TLSClientConfig: TLSClientConfig{ + Insecure: config.TLSClientConfig.Insecure, + ServerName: config.TLSClientConfig.ServerName, + CertFile: config.TLSClientConfig.CertFile, + KeyFile: config.TLSClientConfig.KeyFile, + CAFile: config.TLSClientConfig.CAFile, + CertData: config.TLSClientConfig.CertData, + KeyData: config.TLSClientConfig.KeyData, + CAData: config.TLSClientConfig.CAData, + }, + UserAgent: config.UserAgent, + Transport: config.Transport, + WrapTransport: config.WrapTransport, + QPS: config.QPS, + Burst: config.Burst, + RateLimiter: config.RateLimiter, + Timeout: config.Timeout, + Dial: config.Dial, } } diff --git a/staging/src/k8s.io/client-go/rest/config_test.go b/staging/src/k8s.io/client-go/rest/config_test.go index ff851e6ade4..0e86442dbd0 100644 --- a/staging/src/k8s.io/client-go/rest/config_test.go +++ b/staging/src/k8s.io/client-go/rest/config_test.go @@ -35,6 +35,8 @@ import ( clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/client-go/util/flowcontrol" + "errors" + "github.com/stretchr/testify/assert" ) @@ -206,6 +208,19 @@ func (n *fakeNegotiatedSerializer) DecoderToVersion(serializer runtime.Decoder, return &fakeCodec{} } +var fakeDialFunc = func(network, addr string) (net.Conn, error) { + return nil, fakeDialerError +} +var fakeDialerError = errors.New("fakedialer") + +type fakeAuthProviderConfigPersister struct{} + +func (fakeAuthProviderConfigPersister) Persist(map[string]string) error { + return fakeAuthProviderConfigPersisterError +} + +var fakeAuthProviderConfigPersisterError = errors.New("fakeAuthProviderConfigPersisterError") + func TestAnonymousConfig(t *testing.T) { f := fuzz.New().NilChance(0.0).NumElements(1, 1) f.Funcs( @@ -268,9 +283,94 @@ func TestAnonymousConfig(t *testing.T) { actual.WrapTransport = nil expected.WrapTransport = nil } + if actual.Dial != nil { + _, actualError := actual.Dial("", "") + _, expectedError := actual.Dial("", "") + if !reflect.DeepEqual(expectedError, actualError) { + t.Fatalf("CopyConfig dropped the Dial field") + } + } else { + actual.Dial = nil + expected.Dial = nil + } if !reflect.DeepEqual(*actual, expected) { t.Fatalf("AnonymousClientConfig dropped unexpected fields, identify whether they are security related or not: %s", diff.ObjectGoPrintDiff(expected, actual)) } } } + +func TestCopyConfig(t *testing.T) { + f := fuzz.New().NilChance(0.0).NumElements(1, 1) + f.Funcs( + func(r *runtime.Codec, f fuzz.Continue) { + codec := &fakeCodec{} + f.Fuzz(codec) + *r = codec + }, + func(r *http.RoundTripper, f fuzz.Continue) { + roundTripper := &fakeRoundTripper{} + f.Fuzz(roundTripper) + *r = roundTripper + }, + func(fn *func(http.RoundTripper) http.RoundTripper, f fuzz.Continue) { + *fn = fakeWrapperFunc + }, + func(r *runtime.NegotiatedSerializer, f fuzz.Continue) { + serializer := &fakeNegotiatedSerializer{} + f.Fuzz(serializer) + *r = serializer + }, + func(r *flowcontrol.RateLimiter, f fuzz.Continue) { + limiter := &fakeLimiter{} + f.Fuzz(limiter) + *r = limiter + }, + func(r *AuthProviderConfigPersister, f fuzz.Continue) { + *r = fakeAuthProviderConfigPersister{} + }, + func(r *func(network, addr string) (net.Conn, error), f fuzz.Continue) { + *r = fakeDialFunc + }, + ) + for i := 0; i < 20; i++ { + original := &Config{} + f.Fuzz(original) + actual := CopyConfig(original) + expected := *original + + // this is the list of known risky fields, add to this list if a new field + // is added to Config, update CopyConfig to preserve the field otherwise. + + // The DeepEqual cannot handle the func comparison, so we just verify if the + // function return the expected object. + if actual.WrapTransport == nil || !reflect.DeepEqual(expected.WrapTransport(nil), &fakeRoundTripper{}) { + t.Fatalf("CopyConfig dropped the WrapTransport field") + } else { + actual.WrapTransport = nil + expected.WrapTransport = nil + } + if actual.Dial != nil { + _, actualError := actual.Dial("", "") + _, expectedError := actual.Dial("", "") + if !reflect.DeepEqual(expectedError, actualError) { + t.Fatalf("CopyConfig dropped the Dial field") + } + } + actual.Dial = nil + expected.Dial = nil + if actual.AuthConfigPersister != nil { + actualError := actual.AuthConfigPersister.Persist(nil) + expectedError := actual.AuthConfigPersister.Persist(nil) + if !reflect.DeepEqual(expectedError, actualError) { + t.Fatalf("CopyConfig dropped the Dial field") + } + } + actual.AuthConfigPersister = nil + expected.AuthConfigPersister = nil + + if !reflect.DeepEqual(*actual, expected) { + t.Fatalf("CopyConfig dropped unexpected fields, identify whether they are security related or not: %s", diff.ObjectReflectDiff(expected, *actual)) + } + } +} diff --git a/staging/src/k8s.io/sample-apiserver/pkg/cmd/server/start.go b/staging/src/k8s.io/sample-apiserver/pkg/cmd/server/start.go index b45290be1a8..f46d0327347 100644 --- a/staging/src/k8s.io/sample-apiserver/pkg/cmd/server/start.go +++ b/staging/src/k8s.io/sample-apiserver/pkg/cmd/server/start.go @@ -119,7 +119,7 @@ func (o WardleServerOptions) Config() (*apiserver.Config, error) { return nil, err } - if err := o.Admission.ApplyTo(&serverConfig.Config, serverConfig.SharedInformerFactory, nil, nil, serverConfig.ClientConfig, apiserver.Scheme, admissionInitializer); err != nil { + if err := o.Admission.ApplyTo(&serverConfig.Config, serverConfig.SharedInformerFactory, serverConfig.ClientConfig, serverConfig.ClientConfig, apiserver.Scheme, admissionInitializer); err != nil { return nil, err }