mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
update admission webhook to accept client config
This commit is contained in:
parent
78ada62c30
commit
0859798e8e
@ -70,6 +70,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apiserver/pkg/storage/etcd3/preflight:go_default_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/informers:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes: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/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:go_default_library",
|
||||||
"//vendor/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1:go_default_library",
|
"//vendor/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1:go_default_library",
|
||||||
|
@ -84,6 +84,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/version"
|
"k8s.io/kubernetes/pkg/version"
|
||||||
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap"
|
"k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap"
|
||||||
|
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
_ "k8s.io/kubernetes/pkg/util/reflector/prometheus" // for reflector metric registration
|
_ "k8s.io/kubernetes/pkg/util/reflector/prometheus" // for reflector metric registration
|
||||||
_ "k8s.io/kubernetes/pkg/util/workqueue/prometheus" // for workqueue 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,
|
client,
|
||||||
sharedInformers,
|
sharedInformers,
|
||||||
serviceResolver,
|
serviceResolver,
|
||||||
proxyTransport,
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, nil, fmt.Errorf("failed to create admission plugin initializer: %v", err)
|
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.
|
// TODO: this is the wrong cert/key pair.
|
||||||
// Given the generic case of webhook admission from a generic apiserver,
|
// 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.
|
// 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 {
|
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)
|
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(
|
err = s.Admission.ApplyTo(
|
||||||
genericConfig,
|
genericConfig,
|
||||||
versionedInformers,
|
versionedInformers,
|
||||||
certBytes,
|
|
||||||
keyBytes,
|
|
||||||
kubeClientConfig,
|
kubeClientConfig,
|
||||||
|
webhookClientConfig,
|
||||||
legacyscheme.Scheme,
|
legacyscheme.Scheme,
|
||||||
pluginInitializer)
|
pluginInitializer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -494,7 +501,7 @@ func BuildGenericConfig(s *options.ServerRunOptions, proxyTransport *http.Transp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BuildAdmissionPluginInitializer constructs the admission plugin initializer
|
// 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
|
var cloudConfig []byte
|
||||||
|
|
||||||
if s.CloudProvider.CloudConfigFile != "" {
|
if s.CloudProvider.CloudConfigFile != "" {
|
||||||
@ -515,7 +522,6 @@ func BuildAdmissionPluginInitializer(s *options.ServerRunOptions, client interna
|
|||||||
pluginInitializer := kubeapiserveradmission.NewPluginInitializer(client, sharedInformers, cloudConfig, restMapper, quotaRegistry)
|
pluginInitializer := kubeapiserveradmission.NewPluginInitializer(client, sharedInformers, cloudConfig, restMapper, quotaRegistry)
|
||||||
|
|
||||||
pluginInitializer = pluginInitializer.SetServiceResolver(serviceResolver)
|
pluginInitializer = pluginInitializer.SetServiceResolver(serviceResolver)
|
||||||
pluginInitializer = pluginInitializer.SetProxyTransport(proxyTransport)
|
|
||||||
|
|
||||||
return pluginInitializer, nil
|
return pluginInitializer, nil
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apiserver/pkg/server/storage:go_default_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/informers:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes: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",
|
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -42,6 +42,7 @@ import (
|
|||||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||||
clientgoinformers "k8s.io/client-go/informers"
|
clientgoinformers "k8s.io/client-go/informers"
|
||||||
clientgoclientset "k8s.io/client-go/kubernetes"
|
clientgoclientset "k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
openapicommon "k8s.io/kube-openapi/pkg/common"
|
openapicommon "k8s.io/kube-openapi/pkg/common"
|
||||||
federationv1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1"
|
federationv1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1"
|
||||||
"k8s.io/kubernetes/federation/cmd/federation-apiserver/app/options"
|
"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(
|
err = s.Admission.ApplyTo(
|
||||||
genericConfig,
|
genericConfig,
|
||||||
versionedInformers,
|
versionedInformers,
|
||||||
nil,
|
|
||||||
nil,
|
|
||||||
kubeClientConfig,
|
kubeClientConfig,
|
||||||
|
rest.AnonymousClientConfig(kubeClientConfig),
|
||||||
legacyscheme.Scheme,
|
legacyscheme.Scheme,
|
||||||
pluginInitializer,
|
pluginInitializer,
|
||||||
)
|
)
|
||||||
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package admission
|
package admission
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
@ -71,12 +70,6 @@ type ServiceResolver interface {
|
|||||||
ResolveEndpoint(namespace, name string) (*url.URL, error)
|
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 {
|
type PluginInitializer struct {
|
||||||
internalClient internalclientset.Interface
|
internalClient internalclientset.Interface
|
||||||
externalClient clientset.Interface
|
externalClient clientset.Interface
|
||||||
@ -86,9 +79,6 @@ type PluginInitializer struct {
|
|||||||
restMapper meta.RESTMapper
|
restMapper meta.RESTMapper
|
||||||
quotaRegistry quota.Registry
|
quotaRegistry quota.Registry
|
||||||
serviceResolver ServiceResolver
|
serviceResolver ServiceResolver
|
||||||
|
|
||||||
// for proving we are apiserver in call-outs
|
|
||||||
proxyTransport *http.Transport
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ admission.PluginInitializer = &PluginInitializer{}
|
var _ admission.PluginInitializer = &PluginInitializer{}
|
||||||
@ -118,12 +108,6 @@ func (i *PluginInitializer) SetServiceResolver(s ServiceResolver) *PluginInitial
|
|||||||
return i
|
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
|
// Initialize checks the initialization interfaces implemented by each plugin
|
||||||
// and provide the appropriate initialization data
|
// and provide the appropriate initialization data
|
||||||
func (i *PluginInitializer) Initialize(plugin admission.Interface) {
|
func (i *PluginInitializer) Initialize(plugin admission.Interface) {
|
||||||
@ -150,8 +134,4 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) {
|
|||||||
if wants, ok := plugin.(WantsServiceResolver); ok {
|
if wants, ok := plugin.(WantsServiceResolver); ok {
|
||||||
wants.SetServiceResolver(i.serviceResolver)
|
wants.SetServiceResolver(i.serviceResolver)
|
||||||
}
|
}
|
||||||
|
|
||||||
if wants, ok := plugin.(WantsProxyTransport); ok {
|
|
||||||
wants.SetProxyTransport(i.proxyTransport)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) {
|
|||||||
whiteList: whiteList,
|
whiteList: whiteList,
|
||||||
}
|
}
|
||||||
|
|
||||||
genericPluginInitializer, err := initializer.New(nil, nil, fakeAuthorizer{}, nil, nil, nil)
|
genericPluginInitializer, err := initializer.New(nil, nil, fakeAuthorizer{}, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ go_test(
|
|||||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/admission: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/apiserver/pkg/authentication/user:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,10 +21,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
@ -105,19 +102,18 @@ type GenericAdmissionWebhook struct {
|
|||||||
hookSource WebhookSource
|
hookSource WebhookSource
|
||||||
serviceResolver admissioninit.ServiceResolver
|
serviceResolver admissioninit.ServiceResolver
|
||||||
negotiatedSerializer runtime.NegotiatedSerializer
|
negotiatedSerializer runtime.NegotiatedSerializer
|
||||||
clientCert []byte
|
|
||||||
clientKey []byte
|
restClientConfig *rest.Config
|
||||||
proxyTransport *http.Transport
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_ = admissioninit.WantsServiceResolver(&GenericAdmissionWebhook{})
|
_ = admissioninit.WantsServiceResolver(&GenericAdmissionWebhook{})
|
||||||
_ = genericadmissioninit.WantsClientCert(&GenericAdmissionWebhook{})
|
_ = genericadmissioninit.WantsWebhookRESTClientConfig(&GenericAdmissionWebhook{})
|
||||||
_ = genericadmissioninit.WantsExternalKubeClientSet(&GenericAdmissionWebhook{})
|
_ = genericadmissioninit.WantsExternalKubeClientSet(&GenericAdmissionWebhook{})
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *GenericAdmissionWebhook) SetProxyTransport(pt *http.Transport) {
|
func (a *GenericAdmissionWebhook) SetWebhookRESTClientConfig(in *rest.Config) {
|
||||||
a.proxyTransport = pt
|
a.restClientConfig = in
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetServiceResolver sets a service resolver for the webhook admission plugin.
|
// 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) {
|
func (a *GenericAdmissionWebhook) SetExternalKubeClientSet(client clientset.Interface) {
|
||||||
a.hookSource = configuration.NewExternalAdmissionHookConfigurationManager(client.Admissionregistration().ExternalAdmissionHookConfigurations())
|
a.hookSource = configuration.NewExternalAdmissionHookConfigurationManager(client.Admissionregistration().ExternalAdmissionHookConfigurations())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *GenericAdmissionWebhook) Validate() error {
|
func (a *GenericAdmissionWebhook) Validate() error {
|
||||||
if a.clientCert == nil || a.clientKey == nil {
|
if a.restClientConfig == nil {
|
||||||
return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a client certificate and the private key to be provided")
|
return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a restClientConfig to be provided")
|
||||||
}
|
}
|
||||||
if a.hookSource == nil {
|
if a.hookSource == nil {
|
||||||
return fmt.Errorf("the GenericAdmissionWebhook admission plugin requires a Kubernetes client to be provided")
|
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
|
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
|
// TODO: cache these instead of constructing one each time
|
||||||
cfg := &rest.Config{
|
cfg := rest.CopyConfig(a.restClientConfig)
|
||||||
Host: u.Host,
|
cfg.Host = u.Host
|
||||||
APIPath: path.Join(u.Path, h.ClientConfig.URLPath),
|
cfg.APIPath = path.Join(u.Path, h.ClientConfig.URLPath)
|
||||||
TLSClientConfig: rest.TLSClientConfig{
|
cfg.TLSClientConfig.ServerName = h.ClientConfig.Service.Name + "." + h.ClientConfig.Service.Namespace + ".svc"
|
||||||
ServerName: h.ClientConfig.Service.Name + "." + h.ClientConfig.Service.Namespace + ".svc",
|
cfg.TLSClientConfig.CAData = h.ClientConfig.CABundle
|
||||||
CAData: h.ClientConfig.CABundle,
|
cfg.ContentConfig.NegotiatedSerializer = a.negotiatedSerializer
|
||||||
CertData: a.clientCert,
|
|
||||||
KeyData: a.clientKey,
|
|
||||||
},
|
|
||||||
UserAgent: "kube-apiserver-admission",
|
|
||||||
Timeout: 30 * time.Second,
|
|
||||||
ContentConfig: rest.ContentConfig{
|
|
||||||
NegotiatedSerializer: a.negotiatedSerializer,
|
|
||||||
},
|
|
||||||
Dial: dial,
|
|
||||||
}
|
|
||||||
return rest.UnversionedRESTClientFor(cfg)
|
return rest.UnversionedRESTClientFor(cfg)
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"k8s.io/apiserver/pkg/authentication/user"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
|
|
||||||
@ -89,8 +90,10 @@ func TestAdmit(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
wh.clientCert = clientCert
|
wh.restClientConfig = &rest.Config{}
|
||||||
wh.clientKey = clientKey
|
wh.restClientConfig.TLSClientConfig.CAData = caCert
|
||||||
|
wh.restClientConfig.TLSClientConfig.CertData = clientCert
|
||||||
|
wh.restClientConfig.TLSClientConfig.KeyData = clientKey
|
||||||
|
|
||||||
// Set up a test object for the call
|
// Set up a test object for the call
|
||||||
kind := api.Kind("Pod").WithVersion("v1")
|
kind := api.Kind("Pod").WithVersion("v1")
|
||||||
|
@ -19,6 +19,7 @@ go_library(
|
|||||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_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/informers:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
|
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,16 +22,15 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pluginInitializer struct {
|
type pluginInitializer struct {
|
||||||
externalClient kubernetes.Interface
|
externalClient kubernetes.Interface
|
||||||
externalInformers informers.SharedInformerFactory
|
externalInformers informers.SharedInformerFactory
|
||||||
authorizer authorizer.Authorizer
|
authorizer authorizer.Authorizer
|
||||||
// serverIdentifyingClientCert used to provide identity when calling out to admission plugins
|
// webhookRESTClientConfig provies a client used to contact webhooks
|
||||||
serverIdentifyingClientCert []byte
|
webhookRESTClientConfig *rest.Config
|
||||||
// serverIdentifyingClientKey private key for the client certificate used when calling out to admission plugins
|
|
||||||
serverIdentifyingClientKey []byte
|
|
||||||
scheme *runtime.Scheme
|
scheme *runtime.Scheme
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,16 +40,14 @@ func New(
|
|||||||
extClientset kubernetes.Interface,
|
extClientset kubernetes.Interface,
|
||||||
extInformers informers.SharedInformerFactory,
|
extInformers informers.SharedInformerFactory,
|
||||||
authz authorizer.Authorizer,
|
authz authorizer.Authorizer,
|
||||||
serverIdentifyingClientCert,
|
webhookRESTClientConfig *rest.Config,
|
||||||
serverIdentifyingClientKey []byte,
|
|
||||||
scheme *runtime.Scheme,
|
scheme *runtime.Scheme,
|
||||||
) (pluginInitializer, error) {
|
) (pluginInitializer, error) {
|
||||||
return pluginInitializer{
|
return pluginInitializer{
|
||||||
externalClient: extClientset,
|
externalClient: extClientset,
|
||||||
externalInformers: extInformers,
|
externalInformers: extInformers,
|
||||||
authorizer: authz,
|
authorizer: authz,
|
||||||
serverIdentifyingClientCert: serverIdentifyingClientCert,
|
webhookRESTClientConfig: webhookRESTClientConfig,
|
||||||
serverIdentifyingClientKey: serverIdentifyingClientKey,
|
|
||||||
scheme: scheme,
|
scheme: scheme,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -70,8 +67,8 @@ func (i pluginInitializer) Initialize(plugin admission.Interface) {
|
|||||||
wants.SetAuthorizer(i.authorizer)
|
wants.SetAuthorizer(i.authorizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if wants, ok := plugin.(WantsClientCert); ok {
|
if wants, ok := plugin.(WantsWebhookRESTClientConfig); ok {
|
||||||
wants.SetClientCert(i.serverIdentifyingClientCert, i.serverIdentifyingClientKey)
|
wants.SetWebhookRESTClientConfig(i.webhookRESTClientConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
if wants, ok := plugin.(WantsScheme); ok {
|
if wants, ok := plugin.(WantsScheme); ok {
|
||||||
|
@ -33,7 +33,7 @@ import (
|
|||||||
// the WantsScheme interface is implemented by a plugin.
|
// the WantsScheme interface is implemented by a plugin.
|
||||||
func TestWantsScheme(t *testing.T) {
|
func TestWantsScheme(t *testing.T) {
|
||||||
scheme := runtime.NewScheme()
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -47,7 +47,7 @@ func TestWantsScheme(t *testing.T) {
|
|||||||
// TestWantsAuthorizer ensures that the authorizer is injected
|
// TestWantsAuthorizer ensures that the authorizer is injected
|
||||||
// when the WantsAuthorizer interface is implemented by a plugin.
|
// when the WantsAuthorizer interface is implemented by a plugin.
|
||||||
func TestWantsAuthorizer(t *testing.T) {
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ func TestWantsAuthorizer(t *testing.T) {
|
|||||||
// when the WantsExternalKubeClientSet interface is implemented by a plugin.
|
// when the WantsExternalKubeClientSet interface is implemented by a plugin.
|
||||||
func TestWantsExternalKubeClientSet(t *testing.T) {
|
func TestWantsExternalKubeClientSet(t *testing.T) {
|
||||||
cs := &fake.Clientset{}
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -78,7 +78,7 @@ func TestWantsExternalKubeClientSet(t *testing.T) {
|
|||||||
func TestWantsExternalKubeInformerFactory(t *testing.T) {
|
func TestWantsExternalKubeInformerFactory(t *testing.T) {
|
||||||
cs := &fake.Clientset{}
|
cs := &fake.Clientset{}
|
||||||
sf := informers.NewSharedInformerFactory(cs, time.Duration(1)*time.Second)
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
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
|
// WantExternalKubeInformerFactory is a test stub that fulfills the WantsExternalKubeInformerFactory interface
|
||||||
type WantExternalKubeInformerFactory struct {
|
type WantExternalKubeInformerFactory struct {
|
||||||
sf informers.SharedInformerFactory
|
sf informers.SharedInformerFactory
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WantsExternalKubeClientSet defines a function which sets external ClientSet for admission plugins that need it
|
// WantsExternalKubeClientSet defines a function which sets external ClientSet for admission plugins that need it
|
||||||
@ -42,10 +43,10 @@ type WantsAuthorizer interface {
|
|||||||
admission.Validator
|
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.
|
// plugins that need to make calls and prove their identity.
|
||||||
type WantsClientCert interface {
|
type WantsWebhookRESTClientConfig interface {
|
||||||
SetClientCert(cert, key []byte)
|
SetWebhookRESTClientConfig(*rest.Config)
|
||||||
admission.Validator
|
admission.Validator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ func newHandlerForTestWithClock(c clientset.Interface, cacheClock clock.Clock) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, f, err
|
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 {
|
if err != nil {
|
||||||
return handler, f, err
|
return handler, f, err
|
||||||
}
|
}
|
||||||
|
@ -80,9 +80,8 @@ func (a *AdmissionOptions) AddFlags(fs *pflag.FlagSet) {
|
|||||||
func (a *AdmissionOptions) ApplyTo(
|
func (a *AdmissionOptions) ApplyTo(
|
||||||
c *server.Config,
|
c *server.Config,
|
||||||
informers informers.SharedInformerFactory,
|
informers informers.SharedInformerFactory,
|
||||||
serverIdentifyingClientCert []byte,
|
kubeAPIServerClientConfig *rest.Config,
|
||||||
serverIdentifyingClientKey []byte,
|
webhookClientConfig *rest.Config,
|
||||||
clientConfig *rest.Config,
|
|
||||||
scheme *runtime.Scheme,
|
scheme *runtime.Scheme,
|
||||||
pluginInitializers ...admission.PluginInitializer,
|
pluginInitializers ...admission.PluginInitializer,
|
||||||
) error {
|
) error {
|
||||||
@ -96,11 +95,11 @@ func (a *AdmissionOptions) ApplyTo(
|
|||||||
return fmt.Errorf("failed to read plugin config: %v", err)
|
return fmt.Errorf("failed to read plugin config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
clientset, err := kubernetes.NewForConfig(clientConfig)
|
clientset, err := kubernetes.NewForConfig(kubeAPIServerClientConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -420,5 +420,45 @@ func AnonymousClientConfig(config *Config) *Config {
|
|||||||
QPS: config.QPS,
|
QPS: config.QPS,
|
||||||
Burst: config.Burst,
|
Burst: config.Burst,
|
||||||
Timeout: config.Timeout,
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@ import (
|
|||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
"k8s.io/client-go/util/flowcontrol"
|
"k8s.io/client-go/util/flowcontrol"
|
||||||
|
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -206,6 +208,19 @@ func (n *fakeNegotiatedSerializer) DecoderToVersion(serializer runtime.Decoder,
|
|||||||
return &fakeCodec{}
|
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) {
|
func TestAnonymousConfig(t *testing.T) {
|
||||||
f := fuzz.New().NilChance(0.0).NumElements(1, 1)
|
f := fuzz.New().NilChance(0.0).NumElements(1, 1)
|
||||||
f.Funcs(
|
f.Funcs(
|
||||||
@ -268,9 +283,94 @@ func TestAnonymousConfig(t *testing.T) {
|
|||||||
actual.WrapTransport = nil
|
actual.WrapTransport = nil
|
||||||
expected.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) {
|
if !reflect.DeepEqual(*actual, expected) {
|
||||||
t.Fatalf("AnonymousClientConfig dropped unexpected fields, identify whether they are security related or not: %s", diff.ObjectGoPrintDiff(expected, actual))
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -119,7 +119,7 @@ func (o WardleServerOptions) Config() (*apiserver.Config, error) {
|
|||||||
return nil, err
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user