Merge pull request #106155 from enj/enj/i/webhook_rest_config

webhook: use rest.Config instead of kubeconfig file as input
This commit is contained in:
Kubernetes Prow Robot 2022-03-18 12:23:56 -07:00 committed by GitHub
commit cd6af9e64b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 142 additions and 64 deletions

View File

@ -35,6 +35,7 @@ import (
"k8s.io/apiserver/pkg/authentication/token/tokenfile"
tokenunion "k8s.io/apiserver/pkg/authentication/token/union"
"k8s.io/apiserver/pkg/server/dynamiccertificates"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
"k8s.io/apiserver/plugin/pkg/authenticator/token/oidc"
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
"k8s.io/kube-openapi/pkg/validation/spec"
@ -299,7 +300,11 @@ func newWebhookTokenAuthenticator(config Config) (authenticator.Token, error) {
return nil, errors.New("retry backoff parameters for authentication webhook has not been specified")
}
webhookTokenAuthenticator, err := webhook.New(config.WebhookTokenAuthnConfigFile, config.WebhookTokenAuthnVersion, config.APIAudiences, *config.WebhookRetryBackoff, config.CustomDial)
clientConfig, err := webhookutil.LoadKubeconfig(config.WebhookTokenAuthnConfigFile, config.CustomDial)
if err != nil {
return nil, err
}
webhookTokenAuthenticator, err := webhook.New(clientConfig, config.WebhookTokenAuthnVersion, config.APIAudiences, *config.WebhookRetryBackoff)
if err != nil {
return nil, err
}

View File

@ -26,6 +26,7 @@ import (
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
"k8s.io/apiserver/pkg/authorization/union"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
"k8s.io/apiserver/plugin/pkg/authorizer/webhook"
versionedinformers "k8s.io/client-go/informers"
"k8s.io/kubernetes/pkg/auth/authorizer/abac"
@ -114,12 +115,16 @@ func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, erro
if config.WebhookRetryBackoff == nil {
return nil, nil, errors.New("retry backoff parameters for authorization webhook has not been specified")
}
webhookAuthorizer, err := webhook.New(config.WebhookConfigFile,
clientConfig, err := webhookutil.LoadKubeconfig(config.WebhookConfigFile, config.CustomDial)
if err != nil {
return nil, nil, err
}
webhookAuthorizer, err := webhook.New(clientConfig,
config.WebhookVersion,
config.WebhookCacheAuthorizedTTL,
config.WebhookCacheUnauthorizedTTL,
*config.WebhookRetryBackoff,
config.CustomDial)
)
if err != nil {
return nil, nil, err
}

View File

@ -261,8 +261,12 @@ func NewImagePolicyWebhook(configFile io.Reader) (*Plugin, error) {
return nil, err
}
clientConfig, err := webhook.LoadKubeconfig(whConfig.KubeConfigFile, nil)
if err != nil {
return nil, err
}
retryBackoff := webhook.DefaultRetryBackoffWithInitialDelay(whConfig.RetryBackoff)
gw, err := webhook.NewGenericWebhook(legacyscheme.Scheme, legacyscheme.Codecs, whConfig.KubeConfigFile, groupVersions, retryBackoff, nil)
gw, err := webhook.NewGenericWebhook(legacyscheme.Scheme, legacyscheme.Codecs, clientConfig, groupVersions, retryBackoff)
if err != nil {
return nil, err
}

View File

@ -72,42 +72,19 @@ func DefaultShouldRetry(err error) bool {
return false
}
// NewGenericWebhook creates a new GenericWebhook from the provided kubeconfig file.
func NewGenericWebhook(scheme *runtime.Scheme, codecFactory serializer.CodecFactory, kubeConfigFile string, groupVersions []schema.GroupVersion, retryBackoff wait.Backoff, customDial utilnet.DialFunc) (*GenericWebhook, error) {
return newGenericWebhook(scheme, codecFactory, kubeConfigFile, groupVersions, retryBackoff, defaultRequestTimeout, customDial)
}
func newGenericWebhook(scheme *runtime.Scheme, codecFactory serializer.CodecFactory, kubeConfigFile string, groupVersions []schema.GroupVersion, retryBackoff wait.Backoff, requestTimeout time.Duration, customDial utilnet.DialFunc) (*GenericWebhook, error) {
// NewGenericWebhook creates a new GenericWebhook from the provided rest.Config.
func NewGenericWebhook(scheme *runtime.Scheme, codecFactory serializer.CodecFactory, config *rest.Config, groupVersions []schema.GroupVersion, retryBackoff wait.Backoff) (*GenericWebhook, error) {
for _, groupVersion := range groupVersions {
if !scheme.IsVersionRegistered(groupVersion) {
return nil, fmt.Errorf("webhook plugin requires enabling extension resource: %s", groupVersion)
}
}
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.ExplicitPath = kubeConfigFile
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
clientConfig, err := loader.ClientConfig()
if err != nil {
return nil, err
}
// Kubeconfigs can't set a timeout, this can only be set through a command line flag.
//
// https://github.com/kubernetes/client-go/blob/master/tools/clientcmd/overrides.go
//
// Set this to something reasonable so request to webhooks don't hang forever.
clientConfig.Timeout = requestTimeout
// Avoid client-side rate limiting talking to the webhook backend.
// Rate limiting should happen when deciding how many requests to serve.
clientConfig.QPS = -1
clientConfig := rest.CopyConfig(config)
codec := codecFactory.LegacyCodec(groupVersions...)
clientConfig.ContentConfig.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec})
clientConfig.Dial = customDial
clientConfig.Wrap(x509metrics.NewMissingSANRoundTripperWrapperConstructor(x509MissingSANCounter))
restClient, err := rest.UnversionedRESTClientFor(clientConfig)
@ -162,3 +139,29 @@ func WithExponentialBackoff(ctx context.Context, retryBackoff wait.Backoff, webh
return nil
}
}
func LoadKubeconfig(kubeConfigFile string, customDial utilnet.DialFunc) (*rest.Config, error) {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
loadingRules.ExplicitPath = kubeConfigFile
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
clientConfig, err := loader.ClientConfig()
if err != nil {
return nil, err
}
clientConfig.Dial = customDial
// Kubeconfigs can't set a timeout, this can only be set through a command line flag.
//
// https://github.com/kubernetes/client-go/blob/master/tools/clientcmd/overrides.go
//
// Set this to something reasonable so request to webhooks don't hang forever.
clientConfig.Timeout = defaultRequestTimeout
// Avoid client-side rate limiting talking to the webhook backend.
// Rate limiting should happen when deciding how many requests to serve.
clientConfig.QPS = -1
return clientConfig, nil
}

View File

@ -288,13 +288,18 @@ MIIDGTCCAgGgAwIBAgIUOS2M
kubeConfig.CurrentContext = tt.currentContext
kubeConfigFile, err := newKubeConfigFile(kubeConfig)
if err == nil {
defer os.Remove(kubeConfigFile)
_, err = NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, kubeConfigFile, groupVersions, retryBackoff, nil)
if err != nil {
return err
}
defer os.Remove(kubeConfigFile)
config, err := LoadKubeconfig(kubeConfigFile, nil)
if err != nil {
return err
}
_, err = NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, config, groupVersions, retryBackoff)
return err
}()
@ -316,7 +321,7 @@ MIIDGTCCAgGgAwIBAgIUOS2M
// TestMissingKubeConfigFile ensures that a kube config path to a missing file is handled properly
func TestMissingKubeConfigFile(t *testing.T) {
kubeConfigPath := "/some/missing/path"
_, err := NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, kubeConfigPath, groupVersions, retryBackoff, nil)
_, err := LoadKubeconfig(kubeConfigPath, nil)
if err == nil {
t.Errorf("creating the webhook should had failed")
@ -445,7 +450,12 @@ func TestTLSConfig(t *testing.T) {
defer os.Remove(configFile)
wh, err := NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, configFile, groupVersions, retryBackoff, nil)
config, err := LoadKubeconfig(configFile, nil)
if err != nil {
t.Fatal(err)
}
wh, err := NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, config, groupVersions, retryBackoff)
if err == nil {
err = wh.RestClient.Get().Do(context.TODO()).Error()
@ -520,7 +530,14 @@ func TestRequestTimeout(t *testing.T) {
var requestTimeout = 10 * time.Millisecond
wh, err := newGenericWebhook(runtime.NewScheme(), scheme.Codecs, configFile, groupVersions, retryBackoff, requestTimeout, nil)
config, err := LoadKubeconfig(configFile, nil)
if err != nil {
t.Fatal(err)
}
config.Timeout = requestTimeout
wh, err := NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, config, groupVersions, retryBackoff)
if err != nil {
t.Fatalf("failed to create the webhook: %v", err)
}
@ -606,7 +623,12 @@ func TestWithExponentialBackoff(t *testing.T) {
defer os.Remove(configFile)
wh, err := NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, configFile, groupVersions, retryBackoff, nil)
config, err := LoadKubeconfig(configFile, nil)
if err != nil {
t.Fatal(err)
}
wh, err := NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, config, groupVersions, retryBackoff)
if err != nil {
t.Fatalf("failed to create the webhook: %v", err)

View File

@ -63,8 +63,12 @@ func retryOnError(err error) bool {
}
func loadWebhook(configFile string, groupVersion schema.GroupVersion, retryBackoff wait.Backoff, customDial utilnet.DialFunc) (*webhook.GenericWebhook, error) {
w, err := webhook.NewGenericWebhook(audit.Scheme, audit.Codecs, configFile,
[]schema.GroupVersion{groupVersion}, retryBackoff, customDial)
clientConfig, err := webhook.LoadKubeconfig(configFile, customDial)
if err != nil {
return nil, err
}
w, err := webhook.NewGenericWebhook(audit.Scheme, audit.Codecs, clientConfig,
[]schema.GroupVersion{groupVersion}, retryBackoff)
if err != nil {
return nil, err
}

View File

@ -29,7 +29,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
@ -70,12 +69,12 @@ func NewFromInterface(tokenReview authenticationv1client.AuthenticationV1Interfa
return newWithBackoff(tokenReviewClient, retryBackoff, implicitAuds, requestTimeout, metrics)
}
// New creates a new WebhookTokenAuthenticator from the provided kubeconfig
// file. It is recommend to wrap this authenticator with the token cache
// New creates a new WebhookTokenAuthenticator from the provided rest
// config. It is recommend to wrap this authenticator with the token cache
// authenticator implemented in
// k8s.io/apiserver/pkg/authentication/token/cache.
func New(kubeConfigFile string, version string, implicitAuds authenticator.Audiences, retryBackoff wait.Backoff, customDial utilnet.DialFunc) (*WebhookTokenAuthenticator, error) {
tokenReview, err := tokenReviewInterfaceFromKubeconfig(kubeConfigFile, version, retryBackoff, customDial)
func New(config *rest.Config, version string, implicitAuds authenticator.Audiences, retryBackoff wait.Backoff) (*WebhookTokenAuthenticator, error) {
tokenReview, err := tokenReviewInterfaceFromConfig(config, version, retryBackoff)
if err != nil {
return nil, err
}
@ -195,10 +194,10 @@ func (w *WebhookTokenAuthenticator) AuthenticateToken(ctx context.Context, token
}, true, nil
}
// tokenReviewInterfaceFromKubeconfig builds a client from the specified kubeconfig file,
// tokenReviewInterfaceFromConfig builds a client from the specified kubeconfig file,
// and returns a TokenReviewInterface that uses that client. Note that the client submits TokenReview
// requests to the exact path specified in the kubeconfig file, so arbitrary non-API servers can be targeted.
func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string, version string, retryBackoff wait.Backoff, customDial utilnet.DialFunc) (tokenReviewer, error) {
func tokenReviewInterfaceFromConfig(config *rest.Config, version string, retryBackoff wait.Backoff) (tokenReviewer, error) {
localScheme := runtime.NewScheme()
if err := scheme.AddToScheme(localScheme); err != nil {
return nil, err
@ -210,7 +209,7 @@ func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string, version string, r
if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
return nil, err
}
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, retryBackoff, customDial)
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, config, groupVersions, retryBackoff)
if err != nil {
return nil, err
}
@ -221,7 +220,7 @@ func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string, version string, r
if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
return nil, err
}
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, retryBackoff, customDial)
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, config, groupVersions, retryBackoff)
if err != nil {
return nil, err
}

