Plumb service port, URL port to webhook client auth resolution

This commit is contained in:
Jordan Liggitt 2019-09-02 22:38:36 -04:00
parent 2f5dde7672
commit d127042cb8
3 changed files with 41 additions and 29 deletions

View File

@ -52,7 +52,7 @@ type authenticationInfoResolver struct {
cacheMisses *int32 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) atomic.AddInt32(a.cacheMisses, 1)
return a.restConfig, nil return a.restConfig, nil
} }

View File

@ -19,7 +19,9 @@ package webhook
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"strconv"
"strings" "strings"
"time" "time"
@ -40,17 +42,17 @@ func NewDefaultAuthenticationInfoResolverWrapper(
webhookAuthResolverWrapper := func(delegate AuthenticationInfoResolver) AuthenticationInfoResolver { webhookAuthResolverWrapper := func(delegate AuthenticationInfoResolver) AuthenticationInfoResolver {
return &AuthenticationInfoResolverDelegator{ return &AuthenticationInfoResolverDelegator{
ClientConfigForFunc: func(server string) (*rest.Config, error) { ClientConfigForFunc: func(hostPort string) (*rest.Config, error) {
if server == "kubernetes.default.svc" { if hostPort == "kubernetes.default.svc:443" {
return kubeapiserverClientConfig, nil return kubeapiserverClientConfig, nil
} }
return delegate.ClientConfigFor(server) return delegate.ClientConfigFor(hostPort)
}, },
ClientConfigForServiceFunc: func(serviceName, serviceNamespace string) (*rest.Config, error) { ClientConfigForServiceFunc: func(serviceName, serviceNamespace string, servicePort int) (*rest.Config, error) {
if serviceName == "kubernetes" && serviceNamespace == corev1.NamespaceDefault { if serviceName == "kubernetes" && serviceNamespace == corev1.NamespaceDefault && servicePort == 443 {
return kubeapiserverClientConfig, nil return kubeapiserverClientConfig, nil
} }
ret, err := delegate.ClientConfigForService(serviceName, serviceNamespace) ret, err := delegate.ClientConfigForService(serviceName, serviceNamespace, servicePort)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -67,27 +69,27 @@ func NewDefaultAuthenticationInfoResolverWrapper(
// AuthenticationInfoResolver builds rest.Config base on the server or service // AuthenticationInfoResolver builds rest.Config base on the server or service
// name and service namespace. // name and service namespace.
type AuthenticationInfoResolver interface { type AuthenticationInfoResolver interface {
// ClientConfigFor builds rest.Config based on the server. // ClientConfigFor builds rest.Config based on the hostPort.
ClientConfigFor(server string) (*rest.Config, error) ClientConfigFor(hostPort string) (*rest.Config, error)
// ClientConfigForService builds rest.Config based on the serviceName and // ClientConfigForService builds rest.Config based on the serviceName and
// serviceNamespace. // serviceNamespace.
ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) ClientConfigForService(serviceName, serviceNamespace string, servicePort int) (*rest.Config, error)
} }
// AuthenticationInfoResolverDelegator implements AuthenticationInfoResolver. // AuthenticationInfoResolverDelegator implements AuthenticationInfoResolver.
type AuthenticationInfoResolverDelegator struct { type AuthenticationInfoResolverDelegator struct {
ClientConfigForFunc func(server string) (*rest.Config, error) ClientConfigForFunc func(hostPort string) (*rest.Config, error)
ClientConfigForServiceFunc func(serviceName, serviceNamespace string) (*rest.Config, error) ClientConfigForServiceFunc func(serviceName, serviceNamespace string, servicePort int) (*rest.Config, error)
} }
// ClientConfigFor returns client config for given server. // ClientConfigFor returns client config for given hostPort.
func (a *AuthenticationInfoResolverDelegator) ClientConfigFor(server string) (*rest.Config, error) { func (a *AuthenticationInfoResolverDelegator) ClientConfigFor(hostPort string) (*rest.Config, error) {
return a.ClientConfigForFunc(server) return a.ClientConfigForFunc(hostPort)
} }
// ClientConfigForService returns client config for given service. // ClientConfigForService returns client config for given service.
func (a *AuthenticationInfoResolverDelegator) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) { func (a *AuthenticationInfoResolverDelegator) ClientConfigForService(serviceName, serviceNamespace string, servicePort int) (*rest.Config, error) {
return a.ClientConfigForServiceFunc(serviceName, serviceNamespace) return a.ClientConfigForServiceFunc(serviceName, serviceNamespace, servicePort)
} }
type defaultAuthenticationInfoResolver struct { type defaultAuthenticationInfoResolver struct {
@ -113,12 +115,12 @@ func NewDefaultAuthenticationInfoResolver(kubeconfigFile string) (Authentication
return &defaultAuthenticationInfoResolver{kubeconfig: clientConfig}, nil return &defaultAuthenticationInfoResolver{kubeconfig: clientConfig}, nil
} }
func (c *defaultAuthenticationInfoResolver) ClientConfigFor(server string) (*rest.Config, error) { func (c *defaultAuthenticationInfoResolver) ClientConfigFor(hostPort string) (*rest.Config, error) {
return c.clientConfig(server) return c.clientConfig(hostPort)
} }
func (c *defaultAuthenticationInfoResolver) ClientConfigForService(serviceName, serviceNamespace string) (*rest.Config, error) { func (c *defaultAuthenticationInfoResolver) ClientConfigForService(serviceName, serviceNamespace string, servicePort int) (*rest.Config, error) {
return c.clientConfig(serviceName + "." + serviceNamespace + ".svc") return c.clientConfig(net.JoinHostPort(serviceName+"."+serviceNamespace+".svc", strconv.Itoa(servicePort)))
} }
func (c *defaultAuthenticationInfoResolver) clientConfig(target string) (*rest.Config, error) { 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 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. // if we can find an in-cluster-config use that. If we can't, fall through.
inClusterConfig, err := rest.InClusterConfig() inClusterConfig, err := rest.InClusterConfig()
if err == nil { if err == nil {

View File

@ -23,6 +23,7 @@ import (
"fmt" "fmt"
"net" "net"
"net/url" "net/url"
"strconv"
"github.com/hashicorp/golang-lru" "github.com/hashicorp/golang-lru"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -151,13 +152,20 @@ func (cm *ClientManager) HookClient(cc ClientConfig) (*rest.RESTClient, error) {
} }
if cc.Service != nil { 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 { if err != nil {
return nil, err return nil, err
} }
cfg := rest.CopyConfig(restConfig) cfg := rest.CopyConfig(restConfig)
serverName := cc.Service.Name + "." + cc.Service.Namespace + ".svc" serverName := cc.Service.Name + "." + cc.Service.Namespace + ".svc"
host := serverName + ":443"
host := net.JoinHostPort(serverName, strconv.Itoa(int(port)))
cfg.Host = "https://" + host cfg.Host = "https://" + host
cfg.APIPath = cc.Service.Path cfg.APIPath = cc.Service.Path
// Set the server name if not already set // 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) { cfg.Dial = func(ctx context.Context, network, addr string) (net.Conn, error) {
if addr == host { if addr == host {
port := cc.Service.Port
if port == 0 {
port = 443
}
u, err := cm.serviceResolver.ResolveEndpoint(cc.Service.Namespace, cc.Service.Name, port) u, err := cm.serviceResolver.ResolveEndpoint(cc.Service.Namespace, cc.Service.Name, port)
if err != nil { if err != nil {
return nil, err 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)} 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 { if err != nil {
return nil, err return nil, err
} }