Merge pull request #35488 from dixudx/keystone-ca-cert

Automatic merge from submit-queue

specify custom ca file to verify the keystone server

<!--  Thanks for sending a pull request!  Here are some tips for you:
1. If this is your first time, read our contributor guidelines https://github.com/kubernetes/kubernetes/blob/master/CONTRIBUTING.md and developer guide https://github.com/kubernetes/kubernetes/blob/master/docs/devel/development.md
2. If you want *faster* PR reviews, read how: https://github.com/kubernetes/kubernetes/blob/master/docs/devel/faster_reviews.md
3. Follow the instructions for writing a release note: https://github.com/kubernetes/kubernetes/blob/master/docs/devel/pull-requests.md#release-notes
-->

**What this PR does / why we need it**:

Sometimes the keystone server's certificate is self-signed, mainly used for internal development, testing and etc.

For this kind of ca, we need a way to verify the keystone server.

Otherwise, below error will occur.

> x509: certificate signed by unknown authority

This patch provide a way to pass in a ca file to verify the keystone server when starting `kube-apiserver`.

**Which issue this PR fixes** : fixes #22695, #24984

**Special notes for your reviewer**:

**Release note**:

<!--  Steps to write your release note:
1. Use the release-note-* labels to set the release note state (if you have access) 
2. Enter your extended release note in the below block; leaving it blank means using the PR title as the release note. If no release note is required, just write `NONE`. 
-->

``` release-note
```
pull/6/head
Kubernetes Submit Queue 2016-11-08 13:13:00 -08:00 committed by GitHub
commit 860cae0933
6 changed files with 49 additions and 8 deletions

View File