View File

@ -37,6 +37,7 @@ import (
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/token/cache"
"k8s.io/apiserver/pkg/authentication/user"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
)
@ -201,7 +202,12 @@ func newV1TokenAuthenticator(serverURL string, clientCert, clientKey, ca []byte,
return nil, err
}
c, err := tokenReviewInterfaceFromKubeconfig(p, "v1", testRetryBackoff, nil)
clientConfig, err := webhookutil.LoadKubeconfig(p, nil)
if err != nil {
return nil, err
}
c, err := tokenReviewInterfaceFromConfig(clientConfig, "v1", testRetryBackoff)
if err != nil {
return nil, err
}

View File

@ -36,6 +36,7 @@ import (
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/token/cache"
"k8s.io/apiserver/pkg/authentication/user"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
)
@ -195,7 +196,12 @@ func newV1beta1TokenAuthenticator(serverURL string, clientCert, clientKey, ca []
return nil, err
}
c, err := tokenReviewInterfaceFromKubeconfig(p, "v1beta1", testRetryBackoff, nil)
clientConfig, err := webhookutil.LoadKubeconfig(p, nil)
if err != nil {
return nil, err
}
c, err := tokenReviewInterfaceFromConfig(clientConfig, "v1beta1", testRetryBackoff)
if err != nil {
return nil, err
}

