exec credential provider: wire in cluster info

Signed-off-by: Monis Khan <mok@vmware.com>

Kubernetes-commit: f97422c8bd57692f5a1a3aa6dc6abc31051ebc82
This commit is contained in:
Monis Khan
2020-05-06 01:01:09 -04:00
committed by Kubernetes Publisher
parent e84213f91f
commit 9e147f192f
9 changed files with 430 additions and 59 deletions

View File

@@ -87,7 +87,7 @@ type Config struct {
AuthConfigPersister AuthProviderConfigPersister
// Exec-based authentication provider.
ExecProvider *clientcmdapi.ExecConfig
Exec Exec
// TLSClientConfig contains settings to enable transport layer security
TLSClientConfig
@@ -160,6 +160,15 @@ func (sanitizedAuthConfigPersister) String() string {
return "rest.AuthProviderConfigPersister(--- REDACTED ---)"
}
type sanitizedObject struct{ runtime.Object }
func (sanitizedObject) GoString() string {
return "runtime.Object(--- REDACTED ---)"
}
func (sanitizedObject) String() string {
return "runtime.Object(--- REDACTED ---)"
}
// GoString implements fmt.GoStringer and sanitizes sensitive fields of Config
// to prevent accidental leaking via logs.
func (c *Config) GoString() string {
@@ -183,10 +192,40 @@ func (c *Config) String() string {
if cc.AuthConfigPersister != nil {
cc.AuthConfigPersister = sanitizedAuthConfigPersister{cc.AuthConfigPersister}
}
if cc.Exec.Config != nil {
cc.Exec.Config = sanitizedObject{Object: cc.Exec.Config}
}
return fmt.Sprintf("%#v", cc)
}
// Exec plugin authentication provider.
type Exec struct {
// ExecProvider provides the config needed to execute the exec plugin.
ExecProvider *clientcmdapi.ExecConfig
// Config holds additional config data that is specific to the exec
// plugin with regards to the cluster being authenticated to.
//
// This data is sourced from the clientcmd Cluster object's extensions[exec] field:
//
// clusters:
// - name: my-cluster
// cluster:
// ...
// extensions:
// - name: exec # reserved extension name for per cluster exec config
// extension:
// audience: 06e3fbd18de8 # arbitrary config
//
// In some environments, the user config may be exactly the same across many clusters
// (i.e. call this exec plugin) minus some details that are specific to each cluster
// such as the audience. This field allows the per cluster config to be directly
// specified with the cluster info. Using this field to store secret data is not
// recommended as one of the prime benefits of exec plugins is that no secrets need
// to be stored directly in the kubeconfig.
Config runtime.Object
}
// ImpersonationConfig has all the available impersonation options
type ImpersonationConfig struct {
// UserName is the username to impersonate on each request.
@@ -603,7 +642,10 @@ func CopyConfig(config *Config) *Config {
},
AuthProvider: config.AuthProvider,
AuthConfigPersister: config.AuthConfigPersister,
ExecProvider: config.ExecProvider,
Exec: Exec{
ExecProvider: config.Exec.ExecProvider,
Config: config.Exec.Config,
},
TLSClientConfig: TLSClientConfig{
Insecure: config.TLSClientConfig.Insecure,
ServerName: config.TLSClientConfig.ServerName,

View File

@@ -337,6 +337,11 @@ func TestAnonymousConfig(t *testing.T) {
func(r *func(*http.Request) (*url.URL, error), f fuzz.Continue) {
*r = fakeProxyFunc
},
func(r *runtime.Object, f fuzz.Continue) {
unknown := &runtime.Unknown{}
f.Fuzz(unknown)
*r = unknown
},
)
for i := 0; i < 20; i++ {
original := &Config{}
@@ -353,7 +358,8 @@ func TestAnonymousConfig(t *testing.T) {
expected.Password = ""
expected.AuthProvider = nil
expected.AuthConfigPersister = nil
expected.ExecProvider = nil
expected.Exec.ExecProvider = nil
expected.Exec.Config = nil
expected.TLSClientConfig.CertData = nil
expected.TLSClientConfig.CertFile = ""
expected.TLSClientConfig.KeyData = nil
@@ -428,6 +434,11 @@ func TestCopyConfig(t *testing.T) {
func(r *func(*http.Request) (*url.URL, error), f fuzz.Continue) {
*r = fakeProxyFunc
},
func(r *runtime.Object, f fuzz.Continue) {
unknown := &runtime.Unknown{}
f.Fuzz(unknown)
*r = unknown
},
)
for i := 0; i < 20; i++ {
original := &Config{}
@@ -524,9 +535,12 @@ func TestConfigStringer(t *testing.T) {
AuthProvider: &clientcmdapi.AuthProviderConfig{
Config: map[string]string{"secret": "s3cr3t"},
},
ExecProvider: &clientcmdapi.ExecConfig{
Args: []string{"secret"},
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
Exec: Exec{
ExecProvider: &clientcmdapi.ExecConfig{
Args: []string{"secret"},
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
},
Config: &runtime.Unknown{Raw: []byte("super secret password")},
},
},
expectContent: []string{
@@ -545,6 +559,8 @@ func TestConfigStringer(t *testing.T) {
formatBytes([]byte("fake key")),
"secret",
"s3cr3t",
"super secret password",
formatBytes([]byte("super secret password")),
},
},
}
@@ -587,10 +603,13 @@ func TestConfigSprint(t *testing.T) {
Config: map[string]string{"secret": "s3cr3t"},
},
AuthConfigPersister: fakeAuthProviderConfigPersister{},
ExecProvider: &clientcmdapi.ExecConfig{
Command: "sudo",
Args: []string{"secret"},
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
Exec: Exec{
ExecProvider: &clientcmdapi.ExecConfig{
Command: "sudo",
Args: []string{"secret"},
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
},
Config: &runtime.Unknown{Raw: []byte("super secret password")},
},
TLSClientConfig: TLSClientConfig{
CertFile: "a.crt",
@@ -611,7 +630,7 @@ func TestConfigSprint(t *testing.T) {
Proxy: fakeProxyFunc,
}
want := fmt.Sprintf(
`&rest.Config{Host:"localhost:8080", APIPath:"v1", ContentConfig:rest.ContentConfig{AcceptContentTypes:"application/json", ContentType:"application/json", GroupVersion:(*schema.GroupVersion)(nil), NegotiatedSerializer:runtime.NegotiatedSerializer(nil)}, Username:"gopher", Password:"--- REDACTED ---", BearerToken:"--- REDACTED ---", BearerTokenFile:"", Impersonate:rest.ImpersonationConfig{UserName:"gopher2", Groups:[]string(nil), Extra:map[string][]string(nil)}, AuthProvider:api.AuthProviderConfig{Name: "gopher", Config: map[string]string{--- REDACTED ---}}, AuthConfigPersister:rest.AuthProviderConfigPersister(--- REDACTED ---), ExecProvider:api.AuthProviderConfig{Command: "sudo", Args: []string{"--- REDACTED ---"}, Env: []ExecEnvVar{--- REDACTED ---}, APIVersion: ""}, TLSClientConfig:rest.sanitizedTLSClientConfig{Insecure:false, ServerName:"", CertFile:"a.crt", KeyFile:"a.key", CAFile:"", CertData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x41, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, KeyData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x52, 0x45, 0x44, 0x41, 0x43, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, CAData:[]uint8(nil), NextProtos:[]string{"h2", "http/1.1"}}, UserAgent:"gobot", DisableCompression:false, Transport:(*rest.fakeRoundTripper)(%p), WrapTransport:(transport.WrapperFunc)(%p), QPS:1, Burst:2, RateLimiter:(*rest.fakeLimiter)(%p), WarningHandler:rest.fakeWarningHandler{}, Timeout:3000000000, Dial:(func(context.Context, string, string) (net.Conn, error))(%p), Proxy:(func(*http.Request) (*url.URL, error))(%p)}`,
`&rest.Config{Host:"localhost:8080", APIPath:"v1", ContentConfig:rest.ContentConfig{AcceptContentTypes:"application/json", ContentType:"application/json", GroupVersion:(*schema.GroupVersion)(nil), NegotiatedSerializer:runtime.NegotiatedSerializer(nil)}, Username:"gopher", Password:"--- REDACTED ---", BearerToken:"--- REDACTED ---", BearerTokenFile:"", Impersonate:rest.ImpersonationConfig{UserName:"gopher2", Groups:[]string(nil), Extra:map[string][]string(nil)}, AuthProvider:api.AuthProviderConfig{Name: "gopher", Config: map[string]string{--- REDACTED ---}}, AuthConfigPersister:rest.AuthProviderConfigPersister(--- REDACTED ---), Exec:rest.Exec{ExecProvider:api.AuthProviderConfig{Command: "sudo", Args: []string{"--- REDACTED ---"}, Env: []ExecEnvVar{--- REDACTED ---}, APIVersion: ""}, Config:runtime.Object(--- REDACTED ---)}, TLSClientConfig:rest.sanitizedTLSClientConfig{Insecure:false, ServerName:"", CertFile:"a.crt", KeyFile:"a.key", CAFile:"", CertData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x54, 0x52, 0x55, 0x4e, 0x43, 0x41, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, KeyData:[]uint8{0x2d, 0x2d, 0x2d, 0x20, 0x52, 0x45, 0x44, 0x41, 0x43, 0x54, 0x45, 0x44, 0x20, 0x2d, 0x2d, 0x2d}, CAData:[]uint8(nil), NextProtos:[]string{"h2", "http/1.1"}}, UserAgent:"gobot", DisableCompression:false, Transport:(*rest.fakeRoundTripper)(%p), WrapTransport:(transport.WrapperFunc)(%p), QPS:1, Burst:2, RateLimiter:(*rest.fakeLimiter)(%p), WarningHandler:rest.fakeWarningHandler{}, Timeout:3000000000, Dial:(func(context.Context, string, string) (net.Conn, error))(%p), Proxy:(func(*http.Request) (*url.URL, error))(%p)}`,
c.Transport, fakeWrapperFunc, c.RateLimiter, fakeDialFunc, fakeProxyFunc,
)

View File

@@ -19,8 +19,10 @@ package rest
import (
"crypto/tls"
"errors"
"fmt"
"net/http"
"k8s.io/client-go/pkg/apis/clientauthentication"
"k8s.io/client-go/plugin/pkg/client/auth/exec"
"k8s.io/client-go/transport"
)
@@ -89,12 +91,22 @@ func (c *Config) TransportConfig() (*transport.Config, error) {
Proxy: c.Proxy,
}
if c.ExecProvider != nil && c.AuthProvider != nil {
if c.Exec.ExecProvider != nil && c.AuthProvider != nil {
return nil, errors.New("execProvider and authProvider cannot be used in combination")
}
if c.ExecProvider != nil {
provider, err := exec.GetAuthenticator(c.ExecProvider)
if c.Exec.ExecProvider != nil {
caData, err := dataFromSliceOrFile(c.CAData, c.CAFile)
if err != nil {
return nil, fmt.Errorf("failed to load CA bundle for execProvider: %v", err)
}
cluster := clientauthentication.Cluster{
Server: c.Host,
ServerName: c.TLSClientConfig.ServerName,
CAData: caData,
Config: c.Exec.Config,
}
provider, err := exec.GetAuthenticator(c.Exec.ExecProvider, cluster)
if err != nil {
return nil, err
}