@ -234,6 +234,7 @@ func Run(s *options.ServerRunOptions) error {
ServiceAccountLookup: s.ServiceAccountLookup,
ServiceAccountTokenGetter: serviceAccountGetter,
KeystoneURL: s.GenericServerRunOptions.KeystoneURL,
KeystoneCAFile: s.GenericServerRunOptions.KeystoneCAFile,
WebhookTokenAuthnConfigFile: s.WebhookTokenAuthnConfigFile,
WebhookTokenAuthnCacheTTL: s.WebhookTokenAuthnCacheTTL,
RequestHeaderConfig: s.GenericServerRunOptions.AuthenticationRequestHeaderConfig(),

View File

@ -190,6 +190,7 @@ exit-on-lock-contention
experimental-allowed-unsafe-sysctls
experimental-bootstrap-kubeconfig
experimental-keystone-url
experimental-keystone-ca-file
experimental-mounter-path
experimental-mounter-rootfs-path
experimental-nvidia-gpus

View File

@ -64,6 +64,7 @@ type AuthenticatorConfig struct {
ServiceAccountLookup bool
ServiceAccountTokenGetter serviceaccount.ServiceAccountTokenGetter
KeystoneURL string
KeystoneCAFile string
WebhookTokenAuthnConfigFile string
WebhookTokenAuthnCacheTTL time.Duration
@ -101,7 +102,7 @@ func New(config AuthenticatorConfig) (authenticator.Request, *spec.SecurityDefin
hasBasicAuth = true
}
if len(config.KeystoneURL) > 0 {
keystoneAuth, err := newAuthenticatorFromKeystoneURL(config.KeystoneURL)
keystoneAuth, err := newAuthenticatorFromKeystoneURL(config.KeystoneURL, config.KeystoneCAFile)
if err != nil {
return nil, nil, err
}
@ -283,8 +284,8 @@ func newAuthenticatorFromClientCAFile(clientCAFile string) (authenticator.Reques
}
// newAuthenticatorFromTokenFile returns an authenticator.Request or an error
func newAuthenticatorFromKeystoneURL(keystoneURL string) (authenticator.Request, error) {
keystoneAuthenticator, err := keystone.NewKeystoneAuthenticator(keystoneURL)
func newAuthenticatorFromKeystoneURL(keystoneURL string, keystoneCAFile string) (authenticator.Request, error) {
keystoneAuthenticator, err := keystone.NewKeystoneAuthenticator(keystoneURL, keystoneCAFile)
if err != nil {
return nil, err
}

View File

@ -91,6 +91,7 @@ type ServerRunOptions struct {
InsecureBindAddress net.IP
InsecurePort int
KeystoneURL string
KeystoneCAFile string
KubernetesServiceNodePort int
LongRunningRequestRE string
MasterCount int
@ -379,6 +380,10 @@ func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.KeystoneURL, "experimental-keystone-url", s.KeystoneURL,
"If passed, activates the keystone authentication plugin.")
fs.StringVar(&s.KeystoneCAFile, "experimental-keystone-ca-file", s.KeystoneCAFile, ""+
"If set, the Keystone server's certificate will be verified by one of the authorities "+
"in the experimental-keystone-ca-file, otherwise the host's root CA set will be used.")
// See #14282 for details on how to test/try this option out.
// TODO: remove this comment once this option is tested in CI.
fs.IntVar(&s.KubernetesServiceNodePort, "kubernetes-service-node-port", s.KubernetesServiceNodePort, ""+

View File

@ -19,6 +19,8 @@ go_library(
tags = ["automanaged"],
deps = [
"//pkg/auth/user:go_default_library",
"//pkg/util/cert:go_default_library",
"//pkg/util/net:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:github.com/rackspace/gophercloud",
"//vendor:github.com/rackspace/gophercloud/openstack",

View File

@ -17,19 +17,24 @@ limitations under the License.
package keystone
import (
"crypto/tls"
"errors"
"net/http"
"strings"
"github.com/golang/glog"
"github.com/rackspace/gophercloud"
"github.com/rackspace/gophercloud/openstack"
"k8s.io/kubernetes/pkg/auth/user"
certutil "k8s.io/kubernetes/pkg/util/cert"
netutil "k8s.io/kubernetes/pkg/util/net"
)
// KeystoneAuthenticator contacts openstack keystone to validate user's credentials passed in the request.
// The keystone endpoint is passed during apiserver startup
type KeystoneAuthenticator struct {
authURL string
authURL string
transport http.RoundTripper
}
// AuthenticatePassword checks the username, password via keystone call
@ -40,23 +45,49 @@ func (keystoneAuthenticator *KeystoneAuthenticator) AuthenticatePassword(usernam
Password: password,
}
_, err := openstack.AuthenticatedClient(opts)
_, err := keystoneAuthenticator.AuthenticatedClient(opts)
if err != nil {
glog.Info("Failed: Starting openstack authenticate client")
glog.Info("Failed: Starting openstack authenticate client:" + err.Error())
return nil, false, errors.New("Failed to authenticate")
}
return &user.DefaultInfo{Name: username}, true, nil
}
// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a
// token, and returns a Client instance that's ready to operate.
func (keystoneAuthenticator *KeystoneAuthenticator) AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
client, err := openstack.NewClient(options.IdentityEndpoint)
if err != nil {
return nil, err
}
if keystoneAuthenticator.transport != nil {
client.HTTPClient.Transport = keystoneAuthenticator.transport
}
err = openstack.Authenticate(client, options)
return client, err
}
// NewKeystoneAuthenticator returns a password authenticator that validates credentials using openstack keystone
func NewKeystoneAuthenticator(authURL string) (*KeystoneAuthenticator, error) {
func NewKeystoneAuthenticator(authURL string, caFile string) (*KeystoneAuthenticator, error) {
if !strings.HasPrefix(authURL, "https") {
return nil, errors.New("Auth URL should be secure and start with https")
}
if authURL == "" {
return nil, errors.New("Auth URL is empty")
}
if caFile != "" {
roots, err := certutil.NewPool(caFile)
if err != nil {
return nil, err
}
config := &tls.Config{}
config.RootCAs = roots
transport := netutil.SetOldTransportDefaults(&http.Transport{TLSClientConfig: config})
return &KeystoneAuthenticator{authURL, transport}, nil
}
return &KeystoneAuthenticator{authURL}, nil
return &KeystoneAuthenticator{authURL: authURL}, nil
}