View File

@ -30,7 +30,6 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/cache"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
@ -93,8 +92,8 @@ func NewFromInterface(subjectAccessReview authorizationv1client.AuthorizationV1I
//
// For additional HTTP configuration, refer to the kubeconfig documentation
// https://kubernetes.io/docs/user-guide/kubeconfig-file/.
func New(kubeConfigFile string, version string, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff, customDial utilnet.DialFunc) (*WebhookAuthorizer, error) {
subjectAccessReview, err := subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile, version, retryBackoff, customDial)
func New(config *rest.Config, version string, authorizedTTL, unauthorizedTTL time.Duration, retryBackoff wait.Backoff) (*WebhookAuthorizer, error) {
subjectAccessReview, err := subjectAccessReviewInterfaceFromConfig(config, version, retryBackoff)
if err != nil {
return nil, err
}
@ -269,10 +268,10 @@ func convertToSARExtra(extra map[string][]string) map[string]authorizationv1.Ext
return ret
}
// subjectAccessReviewInterfaceFromKubeconfig builds a client from the specified kubeconfig file,
// subjectAccessReviewInterfaceFromConfig builds a client from the specified kubeconfig file,
// and returns a SubjectAccessReviewInterface that uses that client. Note that the client submits SubjectAccessReview
// requests to the exact path specified in the kubeconfig file, so arbitrary non-API servers can be targeted.
func subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile string, version string, retryBackoff wait.Backoff, customDial utilnet.DialFunc) (subjectAccessReviewer, error) {
func subjectAccessReviewInterfaceFromConfig(config *rest.Config, version string, retryBackoff wait.Backoff) (subjectAccessReviewer, error) {
localScheme := runtime.NewScheme()
if err := scheme.AddToScheme(localScheme); err != nil {
return nil, err
@ -284,7 +283,7 @@ func subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile string, version s
if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
return nil, err
}
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, retryBackoff, customDial)
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, config, groupVersions, retryBackoff)
if err != nil {
return nil, err
}
@ -295,7 +294,7 @@ func subjectAccessReviewInterfaceFromKubeconfig(kubeConfigFile string, version s
if err := localScheme.SetVersionPriority(groupVersions...); err != nil {
return nil, err
}
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, retryBackoff, customDial)
gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, config, groupVersions, retryBackoff)
if err != nil {
return nil, err
}

