Merge pull request #54156 from deads2k/admission-06-restclient

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

update admission webhook to accept client config

Fixes https://github.com/kubernetes/kubernetes/issues/53827

This plumbs a complete client through the plugin initializer for admission webhooks.  It achieves parity with our existing webhooks and provides flexibility if people want to do something special or different.  Easy things are easy, hard things are possible.  This does not change behavior for kube-apiserver.

@kubernetes/sig-auth-api-reviews @kubernetes/sig-api-machinery-bugs
This commit is contained in:
Kubernetes Submit Queue 2017-10-19 10:55:23 -07:00 committed by GitHub
commit f07b359e5b
18 changed files with 202 additions and 110 deletions

View File

@ -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",

View File

@ -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
}

View File

@ -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",
],
)

View File

@ -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,
)

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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",
],
)

View File

@ -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)
}

View File

@ -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")

View File

@ -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",
],
)

View File

@ -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 {

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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,
}
}

View File

@ -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))
}
}
}

View File

@ -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
}