mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
Merge pull request #23066 from cjcullen/clientplugin
Automatic merge from submit-queue Client auth provider plugin framework Allows client plugins to modify the underlying transport to, for example, add custom authorization headers.
This commit is contained in:
commit
f4beccf000
@ -30,6 +30,7 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||||
|
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||||
"k8s.io/kubernetes/pkg/runtime"
|
"k8s.io/kubernetes/pkg/runtime"
|
||||||
"k8s.io/kubernetes/pkg/util/crypto"
|
"k8s.io/kubernetes/pkg/util/crypto"
|
||||||
"k8s.io/kubernetes/pkg/version"
|
"k8s.io/kubernetes/pkg/version"
|
||||||
@ -65,6 +66,9 @@ type Config struct {
|
|||||||
// Impersonate is the username that this RESTClient will impersonate
|
// Impersonate is the username that this RESTClient will impersonate
|
||||||
Impersonate string
|
Impersonate string
|
||||||
|
|
||||||
|
// Server requires plugin-specified authentication.
|
||||||
|
AuthProvider *clientcmdapi.AuthProviderConfig
|
||||||
|
|
||||||
// TLSClientConfig contains settings to enable transport layer security
|
// TLSClientConfig contains settings to enable transport layer security
|
||||||
TLSClientConfig
|
TLSClientConfig
|
||||||
|
|
||||||
|
60
pkg/client/restclient/plugin.go
Normal file
60
pkg/client/restclient/plugin.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package restclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
|
||||||
|
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthProvider interface {
|
||||||
|
// WrapTransport allows the plugin to create a modified RoundTripper that
|
||||||
|
// attaches authorization headers (or other info) to requests.
|
||||||
|
WrapTransport(http.RoundTripper) http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
type Factory func() (AuthProvider, error)
|
||||||
|
|
||||||
|
// All registered auth provider plugins.
|
||||||
|
var pluginsLock sync.Mutex
|
||||||
|
var plugins = make(map[string]Factory)
|
||||||
|
|
||||||
|
func RegisterAuthProviderPlugin(name string, plugin Factory) error {
|
||||||
|
pluginsLock.Lock()
|
||||||
|
defer pluginsLock.Unlock()
|
||||||
|
if _, found := plugins[name]; found {
|
||||||
|
return fmt.Errorf("Auth Provider Plugin %q was registered twice", name)
|
||||||
|
}
|
||||||
|
glog.V(4).Infof("Registered Auth Provider Plugin %q", name)
|
||||||
|
plugins[name] = plugin
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAuthProvider(apc *clientcmdapi.AuthProviderConfig) (AuthProvider, error) {
|
||||||
|
pluginsLock.Lock()
|
||||||
|
defer pluginsLock.Unlock()
|
||||||
|
p, ok := plugins[apc.Name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("No Auth Provider found for name %q", apc.Name)
|
||||||
|
}
|
||||||
|
return p()
|
||||||
|
}
|
@ -26,14 +26,22 @@ import (
|
|||||||
// TLSConfigFor returns a tls.Config that will provide the transport level security defined
|
// TLSConfigFor returns a tls.Config that will provide the transport level security defined
|
||||||
// by the provided Config. Will return nil if no transport level security is requested.
|
// by the provided Config. Will return nil if no transport level security is requested.
|
||||||
func TLSConfigFor(config *Config) (*tls.Config, error) {
|
func TLSConfigFor(config *Config) (*tls.Config, error) {
|
||||||
return transport.TLSConfigFor(config.transportConfig())
|
cfg, err := config.transportConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return transport.TLSConfigFor(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TransportFor returns an http.RoundTripper that will provide the authentication
|
// TransportFor returns an http.RoundTripper that will provide the authentication
|
||||||
// or transport level security defined by the provided Config. Will return the
|
// or transport level security defined by the provided Config. Will return the
|
||||||
// default http.DefaultTransport if no special case behavior is needed.
|
// default http.DefaultTransport if no special case behavior is needed.
|
||||||
func TransportFor(config *Config) (http.RoundTripper, error) {
|
func TransportFor(config *Config) (http.RoundTripper, error) {
|
||||||
return transport.New(config.transportConfig())
|
cfg, err := config.transportConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return transport.New(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPWrappersForConfig wraps a round tripper with any relevant layered behavior from the
|
// HTTPWrappersForConfig wraps a round tripper with any relevant layered behavior from the
|
||||||
@ -41,15 +49,34 @@ func TransportFor(config *Config) (http.RoundTripper, error) {
|
|||||||
// the underlying connection (like WebSocket or HTTP2 clients). Pure HTTP clients should use
|
// the underlying connection (like WebSocket or HTTP2 clients). Pure HTTP clients should use
|
||||||
// the higher level TransportFor or RESTClientFor methods.
|
// the higher level TransportFor or RESTClientFor methods.
|
||||||
func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTripper, error) {
|
func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTripper, error) {
|
||||||
return transport.HTTPWrappersForConfig(config.transportConfig(), rt)
|
cfg, err := config.transportConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return transport.HTTPWrappersForConfig(cfg, rt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// transportConfig converts a client config to an appropriate transport config.
|
// transportConfig converts a client config to an appropriate transport config.
|
||||||
func (c *Config) transportConfig() *transport.Config {
|
func (c *Config) transportConfig() (*transport.Config, error) {
|
||||||
|
wt := c.WrapTransport
|
||||||
|
if c.AuthProvider != nil {
|
||||||
|
provider, err := GetAuthProvider(c.AuthProvider)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if wt != nil {
|
||||||
|
previousWT := wt
|
||||||
|
wt = func(rt http.RoundTripper) http.RoundTripper {
|
||||||
|
return provider.WrapTransport(previousWT(rt))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wt = provider.WrapTransport
|
||||||
|
}
|
||||||
|
}
|
||||||
return &transport.Config{
|
return &transport.Config{
|
||||||
UserAgent: c.UserAgent,
|
UserAgent: c.UserAgent,
|
||||||
Transport: c.Transport,
|
Transport: c.Transport,
|
||||||
WrapTransport: c.WrapTransport,
|
WrapTransport: wt,
|
||||||
TLS: transport.TLSConfig{
|
TLS: transport.TLSConfig{
|
||||||
CAFile: c.CAFile,
|
CAFile: c.CAFile,
|
||||||
CAData: c.CAData,
|
CAData: c.CAData,
|
||||||
@ -63,5 +90,5 @@ func (c *Config) transportConfig() *transport.Config {
|
|||||||
Password: c.Password,
|
Password: c.Password,
|
||||||
BearerToken: c.BearerToken,
|
BearerToken: c.BearerToken,
|
||||||
Impersonate: c.Impersonate,
|
Impersonate: c.Impersonate,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
171
pkg/client/restclient/transport_test.go
Normal file
171
pkg/client/restclient/transport_test.go
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package restclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTransportConfigAuthPlugins(t *testing.T) {
|
||||||
|
if err := RegisterAuthProviderPlugin("pluginA", pluginAProvider); err != nil {
|
||||||
|
t.Errorf("Unexpected error: failed to register pluginA: %v", err)
|
||||||
|
}
|
||||||
|
if err := RegisterAuthProviderPlugin("pluginB", pluginBProvider); err != nil {
|
||||||
|
t.Errorf("Unexpected error: failed to register pluginB: %v", err)
|
||||||
|
}
|
||||||
|
if err := RegisterAuthProviderPlugin("pluginFail", pluginFailProvider); err != nil {
|
||||||
|
t.Errorf("Unexpected error: failed to register pluginFail: %v", err)
|
||||||
|
}
|
||||||
|
testCases := []struct {
|
||||||
|
useWrapTransport bool
|
||||||
|
plugin string
|
||||||
|
expectErr bool
|
||||||
|
expectPluginA bool
|
||||||
|
expectPluginB bool
|
||||||
|
}{
|
||||||
|
{false, "", false, false, false},
|
||||||
|
{false, "pluginA", false, true, false},
|
||||||
|
{false, "pluginB", false, false, true},
|
||||||
|
{false, "pluginFail", true, false, false},
|
||||||
|
{false, "pluginUnknown", true, false, false},
|
||||||
|
}
|
||||||
|
for i, tc := range testCases {
|
||||||
|
c := Config{}
|
||||||
|
if tc.useWrapTransport {
|
||||||
|
// Specify an existing WrapTransport in the config to make sure that
|
||||||
|
// plugins play nicely.
|
||||||
|
c.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
|
||||||
|
return &wrapTransport{rt}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(tc.plugin) != 0 {
|
||||||
|
c.AuthProvider = &clientcmdapi.AuthProviderConfig{Name: tc.plugin}
|
||||||
|
}
|
||||||
|
tConfig, err := c.transportConfig()
|
||||||
|
if err != nil {
|
||||||
|
// Unknown/bad plugins are expected to fail here.
|
||||||
|
if !tc.expectErr {
|
||||||
|
t.Errorf("%d. Did not expect errors loading Auth Plugin: %q. Got: %v", i, tc.plugin, err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var fullyWrappedTransport http.RoundTripper
|
||||||
|
fullyWrappedTransport = &emptyTransport{}
|
||||||
|
if tConfig.WrapTransport != nil {
|
||||||
|
fullyWrappedTransport = tConfig.WrapTransport(&emptyTransport{})
|
||||||
|
}
|
||||||
|
res, err := fullyWrappedTransport.RoundTrip(&http.Request{})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d. Unexpected error in RoundTrip: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
hasWrapTransport := res.Header.Get("wrapTransport") == "Y"
|
||||||
|
hasPluginA := res.Header.Get("pluginA") == "Y"
|
||||||
|
hasPluginB := res.Header.Get("pluginB") == "Y"
|
||||||
|
if hasWrapTransport != tc.useWrapTransport {
|
||||||
|
t.Errorf("%d. Expected Existing config.WrapTransport: %t; Got: %t", i, tc.useWrapTransport, hasWrapTransport)
|
||||||
|
}
|
||||||
|
if hasPluginA != tc.expectPluginA {
|
||||||
|
t.Errorf("%d. Expected Plugin A: %t; Got: %t", i, tc.expectPluginA, hasPluginA)
|
||||||
|
}
|
||||||
|
if hasPluginB != tc.expectPluginB {
|
||||||
|
t.Errorf("%d. Expected Plugin B: %t; Got: %t", i, tc.expectPluginB, hasPluginB)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// emptyTransport provides an empty http.Response with an initialized header
|
||||||
|
// to allow wrapping RoundTrippers to set header values.
|
||||||
|
type emptyTransport struct{}
|
||||||
|
|
||||||
|
func (*emptyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
res := &http.Response{
|
||||||
|
Header: make(map[string][]string),
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapTransport sets "wrapTransport" = "Y" on the response.
|
||||||
|
type wrapTransport struct {
|
||||||
|
rt http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
res, err := w.rt.RoundTrip(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res.Header.Add("wrapTransport", "Y")
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapTransportA sets "pluginA" = "Y" on the response.
|
||||||
|
type wrapTransportA struct {
|
||||||
|
rt http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapTransportA) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
res, err := w.rt.RoundTrip(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res.Header.Add("pluginA", "Y")
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type pluginA struct{}
|
||||||
|
|
||||||
|
func (*pluginA) WrapTransport(rt http.RoundTripper) http.RoundTripper {
|
||||||
|
return &wrapTransportA{rt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pluginAProvider() (AuthProvider, error) {
|
||||||
|
return &pluginA{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapTransportB sets "pluginB" = "Y" on the response.
|
||||||
|
type wrapTransportB struct {
|
||||||
|
rt http.RoundTripper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapTransportB) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||||
|
res, err := w.rt.RoundTrip(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res.Header.Add("pluginB", "Y")
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type pluginB struct{}
|
||||||
|
|
||||||
|
func (*pluginB) WrapTransport(rt http.RoundTripper) http.RoundTripper {
|
||||||
|
return &wrapTransportB{rt}
|
||||||
|
}
|
||||||
|
|
||||||
|
func pluginBProvider() (AuthProvider, error) {
|
||||||
|
return &pluginB{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pluginFailProvider simulates a registered AuthPlugin that fails to load.
|
||||||
|
func pluginFailProvider() (AuthProvider, error) {
|
||||||
|
return nil, fmt.Errorf("Failed to load AuthProvider")
|
||||||
|
}
|
@ -94,6 +94,8 @@ type AuthInfo struct {
|
|||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
// Password is the password for basic authentication to the kubernetes cluster.
|
// Password is the password for basic authentication to the kubernetes cluster.
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
|
// AuthProvider specifies a custom authentication plugin for the kubernetes cluster.
|
||||||
|
AuthProvider *AuthProviderConfig `json:"auth-provider,omitempty"`
|
||||||
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
|
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
|
||||||
Extensions map[string]runtime.Object `json:"extensions,omitempty"`
|
Extensions map[string]runtime.Object `json:"extensions,omitempty"`
|
||||||
}
|
}
|
||||||
@ -112,6 +114,11 @@ type Context struct {
|
|||||||
Extensions map[string]runtime.Object `json:"extensions,omitempty"`
|
Extensions map[string]runtime.Object `json:"extensions,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuthProviderConfig holds the configuration for a specified auth provider.
|
||||||
|
type AuthProviderConfig struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
// NewConfig is a convenience function that returns a new Config object with non-nil maps
|
// NewConfig is a convenience function that returns a new Config object with non-nil maps
|
||||||
func NewConfig() *Config {
|
func NewConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
|
@ -58,14 +58,17 @@ func Example_ofOptionsConfig() {
|
|||||||
defaultConfig.AuthInfos["red-mage-via-token"] = &AuthInfo{
|
defaultConfig.AuthInfos["red-mage-via-token"] = &AuthInfo{
|
||||||
Token: "my-secret-token",
|
Token: "my-secret-token",
|
||||||
}
|
}
|
||||||
|
defaultConfig.AuthInfos["black-mage-via-auth-provider"] = &AuthInfo{
|
||||||
|
AuthProvider: &AuthProviderConfig{Name: "gcp"},
|
||||||
|
}
|
||||||
defaultConfig.Contexts["bravo-as-black-mage"] = &Context{
|
defaultConfig.Contexts["bravo-as-black-mage"] = &Context{
|
||||||
Cluster: "bravo",
|
Cluster: "bravo",
|
||||||
AuthInfo: "black-mage-via-file",
|
AuthInfo: "black-mage-via-auth-provider",
|
||||||
Namespace: "yankee",
|
Namespace: "yankee",
|
||||||
}
|
}
|
||||||
defaultConfig.Contexts["alfa-as-black-mage"] = &Context{
|
defaultConfig.Contexts["alfa-as-black-mage"] = &Context{
|
||||||
Cluster: "alfa",
|
Cluster: "alfa",
|
||||||
AuthInfo: "black-mage-via-file",
|
AuthInfo: "black-mage-via-auth-provider",
|
||||||
Namespace: "zulu",
|
Namespace: "zulu",
|
||||||
}
|
}
|
||||||
defaultConfig.Contexts["alfa-as-white-mage"] = &Context{
|
defaultConfig.Contexts["alfa-as-white-mage"] = &Context{
|
||||||
@ -95,7 +98,7 @@ func Example_ofOptionsConfig() {
|
|||||||
// LocationOfOrigin: ""
|
// LocationOfOrigin: ""
|
||||||
// cluster: alfa
|
// cluster: alfa
|
||||||
// namespace: zulu
|
// namespace: zulu
|
||||||
// user: black-mage-via-file
|
// user: black-mage-via-auth-provider
|
||||||
// alfa-as-white-mage:
|
// alfa-as-white-mage:
|
||||||
// LocationOfOrigin: ""
|
// LocationOfOrigin: ""
|
||||||
// cluster: alfa
|
// cluster: alfa
|
||||||
@ -104,11 +107,15 @@ func Example_ofOptionsConfig() {
|
|||||||
// LocationOfOrigin: ""
|
// LocationOfOrigin: ""
|
||||||
// cluster: bravo
|
// cluster: bravo
|
||||||
// namespace: yankee
|
// namespace: yankee
|
||||||
// user: black-mage-via-file
|
// user: black-mage-via-auth-provider
|
||||||
// current-context: alfa-as-white-mage
|
// current-context: alfa-as-white-mage
|
||||||
// preferences:
|
// preferences:
|
||||||
// colors: true
|
// colors: true
|
||||||
// users:
|
// users:
|
||||||
|
// black-mage-via-auth-provider:
|
||||||
|
// LocationOfOrigin: ""
|
||||||
|
// auth-provider:
|
||||||
|
// name: gcp
|
||||||
// red-mage-via-token:
|
// red-mage-via-token:
|
||||||
// LocationOfOrigin: ""
|
// LocationOfOrigin: ""
|
||||||
// token: my-secret-token
|
// token: my-secret-token
|
||||||
|
@ -88,6 +88,8 @@ type AuthInfo struct {
|
|||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
// Password is the password for basic authentication to the kubernetes cluster.
|
// Password is the password for basic authentication to the kubernetes cluster.
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
|
// AuthProvider specifies a custom authentication plugin for the kubernetes cluster.
|
||||||
|
AuthProvider *AuthProviderConfig `json:"auth-provider,omitempty"`
|
||||||
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
|
// Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields
|
||||||
Extensions []NamedExtension `json:"extensions,omitempty"`
|
Extensions []NamedExtension `json:"extensions,omitempty"`
|
||||||
}
|
}
|
||||||
@ -135,3 +137,8 @@ type NamedExtension struct {
|
|||||||
// Extension holds the extension information
|
// Extension holds the extension information
|
||||||
Extension runtime.RawExtension `json:"extension"`
|
Extension runtime.RawExtension `json:"extension"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuthProviderConfig holds the configuration for a specified auth provider.
|
||||||
|
type AuthProviderConfig struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
@ -172,6 +172,9 @@ func getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fa
|
|||||||
mergedConfig.Username = configAuthInfo.Username
|
mergedConfig.Username = configAuthInfo.Username
|
||||||
mergedConfig.Password = configAuthInfo.Password
|
mergedConfig.Password = configAuthInfo.Password
|
||||||
}
|
}
|
||||||
|
if configAuthInfo.AuthProvider != nil {
|
||||||
|
mergedConfig.AuthProvider = configAuthInfo.AuthProvider
|
||||||
|
}
|
||||||
|
|
||||||
// if there still isn't enough information to authenticate the user, try prompting
|
// if there still isn't enough information to authenticate the user, try prompting
|
||||||
if !canIdentifyUser(*mergedConfig) && (fallbackReader != nil) {
|
if !canIdentifyUser(*mergedConfig) && (fallbackReader != nil) {
|
||||||
@ -212,8 +215,8 @@ func makeServerIdentificationConfig(info clientauth.Info) restclient.Config {
|
|||||||
func canIdentifyUser(config restclient.Config) bool {
|
func canIdentifyUser(config restclient.Config) bool {
|
||||||
return len(config.Username) > 0 ||
|
return len(config.Username) > 0 ||
|
||||||
(len(config.CertFile) > 0 || len(config.CertData) > 0) ||
|
(len(config.CertFile) > 0 || len(config.CertData) > 0) ||
|
||||||
len(config.BearerToken) > 0
|
len(config.BearerToken) > 0 ||
|
||||||
|
config.AuthProvider != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Namespace implements KubeConfig
|
// Namespace implements KubeConfig
|
||||||
|
@ -30,6 +30,8 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||||
"k8s.io/kubernetes/pkg/util/sets"
|
"k8s.io/kubernetes/pkg/util/sets"
|
||||||
"k8s.io/kubernetes/pkg/version"
|
"k8s.io/kubernetes/pkg/version"
|
||||||
|
// Import solely to initialize client auth plugins.
|
||||||
|
_ "k8s.io/kubernetes/plugin/pkg/client/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
53
plugin/pkg/client/auth/gcp/gcp.go
Normal file
53
plugin/pkg/client/auth/gcp/gcp.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package gcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/google"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/client/restclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if err := restclient.RegisterAuthProviderPlugin("gcp", newGCPAuthProvider); err != nil {
|
||||||
|
glog.Fatalf("Failed to register gcp auth plugin: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type gcpAuthProvider struct {
|
||||||
|
tokenSource oauth2.TokenSource
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGCPAuthProvider() (restclient.AuthProvider, error) {
|
||||||
|
ts, err := google.DefaultTokenSource(context.TODO(), "https://www.googleapis.com/auth/cloud-platform")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gcpAuthProvider{ts}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *gcpAuthProvider) WrapTransport(rt http.RoundTripper) http.RoundTripper {
|
||||||
|
return &oauth2.Transport{
|
||||||
|
Source: g.tokenSource,
|
||||||
|
Base: rt,
|
||||||
|
}
|
||||||
|
}
|
22
plugin/pkg/client/auth/plugins.go
Normal file
22
plugin/pkg/client/auth/plugins.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package plugins
|
||||||
|
|
||||||
|
import (
|
||||||
|
// Initialize all known client auth plugins.
|
||||||
|
_ "k8s.io/kubernetes/plugin/pkg/client/auth/gcp"
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user