mirror of https://github.com/k3s-io/k3s
update admission webhook to handle multiple auth domains
parent
3ae0b84e0b
commit
fd4ab3e061
|
@ -466,26 +466,6 @@ func BuildGenericConfig(s *options.ServerRunOptions, proxyTransport *http.Transp
|
|||
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.
|
||||
// Read client cert/key for plugins that need to make calls out
|
||||
certBytes, keyBytes := []byte{}, []byte{}
|
||||
if len(s.ProxyClientCertFile) > 0 && len(s.ProxyClientKeyFile) > 0 {
|
||||
var err error
|
||||
certBytes, err = ioutil.ReadFile(s.ProxyClientCertFile)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, nil, fmt.Errorf("failed to read proxy client cert file from: %s, err: %v", s.ProxyClientCertFile, err)
|
||||
}
|
||||
keyBytes, err = ioutil.ReadFile(s.ProxyClientKeyFile)
|
||||
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,
|
||||
|
|
|
@ -5,6 +5,8 @@ go_library(
|
|||
srcs = [
|
||||
"admission.go",
|
||||
"admissionreview.go",
|
||||
"authentication.go",
|
||||
"config.go",
|
||||
"doc.go",
|
||||
"rules.go",
|
||||
"serviceresolver.go",
|
||||
|
@ -21,15 +23,17 @@ go_library(
|
|||
"//vendor/k8s.io/apimachinery/pkg/api/errors: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/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission/configuration:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission/initializer: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/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -37,6 +41,7 @@ go_test(
|
|||
name = "go_default_test",
|
||||
srcs = [
|
||||
"admission_test.go",
|
||||
"authentication_test.go",
|
||||
"certs_test.go",
|
||||
"rules_test.go",
|
||||
"serviceresolver_test.go",
|
||||
|
@ -49,11 +54,14 @@ go_test(
|
|||
"//pkg/apis/admission/install:go_default_library",
|
||||
"//vendor/k8s.io/api/admission/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/api/admissionregistration/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff: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",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
@ -30,10 +31,10 @@ import (
|
|||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/configuration"
|
||||
genericadmissioninit "k8s.io/apiserver/pkg/admission/initializer"
|
||||
|
@ -42,17 +43,9 @@ import (
|
|||
admissioninit "k8s.io/kubernetes/pkg/kubeapiserver/admission"
|
||||
|
||||
// install the clientgo admission API for use with api registry
|
||||
"path"
|
||||
|
||||
_ "k8s.io/kubernetes/pkg/apis/admission/install"
|
||||
)
|
||||
|
||||
var (
|
||||
groupVersions = []schema.GroupVersion{
|
||||
admissionv1alpha1.SchemeGroupVersion,
|
||||
}
|
||||
)
|
||||
|
||||
type ErrCallingWebhook struct {
|
||||
WebhookName string
|
||||
Reason error
|
||||
|
@ -68,7 +61,7 @@ func (e *ErrCallingWebhook) Error() string {
|
|||
// Register registers a plugin
|
||||
func Register(plugins *admission.Plugins) {
|
||||
plugins.Register("GenericAdmissionWebhook", func(configFile io.Reader) (admission.Interface, error) {
|
||||
plugin, err := NewGenericAdmissionWebhook()
|
||||
plugin, err := NewGenericAdmissionWebhook(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -84,7 +77,23 @@ type WebhookSource interface {
|
|||
}
|
||||
|
||||
// NewGenericAdmissionWebhook returns a generic admission webhook plugin.
|
||||
func NewGenericAdmissionWebhook() (*GenericAdmissionWebhook, error) {
|
||||
func NewGenericAdmissionWebhook(configFile io.Reader) (*GenericAdmissionWebhook, error) {
|
||||
kubeconfigFile := ""
|
||||
if configFile != nil {
|
||||
// TODO: move this to a versioned configuration file format
|
||||
var config AdmissionConfig
|
||||
d := yaml.NewYAMLOrJSONDecoder(configFile, 4096)
|
||||
err := d.Decode(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kubeconfigFile = config.KubeConfigFile
|
||||
}
|
||||
authInfoResolver, err := newDefaultAuthenticationInfoResolver(kubeconfigFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &GenericAdmissionWebhook{
|
||||
Handler: admission.NewHandler(
|
||||
admission.Connect,
|
||||
|
@ -92,7 +101,8 @@ func NewGenericAdmissionWebhook() (*GenericAdmissionWebhook, error) {
|
|||
admission.Delete,
|
||||
admission.Update,
|
||||
),
|
||||
serviceResolver: defaultServiceResolver{},
|
||||
authInfoResolver: authInfoResolver,
|
||||
serviceResolver: defaultServiceResolver{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -103,7 +113,7 @@ type GenericAdmissionWebhook struct {
|
|||
serviceResolver admissioninit.ServiceResolver
|
||||
negotiatedSerializer runtime.NegotiatedSerializer
|
||||
|
||||
restClientConfig *rest.Config
|
||||
authInfoResolver AuthenticationInfoResolver
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -112,8 +122,14 @@ var (
|
|||
_ = genericadmissioninit.WantsExternalKubeClientSet(&GenericAdmissionWebhook{})
|
||||
)
|
||||
|
||||
// TODO find a better way wire this, but keep this pull small for now.
|
||||
func (a *GenericAdmissionWebhook) SetWebhookRESTClientConfig(in *rest.Config) {
|
||||
a.restClientConfig = in
|
||||
if in != nil {
|
||||
a.authInfoResolver = &dialOverridingAuthenticationInfoResolver{
|
||||
dialFn: in.Dial,
|
||||
delegate: a.authInfoResolver,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetServiceResolver sets a service resolver for the webhook admission plugin.
|
||||
|
@ -138,9 +154,6 @@ func (a *GenericAdmissionWebhook) SetExternalKubeClientSet(client clientset.Inte
|
|||
}
|
||||
|
||||
func (a *GenericAdmissionWebhook) Validate() error {
|
||||
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")
|
||||
}
|
||||
|
@ -266,16 +279,21 @@ func (a *GenericAdmissionWebhook) callHook(ctx context.Context, h *v1alpha1.Exte
|
|||
}
|
||||
|
||||
func (a *GenericAdmissionWebhook) hookClient(h *v1alpha1.ExternalAdmissionHook) (*rest.RESTClient, error) {
|
||||
serverName := h.ClientConfig.Service.Name + "." + h.ClientConfig.Service.Namespace + ".svc"
|
||||
u, err := a.serviceResolver.ResolveEndpoint(h.ClientConfig.Service.Namespace, h.ClientConfig.Service.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: cache these instead of constructing one each time
|
||||
cfg := rest.CopyConfig(a.restClientConfig)
|
||||
restConfig, err := a.authInfoResolver.ClientConfigFor(serverName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg := rest.CopyConfig(restConfig)
|
||||
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.ServerName = serverName
|
||||
cfg.TLSClientConfig.CAData = h.ClientConfig.CABundle
|
||||
cfg.ContentConfig.NegotiatedSerializer = a.negotiatedSerializer
|
||||
return rest.UnversionedRESTClientFor(cfg)
|
||||
|
|
|
@ -32,10 +32,10 @@ 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"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
_ "k8s.io/kubernetes/pkg/apis/admission/install"
|
||||
)
|
||||
|
||||
|
@ -86,14 +86,19 @@ func TestAdmit(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("this should never happen? %v", err)
|
||||
}
|
||||
wh, err := NewGenericAdmissionWebhook()
|
||||
wh, err := NewGenericAdmissionWebhook(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wh.restClientConfig = &rest.Config{}
|
||||
wh.restClientConfig.TLSClientConfig.CAData = caCert
|
||||
wh.restClientConfig.TLSClientConfig.CertData = clientCert
|
||||
wh.restClientConfig.TLSClientConfig.KeyData = clientKey
|
||||
wh.authInfoResolver = &fakeAuthenticationInfoResolver{
|
||||
restConfig: &rest.Config{
|
||||
TLSClientConfig: rest.TLSClientConfig{
|
||||
CAData: caCert,
|
||||
CertData: clientCert,
|
||||
KeyData: clientKey,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Set up a test object for the call
|
||||
kind := api.Kind("Pod").WithVersion("v1")
|
||||
|
@ -318,3 +323,11 @@ func webhookHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.NotFound(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeAuthenticationInfoResolver struct {
|
||||
restConfig *rest.Config
|
||||
}
|
||||
|
||||
func (c *fakeAuthenticationInfoResolver) ClientConfigFor(server string) (*rest.Config, error) {
|
||||
return c.restConfig, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
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 webhook
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
type AuthenticationInfoResolver interface {
|
||||
ClientConfigFor(server string) (*rest.Config, error)
|
||||
}
|
||||
|
||||
type defaultAuthenticationInfoResolver struct {
|
||||
kubeconfig clientcmdapi.Config
|
||||
}
|
||||
|
||||
func newDefaultAuthenticationInfoResolver(kubeconfigFile string) (AuthenticationInfoResolver, error) {
|
||||
if len(kubeconfigFile) == 0 {
|
||||
return &defaultAuthenticationInfoResolver{}, nil
|
||||
}
|
||||
|
||||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
loadingRules.ExplicitPath = kubeconfigFile
|
||||
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
|
||||
clientConfig, err := loader.RawConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &defaultAuthenticationInfoResolver{kubeconfig: clientConfig}, nil
|
||||
}
|
||||
|
||||
func (c *defaultAuthenticationInfoResolver) ClientConfigFor(server string) (*rest.Config, error) {
|
||||
// exact match
|
||||
if authConfig, ok := c.kubeconfig.AuthInfos[server]; ok {
|
||||
return restConfigFromKubeconfig(authConfig)
|
||||
}
|
||||
|
||||
// star prefixed match
|
||||
serverSteps := strings.Split(server, ".")
|
||||
for i := 1; i < len(serverSteps); i++ {
|
||||
nickName := "*." + strings.Join(serverSteps[i:], ".")
|
||||
if authConfig, ok := c.kubeconfig.AuthInfos[nickName]; ok {
|
||||
return restConfigFromKubeconfig(authConfig)
|
||||
}
|
||||
}
|
||||
|
||||
// if we're trying to hit the kube-apiserver and there wasn't an explicit config, use the in-cluster config
|
||||
if server == "kubernetes.default.svc" {
|
||||
// if we can find an in-cluster-config use that. If we can't, fall through.
|
||||
inClusterConfig, err := rest.InClusterConfig()
|
||||
if err == nil {
|
||||
return setGlobalDefaults(inClusterConfig), nil
|
||||
}
|
||||
}
|
||||
|
||||
// star (default) match
|
||||
if authConfig, ok := c.kubeconfig.AuthInfos["*"]; ok {
|
||||
return restConfigFromKubeconfig(authConfig)
|
||||
}
|
||||
|
||||
// use the current context from the kubeconfig if possible
|
||||
if len(c.kubeconfig.CurrentContext) > 0 {
|
||||
if currContext, ok := c.kubeconfig.Contexts[c.kubeconfig.CurrentContext]; ok {
|
||||
if len(currContext.AuthInfo) > 0 {
|
||||
if currAuth, ok := c.kubeconfig.AuthInfos[currContext.AuthInfo]; ok {
|
||||
return restConfigFromKubeconfig(currAuth)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// anonymous
|
||||
return setGlobalDefaults(&rest.Config{}), nil
|
||||
}
|
||||
|
||||
func restConfigFromKubeconfig(configAuthInfo *clientcmdapi.AuthInfo) (*rest.Config, error) {
|
||||
config := &rest.Config{}
|
||||
|
||||
// blindly overwrite existing values based on precedence
|
||||
if len(configAuthInfo.Token) > 0 {
|
||||
config.BearerToken = configAuthInfo.Token
|
||||
} else if len(configAuthInfo.TokenFile) > 0 {
|
||||
tokenBytes, err := ioutil.ReadFile(configAuthInfo.TokenFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.BearerToken = string(tokenBytes)
|
||||
}
|
||||
if len(configAuthInfo.Impersonate) > 0 {
|
||||
config.Impersonate = rest.ImpersonationConfig{
|
||||
UserName: configAuthInfo.Impersonate,
|
||||
Groups: configAuthInfo.ImpersonateGroups,
|
||||
Extra: configAuthInfo.ImpersonateUserExtra,
|
||||
}
|
||||
}
|
||||
if len(configAuthInfo.ClientCertificate) > 0 || len(configAuthInfo.ClientCertificateData) > 0 {
|
||||
config.CertFile = configAuthInfo.ClientCertificate
|
||||
config.CertData = configAuthInfo.ClientCertificateData
|
||||
config.KeyFile = configAuthInfo.ClientKey
|
||||
config.KeyData = configAuthInfo.ClientKeyData
|
||||
}
|
||||
if len(configAuthInfo.Username) > 0 || len(configAuthInfo.Password) > 0 {
|
||||
config.Username = configAuthInfo.Username
|
||||
config.Password = configAuthInfo.Password
|
||||
}
|
||||
if configAuthInfo.AuthProvider != nil {
|
||||
return nil, fmt.Errorf("auth provider not supported")
|
||||
}
|
||||
|
||||
return setGlobalDefaults(config), nil
|
||||
}
|
||||
|
||||
func setGlobalDefaults(config *rest.Config) *rest.Config {
|
||||
config.UserAgent = "kube-apiserver-admission"
|
||||
config.Timeout = 30 * time.Second
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type dialOverridingAuthenticationInfoResolver struct {
|
||||
dialFn func(network, addr string) (net.Conn, error)
|
||||
delegate AuthenticationInfoResolver
|
||||
}
|
||||
|
||||
func (c *dialOverridingAuthenticationInfoResolver) ClientConfigFor(server string) (*rest.Config, error) {
|
||||
cfg, err := c.delegate.ClientConfigFor(server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg.Dial = c.dialFn
|
||||
return cfg, nil
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
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 webhook
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/client-go/rest"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
func TestAuthenticationDetection(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
kubeconfig clientcmdapi.Config
|
||||
serverName string
|
||||
expected rest.Config
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
serverName: "foo.com",
|
||||
},
|
||||
{
|
||||
name: "fallback to current context",
|
||||
serverName: "foo.com",
|
||||
kubeconfig: clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"bar.com": {Token: "bar"},
|
||||
},
|
||||
Contexts: map[string]*clientcmdapi.Context{
|
||||
"ctx": {
|
||||
AuthInfo: "bar.com",
|
||||
},
|
||||
},
|
||||
CurrentContext: "ctx",
|
||||
},
|
||||
expected: rest.Config{BearerToken: "bar"},
|
||||
},
|
||||
{
|
||||
name: "exact match",
|
||||
serverName: "foo.com",
|
||||
kubeconfig: clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"foo.com": {Token: "foo"},
|
||||
"*.com": {Token: "foo-star"},
|
||||
"bar.com": {Token: "bar"},
|
||||
},
|
||||
},
|
||||
expected: rest.Config{BearerToken: "foo"},
|
||||
},
|
||||
{
|
||||
name: "partial star match",
|
||||
serverName: "foo.com",
|
||||
kubeconfig: clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"*.com": {Token: "foo-star"},
|
||||
"bar.com": {Token: "bar"},
|
||||
},
|
||||
},
|
||||
expected: rest.Config{BearerToken: "foo-star"},
|
||||
},
|
||||
{
|
||||
name: "full star match",
|
||||
serverName: "foo.com",
|
||||
kubeconfig: clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"*": {Token: "star"},
|
||||
"bar.com": {Token: "bar"},
|
||||
},
|
||||
},
|
||||
expected: rest.Config{BearerToken: "star"},
|
||||
},
|
||||
{
|
||||
name: "skip bad in cluster config",
|
||||
serverName: "kubernetes.default.svc",
|
||||
kubeconfig: clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"*": {Token: "star"},
|
||||
"bar.com": {Token: "bar"},
|
||||
},
|
||||
},
|
||||
expected: rest.Config{BearerToken: "star"},
|
||||
},
|
||||
{
|
||||
name: "most selective",
|
||||
serverName: "one.two.three.com",
|
||||
kubeconfig: clientcmdapi.Config{
|
||||
AuthInfos: map[string]*clientcmdapi.AuthInfo{
|
||||
"*.two.three.com": {Token: "first"},
|
||||
"*.three.com": {Token: "second"},
|
||||
"*.com": {Token: "third"},
|
||||
},
|
||||
},
|
||||
expected: rest.Config{BearerToken: "first"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
resolver := defaultAuthenticationInfoResolver{kubeconfig: tc.kubeconfig}
|
||||
actual, err := resolver.ClientConfigFor(tc.serverName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual.UserAgent = ""
|
||||
actual.Timeout = 0
|
||||
|
||||
if !equality.Semantic.DeepEqual(*actual, tc.expected) {
|
||||
t.Errorf("%v", diff.ObjectReflectDiff(tc.expected, *actual))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
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 webhook
|
||||
|
||||
// AdmissionConfig holds config data that is unique to each API server.
|
||||
type AdmissionConfig struct {
|
||||
KubeConfigFile string `json:"kubeConfigFile"`
|
||||
}
|
|
@ -30,6 +30,7 @@ type pluginInitializer struct {
|
|||
externalInformers informers.SharedInformerFactory
|
||||
authorizer authorizer.Authorizer
|
||||
// webhookRESTClientConfig provies a client used to contact webhooks
|
||||
// TODO clean out cruft
|
||||
webhookRESTClientConfig *rest.Config
|
||||
scheme *runtime.Scheme
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue