Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automated cherry pick of #58644: Use SSH tunnel for webhook communication iff the webhook is deployed as service #58897

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
38 changes: 23 additions & 15 deletions cmd/kube-apiserver/app/server.go
Expand Up @@ -451,27 +451,35 @@ func BuildGenericConfig(s *options.ServerRunOptions, proxyTransport *http.Transp
genericConfig.DisabledPostStartHooks.Insert(rbacrest.PostStartHookName)
}

webhookAuthResolver := func(delegate webhookconfig.AuthenticationInfoResolver) webhookconfig.AuthenticationInfoResolver {
return webhookconfig.AuthenticationInfoResolverFunc(func(server string) (*rest.Config, error) {
if server == "kubernetes.default.svc" {
return genericConfig.LoopbackClientConfig, nil
}
ret, err := delegate.ClientConfigFor(server)
if err != nil {
return nil, err
}
if proxyTransport != nil && proxyTransport.Dial != nil {
ret.Dial = proxyTransport.Dial
}
return ret, err
})
webhookAuthResolverWrapper := func(delegate webhookconfig.AuthenticationInfoResolver) webhookconfig.AuthenticationInfoResolver {
return &webhookconfig.AuthenticationInfoResolverDelegator{
ClientConfigForFunc: func(server string) (*rest.Config, error) {
if server == "kubernetes.default.svc" {
return genericConfig.LoopbackClientConfig, nil
}
return delegate.ClientConfigFor(server)
},
ClientConfigForServiceFunc: func(serviceName, serviceNamespace string) (*rest.Config, error) {
if serviceName == "kubernetes" && serviceNamespace == "default" {
return genericConfig.LoopbackClientConfig, nil
}
ret, err := delegate.ClientConfigForService(serviceName, serviceNamespace)
if err != nil {
return nil, err
}
if proxyTransport != nil && proxyTransport.Dial != nil {
ret.Dial = proxyTransport.Dial
}
return ret, err
},
}
}
pluginInitializers, err := BuildAdmissionPluginInitializers(
s,
client,
sharedInformers,
serviceResolver,
webhookAuthResolver,
webhookAuthResolverWrapper,
)
if err != nil {
return nil, nil, nil, nil, nil, fmt.Errorf("failed to create admission plugin initializer: %v", err)
Expand Down
Expand Up @@ -31,17 +31,28 @@ import (
// rest.Config generated by the resolver.
type AuthenticationInfoResolverWrapper func(AuthenticationInfoResolver) AuthenticationInfoResolver

// AuthenticationInfoResolver builds rest.Config base on the server name.
// AuthenticationInfoResolver builds rest.Config base on the server or service
// name and service namespace.
type AuthenticationInfoResolver interface {
// ClientConfigFor builds rest.Config based on the server.
ClientConfigFor(server string) (*rest.Config, error)
// ClientConfigForService builds rest.Config based on the serviceName and
// serviceNamespace.
ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error)
}

// AuthenticationInfoResolverFunc implements AuthenticationInfoResolver.
type AuthenticationInfoResolverFunc func(server string) (*rest.Config, error)
// AuthenticationInfoResolverDelegator implements AuthenticationInfoResolver.
type AuthenticationInfoResolverDelegator struct {
ClientConfigForFunc func(server string) (*rest.Config, error)
ClientConfigForServiceFunc func(serviceName, serviceNamespace string) (*rest.Config, error)
}

func (a *AuthenticationInfoResolverDelegator) ClientConfigFor(server string) (*rest.Config, error) {
return a.ClientConfigForFunc(server)
}

//ClientConfigFor implements AuthenticationInfoResolver.
func (a AuthenticationInfoResolverFunc) ClientConfigFor(server string) (*rest.Config, error) {
return a(server)
func (a *AuthenticationInfoResolverDelegator) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) {
return a.ClientConfigForServiceFunc(serviceName, serviceNamespace)
}

type defaultAuthenticationInfoResolver struct {
Expand All @@ -68,13 +79,21 @@ func NewDefaultAuthenticationInfoResolver(kubeconfigFile string) (Authentication
}

func (c *defaultAuthenticationInfoResolver) ClientConfigFor(server string) (*rest.Config, error) {
return c.clientConfig(server)
}

func (c *defaultAuthenticationInfoResolver) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) {
return c.clientConfig(serviceName + "." + serviceNamespace + ".svc")
}

func (c *defaultAuthenticationInfoResolver) clientConfig(target string) (*rest.Config, error) {
// exact match
if authConfig, ok := c.kubeconfig.AuthInfos[server]; ok {
if authConfig, ok := c.kubeconfig.AuthInfos[target]; ok {
return restConfigFromKubeconfig(authConfig)
}

// star prefixed match
serverSteps := strings.Split(server, ".")
serverSteps := strings.Split(target, ".")
for i := 1; i < len(serverSteps); i++ {
nickName := "*." + strings.Join(serverSteps[i:], ".")
if authConfig, ok := c.kubeconfig.AuthInfos[nickName]; ok {
Expand All @@ -83,7 +102,7 @@ func (c *defaultAuthenticationInfoResolver) ClientConfigFor(server string) (*res
}

// 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 target == "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 {
Expand Down
Expand Up @@ -122,12 +122,12 @@ func (cm *ClientManager) HookClient(h *v1beta1.Webhook) (*rest.RESTClient, error
}

if svc := h.ClientConfig.Service; svc != nil {
serverName := svc.Name + "." + svc.Namespace + ".svc"
restConfig, err := cm.authInfoResolver.ClientConfigFor(serverName)
restConfig, err := cm.authInfoResolver.ClientConfigForService(svc.Name, svc.Namespace)
if err != nil {
return nil, err
}
cfg := rest.CopyConfig(restConfig)
serverName := svc.Name + "." + svc.Namespace + ".svc"
host := serverName + ":443"
cfg.Host = "https://" + host
if svc.Path != nil {
Expand Down
Expand Up @@ -637,6 +637,11 @@ func (c *fakeAuthenticationInfoResolver) ClientConfigFor(server string) (*rest.C
return c.restConfig, nil
}

func (c *fakeAuthenticationInfoResolver) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) {
atomic.AddInt32(c.cachedCount, 1)
return c.restConfig, nil
}

func newMatchEverythingRules() []registrationv1beta1.RuleWithOperations {
return []registrationv1beta1.RuleWithOperations{{
Operations: []registrationv1beta1.OperationType{registrationv1beta1.OperationAll},
Expand Down
Expand Up @@ -662,6 +662,11 @@ func (c *fakeAuthenticationInfoResolver) ClientConfigFor(server string) (*rest.C
return c.restConfig, nil
}

func (c *fakeAuthenticationInfoResolver) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) {
atomic.AddInt32(c.cachedCount, 1)
return c.restConfig, nil
}

func newMatchEverythingRules() []registrationv1beta1.RuleWithOperations {
return []registrationv1beta1.RuleWithOperations{{
Operations: []registrationv1beta1.OperationType{registrationv1beta1.OperationAll},
Expand Down