From 4f625db1334f25d47e44385d205a0b108b82f444 Mon Sep 17 00:00:00 2001 From: deads2k Date: Mon, 5 Dec 2016 13:26:38 -0500 Subject: [PATCH 1/3] move client-ca to authentication args --- cmd/kube-apiserver/app/server.go | 2 +- .../cmd/federation-apiserver/app/server.go | 2 +- pkg/genericapiserver/config.go | 5 ++- .../options/authentication.go | 34 ++++++++++++++++--- pkg/genericapiserver/options/serving.go | 7 ---- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 4d4470d9e65..d7f7f2fd2eb 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -219,7 +219,7 @@ func Run(s *options.ServerRunOptions) error { } } - authenticatorConfig := s.Authentication.ToAuthenticationConfig(s.SecureServing.ClientCA) + authenticatorConfig := s.Authentication.ToAuthenticationConfig() if s.Authentication.ServiceAccounts.Lookup { // If we need to look up service accounts and tokens, // go directly to etcd to avoid recursive auth insanity diff --git a/federation/cmd/federation-apiserver/app/server.go b/federation/cmd/federation-apiserver/app/server.go index d05e0a2c291..fd8c75215cd 100644 --- a/federation/cmd/federation-apiserver/app/server.go +++ b/federation/cmd/federation-apiserver/app/server.go @@ -126,7 +126,7 @@ func Run(s *options.ServerRunOptions) error { storageFactory.SetEtcdLocation(groupResource, servers) } - apiAuthenticator, securityDefinitions, err := authenticator.New(s.Authentication.ToAuthenticationConfig(s.SecureServing.ClientCA)) + apiAuthenticator, securityDefinitions, err := authenticator.New(s.Authentication.ToAuthenticationConfig()) if err != nil { glog.Fatalf("Invalid Authentication Config: %v", err) } diff --git a/pkg/genericapiserver/config.go b/pkg/genericapiserver/config.go index fe3fdcada43..58d5140ba6e 100644 --- a/pkg/genericapiserver/config.go +++ b/pkg/genericapiserver/config.go @@ -232,7 +232,6 @@ func (c *Config) ApplySecureServingOptions(secureServing *options.SecureServingO ServingInfo: ServingInfo{ BindAddress: net.JoinHostPort(secureServing.ServingOptions.BindAddress.String(), strconv.Itoa(secureServing.ServingOptions.BindPort)), }, - ClientCA: secureServing.ClientCA, } serverCertFile, serverKeyFile := secureServing.ServerCert.CertKey.CertFile, secureServing.ServerCert.CertKey.KeyFile @@ -305,6 +304,10 @@ func (c *Config) ApplyAuthenticationOptions(o *options.BuiltInAuthenticationOpti return c } + if o.ClientCert != nil && c.SecureServingInfo != nil { + c.SecureServingInfo.ClientCA = o.ClientCert.ClientCA + } + c.SupportsBasicAuth = len(o.PasswordFile.BasicAuthFile) > 0 return c } diff --git a/pkg/genericapiserver/options/authentication.go b/pkg/genericapiserver/options/authentication.go index fd76c1a3286..3239f33b047 100644 --- a/pkg/genericapiserver/options/authentication.go +++ b/pkg/genericapiserver/options/authentication.go @@ -29,6 +29,7 @@ import ( type BuiltInAuthenticationOptions struct { Anonymous *AnonymousAuthenticationOptions AnyToken *AnyTokenAuthenticationOptions + ClientCert *ClientCertAuthenticationOptions Keystone *KeystoneAuthenticationOptions OIDC *OIDCAuthenticationOptions PasswordFile *PasswordFileAuthenticationOptions @@ -85,6 +86,7 @@ func (s *BuiltInAuthenticationOptions) WithAll() *BuiltInAuthenticationOptions { return s. WithAnyonymous(). WithAnyToken(). + WithClientCert(). WithKeystone(). WithOIDC(). WithPasswordFile(). @@ -104,6 +106,11 @@ func (s *BuiltInAuthenticationOptions) WithAnyToken() *BuiltInAuthenticationOpti return s } +func (s *BuiltInAuthenticationOptions) WithClientCert() *BuiltInAuthenticationOptions { + s.ClientCert = &ClientCertAuthenticationOptions{} + return s +} + func (s *BuiltInAuthenticationOptions) WithKeystone() *BuiltInAuthenticationOptions { s.Keystone = &KeystoneAuthenticationOptions{} return s @@ -161,6 +168,10 @@ func (s *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { } + if s.ClientCert != nil { + s.ClientCert.AddFlags(fs) + } + if s.Keystone != nil { fs.StringVar(&s.Keystone.URL, "experimental-keystone-url", s.Keystone.URL, "If passed, activates the keystone authentication plugin.") @@ -229,10 +240,9 @@ func (s *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { } } -func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig(clientCAFile string) authenticator.AuthenticatorConfig { - ret := authenticator.AuthenticatorConfig{ - ClientCAFile: clientCAFile, - } +func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() authenticator.AuthenticatorConfig { + ret := authenticator.AuthenticatorConfig{} + if s.Anonymous != nil { ret.Anonymous = s.Anonymous.Allow } @@ -241,6 +251,10 @@ func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig(clientCAFile strin ret.AnyToken = s.AnyToken.Allow } + if s.ClientCert != nil { + ret.ClientCAFile = s.ClientCert.ClientCA + } + if s.Keystone != nil { ret.KeystoneURL = s.Keystone.URL ret.KeystoneCAFile = s.Keystone.CAFile @@ -323,6 +337,18 @@ func (s *RequestHeaderAuthenticationOptions) ToAuthenticationRequestHeaderConfig } } +type ClientCertAuthenticationOptions struct { + // ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates + ClientCA string +} + +func (s *ClientCertAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { + fs.StringVar(&s.ClientCA, "client-ca-file", s.ClientCA, ""+ + "If set, any request presenting a client certificate signed by one of "+ + "the authorities in the client-ca-file is authenticated with an identity "+ + "corresponding to the CommonName of the client certificate.") +} + // DelegatingAuthenticationOptions provides an easy way for composing API servers to delegate their authentication to // the root kube API server type DelegatingAuthenticationOptions struct { diff --git a/pkg/genericapiserver/options/serving.go b/pkg/genericapiserver/options/serving.go index bcf880c09d5..fee39ddf5f6 100644 --- a/pkg/genericapiserver/options/serving.go +++ b/pkg/genericapiserver/options/serving.go @@ -41,8 +41,6 @@ type SecureServingOptions struct { ServerCert GeneratableKeyCert // SNICertKeys are named CertKeys for serving secure traffic with SNI support. SNICertKeys []config.NamedCertKey - // ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates - ClientCA string } type CertKey struct { @@ -124,11 +122,6 @@ func (s *SecureServingOptions) AddFlags(fs *pflag.FlagSet) { "trump over extracted names. For multiple key/certificate pairs, use the "+ "--tls-sni-cert-key multiple times. "+ "Examples: \"example.key,example.crt\" or \"*.foo.com,foo.com:foo.key,foo.crt\".") - - fs.StringVar(&s.ClientCA, "client-ca-file", s.ClientCA, ""+ - "If set, any request presenting a client certificate signed by one of "+ - "the authorities in the client-ca-file is authenticated with an identity "+ - "corresponding to the CommonName of the client certificate.") } func (s *SecureServingOptions) AddDeprecatedFlags(fs *pflag.FlagSet) { From fbb35b72ed144ca98d88efce8cfc8832336d2a8d Mon Sep 17 00:00:00 2001 From: deads2k Date: Mon, 5 Dec 2016 13:46:25 -0500 Subject: [PATCH 2/3] update delegating auth to include front-proxy --- cmd/kube-apiserver/app/server.go | 12 ++++---- examples/apiserver/apiserver.go | 10 +++---- .../cmd/federation-apiserver/app/server.go | 12 ++++---- pkg/apiserver/authenticator/delegating.go | 20 ++++++++++++- .../options/authentication.go | 28 +++++++++++++++---- 5 files changed, 59 insertions(+), 23 deletions(-) diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index d7f7f2fd2eb..69588b0d158 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -96,14 +96,14 @@ func Run(s *options.ServerRunOptions) error { genericapiserver.DefaultAndValidateRunOptions(s.GenericServerRunOptions) - genericConfig, err := genericapiserver.NewConfig(). // create the new config - ApplyOptions(s.GenericServerRunOptions). // apply the options selected - ApplyInsecureServingOptions(s.InsecureServing). - ApplyAuthenticationOptions(s.Authentication). - ApplySecureServingOptions(s.SecureServing) - if err != nil { + genericConfig := genericapiserver.NewConfig(). // create the new config + ApplyOptions(s.GenericServerRunOptions). // apply the options selected + ApplyInsecureServingOptions(s.InsecureServing) + + if _, err := genericConfig.ApplySecureServingOptions(s.SecureServing); err != nil { return fmt.Errorf("failed to configure https: %s", err) } + genericConfig.ApplyAuthenticationOptions(s.Authentication) capabilities.Initialize(capabilities.Capabilities{ AllowPrivileged: s.AllowPrivileged, diff --git a/examples/apiserver/apiserver.go b/examples/apiserver/apiserver.go index 76044259fd8..6b0460020c6 100644 --- a/examples/apiserver/apiserver.go +++ b/examples/apiserver/apiserver.go @@ -99,14 +99,14 @@ func (serverOptions *ServerRunOptions) Run(stopCh <-chan struct{}) error { glog.Fatalf("Error creating self-signed certificates: %v", err) } - config, err := genericapiserver.NewConfig(). + config := genericapiserver.NewConfig(). ApplyOptions(serverOptions.GenericServerRunOptions). - ApplyInsecureServingOptions(serverOptions.InsecureServing). - ApplyAuthenticationOptions(serverOptions.Authentication). - ApplySecureServingOptions(serverOptions.SecureServing) - if err != nil { + ApplyInsecureServingOptions(serverOptions.InsecureServing) + + if _, err := config.ApplySecureServingOptions(serverOptions.SecureServing); err != nil { return fmt.Errorf("failed to configure https: %s", err) } + config.ApplyAuthenticationOptions(serverOptions.Authentication) config.Authorizer = authorizer.NewAlwaysAllowAuthorizer() s, err := config.Complete().New() diff --git a/federation/cmd/federation-apiserver/app/server.go b/federation/cmd/federation-apiserver/app/server.go index fd8c75215cd..bf444833909 100644 --- a/federation/cmd/federation-apiserver/app/server.go +++ b/federation/cmd/federation-apiserver/app/server.go @@ -78,14 +78,14 @@ func Run(s *options.ServerRunOptions) error { } genericapiserver.DefaultAndValidateRunOptions(s.GenericServerRunOptions) - genericConfig, err := genericapiserver.NewConfig(). // create the new config - ApplyOptions(s.GenericServerRunOptions). // apply the options selected - ApplyInsecureServingOptions(s.InsecureServing). - ApplyAuthenticationOptions(s.Authentication). - ApplySecureServingOptions(s.SecureServing) - if err != nil { + genericConfig := genericapiserver.NewConfig(). // create the new config + ApplyOptions(s.GenericServerRunOptions). // apply the options selected + ApplyInsecureServingOptions(s.InsecureServing) + + if _, err := genericConfig.ApplySecureServingOptions(s.SecureServing); err != nil { return fmt.Errorf("failed to configure https: %s", err) } + genericConfig.ApplyAuthenticationOptions(s.Authentication) // TODO: register cluster federation resources here. resourceConfig := genericapiserver.NewResourceConfig() diff --git a/pkg/apiserver/authenticator/delegating.go b/pkg/apiserver/authenticator/delegating.go index 185323bc869..74f7b0cdcbd 100644 --- a/pkg/apiserver/authenticator/delegating.go +++ b/pkg/apiserver/authenticator/delegating.go @@ -30,6 +30,7 @@ import ( authenticationclient "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/typed/authentication/v1beta1" "k8s.io/kubernetes/pkg/util/cert" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/anonymous" + "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/headerrequest" unionauth "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/x509" webhooktoken "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/webhook" @@ -47,12 +48,30 @@ type DelegatingAuthenticatorConfig struct { // ClientCAFile is the CA bundle file used to authenticate client certificates ClientCAFile string + + RequestHeaderConfig *RequestHeaderConfig } func (c DelegatingAuthenticatorConfig) New() (authenticator.Request, *spec.SecurityDefinitions, error) { authenticators := []authenticator.Request{} securityDefinitions := spec.SecurityDefinitions{} + // front-proxy first, then remote + // Add the front proxy authenticator if requested + if c.RequestHeaderConfig != nil { + requestHeaderAuthenticator, err := headerrequest.NewSecure( + c.RequestHeaderConfig.ClientCA, + c.RequestHeaderConfig.AllowedClientNames, + c.RequestHeaderConfig.UsernameHeaders, + c.RequestHeaderConfig.GroupHeaders, + c.RequestHeaderConfig.ExtraHeaderPrefixes, + ) + if err != nil { + return nil, nil, err + } + authenticators = append(authenticators, requestHeaderAuthenticator) + } + // x509 client cert auth if len(c.ClientCAFile) > 0 { clientCAs, err := cert.NewPool(c.ClientCAFile) @@ -93,5 +112,4 @@ func (c DelegatingAuthenticatorConfig) New() (authenticator.Request, *spec.Secur authenticator = unionauth.NewFailOnError(authenticator, anonymous.NewAuthenticator()) } return authenticator, &securityDefinitions, nil - } diff --git a/pkg/genericapiserver/options/authentication.go b/pkg/genericapiserver/options/authentication.go index 3239f33b047..9d0952f4091 100644 --- a/pkg/genericapiserver/options/authentication.go +++ b/pkg/genericapiserver/options/authentication.go @@ -350,7 +350,8 @@ func (s *ClientCertAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { } // DelegatingAuthenticationOptions provides an easy way for composing API servers to delegate their authentication to -// the root kube API server +// the root kube API server. The API federator will act as +// a front proxy and direction connections will be able to delegate to the core kube API server type DelegatingAuthenticationOptions struct { // RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the // TokenAccessReview.authentication.k8s.io endpoint for checking tokens. @@ -358,11 +359,21 @@ type DelegatingAuthenticationOptions struct { // CacheTTL is the length of time that a token authentication answer will be cached. CacheTTL time.Duration + + ClientCert ClientCertAuthenticationOptions + RequestHeader RequestHeaderAuthenticationOptions } func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions { return &DelegatingAuthenticationOptions{ - CacheTTL: 5 * time.Minute, + // very low for responsiveness, but high enough to handle storms + CacheTTL: 10 * time.Second, + ClientCert: ClientCertAuthenticationOptions{}, + RequestHeader: RequestHeaderAuthenticationOptions{ + UsernameHeaders: []string{"x-remote-user"}, + GroupHeaders: []string{"x-remote-group"}, + ExtraHeaderPrefixes: []string{"x-remote-extra-"}, + }, } } @@ -374,10 +385,16 @@ func (s *DelegatingAuthenticationOptions) Validate() []error { func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { fs.StringVar(&s.RemoteKubeConfigFile, "authentication-kubeconfig", s.RemoteKubeConfigFile, ""+ "kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+ - " tokenaccessreviews.authentication.k8s.io.") + "tokenaccessreviews.authentication.k8s.io.") + + fs.DurationVar(&s.CacheTTL, "authentication-token-webhook-cache-ttl", s.CacheTTL, + "The duration to cache responses from the webhook token authenticator.") + + s.ClientCert.AddFlags(fs) + s.RequestHeader.AddFlags(fs) } -func (s *DelegatingAuthenticationOptions) ToAuthenticationConfig(clientCAFile string) (authenticator.DelegatingAuthenticatorConfig, error) { +func (s *DelegatingAuthenticationOptions) ToAuthenticationConfig() (authenticator.DelegatingAuthenticatorConfig, error) { tokenClient, err := s.newTokenAccessReview() if err != nil { return authenticator.DelegatingAuthenticatorConfig{}, err @@ -387,7 +404,8 @@ func (s *DelegatingAuthenticationOptions) ToAuthenticationConfig(clientCAFile st Anonymous: true, TokenAccessReviewClient: tokenClient, CacheTTL: s.CacheTTL, - ClientCAFile: clientCAFile, + ClientCAFile: s.ClientCert.ClientCA, + RequestHeaderConfig: s.RequestHeader.ToAuthenticationRequestHeaderConfig(), } return ret, nil } From 6ea1d5d53d89ad04b642da582cc1ad72a51e577c Mon Sep 17 00:00:00 2001 From: deads2k Date: Mon, 5 Dec 2016 15:30:13 -0500 Subject: [PATCH 3/3] join client CA bundles into the accept path for genericapiserver --- cmd/kube-apiserver/app/server.go | 4 +- examples/apiserver/apiserver.go | 4 +- .../cmd/federation-apiserver/app/server.go | 4 +- hack/local-up-cluster.sh | 4 +- pkg/genericapiserver/config.go | 37 ++++++++++++++++--- pkg/genericapiserver/serve.go | 9 +---- pkg/util/cert/io.go | 6 +-- 7 files changed, 46 insertions(+), 22 deletions(-) diff --git a/cmd/kube-apiserver/app/server.go b/cmd/kube-apiserver/app/server.go index 69588b0d158..b65280fced7 100644 --- a/cmd/kube-apiserver/app/server.go +++ b/cmd/kube-apiserver/app/server.go @@ -103,7 +103,9 @@ func Run(s *options.ServerRunOptions) error { if _, err := genericConfig.ApplySecureServingOptions(s.SecureServing); err != nil { return fmt.Errorf("failed to configure https: %s", err) } - genericConfig.ApplyAuthenticationOptions(s.Authentication) + if _, err = genericConfig.ApplyAuthenticationOptions(s.Authentication); err != nil { + return fmt.Errorf("failed to configure authentication: %s", err) + } capabilities.Initialize(capabilities.Capabilities{ AllowPrivileged: s.AllowPrivileged, diff --git a/examples/apiserver/apiserver.go b/examples/apiserver/apiserver.go index 6b0460020c6..5442a6f1d15 100644 --- a/examples/apiserver/apiserver.go +++ b/examples/apiserver/apiserver.go @@ -106,7 +106,9 @@ func (serverOptions *ServerRunOptions) Run(stopCh <-chan struct{}) error { if _, err := config.ApplySecureServingOptions(serverOptions.SecureServing); err != nil { return fmt.Errorf("failed to configure https: %s", err) } - config.ApplyAuthenticationOptions(serverOptions.Authentication) + if _, err := config.ApplyAuthenticationOptions(serverOptions.Authentication); err != nil { + return fmt.Errorf("failed to configure authentication: %s", err) + } config.Authorizer = authorizer.NewAlwaysAllowAuthorizer() s, err := config.Complete().New() diff --git a/federation/cmd/federation-apiserver/app/server.go b/federation/cmd/federation-apiserver/app/server.go index bf444833909..435d8201e3a 100644 --- a/federation/cmd/federation-apiserver/app/server.go +++ b/federation/cmd/federation-apiserver/app/server.go @@ -85,7 +85,9 @@ func Run(s *options.ServerRunOptions) error { if _, err := genericConfig.ApplySecureServingOptions(s.SecureServing); err != nil { return fmt.Errorf("failed to configure https: %s", err) } - genericConfig.ApplyAuthenticationOptions(s.Authentication) + if _, err := genericConfig.ApplyAuthenticationOptions(s.Authentication); err != nil { + return fmt.Errorf("failed to configure authentication: %s", err) + } // TODO: register cluster federation resources here. resourceConfig := genericapiserver.NewResourceConfig() diff --git a/hack/local-up-cluster.sh b/hack/local-up-cluster.sh index ccfd1a39017..20d9ca28d2c 100755 --- a/hack/local-up-cluster.sh +++ b/hack/local-up-cluster.sh @@ -455,14 +455,12 @@ EOF EOF create_client_certkey auth-proxy-client-ca auth-proxy system:auth-proxy - sudo bash -c "cat '${CERT_DIR}/client-ca.crt' '${CERT_DIR}/auth-proxy-client-ca.crt' > '${CERT_DIR}/client-ca-bundle.crt'" - APISERVER_LOG=/tmp/kube-apiserver.log ${CONTROLPLANE_SUDO} "${GO_OUT}/hyperkube" apiserver ${anytoken_arg} ${auth_proxy_arg} ${authorizer_arg} ${priv_arg} ${runtime_config}\ ${advertise_address} \ --v=${LOG_LEVEL} \ --cert-dir="${CERT_DIR}" \ - --client-ca-file="${CERT_DIR}/client-ca-bundle.crt" \ + --client-ca-file="${CERT_DIR}/client-ca.crt" \ --service-account-key-file="${SERVICE_ACCOUNT_KEY}" \ --service-account-lookup="${SERVICE_ACCOUNT_LOOKUP}" \ --admission-control="${ADMISSION_CONTROL}" \ diff --git a/pkg/genericapiserver/config.go b/pkg/genericapiserver/config.go index 58d5140ba6e..84f7f067bf8 100644 --- a/pkg/genericapiserver/config.go +++ b/pkg/genericapiserver/config.go @@ -18,6 +18,7 @@ package genericapiserver import ( "crypto/tls" + "crypto/x509" "encoding/pem" "fmt" "io" @@ -60,6 +61,7 @@ import ( "k8s.io/kubernetes/pkg/genericapiserver/routes" genericvalidation "k8s.io/kubernetes/pkg/genericapiserver/validation" "k8s.io/kubernetes/pkg/runtime" + certutil "k8s.io/kubernetes/pkg/util/cert" "k8s.io/kubernetes/pkg/util/sets" "k8s.io/kubernetes/pkg/version" authenticatorunion "k8s.io/kubernetes/plugin/pkg/auth/authenticator/request/union" @@ -180,7 +182,7 @@ type SecureServingInfo struct { SNICerts map[string]*tls.Certificate // ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates - ClientCA string + ClientCA *x509.CertPool } // NewConfig returns a Config struct with the default values @@ -299,17 +301,40 @@ func (c *Config) ApplyInsecureServingOptions(insecureServing *options.ServingOpt return c } -func (c *Config) ApplyAuthenticationOptions(o *options.BuiltInAuthenticationOptions) *Config { +func (c *Config) ApplyAuthenticationOptions(o *options.BuiltInAuthenticationOptions) (*Config, error) { if o == nil || o.PasswordFile == nil { - return c + return c, nil } - if o.ClientCert != nil && c.SecureServingInfo != nil { - c.SecureServingInfo.ClientCA = o.ClientCert.ClientCA + if c.SecureServingInfo != nil { + if o.ClientCert != nil && len(o.ClientCert.ClientCA) > 0 { + clientCAs, err := certutil.CertsFromFile(o.ClientCert.ClientCA) + if err != nil { + return nil, fmt.Errorf("unable to load client CA file: %v", err) + } + if c.SecureServingInfo.ClientCA == nil { + c.SecureServingInfo.ClientCA = x509.NewCertPool() + } + for _, cert := range clientCAs { + c.SecureServingInfo.ClientCA.AddCert(cert) + } + } + if o.RequestHeader != nil && len(o.RequestHeader.ClientCAFile) > 0 { + clientCAs, err := certutil.CertsFromFile(o.RequestHeader.ClientCAFile) + if err != nil { + return nil, fmt.Errorf("unable to load requestheader client CA file: %v", err) + } + if c.SecureServingInfo.ClientCA == nil { + c.SecureServingInfo.ClientCA = x509.NewCertPool() + } + for _, cert := range clientCAs { + c.SecureServingInfo.ClientCA.AddCert(cert) + } + } } c.SupportsBasicAuth = len(o.PasswordFile.BasicAuthFile) > 0 - return c + return c, nil } // ApplyOptions applies the run options to the method receiver and returns self diff --git a/pkg/genericapiserver/serve.go b/pkg/genericapiserver/serve.go index 1a17eca3ea1..c9b35fcf75c 100644 --- a/pkg/genericapiserver/serve.go +++ b/pkg/genericapiserver/serve.go @@ -26,7 +26,6 @@ import ( "sync" "time" - certutil "k8s.io/kubernetes/pkg/util/cert" utilruntime "k8s.io/kubernetes/pkg/util/runtime" "k8s.io/kubernetes/pkg/util/validation" @@ -69,16 +68,12 @@ func (s *GenericAPIServer) serveSecurely(stopCh <-chan struct{}) error { secureServer.TLSConfig.Certificates = append(secureServer.TLSConfig.Certificates, *c) } - if len(s.SecureServingInfo.ClientCA) > 0 { - clientCAs, err := certutil.NewPool(s.SecureServingInfo.ClientCA) - if err != nil { - return fmt.Errorf("unable to load client CA file: %v", err) - } + if s.SecureServingInfo.ClientCA != nil { // Populate PeerCertificates in requests, but don't reject connections without certificates // This allows certificates to be validated by authenticators, while still allowing other auth types secureServer.TLSConfig.ClientAuth = tls.RequestClientCert // Specify allowed CAs for client certificates - secureServer.TLSConfig.ClientCAs = clientCAs + secureServer.TLSConfig.ClientCAs = s.SecureServingInfo.ClientCA } glog.Infof("Serving securely on %s", s.SecureServingInfo.BindAddress) diff --git a/pkg/util/cert/io.go b/pkg/util/cert/io.go index 377b3d5892f..2b6201fc30a 100644 --- a/pkg/util/cert/io.go +++ b/pkg/util/cert/io.go @@ -90,7 +90,7 @@ func WriteKey(keyPath string, data []byte) error { // NewPool returns an x509.CertPool containing the certificates in the given PEM-encoded file. // Returns an error if the file could not be read, a certificate could not be parsed, or if the file does not contain any certificates func NewPool(filename string) (*x509.CertPool, error) { - certs, err := certsFromFile(filename) + certs, err := CertsFromFile(filename) if err != nil { return nil, err } @@ -101,9 +101,9 @@ func NewPool(filename string) (*x509.CertPool, error) { return pool, nil } -// certsFromFile returns the x509.Certificates contained in the given PEM-encoded file. +// CertsFromFile returns the x509.Certificates contained in the given PEM-encoded file. // Returns an error if the file could not be read, a certificate could not be parsed, or if the file does not contain any certificates -func certsFromFile(file string) ([]*x509.Certificate, error) { +func CertsFromFile(file string) ([]*x509.Certificate, error) { if len(file) == 0 { return nil, errors.New("error reading certificates from an empty filename") }