View File

@ -40,6 +40,7 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
)
@ -194,7 +195,11 @@ current-context: default
return fmt.Errorf("failed to execute test template: %v", err)
}
// Create a new authorizer
sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1", testRetryBackoff, nil)
clientConfig, err := webhookutil.LoadKubeconfig(p, nil)
if err != nil {
return err
}
sarClient, err := subjectAccessReviewInterfaceFromConfig(clientConfig, "v1", testRetryBackoff)
if err != nil {
return fmt.Errorf("error building sar client: %v", err)
}
@ -333,7 +338,11 @@ func newV1Authorizer(callbackURL string, clientCert, clientKey, ca []byte, cache
if err := json.NewEncoder(tempfile).Encode(config); err != nil {
return nil, err
}
sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1", testRetryBackoff, nil)
clientConfig, err := webhookutil.LoadKubeconfig(p, nil)
if err != nil {
return nil, err
}
sarClient, err := subjectAccessReviewInterfaceFromConfig(clientConfig, "v1", testRetryBackoff)
if err != nil {
return nil, fmt.Errorf("error building sar client: %v", err)
}

View File

@ -39,6 +39,7 @@ import (
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
v1 "k8s.io/client-go/tools/clientcmd/api/v1"
)
@ -186,7 +187,11 @@ current-context: default
return fmt.Errorf("failed to execute test template: %v", err)
}
// Create a new authorizer
sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1beta1", testRetryBackoff, nil)
clientConfig, err := webhookutil.LoadKubeconfig(p, nil)
if err != nil {
return err
}
sarClient, err := subjectAccessReviewInterfaceFromConfig(clientConfig, "v1beta1", testRetryBackoff)
if err != nil {
return fmt.Errorf("error building sar client: %v", err)
}
@ -325,7 +330,11 @@ func newV1beta1Authorizer(callbackURL string, clientCert, clientKey, ca []byte,
if err := json.NewEncoder(tempfile).Encode(config); err != nil {
return nil, err
}
sarClient, err := subjectAccessReviewInterfaceFromKubeconfig(p, "v1beta1", testRetryBackoff, nil)
clientConfig, err := webhookutil.LoadKubeconfig(p, nil)
if err != nil {
return nil, err
}
sarClient, err := subjectAccessReviewInterfaceFromConfig(clientConfig, "v1beta1", testRetryBackoff)
if err != nil {
return nil, fmt.Errorf("error building sar client: %v", err)
}

View File

@ -57,6 +57,7 @@ import (
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
webhookutil "k8s.io/apiserver/pkg/util/webhook"
"k8s.io/apiserver/plugin/pkg/authenticator/token/tokentest"
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
clientset "k8s.io/client-go/kubernetes"
@ -109,7 +110,13 @@ func getTestWebhookTokenAuth(serverURL string, customDial utilnet.DialFunc) (aut
Jitter: 0.2,
Steps: 5,
}
webhookTokenAuth, err := webhook.New(kubecfgFile.Name(), "v1beta1", nil, retryBackoff, customDial)
clientConfig, err := webhookutil.LoadKubeconfig(kubecfgFile.Name(), customDial)
if err != nil {
return nil, err
}
webhookTokenAuth, err := webhook.New(clientConfig, "v1beta1", nil, retryBackoff)
if err != nil {
return nil, err
}