diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 6c58e076515..3cb01639651 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -498,7 +498,7 @@ func buildGenericConfig( } versionedInformers = clientgoinformers.NewSharedInformerFactory(clientgoExternalClient, 10*time.Minute) - genericConfig.Authentication.Authenticator, genericConfig.OpenAPIConfig.SecurityDefinitions, err = BuildAuthenticator(s, clientgoExternalClient, versionedInformers) + genericConfig.Authentication.Authenticator, genericConfig.OpenAPIConfig.SecurityDefinitions, err = BuildAuthenticator(s, genericConfig, clientgoExternalClient, versionedInformers) if err != nil { lastErr = fmt.Errorf("invalid authentication config: %v", err) return @@ -560,7 +560,7 @@ func buildGenericConfig( } // BuildAuthenticator constructs the authenticator -func BuildAuthenticator(s *options.ServerRunOptions, extclient clientgoclientset.Interface, versionedInformer clientgoinformers.SharedInformerFactory) (authenticator.Request, *spec.SecurityDefinitions, error) { +func BuildAuthenticator(s *options.ServerRunOptions, c *genericapiserver.Config, extclient clientgoclientset.Interface, versionedInformer clientgoinformers.SharedInformerFactory) (authenticator.Request, *spec.SecurityDefinitions, error) { authenticatorConfig, err := s.Authentication.ToAuthenticationConfig() if err != nil { return nil, nil, err @@ -577,6 +577,10 @@ func BuildAuthenticator(s *options.ServerRunOptions, extclient clientgoclientset versionedInformer.Core().V1().Secrets().Lister().Secrets(v1.NamespaceSystem), ) + if c.EgressSelector != nil { + authenticatorConfig.EgressLookup = c.EgressSelector.Lookup + } + return authenticatorConfig.New() } diff --git a/pkg/kubeapiserver/authenticator/BUILD b/pkg/kubeapiserver/authenticator/BUILD index 68c7c30d862..9f537e18760 100644 --- a/pkg/kubeapiserver/authenticator/BUILD +++ b/pkg/kubeapiserver/authenticator/BUILD @@ -25,6 +25,7 @@ go_library( "//staging/src/k8s.io/apiserver/pkg/authentication/token/tokenfile:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/token/union:go_default_library", "//staging/src/k8s.io/apiserver/pkg/server/dynamiccertificates:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/server/egressselector:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", "//staging/src/k8s.io/apiserver/plugin/pkg/authenticator/password/passwordfile:go_default_library", "//staging/src/k8s.io/apiserver/plugin/pkg/authenticator/request/basicauth:go_default_library", diff --git a/pkg/kubeapiserver/authenticator/config.go b/pkg/kubeapiserver/authenticator/config.go index ad541fc8a11..daf1894ad82 100644 --- a/pkg/kubeapiserver/authenticator/config.go +++ b/pkg/kubeapiserver/authenticator/config.go @@ -34,6 +34,7 @@ import ( "k8s.io/apiserver/pkg/authentication/token/tokenfile" tokenunion "k8s.io/apiserver/pkg/authentication/token/union" "k8s.io/apiserver/pkg/server/dynamiccertificates" + "k8s.io/apiserver/pkg/server/egressselector" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/apiserver/plugin/pkg/authenticator/password/passwordfile" "k8s.io/apiserver/plugin/pkg/authenticator/request/basicauth" @@ -83,6 +84,9 @@ type Config struct { // Generally this is the CA bundle file used to authenticate client certificates // If this value is nil, then mutual TLS is disabled. ClientCAContentProvider dynamiccertificates.CAContentProvider + + // Lookup will give us a dialer if the egress selector is configured for it + EgressLookup egressselector.Lookup } // New returns an authenticator.Request or an error that supports the standard @@ -179,10 +183,11 @@ func (config Config) New() (authenticator.Request, *spec.SecurityDefinitions, er tokenAuthenticators = append(tokenAuthenticators, authenticator.WrapAudienceAgnosticToken(config.APIAudiences, oidcAuth)) } if len(config.WebhookTokenAuthnConfigFile) > 0 { - webhookTokenAuth, err := newWebhookTokenAuthenticator(config.WebhookTokenAuthnConfigFile, config.WebhookTokenAuthnVersion, config.WebhookTokenAuthnCacheTTL, config.APIAudiences) + webhookTokenAuth, err := newWebhookTokenAuthenticator(config) if err != nil { return nil, nil, err } + tokenAuthenticators = append(tokenAuthenticators, webhookTokenAuth) } @@ -305,8 +310,13 @@ func newServiceAccountAuthenticator(iss string, keyfiles []string, apiAudiences return tokenAuthenticator, nil } -func newWebhookTokenAuthenticator(webhookConfigFile string, version string, ttl time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) { - webhookTokenAuthenticator, err := webhook.New(webhookConfigFile, version, implicitAuds) +func newWebhookTokenAuthenticator(config Config) (authenticator.Token, error) { + webhookConfigFile := config.WebhookTokenAuthnConfigFile + version := config.WebhookTokenAuthnVersion + ttl := config.WebhookTokenAuthnCacheTTL + implicitAuds := config.APIAudiences + + webhookTokenAuthenticator, err := webhook.New(webhookConfigFile, version, implicitAuds, config.EgressLookup) if err != nil { return nil, err } diff --git a/plugin/pkg/admission/imagepolicy/admission.go b/plugin/pkg/admission/imagepolicy/admission.go index 50921fc8cee..ddd06b43b56 100644 --- a/plugin/pkg/admission/imagepolicy/admission.go +++ b/plugin/pkg/admission/imagepolicy/admission.go @@ -261,7 +261,7 @@ func NewImagePolicyWebhook(configFile io.Reader) (*Plugin, error) { return nil, err } - gw, err := webhook.NewGenericWebhook(legacyscheme.Scheme, legacyscheme.Codecs, whConfig.KubeConfigFile, groupVersions, whConfig.RetryBackoff) + gw, err := webhook.NewGenericWebhook(legacyscheme.Scheme, legacyscheme.Codecs, whConfig.KubeConfigFile, groupVersions, whConfig.RetryBackoff, nil) if err != nil { return nil, err } diff --git a/staging/src/k8s.io/apiserver/pkg/util/webhook/webhook.go b/staging/src/k8s.io/apiserver/pkg/util/webhook/webhook.go index d2c6e2273f7..8975129b0b1 100644 --- a/staging/src/k8s.io/apiserver/pkg/util/webhook/webhook.go +++ b/staging/src/k8s.io/apiserver/pkg/util/webhook/webhook.go @@ -27,7 +27,9 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/util/net" + utilnet "k8s.io/apimachinery/pkg/util/net" "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/apiserver/pkg/server/egressselector" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" ) @@ -61,11 +63,11 @@ func DefaultShouldRetry(err error) bool { } // NewGenericWebhook creates a new GenericWebhook from the provided kubeconfig file. -func NewGenericWebhook(scheme *runtime.Scheme, codecFactory serializer.CodecFactory, kubeConfigFile string, groupVersions []schema.GroupVersion, initialBackoff time.Duration) (*GenericWebhook, error) { - return newGenericWebhook(scheme, codecFactory, kubeConfigFile, groupVersions, initialBackoff, defaultRequestTimeout) +func NewGenericWebhook(scheme *runtime.Scheme, codecFactory serializer.CodecFactory, kubeConfigFile string, groupVersions []schema.GroupVersion, initialBackoff time.Duration, egressLookup egressselector.Lookup) (*GenericWebhook, error) { + return newGenericWebhook(scheme, codecFactory, kubeConfigFile, groupVersions, initialBackoff, defaultRequestTimeout, egressLookup) } -func newGenericWebhook(scheme *runtime.Scheme, codecFactory serializer.CodecFactory, kubeConfigFile string, groupVersions []schema.GroupVersion, initialBackoff, requestTimeout time.Duration) (*GenericWebhook, error) { +func newGenericWebhook(scheme *runtime.Scheme, codecFactory serializer.CodecFactory, kubeConfigFile string, groupVersions []schema.GroupVersion, initialBackoff, requestTimeout time.Duration, egressLookup egressselector.Lookup) (*GenericWebhook, error) { for _, groupVersion := range groupVersions { if !scheme.IsVersionRegistered(groupVersion) { return nil, fmt.Errorf("webhook plugin requires enabling extension resource: %s", groupVersion) @@ -94,6 +96,15 @@ func newGenericWebhook(scheme *runtime.Scheme, codecFactory serializer.CodecFact codec := codecFactory.LegacyCodec(groupVersions...) clientConfig.ContentConfig.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec}) + if egressLookup != nil { + networkContext := egressselector.Master.AsNetworkContext() + var egressDialer utilnet.DialFunc + egressDialer, err = egressLookup(networkContext) + if err != nil { + return nil, err + } + clientConfig.Dial = egressDialer + } restClient, err := rest.UnversionedRESTClientFor(clientConfig) if err != nil { diff --git a/staging/src/k8s.io/apiserver/pkg/util/webhook/webhook_test.go b/staging/src/k8s.io/apiserver/pkg/util/webhook/webhook_test.go index 7fda708749d..b70d146e838 100644 --- a/staging/src/k8s.io/apiserver/pkg/util/webhook/webhook_test.go +++ b/staging/src/k8s.io/apiserver/pkg/util/webhook/webhook_test.go @@ -259,7 +259,7 @@ func TestKubeConfigFile(t *testing.T) { if err == nil { defer os.Remove(kubeConfigFile) - _, err = NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, kubeConfigFile, groupVersions, retryBackoff) + _, err = NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, kubeConfigFile, groupVersions, retryBackoff, nil) } return err @@ -282,7 +282,7 @@ func TestKubeConfigFile(t *testing.T) { // 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) + _, err := NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, kubeConfigPath, groupVersions, retryBackoff, nil) if err == nil { t.Errorf("creating the webhook should had failed") @@ -394,7 +394,7 @@ func TestTLSConfig(t *testing.T) { defer os.Remove(configFile) - wh, err := NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, configFile, groupVersions, retryBackoff) + wh, err := NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, configFile, groupVersions, retryBackoff, nil) if err == nil { err = wh.RestClient.Get().Do(context.TODO()).Error() @@ -459,7 +459,7 @@ func TestRequestTimeout(t *testing.T) { var requestTimeout = 10 * time.Millisecond - wh, err := newGenericWebhook(runtime.NewScheme(), scheme.Codecs, configFile, groupVersions, retryBackoff, requestTimeout) + wh, err := newGenericWebhook(runtime.NewScheme(), scheme.Codecs, configFile, groupVersions, retryBackoff, requestTimeout, nil) if err != nil { t.Fatalf("failed to create the webhook: %v", err) } @@ -545,7 +545,7 @@ func TestWithExponentialBackoff(t *testing.T) { defer os.Remove(configFile) - wh, err := NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, configFile, groupVersions, retryBackoff) + wh, err := NewGenericWebhook(runtime.NewScheme(), scheme.Codecs, configFile, groupVersions, retryBackoff, nil) if err != nil { t.Fatalf("failed to create the webhook: %v", err) diff --git a/staging/src/k8s.io/apiserver/plugin/pkg/audit/webhook/webhook.go b/staging/src/k8s.io/apiserver/plugin/pkg/audit/webhook/webhook.go index f73c1fd5626..861ec72dd3e 100644 --- a/staging/src/k8s.io/apiserver/plugin/pkg/audit/webhook/webhook.go +++ b/staging/src/k8s.io/apiserver/plugin/pkg/audit/webhook/webhook.go @@ -62,7 +62,7 @@ func retryOnError(err error) bool { func loadWebhook(configFile string, groupVersion schema.GroupVersion, initialBackoff time.Duration) (*webhook.GenericWebhook, error) { w, err := webhook.NewGenericWebhook(audit.Scheme, audit.Codecs, configFile, - []schema.GroupVersion{groupVersion}, initialBackoff) + []schema.GroupVersion{groupVersion}, initialBackoff, nil) w.ShouldRetry = retryOnError return w, err } diff --git a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/BUILD b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/BUILD index dc2839f18d2..9159bcb5f89 100644 --- a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/BUILD +++ b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/BUILD @@ -41,6 +41,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library", "//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/server/egressselector:go_default_library", "//staging/src/k8s.io/apiserver/pkg/util/webhook:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library", "//staging/src/k8s.io/client-go/kubernetes/typed/authentication/v1:go_default_library", diff --git a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook.go b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook.go index 7410d1959ed..1d5a90f6b8e 100644 --- a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook.go +++ b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook.go @@ -30,6 +30,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/apiserver/pkg/server/egressselector" "k8s.io/apiserver/pkg/util/webhook" "k8s.io/client-go/kubernetes/scheme" authenticationv1client "k8s.io/client-go/kubernetes/typed/authentication/v1" @@ -63,8 +64,8 @@ func NewFromInterface(tokenReview authenticationv1client.TokenReviewInterface, i // file. 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) (*WebhookTokenAuthenticator, error) { - tokenReview, err := tokenReviewInterfaceFromKubeconfig(kubeConfigFile, version) +func New(kubeConfigFile string, version string, implicitAuds authenticator.Audiences, egressLookup egressselector.Lookup) (*WebhookTokenAuthenticator, error) { + tokenReview, err := tokenReviewInterfaceFromKubeconfig(kubeConfigFile, version, egressLookup) if err != nil { return nil, err } @@ -153,7 +154,7 @@ func (w *WebhookTokenAuthenticator) AuthenticateToken(ctx context.Context, token // tokenReviewInterfaceFromKubeconfig 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) (tokenReviewer, error) { +func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string, version string, egressLookup egressselector.Lookup) (tokenReviewer, error) { localScheme := runtime.NewScheme() if err := scheme.AddToScheme(localScheme); err != nil { return nil, err @@ -165,7 +166,7 @@ func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string, version string) ( if err := localScheme.SetVersionPriority(groupVersions...); err != nil { return nil, err } - gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0) + gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0, egressLookup) if err != nil { return nil, err } @@ -176,7 +177,7 @@ func tokenReviewInterfaceFromKubeconfig(kubeConfigFile string, version string) ( if err := localScheme.SetVersionPriority(groupVersions...); err != nil { return nil, err } - gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0) + gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0, egressLookup) if err != nil { return nil, err } diff --git a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook_v1_test.go b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook_v1_test.go index 884317242cd..e9bf2a36d7b 100644 --- a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook_v1_test.go +++ b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook_v1_test.go @@ -193,7 +193,7 @@ func newV1TokenAuthenticator(serverURL string, clientCert, clientKey, ca []byte, return nil, err } - c, err := tokenReviewInterfaceFromKubeconfig(p, "v1") + c, err := tokenReviewInterfaceFromKubeconfig(p, "v1", nil) if err != nil { return nil, err } diff --git a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook_v1beta1_test.go b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook_v1beta1_test.go index 514fd9df3f3..e5ea01baee9 100644 --- a/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook_v1beta1_test.go +++ b/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook_v1beta1_test.go @@ -195,7 +195,7 @@ func newV1beta1TokenAuthenticator(serverURL string, clientCert, clientKey, ca [] return nil, err } - c, err := tokenReviewInterfaceFromKubeconfig(p, "v1beta1") + c, err := tokenReviewInterfaceFromKubeconfig(p, "v1beta1", nil) if err != nil { return nil, err } diff --git a/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go b/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go index cc8b5e78bc6..b496b6c5874 100644 --- a/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go +++ b/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go @@ -257,7 +257,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, 0) + gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0, nil) if err != nil { return nil, err } @@ -268,7 +268,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, 0) + gw, err := webhook.NewGenericWebhook(localScheme, scheme.Codecs, kubeConfigFile, groupVersions, 0, nil) if err != nil { return nil, err } diff --git a/test/integration/auth/auth_test.go b/test/integration/auth/auth_test.go index 3ecfab3a7fd..49418001e29 100644 --- a/test/integration/auth/auth_test.go +++ b/test/integration/auth/auth_test.go @@ -85,7 +85,7 @@ func getTestWebhookTokenAuth(serverURL string) (authenticator.Request, error) { if err := json.NewEncoder(kubecfgFile).Encode(config); err != nil { return nil, err } - webhookTokenAuth, err := webhook.New(kubecfgFile.Name(), "v1beta1", nil) + webhookTokenAuth, err := webhook.New(kubecfgFile.Name(), "v1beta1", nil, nil) if err != nil { return nil, err }