diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/authentication_info_resolver.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/authentication_info_resolver.go index eef54ee86ca..49ba09be136 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/authentication_info_resolver.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing/authentication_info_resolver.go @@ -52,7 +52,7 @@ type authenticationInfoResolver struct { cacheMisses *int32 } -func (a *authenticationInfoResolver) ClientConfigFor(server string) (*rest.Config, error) { +func (a *authenticationInfoResolver) ClientConfigFor(hostPort string) (*rest.Config, error) { atomic.AddInt32(a.cacheMisses, 1) return a.restConfig, nil } diff --git a/staging/src/k8s.io/apiserver/pkg/util/webhook/authentication.go b/staging/src/k8s.io/apiserver/pkg/util/webhook/authentication.go index 573bcf79848..9d78e936a54 100644 --- a/staging/src/k8s.io/apiserver/pkg/util/webhook/authentication.go +++ b/staging/src/k8s.io/apiserver/pkg/util/webhook/authentication.go @@ -19,7 +19,9 @@ package webhook import ( "fmt" "io/ioutil" + "net" "net/http" + "strconv" "strings" "time" @@ -40,17 +42,17 @@ func NewDefaultAuthenticationInfoResolverWrapper( webhookAuthResolverWrapper := func(delegate AuthenticationInfoResolver) AuthenticationInfoResolver { return &AuthenticationInfoResolverDelegator{ - ClientConfigForFunc: func(server string) (*rest.Config, error) { - if server == "kubernetes.default.svc" { + ClientConfigForFunc: func(hostPort string) (*rest.Config, error) { + if hostPort == "kubernetes.default.svc:443" { return kubeapiserverClientConfig, nil } - return delegate.ClientConfigFor(server) + return delegate.ClientConfigFor(hostPort) }, - ClientConfigForServiceFunc: func(serviceName, serviceNamespace string) (*rest.Config, error) { - if serviceName == "kubernetes" && serviceNamespace == corev1.NamespaceDefault { + ClientConfigForServiceFunc: func(serviceName, serviceNamespace string, servicePort int) (*rest.Config, error) { + if serviceName == "kubernetes" && serviceNamespace == corev1.NamespaceDefault && servicePort == 443 { return kubeapiserverClientConfig, nil } - ret, err := delegate.ClientConfigForService(serviceName, serviceNamespace) + ret, err := delegate.ClientConfigForService(serviceName, serviceNamespace, servicePort) if err != nil { return nil, err } @@ -67,27 +69,27 @@ func NewDefaultAuthenticationInfoResolverWrapper( // 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) + // ClientConfigFor builds rest.Config based on the hostPort. + ClientConfigFor(hostPort string) (*rest.Config, error) // ClientConfigForService builds rest.Config based on the serviceName and // serviceNamespace. - ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) + ClientConfigForService(serviceName, serviceNamespace string, servicePort int) (*rest.Config, error) } // AuthenticationInfoResolverDelegator implements AuthenticationInfoResolver. type AuthenticationInfoResolverDelegator struct { - ClientConfigForFunc func(server string) (*rest.Config, error) - ClientConfigForServiceFunc func(serviceName, serviceNamespace string) (*rest.Config, error) + ClientConfigForFunc func(hostPort string) (*rest.Config, error) + ClientConfigForServiceFunc func(serviceName, serviceNamespace string, servicePort int) (*rest.Config, error) } -// ClientConfigFor returns client config for given server. -func (a *AuthenticationInfoResolverDelegator) ClientConfigFor(server string) (*rest.Config, error) { - return a.ClientConfigForFunc(server) +// ClientConfigFor returns client config for given hostPort. +func (a *AuthenticationInfoResolverDelegator) ClientConfigFor(hostPort string) (*rest.Config, error) { + return a.ClientConfigForFunc(hostPort) } // ClientConfigForService returns client config for given service. -func (a *AuthenticationInfoResolverDelegator) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) { - return a.ClientConfigForServiceFunc(serviceName, serviceNamespace) +func (a *AuthenticationInfoResolverDelegator) ClientConfigForService(serviceName, serviceNamespace string, servicePort int) (*rest.Config, error) { + return a.ClientConfigForServiceFunc(serviceName, serviceNamespace, servicePort) } type defaultAuthenticationInfoResolver struct { @@ -113,12 +115,12 @@ func NewDefaultAuthenticationInfoResolver(kubeconfigFile string) (Authentication return &defaultAuthenticationInfoResolver{kubeconfig: clientConfig}, nil } -func (c *defaultAuthenticationInfoResolver) ClientConfigFor(server string) (*rest.Config, error) { - return c.clientConfig(server) +func (c *defaultAuthenticationInfoResolver) ClientConfigFor(hostPort string) (*rest.Config, error) { + return c.clientConfig(hostPort) } -func (c *defaultAuthenticationInfoResolver) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) { - return c.clientConfig(serviceName + "." + serviceNamespace + ".svc") +func (c *defaultAuthenticationInfoResolver) ClientConfigForService(serviceName, serviceNamespace string, servicePort int) (*rest.Config, error) { + return c.clientConfig(net.JoinHostPort(serviceName+"."+serviceNamespace+".svc", strconv.Itoa(servicePort))) } func (c *defaultAuthenticationInfoResolver) clientConfig(target string) (*rest.Config, error) { @@ -154,7 +156,7 @@ func (c *defaultAuthenticationInfoResolver) clientConfig(target string) (*rest.C } // if we're trying to hit the kube-apiserver and there wasn't an explicit config, use the in-cluster config - if target == "kubernetes.default.svc" { + if target == "kubernetes.default.svc:443" { // if we can find an in-cluster-config use that. If we can't, fall through. inClusterConfig, err := rest.InClusterConfig() if err == nil { diff --git a/staging/src/k8s.io/apiserver/pkg/util/webhook/client.go b/staging/src/k8s.io/apiserver/pkg/util/webhook/client.go index 32d8fe28960..02bf38ff980 100644 --- a/staging/src/k8s.io/apiserver/pkg/util/webhook/client.go +++ b/staging/src/k8s.io/apiserver/pkg/util/webhook/client.go @@ -23,6 +23,7 @@ import ( "fmt" "net" "net/url" + "strconv" "github.com/hashicorp/golang-lru" "k8s.io/apimachinery/pkg/runtime" @@ -151,13 +152,20 @@ func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) { } if cc.Service != nil { - restConfig, err := cm.authInfoResolver.ClientConfigForService(cc.Service.Name, cc.Service.Namespace) + port := cc.Service.Port + if port == 0 { + // Default to port 443 if no service port is specified + port = 443 + } + + restConfig, err := cm.authInfoResolver.ClientConfigForService(cc.Service.Name, cc.Service.Namespace, int(port)) if err != nil { return nil, err } cfg := rest.CopyConfig(restConfig) serverName := cc.Service.Name + "." + cc.Service.Namespace + ".svc" - host := serverName + ":443" + + host := net.JoinHostPort(serverName, strconv.Itoa(int(port))) cfg.Host = "https://" + host cfg.APIPath = cc.Service.Path // Set the server name if not already set @@ -172,10 +180,6 @@ func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) { } cfg.Dial = func(ctx context.Context, network, addr string) (net.Conn, error) { if addr == host { - port := cc.Service.Port - if port == 0 { - port = 443 - } u, err := cm.serviceResolver.ResolveEndpoint(cc.Service.Namespace, cc.Service.Name, port) if err != nil { return nil, err @@ -197,7 +201,13 @@ func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) { return nil, &ErrCallingWebhook{WebhookName: cc.Name, Reason: fmt.Errorf("Unparsable URL: %v", err)} } - restConfig, err := cm.authInfoResolver.ClientConfigFor(u.Host) + hostPort := u.Host + if len(u.Port()) == 0 { + // Default to port 443 if no port is specified + hostPort = net.JoinHostPort(hostPort, "443") + } + + restConfig, err := cm.authInfoResolver.ClientConfigFor(hostPort) if err != nil { return nil, err }