mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 21:47:07 +00:00
exec credential provider: ProvideClusterInfo and kubeconfig shadow
- The main idea here is that we want to 1) prevent potentially large CA bundles from being set in an exec plugin's environment and 2) ensure that the exec plugin is getting everything it needs in order to talk to a cluster. - Avoid breaking existing manual declarations of rest.Config instances by moving exec Cluster to kubeconfig internal type. - Use client.authentication.k8s.io/exec to qualify exec cluster extension. - Deep copy the exec Cluster.Config when we copy a rest.Config. Signed-off-by: Andrew Keesler <akeesler@vmware.com>
This commit is contained in:
parent
0284139660
commit
c4299d15d5
@ -466,6 +466,10 @@ API rule violation: names_match,k8s.io/apimachinery/pkg/runtime,Unknown,Raw
|
|||||||
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,IntVal
|
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,IntVal
|
||||||
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,StrVal
|
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,StrVal
|
||||||
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,Type
|
API rule violation: names_match,k8s.io/apimachinery/pkg/util/intstr,IntOrString,Type
|
||||||
|
API rule violation: names_match,k8s.io/client-go/pkg/apis/clientauthentication/v1beta1,Cluster,CertificateAuthorityData
|
||||||
|
API rule violation: names_match,k8s.io/client-go/pkg/apis/clientauthentication/v1beta1,Cluster,InsecureSkipTLSVerify
|
||||||
|
API rule violation: names_match,k8s.io/client-go/pkg/apis/clientauthentication/v1beta1,Cluster,ProxyURL
|
||||||
|
API rule violation: names_match,k8s.io/client-go/pkg/apis/clientauthentication/v1beta1,Cluster,TLSServerName
|
||||||
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,Generic
|
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,Generic
|
||||||
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,KubeCloudShared
|
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,KubeCloudShared
|
||||||
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,NodeStatusUpdateFrequency
|
API rule violation: names_match,k8s.io/cloud-provider/app/apis/config/v1alpha1,CloudControllerManagerConfiguration,NodeStatusUpdateFrequency
|
||||||
|
@ -246,7 +246,7 @@ func restConfigFromKubeconfig(configAuthInfo *clientcmdapi.AuthInfo) (*rest.Conf
|
|||||||
config.Password = configAuthInfo.Password
|
config.Password = configAuthInfo.Password
|
||||||
}
|
}
|
||||||
if configAuthInfo.Exec != nil {
|
if configAuthInfo.Exec != nil {
|
||||||
config.Exec.ExecProvider = configAuthInfo.Exec.DeepCopy()
|
config.ExecProvider = configAuthInfo.Exec.DeepCopy()
|
||||||
}
|
}
|
||||||
if configAuthInfo.AuthProvider != nil {
|
if configAuthInfo.AuthProvider != nil {
|
||||||
return nil, fmt.Errorf("auth provider not supported")
|
return nil, fmt.Errorf("auth provider not supported")
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|
||||||
// ExecCredentials is used by exec-based plugins to communicate credentials to
|
// ExecCredential is used by exec-based plugins to communicate credentials to
|
||||||
// HTTP transports.
|
// HTTP transports.
|
||||||
type ExecCredential struct {
|
type ExecCredential struct {
|
||||||
metav1.TypeMeta
|
metav1.TypeMeta
|
||||||
@ -38,7 +38,7 @@ type ExecCredential struct {
|
|||||||
Status *ExecCredentialStatus
|
Status *ExecCredentialStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecCredenitalSpec holds request and runtime specific information provided by
|
// ExecCredentialSpec holds request and runtime specific information provided by
|
||||||
// the transport.
|
// the transport.
|
||||||
type ExecCredentialSpec struct {
|
type ExecCredentialSpec struct {
|
||||||
// Response is populated when the transport encounters HTTP status codes, such as 401,
|
// Response is populated when the transport encounters HTTP status codes, such as 401,
|
||||||
@ -51,9 +51,12 @@ type ExecCredentialSpec struct {
|
|||||||
// +optional
|
// +optional
|
||||||
Interactive bool
|
Interactive bool
|
||||||
|
|
||||||
// Cluster contains information to allow an exec plugin to communicate
|
// Cluster contains information to allow an exec plugin to communicate with the
|
||||||
// with the kubernetes cluster being authenticated to.
|
// kubernetes cluster being authenticated to. Note that Cluster is non-nil only
|
||||||
Cluster Cluster
|
// when provideClusterInfo is set to true in the exec provider config (i.e.,
|
||||||
|
// ExecConfig.ProvideClusterInfo).
|
||||||
|
// +optional
|
||||||
|
Cluster *Cluster
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecCredentialStatus holds credentials for the transport to use.
|
// ExecCredentialStatus holds credentials for the transport to use.
|
||||||
@ -83,19 +86,32 @@ type Response struct {
|
|||||||
|
|
||||||
// Cluster contains information to allow an exec plugin to communicate
|
// Cluster contains information to allow an exec plugin to communicate
|
||||||
// with the kubernetes cluster being authenticated to.
|
// with the kubernetes cluster being authenticated to.
|
||||||
|
//
|
||||||
|
// To ensure that this struct contains everything someone would need to communicate
|
||||||
|
// with a kubernetes cluster (just like they would via a kubeconfig), the fields
|
||||||
|
// should shadow "k8s.io/client-go/tools/clientcmd/api/v1".Cluster, with the exception
|
||||||
|
// of CertificateAuthority, since CA data will always be passed to the plugin as bytes.
|
||||||
type Cluster struct {
|
type Cluster struct {
|
||||||
// Server is the address of the kubernetes cluster (https://hostname:port).
|
// Server is the address of the kubernetes cluster (https://hostname:port).
|
||||||
Server string
|
Server string
|
||||||
// ServerName is passed to the server for SNI and is used in the client to check server
|
// TLSServerName is passed to the server for SNI and is used in the client to
|
||||||
// certificates against. If ServerName is empty, the hostname used to contact the
|
// check server certificates against. If ServerName is empty, the hostname
|
||||||
// server is used.
|
// used to contact the server is used.
|
||||||
// +optional
|
// +optional
|
||||||
ServerName string
|
TLSServerName string
|
||||||
|
// InsecureSkipTLSVerify skips the validity check for the server's certificate.
|
||||||
|
// This will make your HTTPS connections insecure.
|
||||||
|
// +optional
|
||||||
|
InsecureSkipTLSVerify bool
|
||||||
// CAData contains PEM-encoded certificate authority certificates.
|
// CAData contains PEM-encoded certificate authority certificates.
|
||||||
// If empty, system roots should be used.
|
// If empty, system roots should be used.
|
||||||
// +listType=atomic
|
// +listType=atomic
|
||||||
// +optional
|
// +optional
|
||||||
CAData []byte
|
CertificateAuthorityData []byte
|
||||||
|
// ProxyURL is the URL to the proxy to be used for all requests to this
|
||||||
|
// cluster.
|
||||||
|
// +optional
|
||||||
|
ProxyURL string
|
||||||
// Config holds additional config data that is specific to the exec
|
// Config holds additional config data that is specific to the exec
|
||||||
// plugin with regards to the cluster being authenticated to.
|
// plugin with regards to the cluster being authenticated to.
|
||||||
//
|
//
|
||||||
@ -106,7 +122,7 @@ type Cluster struct {
|
|||||||
// cluster:
|
// cluster:
|
||||||
// ...
|
// ...
|
||||||
// extensions:
|
// extensions:
|
||||||
// - name: exec # reserved extension name for per cluster exec config
|
// - name: client.authentication.k8s.io/exec # reserved extension name for per cluster exec config
|
||||||
// extension:
|
// extension:
|
||||||
// audience: 06e3fbd18de8 # arbitrary config
|
// audience: 06e3fbd18de8 # arbitrary config
|
||||||
//
|
//
|
||||||
|
@ -22,5 +22,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Convert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
func Convert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
||||||
|
// This conversion intentionally omits the Cluster field which is only supported in newer versions.
|
||||||
return autoConvert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(in, out, s)
|
return autoConvert_clientauthentication_ExecCredentialSpec_To_v1alpha1_ExecCredentialSpec(in, out, s)
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ type ExecCredential struct {
|
|||||||
Status *ExecCredentialStatus `json:"status,omitempty"`
|
Status *ExecCredentialStatus `json:"status,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecCredenitalSpec holds request and runtime specific information provided by
|
// ExecCredentialSpec holds request and runtime specific information provided by
|
||||||
// the transport.
|
// the transport.
|
||||||
type ExecCredentialSpec struct {
|
type ExecCredentialSpec struct {
|
||||||
// Response is populated when the transport encounters HTTP status codes, such as 401,
|
// Response is populated when the transport encounters HTTP status codes, such as 401,
|
||||||
|
@ -22,5 +22,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Convert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
func Convert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
||||||
|
// This conversion intentionally omits the Response and Interactive fields, which were only
|
||||||
|
// supported in v1alpha1.
|
||||||
return autoConvert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in, out, s)
|
return autoConvert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in, out, s)
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,12 @@ type ExecCredential struct {
|
|||||||
// ExecCredentialSpec holds request and runtime specific information provided by
|
// ExecCredentialSpec holds request and runtime specific information provided by
|
||||||
// the transport.
|
// the transport.
|
||||||
type ExecCredentialSpec struct {
|
type ExecCredentialSpec struct {
|
||||||
// Cluster contains information to allow an exec plugin to communicate
|
// Cluster contains information to allow an exec plugin to communicate with the
|
||||||
// with the kubernetes cluster being authenticated to.
|
// kubernetes cluster being authenticated to. Note that Cluster is non-nil only
|
||||||
Cluster Cluster `json:"cluster"`
|
// when provideClusterInfo is set to true in the exec provider config (i.e.,
|
||||||
|
// ExecConfig.ProvideClusterInfo).
|
||||||
|
// +optional
|
||||||
|
Cluster *Cluster `json:"cluster,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecCredentialStatus holds credentials for the transport to use.
|
// ExecCredentialStatus holds credentials for the transport to use.
|
||||||
@ -64,19 +67,32 @@ type ExecCredentialStatus struct {
|
|||||||
|
|
||||||
// Cluster contains information to allow an exec plugin to communicate
|
// Cluster contains information to allow an exec plugin to communicate
|
||||||
// with the kubernetes cluster being authenticated to.
|
// with the kubernetes cluster being authenticated to.
|
||||||
|
//
|
||||||
|
// To ensure that this struct contains everything someone would need to communicate
|
||||||
|
// with a kubernetes cluster (just like they would via a kubeconfig), the fields
|
||||||
|
// should shadow "k8s.io/client-go/tools/clientcmd/api/v1".Cluster, with the exception
|
||||||
|
// of CertificateAuthority, since CA data will always be passed to the plugin as bytes.
|
||||||
type Cluster struct {
|
type Cluster struct {
|
||||||
// Server is the address of the kubernetes cluster (https://hostname:port).
|
// Server is the address of the kubernetes cluster (https://hostname:port).
|
||||||
Server string `json:"server"`
|
Server string `json:"server"`
|
||||||
// ServerName is passed to the server for SNI and is used in the client to check server
|
// TLSServerName is passed to the server for SNI and is used in the client to
|
||||||
// certificates against. If ServerName is empty, the hostname used to contact the
|
// check server certificates against. If ServerName is empty, the hostname
|
||||||
// server is used.
|
// used to contact the server is used.
|
||||||
// +optional
|
// +optional
|
||||||
ServerName string `json:"serverName,omitempty"`
|
TLSServerName string `json:"tls-server-name,omitempty"`
|
||||||
|
// InsecureSkipTLSVerify skips the validity check for the server's certificate.
|
||||||
|
// This will make your HTTPS connections insecure.
|
||||||
|
// +optional
|
||||||
|
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"`
|
||||||
// CAData contains PEM-encoded certificate authority certificates.
|
// CAData contains PEM-encoded certificate authority certificates.
|
||||||
// If empty, system roots should be used.
|
// If empty, system roots should be used.
|
||||||
// +listType=atomic
|
// +listType=atomic
|
||||||
// +optional
|
// +optional
|
||||||
CAData []byte `json:"caData,omitempty"`
|
CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
|
||||||
|
// ProxyURL is the URL to the proxy to be used for all requests to this
|
||||||
|
// cluster.
|
||||||
|
// +optional
|
||||||
|
ProxyURL string `json:"proxy-url,omitempty"`
|
||||||
// Config holds additional config data that is specific to the exec
|
// Config holds additional config data that is specific to the exec
|
||||||
// plugin with regards to the cluster being authenticated to.
|
// plugin with regards to the cluster being authenticated to.
|
||||||
//
|
//
|
||||||
@ -87,7 +103,7 @@ type Cluster struct {
|
|||||||
// cluster:
|
// cluster:
|
||||||
// ...
|
// ...
|
||||||
// extensions:
|
// extensions:
|
||||||
// - name: exec # reserved extension name for per cluster exec config
|
// - name: client.authentication.k8s.io/exec # reserved extension name for per cluster exec config
|
||||||
// extension:
|
// extension:
|
||||||
// audience: 06e3fbd18de8 # arbitrary config
|
// audience: 06e3fbd18de8 # arbitrary config
|
||||||
//
|
//
|
||||||
|
@ -81,8 +81,10 @@ func RegisterConversions(s *runtime.Scheme) error {
|
|||||||
|
|
||||||
func autoConvert_v1beta1_Cluster_To_clientauthentication_Cluster(in *Cluster, out *clientauthentication.Cluster, s conversion.Scope) error {
|
func autoConvert_v1beta1_Cluster_To_clientauthentication_Cluster(in *Cluster, out *clientauthentication.Cluster, s conversion.Scope) error {
|
||||||
out.Server = in.Server
|
out.Server = in.Server
|
||||||
out.ServerName = in.ServerName
|
out.TLSServerName = in.TLSServerName
|
||||||
out.CAData = *(*[]byte)(unsafe.Pointer(&in.CAData))
|
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
|
||||||
|
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
|
||||||
|
out.ProxyURL = in.ProxyURL
|
||||||
if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&in.Config, &out.Config, s); err != nil {
|
if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&in.Config, &out.Config, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -96,8 +98,10 @@ func Convert_v1beta1_Cluster_To_clientauthentication_Cluster(in *Cluster, out *c
|
|||||||
|
|
||||||
func autoConvert_clientauthentication_Cluster_To_v1beta1_Cluster(in *clientauthentication.Cluster, out *Cluster, s conversion.Scope) error {
|
func autoConvert_clientauthentication_Cluster_To_v1beta1_Cluster(in *clientauthentication.Cluster, out *Cluster, s conversion.Scope) error {
|
||||||
out.Server = in.Server
|
out.Server = in.Server
|
||||||
out.ServerName = in.ServerName
|
out.TLSServerName = in.TLSServerName
|
||||||
out.CAData = *(*[]byte)(unsafe.Pointer(&in.CAData))
|
out.InsecureSkipTLSVerify = in.InsecureSkipTLSVerify
|
||||||
|
out.CertificateAuthorityData = *(*[]byte)(unsafe.Pointer(&in.CertificateAuthorityData))
|
||||||
|
out.ProxyURL = in.ProxyURL
|
||||||
if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&in.Config, &out.Config, s); err != nil {
|
if err := runtime.Convert_runtime_Object_To_runtime_RawExtension(&in.Config, &out.Config, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -136,8 +140,14 @@ func Convert_clientauthentication_ExecCredential_To_v1beta1_ExecCredential(in *c
|
|||||||
}
|
}
|
||||||
|
|
||||||
func autoConvert_v1beta1_ExecCredentialSpec_To_clientauthentication_ExecCredentialSpec(in *ExecCredentialSpec, out *clientauthentication.ExecCredentialSpec, s conversion.Scope) error {
|
func autoConvert_v1beta1_ExecCredentialSpec_To_clientauthentication_ExecCredentialSpec(in *ExecCredentialSpec, out *clientauthentication.ExecCredentialSpec, s conversion.Scope) error {
|
||||||
if err := Convert_v1beta1_Cluster_To_clientauthentication_Cluster(&in.Cluster, &out.Cluster, s); err != nil {
|
if in.Cluster != nil {
|
||||||
return err
|
in, out := &in.Cluster, &out.Cluster
|
||||||
|
*out = new(clientauthentication.Cluster)
|
||||||
|
if err := Convert_v1beta1_Cluster_To_clientauthentication_Cluster(*in, *out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Cluster = nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -150,8 +160,14 @@ func Convert_v1beta1_ExecCredentialSpec_To_clientauthentication_ExecCredentialSp
|
|||||||
func autoConvert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
func autoConvert_clientauthentication_ExecCredentialSpec_To_v1beta1_ExecCredentialSpec(in *clientauthentication.ExecCredentialSpec, out *ExecCredentialSpec, s conversion.Scope) error {
|
||||||
// WARNING: in.Response requires manual conversion: does not exist in peer-type
|
// WARNING: in.Response requires manual conversion: does not exist in peer-type
|
||||||
// WARNING: in.Interactive requires manual conversion: does not exist in peer-type
|
// WARNING: in.Interactive requires manual conversion: does not exist in peer-type
|
||||||
if err := Convert_clientauthentication_Cluster_To_v1beta1_Cluster(&in.Cluster, &out.Cluster, s); err != nil {
|
if in.Cluster != nil {
|
||||||
return err
|
in, out := &in.Cluster, &out.Cluster
|
||||||
|
*out = new(Cluster)
|
||||||
|
if err := Convert_clientauthentication_Cluster_To_v1beta1_Cluster(*in, *out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Cluster = nil
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ import (
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Cluster) DeepCopyInto(out *Cluster) {
|
func (in *Cluster) DeepCopyInto(out *Cluster) {
|
||||||
*out = *in
|
*out = *in
|
||||||
if in.CAData != nil {
|
if in.CertificateAuthorityData != nil {
|
||||||
in, out := &in.CAData, &out.CAData
|
in, out := &in.CertificateAuthorityData, &out.CertificateAuthorityData
|
||||||
*out = make([]byte, len(*in))
|
*out = make([]byte, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
@ -80,7 +80,11 @@ func (in *ExecCredential) DeepCopyObject() runtime.Object {
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *ExecCredentialSpec) DeepCopyInto(out *ExecCredentialSpec) {
|
func (in *ExecCredentialSpec) DeepCopyInto(out *ExecCredentialSpec) {
|
||||||
*out = *in
|
*out = *in
|
||||||
in.Cluster.DeepCopyInto(&out.Cluster)
|
if in.Cluster != nil {
|
||||||
|
in, out := &in.Cluster, &out.Cluster
|
||||||
|
*out = new(Cluster)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ import (
|
|||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *Cluster) DeepCopyInto(out *Cluster) {
|
func (in *Cluster) DeepCopyInto(out *Cluster) {
|
||||||
*out = *in
|
*out = *in
|
||||||
if in.CAData != nil {
|
if in.CertificateAuthorityData != nil {
|
||||||
in, out := &in.CAData, &out.CAData
|
in, out := &in.CertificateAuthorityData, &out.CertificateAuthorityData
|
||||||
*out = make([]byte, len(*in))
|
*out = make([]byte, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
@ -87,7 +87,11 @@ func (in *ExecCredentialSpec) DeepCopyInto(out *ExecCredentialSpec) {
|
|||||||
*out = new(Response)
|
*out = new(Response)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
in.Cluster.DeepCopyInto(&out.Cluster)
|
if in.Cluster != nil {
|
||||||
|
in, out := &in.Cluster, &out.Cluster
|
||||||
|
*out = new(Cluster)
|
||||||
|
(*in).DeepCopyInto(*out)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,10 +87,10 @@ func newCache() *cache {
|
|||||||
|
|
||||||
var spewConfig = &spew.ConfigState{DisableMethods: true, Indent: " "}
|
var spewConfig = &spew.ConfigState{DisableMethods: true, Indent: " "}
|
||||||
|
|
||||||
func cacheKey(conf *api.ExecConfig, cluster clientauthentication.Cluster) string {
|
func cacheKey(conf *api.ExecConfig, cluster *clientauthentication.Cluster) string {
|
||||||
key := struct {
|
key := struct {
|
||||||
conf *api.ExecConfig
|
conf *api.ExecConfig
|
||||||
cluster clientauthentication.Cluster
|
cluster *clientauthentication.Cluster
|
||||||
}{
|
}{
|
||||||
conf: conf,
|
conf: conf,
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
@ -162,11 +162,11 @@ func (s *sometimes) Do(f func()) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetAuthenticator returns an exec-based plugin for providing client credentials.
|
// GetAuthenticator returns an exec-based plugin for providing client credentials.
|
||||||
func GetAuthenticator(config *api.ExecConfig, cluster clientauthentication.Cluster) (*Authenticator, error) {
|
func GetAuthenticator(config *api.ExecConfig, cluster *clientauthentication.Cluster) (*Authenticator, error) {
|
||||||
return newAuthenticator(globalCache, config, cluster)
|
return newAuthenticator(globalCache, config, cluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAuthenticator(c *cache, config *api.ExecConfig, cluster clientauthentication.Cluster) (*Authenticator, error) {
|
func newAuthenticator(c *cache, config *api.ExecConfig, cluster *clientauthentication.Cluster) (*Authenticator, error) {
|
||||||
key := cacheKey(config, cluster)
|
key := cacheKey(config, cluster)
|
||||||
if a, ok := c.get(key); ok {
|
if a, ok := c.get(key); ok {
|
||||||
return a, nil
|
return a, nil
|
||||||
@ -178,10 +178,11 @@ func newAuthenticator(c *cache, config *api.ExecConfig, cluster clientauthentica
|
|||||||
}
|
}
|
||||||
|
|
||||||
a := &Authenticator{
|
a := &Authenticator{
|
||||||
cmd: config.Command,
|
cmd: config.Command,
|
||||||
args: config.Args,
|
args: config.Args,
|
||||||
group: gv,
|
group: gv,
|
||||||
cluster: cluster,
|
cluster: cluster,
|
||||||
|
provideClusterInfo: config.ProvideClusterInfo,
|
||||||
|
|
||||||
installHint: config.InstallHint,
|
installHint: config.InstallHint,
|
||||||
sometimes: &sometimes{
|
sometimes: &sometimes{
|
||||||
@ -208,11 +209,12 @@ func newAuthenticator(c *cache, config *api.ExecConfig, cluster clientauthentica
|
|||||||
// The plugin input and output are defined by the API group client.authentication.k8s.io.
|
// The plugin input and output are defined by the API group client.authentication.k8s.io.
|
||||||
type Authenticator struct {
|
type Authenticator struct {
|
||||||
// Set by the config
|
// Set by the config
|
||||||
cmd string
|
cmd string
|
||||||
args []string
|
args []string
|
||||||
group schema.GroupVersion
|
group schema.GroupVersion
|
||||||
env []string
|
env []string
|
||||||
cluster clientauthentication.Cluster
|
cluster *clientauthentication.Cluster
|
||||||
|
provideClusterInfo bool
|
||||||
|
|
||||||
// Used to avoid log spew by rate limiting install hint printing. We didn't do
|
// Used to avoid log spew by rate limiting install hint printing. We didn't do
|
||||||
// this by interval based rate limiting alone since that way may have prevented
|
// this by interval based rate limiting alone since that way may have prevented
|
||||||
@ -374,9 +376,11 @@ func (a *Authenticator) refreshCredsLocked(r *clientauthentication.Response) err
|
|||||||
Spec: clientauthentication.ExecCredentialSpec{
|
Spec: clientauthentication.ExecCredentialSpec{
|
||||||
Response: r,
|
Response: r,
|
||||||
Interactive: a.interactive,
|
Interactive: a.interactive,
|
||||||
Cluster: a.cluster,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if a.provideClusterInfo {
|
||||||
|
cred.Spec.Cluster = a.cluster
|
||||||
|
}
|
||||||
|
|
||||||
env := append(a.environ(), a.env...)
|
env := append(a.environ(), a.env...)
|
||||||
data, err := runtime.Encode(codecs.LegacyCodec(a.group), cred)
|
data, err := runtime.Encode(codecs.LegacyCodec(a.group), cred)
|
||||||
|
@ -115,12 +115,13 @@ func TestCacheKey(t *testing.T) {
|
|||||||
{Name: "5", Value: "6"},
|
{Name: "5", Value: "6"},
|
||||||
{Name: "7", Value: "8"},
|
{Name: "7", Value: "8"},
|
||||||
},
|
},
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
ProvideClusterInfo: true,
|
||||||
}
|
}
|
||||||
c1c := clientauthentication.Cluster{
|
c1c := &clientauthentication.Cluster{
|
||||||
Server: "foo",
|
Server: "foo",
|
||||||
ServerName: "bar",
|
TLSServerName: "bar",
|
||||||
CAData: []byte("baz"),
|
CertificateAuthorityData: []byte("baz"),
|
||||||
Config: &runtime.Unknown{
|
Config: &runtime.Unknown{
|
||||||
TypeMeta: runtime.TypeMeta{
|
TypeMeta: runtime.TypeMeta{
|
||||||
APIVersion: "",
|
APIVersion: "",
|
||||||
@ -140,12 +141,13 @@ func TestCacheKey(t *testing.T) {
|
|||||||
{Name: "5", Value: "6"},
|
{Name: "5", Value: "6"},
|
||||||
{Name: "7", Value: "8"},
|
{Name: "7", Value: "8"},
|
||||||
},
|
},
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
ProvideClusterInfo: true,
|
||||||
}
|
}
|
||||||
c2c := clientauthentication.Cluster{
|
c2c := &clientauthentication.Cluster{
|
||||||
Server: "foo",
|
Server: "foo",
|
||||||
ServerName: "bar",
|
TLSServerName: "bar",
|
||||||
CAData: []byte("baz"),
|
CertificateAuthorityData: []byte("baz"),
|
||||||
Config: &runtime.Unknown{
|
Config: &runtime.Unknown{
|
||||||
TypeMeta: runtime.TypeMeta{
|
TypeMeta: runtime.TypeMeta{
|
||||||
APIVersion: "",
|
APIVersion: "",
|
||||||
@ -166,10 +168,10 @@ func TestCacheKey(t *testing.T) {
|
|||||||
},
|
},
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
}
|
}
|
||||||
c3c := clientauthentication.Cluster{
|
c3c := &clientauthentication.Cluster{
|
||||||
Server: "foo",
|
Server: "foo",
|
||||||
ServerName: "bar",
|
TLSServerName: "bar",
|
||||||
CAData: []byte("baz"),
|
CertificateAuthorityData: []byte("baz"),
|
||||||
Config: &runtime.Unknown{
|
Config: &runtime.Unknown{
|
||||||
TypeMeta: runtime.TypeMeta{
|
TypeMeta: runtime.TypeMeta{
|
||||||
APIVersion: "",
|
APIVersion: "",
|
||||||
@ -190,10 +192,10 @@ func TestCacheKey(t *testing.T) {
|
|||||||
},
|
},
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
}
|
}
|
||||||
c4c := clientauthentication.Cluster{
|
c4c := &clientauthentication.Cluster{
|
||||||
Server: "foo",
|
Server: "foo",
|
||||||
ServerName: "bar",
|
TLSServerName: "bar",
|
||||||
CAData: []byte("baz"),
|
CertificateAuthorityData: []byte("baz"),
|
||||||
Config: &runtime.Unknown{
|
Config: &runtime.Unknown{
|
||||||
TypeMeta: runtime.TypeMeta{
|
TypeMeta: runtime.TypeMeta{
|
||||||
APIVersion: "",
|
APIVersion: "",
|
||||||
@ -205,10 +207,49 @@ func TestCacheKey(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// c5/c5c should be the same as c4/c4c, except c5 has ProvideClusterInfo set to true.
|
||||||
|
c5 := &api.ExecConfig{
|
||||||
|
Command: "foo-bar",
|
||||||
|
Args: []string{"1", "2"},
|
||||||
|
Env: []api.ExecEnvVar{
|
||||||
|
{Name: "3", Value: "4"},
|
||||||
|
{Name: "5", Value: "6"},
|
||||||
|
},
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
}
|
||||||
|
c5c := &clientauthentication.Cluster{
|
||||||
|
Server: "foo",
|
||||||
|
TLSServerName: "bar",
|
||||||
|
CertificateAuthorityData: []byte("baz"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
TypeMeta: runtime.TypeMeta{
|
||||||
|
APIVersion: "",
|
||||||
|
Kind: "",
|
||||||
|
},
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"panda"}}`),
|
||||||
|
ContentEncoding: "",
|
||||||
|
ContentType: "application/json",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// c6 should be the same as c4, except c6 is passed with a nil cluster
|
||||||
|
c6 := &api.ExecConfig{
|
||||||
|
Command: "foo-bar",
|
||||||
|
Args: []string{"1", "2"},
|
||||||
|
Env: []api.ExecEnvVar{
|
||||||
|
{Name: "3", Value: "4"},
|
||||||
|
{Name: "5", Value: "6"},
|
||||||
|
},
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
|
}
|
||||||
|
|
||||||
key1 := cacheKey(c1, c1c)
|
key1 := cacheKey(c1, c1c)
|
||||||
key2 := cacheKey(c2, c2c)
|
key2 := cacheKey(c2, c2c)
|
||||||
key3 := cacheKey(c3, c3c)
|
key3 := cacheKey(c3, c3c)
|
||||||
key4 := cacheKey(c4, c4c)
|
key4 := cacheKey(c4, c4c)
|
||||||
|
key5 := cacheKey(c5, c5c)
|
||||||
|
key6 := cacheKey(c6, nil)
|
||||||
if key1 != key2 {
|
if key1 != key2 {
|
||||||
t.Error("key1 and key2 didn't match")
|
t.Error("key1 and key2 didn't match")
|
||||||
}
|
}
|
||||||
@ -221,6 +262,12 @@ func TestCacheKey(t *testing.T) {
|
|||||||
if key3 == key4 {
|
if key3 == key4 {
|
||||||
t.Error("key3 and key4 matched")
|
t.Error("key3 and key4 matched")
|
||||||
}
|
}
|
||||||
|
if key4 == key5 {
|
||||||
|
t.Error("key3 and key4 matched")
|
||||||
|
}
|
||||||
|
if key6 == key4 {
|
||||||
|
t.Error("key6 and key4 matched")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compJSON(t *testing.T, got, want []byte) {
|
func compJSON(t *testing.T, got, want []byte) {
|
||||||
@ -246,7 +293,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
config api.ExecConfig
|
config api.ExecConfig
|
||||||
exitCode int
|
exitCode int
|
||||||
cluster clientauthentication.Cluster
|
cluster *clientauthentication.Cluster
|
||||||
output string
|
output string
|
||||||
interactive bool
|
interactive bool
|
||||||
response *clientauthentication.Response
|
response *clientauthentication.Response
|
||||||
@ -470,12 +517,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
"spec": {
|
"spec": {}
|
||||||
"cluster": {
|
|
||||||
"server": "",
|
|
||||||
"config": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
}`,
|
||||||
output: `{
|
output: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
@ -494,12 +536,7 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
wantInput: `{
|
wantInput: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
"spec": {
|
"spec": {}
|
||||||
"cluster": {
|
|
||||||
"server": "",
|
|
||||||
"config": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`,
|
}`,
|
||||||
output: `{
|
output: `{
|
||||||
"kind": "ExecCredential",
|
"kind": "ExecCredential",
|
||||||
@ -572,10 +609,10 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
},
|
},
|
||||||
cluster: clientauthentication.Cluster{
|
cluster: &clientauthentication.Cluster{
|
||||||
Server: "foo",
|
Server: "foo",
|
||||||
ServerName: "bar",
|
TLSServerName: "bar",
|
||||||
CAData: []byte("baz"),
|
CertificateAuthorityData: []byte("baz"),
|
||||||
Config: &runtime.Unknown{
|
Config: &runtime.Unknown{
|
||||||
TypeMeta: runtime.TypeMeta{
|
TypeMeta: runtime.TypeMeta{
|
||||||
APIVersion: "",
|
APIVersion: "",
|
||||||
@ -616,14 +653,15 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
wantCreds: credentials{token: "foo-bar"},
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "beta-with-cluster-is-serialized",
|
name: "beta-with-cluster-and-provide-cluster-info-is-serialized",
|
||||||
config: api.ExecConfig{
|
config: api.ExecConfig{
|
||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
ProvideClusterInfo: true,
|
||||||
},
|
},
|
||||||
cluster: clientauthentication.Cluster{
|
cluster: &clientauthentication.Cluster{
|
||||||
Server: "foo",
|
Server: "foo",
|
||||||
ServerName: "bar",
|
TLSServerName: "bar",
|
||||||
CAData: []byte("baz"),
|
CertificateAuthorityData: []byte("baz"),
|
||||||
Config: &runtime.Unknown{
|
Config: &runtime.Unknown{
|
||||||
TypeMeta: runtime.TypeMeta{
|
TypeMeta: runtime.TypeMeta{
|
||||||
APIVersion: "",
|
APIVersion: "",
|
||||||
@ -646,8 +684,8 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
"spec": {
|
"spec": {
|
||||||
"cluster": {
|
"cluster": {
|
||||||
"server": "foo",
|
"server": "foo",
|
||||||
"serverName": "bar",
|
"tls-server-name": "bar",
|
||||||
"caData": "YmF6",
|
"certificate-authority-data": "YmF6",
|
||||||
"config": {
|
"config": {
|
||||||
"apiVersion": "group/v1",
|
"apiVersion": "group/v1",
|
||||||
"kind": "PluginConfig",
|
"kind": "PluginConfig",
|
||||||
@ -667,6 +705,45 @@ func TestRefreshCreds(t *testing.T) {
|
|||||||
}`,
|
}`,
|
||||||
wantCreds: credentials{token: "foo-bar"},
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "beta-with-cluster-and-without-provide-cluster-info-is-not-serialized",
|
||||||
|
config: api.ExecConfig{
|
||||||
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
|
},
|
||||||
|
cluster: &clientauthentication.Cluster{
|
||||||
|
Server: "foo",
|
||||||
|
TLSServerName: "bar",
|
||||||
|
CertificateAuthorityData: []byte("baz"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
TypeMeta: runtime.TypeMeta{
|
||||||
|
APIVersion: "",
|
||||||
|
Kind: "",
|
||||||
|
},
|
||||||
|
Raw: []byte(`{"apiVersion":"group/v1","kind":"PluginConfig","spec":{"audience":"snorlax"}}`),
|
||||||
|
ContentEncoding: "",
|
||||||
|
ContentType: "application/json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
response: &clientauthentication.Response{
|
||||||
|
Header: map[string][]string{
|
||||||
|
"WWW-Authenticate": {`Basic realm="Access to the staging site", charset="UTF-8"`},
|
||||||
|
},
|
||||||
|
Code: 401,
|
||||||
|
},
|
||||||
|
wantInput: `{
|
||||||
|
"kind":"ExecCredential",
|
||||||
|
"apiVersion":"client.authentication.k8s.io/v1beta1",
|
||||||
|
"spec": {}
|
||||||
|
}`,
|
||||||
|
output: `{
|
||||||
|
"kind": "ExecCredential",
|
||||||
|
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
||||||
|
"status": {
|
||||||
|
"token": "foo-bar"
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
wantCreds: credentials{token: "foo-bar"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -763,7 +840,7 @@ func TestRoundTripper(t *testing.T) {
|
|||||||
Command: "./testdata/test-plugin.sh",
|
Command: "./testdata/test-plugin.sh",
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
}
|
}
|
||||||
a, err := newAuthenticator(newCache(), &c, clientauthentication.Cluster{})
|
a, err := newAuthenticator(newCache(), &c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -849,7 +926,7 @@ func TestTokenPresentCancelsExecAction(t *testing.T) {
|
|||||||
a, err := newAuthenticator(newCache(), &api.ExecConfig{
|
a, err := newAuthenticator(newCache(), &api.ExecConfig{
|
||||||
Command: "./testdata/test-plugin.sh",
|
Command: "./testdata/test-plugin.sh",
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
})
|
}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -888,7 +965,7 @@ func TestTLSCredentials(t *testing.T) {
|
|||||||
a, err := newAuthenticator(newCache(), &api.ExecConfig{
|
a, err := newAuthenticator(newCache(), &api.ExecConfig{
|
||||||
Command: "./testdata/test-plugin.sh",
|
Command: "./testdata/test-plugin.sh",
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
}, clientauthentication.Cluster{})
|
}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -978,7 +1055,7 @@ func TestConcurrentUpdateTransportConfig(t *testing.T) {
|
|||||||
Command: "./testdata/test-plugin.sh",
|
Command: "./testdata/test-plugin.sh",
|
||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
}
|
}
|
||||||
a, err := newAuthenticator(newCache(), &c, clientauthentication.Cluster{})
|
a, err := newAuthenticator(newCache(), &c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -1045,7 +1122,7 @@ func TestInstallHintRateLimit(t *testing.T) {
|
|||||||
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
APIVersion: "client.authentication.k8s.io/v1alpha1",
|
||||||
InstallHint: "some install hint",
|
InstallHint: "some install hint",
|
||||||
}
|
}
|
||||||
a, err := newAuthenticator(newCache(), &c)
|
a, err := newAuthenticator(newCache(), &c, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,13 @@ go_test(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"client_test.go",
|
"client_test.go",
|
||||||
"config_test.go",
|
"config_test.go",
|
||||||
|
"exec_test.go",
|
||||||
"plugin_test.go",
|
"plugin_test.go",
|
||||||
"request_test.go",
|
"request_test.go",
|
||||||
"url_utils_test.go",
|
"url_utils_test.go",
|
||||||
"urlbackoff_test.go",
|
"urlbackoff_test.go",
|
||||||
],
|
],
|
||||||
|
data = glob(["testdata/**"]),
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||||
@ -33,6 +35,7 @@ go_test(
|
|||||||
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
|
||||||
|
"//staging/src/k8s.io/client-go/pkg/apis/clientauthentication:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/rest/watch:go_default_library",
|
"//staging/src/k8s.io/client-go/rest/watch:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
"//staging/src/k8s.io/client-go/tools/clientcmd/api:go_default_library",
|
||||||
"//staging/src/k8s.io/client-go/transport:go_default_library",
|
"//staging/src/k8s.io/client-go/transport:go_default_library",
|
||||||
@ -50,6 +53,7 @@ go_library(
|
|||||||
srcs = [
|
srcs = [
|
||||||
"client.go",
|
"client.go",
|
||||||
"config.go",
|
"config.go",
|
||||||
|
"exec.go",
|
||||||
"plugin.go",
|
"plugin.go",
|
||||||
"request.go",
|
"request.go",
|
||||||
"transport.go",
|
"transport.go",
|
||||||
|
@ -87,7 +87,7 @@ type Config struct {
|
|||||||
AuthConfigPersister AuthProviderConfigPersister
|
AuthConfigPersister AuthProviderConfigPersister
|
||||||
|
|
||||||
// Exec-based authentication provider.
|
// Exec-based authentication provider.
|
||||||
Exec Exec
|
ExecProvider *clientcmdapi.ExecConfig
|
||||||
|
|
||||||
// TLSClientConfig contains settings to enable transport layer security
|
// TLSClientConfig contains settings to enable transport layer security
|
||||||
TLSClientConfig
|
TLSClientConfig
|
||||||
@ -192,40 +192,12 @@ func (c *Config) String() string {
|
|||||||
if cc.AuthConfigPersister != nil {
|
if cc.AuthConfigPersister != nil {
|
||||||
cc.AuthConfigPersister = sanitizedAuthConfigPersister{cc.AuthConfigPersister}
|
cc.AuthConfigPersister = sanitizedAuthConfigPersister{cc.AuthConfigPersister}
|
||||||
}
|
}
|
||||||
if cc.Exec.Config != nil {
|
if cc.ExecProvider != nil && cc.ExecProvider.Config != nil {
|
||||||
cc.Exec.Config = sanitizedObject{Object: cc.Exec.Config}
|
cc.ExecProvider.Config = sanitizedObject{Object: cc.ExecProvider.Config}
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%#v", cc)
|
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
|
// ImpersonationConfig has all the available impersonation options
|
||||||
type ImpersonationConfig struct {
|
type ImpersonationConfig struct {
|
||||||
// UserName is the username to impersonate on each request.
|
// UserName is the username to impersonate on each request.
|
||||||
@ -627,7 +599,7 @@ func AnonymousClientConfig(config *Config) *Config {
|
|||||||
|
|
||||||
// CopyConfig returns a copy of the given config
|
// CopyConfig returns a copy of the given config
|
||||||
func CopyConfig(config *Config) *Config {
|
func CopyConfig(config *Config) *Config {
|
||||||
return &Config{
|
c := &Config{
|
||||||
Host: config.Host,
|
Host: config.Host,
|
||||||
APIPath: config.APIPath,
|
APIPath: config.APIPath,
|
||||||
ContentConfig: config.ContentConfig,
|
ContentConfig: config.ContentConfig,
|
||||||
@ -642,10 +614,7 @@ func CopyConfig(config *Config) *Config {
|
|||||||
},
|
},
|
||||||
AuthProvider: config.AuthProvider,
|
AuthProvider: config.AuthProvider,
|
||||||
AuthConfigPersister: config.AuthConfigPersister,
|
AuthConfigPersister: config.AuthConfigPersister,
|
||||||
Exec: Exec{
|
ExecProvider: config.ExecProvider,
|
||||||
ExecProvider: config.Exec.ExecProvider,
|
|
||||||
Config: config.Exec.Config,
|
|
||||||
},
|
|
||||||
TLSClientConfig: TLSClientConfig{
|
TLSClientConfig: TLSClientConfig{
|
||||||
Insecure: config.TLSClientConfig.Insecure,
|
Insecure: config.TLSClientConfig.Insecure,
|
||||||
ServerName: config.TLSClientConfig.ServerName,
|
ServerName: config.TLSClientConfig.ServerName,
|
||||||
@ -669,4 +638,8 @@ func CopyConfig(config *Config) *Config {
|
|||||||
Dial: config.Dial,
|
Dial: config.Dial,
|
||||||
Proxy: config.Proxy,
|
Proxy: config.Proxy,
|
||||||
}
|
}
|
||||||
|
if config.ExecProvider != nil && config.ExecProvider.Config != nil {
|
||||||
|
c.ExecProvider.Config = config.ExecProvider.Config.DeepCopyObject()
|
||||||
|
}
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
@ -358,8 +358,7 @@ func TestAnonymousConfig(t *testing.T) {
|
|||||||
expected.Password = ""
|
expected.Password = ""
|
||||||
expected.AuthProvider = nil
|
expected.AuthProvider = nil
|
||||||
expected.AuthConfigPersister = nil
|
expected.AuthConfigPersister = nil
|
||||||
expected.Exec.ExecProvider = nil
|
expected.ExecProvider = nil
|
||||||
expected.Exec.Config = nil
|
|
||||||
expected.TLSClientConfig.CertData = nil
|
expected.TLSClientConfig.CertData = nil
|
||||||
expected.TLSClientConfig.CertFile = ""
|
expected.TLSClientConfig.CertFile = ""
|
||||||
expected.TLSClientConfig.KeyData = nil
|
expected.TLSClientConfig.KeyData = nil
|
||||||
@ -535,12 +534,10 @@ func TestConfigStringer(t *testing.T) {
|
|||||||
AuthProvider: &clientcmdapi.AuthProviderConfig{
|
AuthProvider: &clientcmdapi.AuthProviderConfig{
|
||||||
Config: map[string]string{"secret": "s3cr3t"},
|
Config: map[string]string{"secret": "s3cr3t"},
|
||||||
},
|
},
|
||||||
Exec: Exec{
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
ExecProvider: &clientcmdapi.ExecConfig{
|
Args: []string{"secret"},
|
||||||
Args: []string{"secret"},
|
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
|
||||||
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
|
Config: &runtime.Unknown{Raw: []byte("here is some config data")},
|
||||||
},
|
|
||||||
Config: &runtime.Unknown{Raw: []byte("super secret password")},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectContent: []string{
|
expectContent: []string{
|
||||||
@ -559,7 +556,7 @@ func TestConfigStringer(t *testing.T) {
|
|||||||
formatBytes([]byte("fake key")),
|
formatBytes([]byte("fake key")),
|
||||||
"secret",
|
"secret",
|
||||||
"s3cr3t",
|
"s3cr3t",
|
||||||
"super secret password",
|
"here is some config data",
|
||||||
formatBytes([]byte("super secret password")),
|
formatBytes([]byte("super secret password")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -603,13 +600,12 @@ func TestConfigSprint(t *testing.T) {
|
|||||||
Config: map[string]string{"secret": "s3cr3t"},
|
Config: map[string]string{"secret": "s3cr3t"},
|
||||||
},
|
},
|
||||||
AuthConfigPersister: fakeAuthProviderConfigPersister{},
|
AuthConfigPersister: fakeAuthProviderConfigPersister{},
|
||||||
Exec: Exec{
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
ExecProvider: &clientcmdapi.ExecConfig{
|
Command: "sudo",
|
||||||
Command: "sudo",
|
Args: []string{"secret"},
|
||||||
Args: []string{"secret"},
|
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
|
||||||
Env: []clientcmdapi.ExecEnvVar{{Name: "secret", Value: "s3cr3t"}},
|
ProvideClusterInfo: true,
|
||||||
},
|
Config: &runtime.Unknown{Raw: []byte("super secret password")},
|
||||||
Config: &runtime.Unknown{Raw: []byte("super secret password")},
|
|
||||||
},
|
},
|
||||||
TLSClientConfig: TLSClientConfig{
|
TLSClientConfig: TLSClientConfig{
|
||||||
CertFile: "a.crt",
|
CertFile: "a.crt",
|
||||||
@ -630,7 +626,7 @@ func TestConfigSprint(t *testing.T) {
|
|||||||
Proxy: fakeProxyFunc,
|
Proxy: fakeProxyFunc,
|
||||||
}
|
}
|
||||||
want := fmt.Sprintf(
|
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 ---), 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)}`,
|
`&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.ExecConfig{Command: "sudo", Args: []string{"--- REDACTED ---"}, Env: []ExecEnvVar{--- REDACTED ---}, APIVersion: "", ProvideClusterInfo: true, 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,
|
c.Transport, fakeWrapperFunc, c.RateLimiter, fakeDialFunc, fakeProxyFunc,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
85
staging/src/k8s.io/client-go/rest/exec.go
Normal file
85
staging/src/k8s.io/client-go/rest/exec.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Kubernetes Authors.
|
||||||
|
|
||||||
|
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 rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
|
clientauthenticationapi "k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This file contains Config logic related to exec credential plugins.
|
||||||
|
|
||||||
|
// ConfigToExecCluster creates a clientauthenticationapi.Cluster with the corresponding fields from
|
||||||
|
// the provided Config.
|
||||||
|
func ConfigToExecCluster(config *Config) (*clientauthenticationapi.Cluster, error) {
|
||||||
|
caData, err := dataFromSliceOrFile(config.CAData, config.CAFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load CA bundle for execProvider: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var proxyURL string
|
||||||
|
if config.Proxy != nil {
|
||||||
|
req, err := http.NewRequest("", config.Host, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create proxy URL request for execProvider: %w", err)
|
||||||
|
}
|
||||||
|
url, err := config.Proxy(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get proxy URL for execProvider: %w", err)
|
||||||
|
}
|
||||||
|
if url != nil {
|
||||||
|
proxyURL = url.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &clientauthentication.Cluster{
|
||||||
|
Server: config.Host,
|
||||||
|
TLSServerName: config.ServerName,
|
||||||
|
InsecureSkipTLSVerify: config.Insecure,
|
||||||
|
CertificateAuthorityData: caData,
|
||||||
|
ProxyURL: proxyURL,
|
||||||
|
Config: config.ExecProvider.Config,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecClusterToConfig creates a Config with the corresponding fields from the provided
|
||||||
|
// clientauthenticationapi.Cluster. The returned Config will be anonymous (i.e., it will not have
|
||||||
|
// any authentication-related fields set).
|
||||||
|
func ExecClusterToConfig(cluster *clientauthentication.Cluster) (*Config, error) {
|
||||||
|
var proxy func(*http.Request) (*url.URL, error)
|
||||||
|
if cluster.ProxyURL != "" {
|
||||||
|
proxyURL, err := url.Parse(cluster.ProxyURL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot parse proxy URL: %w", err)
|
||||||
|
}
|
||||||
|
proxy = http.ProxyURL(proxyURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Config{
|
||||||
|
Host: cluster.Server,
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
Insecure: cluster.InsecureSkipTLSVerify,
|
||||||
|
ServerName: cluster.TLSServerName,
|
||||||
|
CAData: cluster.CertificateAuthorityData,
|
||||||
|
},
|
||||||
|
Proxy: proxy,
|
||||||
|
}, nil
|
||||||
|
}
|
384
staging/src/k8s.io/client-go/rest/exec_test.go
Normal file
384
staging/src/k8s.io/client-go/rest/exec_test.go
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2020 The Kubernetes Authors.
|
||||||
|
|
||||||
|
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 rest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
fuzz "github.com/google/gofuzz"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
|
clientauthenticationapi "k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
|
"k8s.io/client-go/transport"
|
||||||
|
"k8s.io/client-go/util/flowcontrol"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfigToExecCluster(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const proxyURL = "https://some-proxy-url.com/tuna/fish"
|
||||||
|
proxy := func(r *http.Request) (*url.URL, error) {
|
||||||
|
return url.Parse(proxyURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
in Config
|
||||||
|
wantOut clientauthenticationapi.Cluster
|
||||||
|
wantErrorPrefix string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "CA data from memory",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Host: "some-host",
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
ServerName: "some-server-name",
|
||||||
|
Insecure: true,
|
||||||
|
CAData: []byte("some-ca-data"),
|
||||||
|
},
|
||||||
|
Proxy: proxy,
|
||||||
|
},
|
||||||
|
wantOut: clientauthenticationapi.Cluster{
|
||||||
|
Server: "some-host",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("some-ca-data"),
|
||||||
|
ProxyURL: proxyURL,
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CA data from file",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Host: "some-host",
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
ServerName: "some-server-name",
|
||||||
|
Insecure: true,
|
||||||
|
CAFile: "testdata/ca.pem",
|
||||||
|
},
|
||||||
|
Proxy: proxy,
|
||||||
|
},
|
||||||
|
wantOut: clientauthenticationapi.Cluster{
|
||||||
|
Server: "some-host",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("a CA bundle lives here"),
|
||||||
|
ProxyURL: proxyURL,
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no CA data",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
},
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
CAFile: "this-file-does-not-exist",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErrorPrefix: "failed to load CA bundle for execProvider: ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nil proxy",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Host: "some-host",
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
ServerName: "some-server-name",
|
||||||
|
Insecure: true,
|
||||||
|
CAFile: "testdata/ca.pem",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantOut: clientauthenticationapi.Cluster{
|
||||||
|
Server: "some-host",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("a CA bundle lives here"),
|
||||||
|
Config: &runtime.Unknown{
|
||||||
|
Raw: []byte("stuff"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "bad proxy",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
},
|
||||||
|
Proxy: func(_ *http.Request) (*url.URL, error) {
|
||||||
|
return nil, errors.New("some proxy error")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErrorPrefix: "failed to get proxy URL for execProvider: some proxy error",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "proxy returns nil",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
},
|
||||||
|
Proxy: func(_ *http.Request) (*url.URL, error) {
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
Host: "some-host",
|
||||||
|
TLSClientConfig: TLSClientConfig{
|
||||||
|
ServerName: "some-server-name",
|
||||||
|
Insecure: true,
|
||||||
|
CAFile: "testdata/ca.pem",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantOut: clientauthenticationapi.Cluster{
|
||||||
|
Server: "some-host",
|
||||||
|
TLSServerName: "some-server-name",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
CertificateAuthorityData: []byte("a CA bundle lives here"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid config host",
|
||||||
|
in: Config{
|
||||||
|
ExecProvider: &clientcmdapi.ExecConfig{
|
||||||
|
ProvideClusterInfo: true,
|
||||||
|
},
|
||||||
|
Proxy: func(_ *http.Request) (*url.URL, error) {
|
||||||
|
return nil, nil
|
||||||
|
},
|
||||||
|
Host: "invalid-config-host\n",
|
||||||
|
},
|
||||||
|
wantErrorPrefix: "failed to create proxy URL request for execProvider: ",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
out, err := ConfigToExecCluster(&test.in)
|
||||||
|
if test.wantErrorPrefix != "" {
|
||||||
|
if err == nil {
|
||||||
|
t.Error("wanted error")
|
||||||
|
} else if !strings.HasPrefix(err.Error(), test.wantErrorPrefix) {
|
||||||
|
t.Errorf("wanted error prefix %q, got %q", test.wantErrorPrefix, err.Error())
|
||||||
|
}
|
||||||
|
} else if diff := cmp.Diff(&test.wantOut, out); diff != "" {
|
||||||
|
t.Errorf("unexpected returned cluster: -got, +want:\n %s", diff)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConfigToExecClusterRoundtrip(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
f := fuzz.New().NilChance(0.5).NumElements(1, 1)
|
||||||
|
f.Funcs(
|
||||||
|
func(r *runtime.Codec, f fuzz.Continue) {
|
||||||
|
codec := &fakeCodec{}
|
||||||
|
f.Fuzz(codec)
|
||||||
|
*r = codec
|
||||||
|
},
|
||||||
|
func(r *http.RoundTripper, f fuzz.Continue) {
|
||||||
|
roundTripper := &fakeRoundTripper{}
|
||||||
|
f.Fuzz(roundTripper)
|
||||||
|
*r = roundTripper
|
||||||
|
},
|
||||||
|
func(fn *func(http.RoundTripper) http.RoundTripper, f fuzz.Continue) {
|
||||||
|
*fn = fakeWrapperFunc
|
||||||
|
},
|
||||||
|
func(fn *transport.WrapperFunc, f fuzz.Continue) {
|
||||||
|
*fn = fakeWrapperFunc
|
||||||
|
},
|
||||||
|
func(r *runtime.NegotiatedSerializer, f fuzz.Continue) {
|
||||||
|
serializer := &fakeNegotiatedSerializer{}
|
||||||
|
f.Fuzz(serializer)
|
||||||
|
*r = serializer
|
||||||
|
},
|
||||||
|
func(r *flowcontrol.RateLimiter, f fuzz.Continue) {
|
||||||
|
limiter := &fakeLimiter{}
|
||||||
|
f.Fuzz(limiter)
|
||||||
|
*r = limiter
|
||||||
|
},
|
||||||
|
func(h *WarningHandler, f fuzz.Continue) {
|
||||||
|
*h = &fakeWarningHandler{}
|
||||||
|
},
|
||||||
|
// Authentication does not require fuzzer
|
||||||
|
func(r *AuthProviderConfigPersister, f fuzz.Continue) {},
|
||||||
|
func(r *clientcmdapi.AuthProviderConfig, f fuzz.Continue) {
|
||||||
|
r.Config = map[string]string{}
|
||||||
|
},
|
||||||
|
func(r *func(ctx context.Context, network, addr string) (net.Conn, error), f fuzz.Continue) {
|
||||||
|
*r = fakeDialFunc
|
||||||
|
},
|
||||||
|
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 < 100; i++ {
|
||||||
|
expected := &Config{}
|
||||||
|
f.Fuzz(expected)
|
||||||
|
|
||||||
|
// This is the list of known fields that this roundtrip doesn't care about. We should add new
|
||||||
|
// fields to this list if we don't want to roundtrip them on exec cluster conversion.
|
||||||
|
expected.APIPath = ""
|
||||||
|
expected.ContentConfig = ContentConfig{}
|
||||||
|
expected.Username = ""
|
||||||
|
expected.Password = ""
|
||||||
|
expected.BearerToken = ""
|
||||||
|
expected.BearerTokenFile = ""
|
||||||
|
expected.Impersonate = ImpersonationConfig{}
|
||||||
|
expected.AuthProvider = nil
|
||||||
|
expected.AuthConfigPersister = nil
|
||||||
|
expected.ExecProvider = &clientcmdapi.ExecConfig{} // ConfigToExecCluster assumes != nil.
|
||||||
|
expected.TLSClientConfig.CertFile = ""
|
||||||
|
expected.TLSClientConfig.KeyFile = ""
|
||||||
|
expected.TLSClientConfig.CAFile = ""
|
||||||
|
expected.TLSClientConfig.CertData = nil
|
||||||
|
expected.TLSClientConfig.KeyData = nil
|
||||||
|
expected.TLSClientConfig.NextProtos = nil
|
||||||
|
expected.UserAgent = ""
|
||||||
|
expected.DisableCompression = false
|
||||||
|
expected.Transport = nil
|
||||||
|
expected.WrapTransport = nil
|
||||||
|
expected.QPS = 0.0
|
||||||
|
expected.Burst = 0
|
||||||
|
expected.RateLimiter = nil
|
||||||
|
expected.WarningHandler = nil
|
||||||
|
expected.Timeout = 0
|
||||||
|
expected.Dial = nil
|
||||||
|
|
||||||
|
// Manually set URLs so we don't get an error when parsing these during the roundtrip.
|
||||||
|
if expected.Host != "" {
|
||||||
|
expected.Host = "https://some-server-url.com/tuna/fish"
|
||||||
|
}
|
||||||
|
if expected.Proxy != nil {
|
||||||
|
expected.Proxy = func(_ *http.Request) (*url.URL, error) {
|
||||||
|
return url.Parse("https://some-proxy-url.com/tuna/fish")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cluster, err := ConfigToExecCluster(expected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err := ExecClusterToConfig(cluster)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if actual.Proxy != nil {
|
||||||
|
actualURL, actualErr := actual.Proxy(nil)
|
||||||
|
expectedURL, expectedErr := expected.Proxy(nil)
|
||||||
|
if actualErr != nil {
|
||||||
|
t.Fatalf("failed to get url from actual proxy func: %s", actualErr.Error())
|
||||||
|
}
|
||||||
|
if expectedErr != nil {
|
||||||
|
t.Fatalf("failed to get url from expected proxy func: %s", actualErr.Error())
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(actualURL, expectedURL); diff != "" {
|
||||||
|
t.Fatal("we dropped the Config.Proxy field during conversion")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actual.Proxy = nil
|
||||||
|
expected.Proxy = nil
|
||||||
|
|
||||||
|
if actual.ExecProvider != nil {
|
||||||
|
t.Fatal("expected actual Config.ExecProvider field to be set to nil")
|
||||||
|
}
|
||||||
|
actual.ExecProvider = nil
|
||||||
|
expected.ExecProvider = nil
|
||||||
|
|
||||||
|
if diff := cmp.Diff(actual, expected); diff != "" {
|
||||||
|
t.Fatalf("we dropped some Config fields during roundtrip, -got, +want:\n %s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExecClusterToConfigRoundtrip(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
f := fuzz.New().NilChance(0.5).NumElements(1, 1)
|
||||||
|
f.Funcs(
|
||||||
|
func(r *runtime.Object, f fuzz.Continue) {
|
||||||
|
// We don't expect the clientauthentication.Cluster.Config to show up in the Config that
|
||||||
|
// comes back from the roundtrip, so just set it to nil.
|
||||||
|
*r = nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
expected := &clientauthentication.Cluster{}
|
||||||
|
f.Fuzz(expected)
|
||||||
|
|
||||||
|
// Manually set URLs so we don't get an error when parsing these during the roundtrip.
|
||||||
|
if expected.Server != "" {
|
||||||
|
expected.Server = "https://some-server-url.com/tuna/fish"
|
||||||
|
}
|
||||||
|
if expected.ProxyURL != "" {
|
||||||
|
expected.ProxyURL = "https://some-proxy-url.com/tuna/fish"
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := ExecClusterToConfig(expected)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigToExecCluster assumes config.ExecProvider is not nil.
|
||||||
|
config.ExecProvider = &clientcmdapi.ExecConfig{}
|
||||||
|
|
||||||
|
actual, err := ConfigToExecCluster(config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(actual, expected); diff != "" {
|
||||||
|
t.Fatalf("we dropped some Cluster fields during roundtrip: -got, +want:\n %s", diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
staging/src/k8s.io/client-go/rest/testdata/ca.pem
vendored
Normal file
1
staging/src/k8s.io/client-go/rest/testdata/ca.pem
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
a CA bundle lives here
|
@ -19,7 +19,6 @@ package rest
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"k8s.io/client-go/pkg/apis/clientauthentication"
|
"k8s.io/client-go/pkg/apis/clientauthentication"
|
||||||
@ -91,22 +90,20 @@ func (c *Config) TransportConfig() (*transport.Config, error) {
|
|||||||
Proxy: c.Proxy,
|
Proxy: c.Proxy,
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Exec.ExecProvider != nil && c.AuthProvider != nil {
|
if c.ExecProvider != nil && c.AuthProvider != nil {
|
||||||
return nil, errors.New("execProvider and authProvider cannot be used in combination")
|
return nil, errors.New("execProvider and authProvider cannot be used in combination")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Exec.ExecProvider != nil {
|
if c.ExecProvider != nil {
|
||||||
caData, err := dataFromSliceOrFile(c.CAData, c.CAFile)
|
var cluster *clientauthentication.Cluster
|
||||||
if err != nil {
|
if c.ExecProvider.ProvideClusterInfo {
|
||||||
return nil, fmt.Errorf("failed to load CA bundle for execProvider: %v", err)
|
var err error
|
||||||
|
cluster, err = ConfigToExecCluster(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cluster := clientauthentication.Cluster{
|
provider, err := exec.GetAuthenticator(c.ExecProvider, 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -215,6 +215,36 @@ type ExecConfig struct {
|
|||||||
// present. For example, `brew install foo-cli` might be a good InstallHint for
|
// present. For example, `brew install foo-cli` might be a good InstallHint for
|
||||||
// foo-cli on Mac OS systems.
|
// foo-cli on Mac OS systems.
|
||||||
InstallHint string `json:"installHint,omitempty"`
|
InstallHint string `json:"installHint,omitempty"`
|
||||||
|
|
||||||
|
// ProvideClusterInfo determines whether or not to provide cluster information,
|
||||||
|
// which could potentially contain very large CA data, to this exec plugin as a
|
||||||
|
// part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set
|
||||||
|
// to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for
|
||||||
|
// reading this environment variable.
|
||||||
|
ProvideClusterInfo bool `json:"provideClusterInfo"`
|
||||||
|
|
||||||
|
// 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: client.authentication.k8s.io/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.
|
||||||
|
// +k8s:conversion-gen=false
|
||||||
|
Config runtime.Object
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ fmt.Stringer = new(ExecConfig)
|
var _ fmt.Stringer = new(ExecConfig)
|
||||||
@ -237,7 +267,11 @@ func (c ExecConfig) String() string {
|
|||||||
if len(c.Env) > 0 {
|
if len(c.Env) > 0 {
|
||||||
env = "[]ExecEnvVar{--- REDACTED ---}"
|
env = "[]ExecEnvVar{--- REDACTED ---}"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("api.AuthProviderConfig{Command: %q, Args: %#v, Env: %s, APIVersion: %q}", c.Command, args, env, c.APIVersion)
|
config := "runtime.Object(nil)"
|
||||||
|
if c.Config != nil {
|
||||||
|
config = "runtime.Object(--- REDACTED ---)"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("api.ExecConfig{Command: %q, Args: %#v, Env: %s, APIVersion: %q, ProvideClusterInfo: %t, Config: %s}", c.Command, args, env, c.APIVersion, c.ProvideClusterInfo, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecEnvVar is used for setting environment variables when executing an exec-based
|
// ExecEnvVar is used for setting environment variables when executing an exec-based
|
||||||
|
@ -214,6 +214,13 @@ type ExecConfig struct {
|
|||||||
// present. For example, `brew install foo-cli` might be a good InstallHint for
|
// present. For example, `brew install foo-cli` might be a good InstallHint for
|
||||||
// foo-cli on Mac OS systems.
|
// foo-cli on Mac OS systems.
|
||||||
InstallHint string `json:"installHint,omitempty"`
|
InstallHint string `json:"installHint,omitempty"`
|
||||||
|
|
||||||
|
// ProvideClusterInfo determines whether or not to provide cluster information,
|
||||||
|
// which could potentially contain very large CA data, to this exec plugin as a
|
||||||
|
// part of the KUBERNETES_EXEC_INFO environment variable. By default, it is set
|
||||||
|
// to false. Package k8s.io/client-go/tools/auth/exec provides helper methods for
|
||||||
|
// reading this environment variable.
|
||||||
|
ProvideClusterInfo bool `json:"provideClusterInfo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExecEnvVar is used for setting environment variables when executing an exec-based
|
// ExecEnvVar is used for setting environment variables when executing an exec-based
|
||||||
|
@ -171,7 +171,15 @@ func autoConvert_v1_AuthInfo_To_api_AuthInfo(in *AuthInfo, out *api.AuthInfo, s
|
|||||||
out.Username = in.Username
|
out.Username = in.Username
|
||||||
out.Password = in.Password
|
out.Password = in.Password
|
||||||
out.AuthProvider = (*api.AuthProviderConfig)(unsafe.Pointer(in.AuthProvider))
|
out.AuthProvider = (*api.AuthProviderConfig)(unsafe.Pointer(in.AuthProvider))
|
||||||
out.Exec = (*api.ExecConfig)(unsafe.Pointer(in.Exec))
|
if in.Exec != nil {
|
||||||
|
in, out := &in.Exec, &out.Exec
|
||||||
|
*out = new(api.ExecConfig)
|
||||||
|
if err := Convert_v1_ExecConfig_To_api_ExecConfig(*in, *out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Exec = nil
|
||||||
|
}
|
||||||
if err := Convert_Slice_v1_NamedExtension_To_Map_string_To_runtime_Object(&in.Extensions, &out.Extensions, s); err != nil {
|
if err := Convert_Slice_v1_NamedExtension_To_Map_string_To_runtime_Object(&in.Extensions, &out.Extensions, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -197,7 +205,15 @@ func autoConvert_api_AuthInfo_To_v1_AuthInfo(in *api.AuthInfo, out *AuthInfo, s
|
|||||||
out.Username = in.Username
|
out.Username = in.Username
|
||||||
out.Password = in.Password
|
out.Password = in.Password
|
||||||
out.AuthProvider = (*AuthProviderConfig)(unsafe.Pointer(in.AuthProvider))
|
out.AuthProvider = (*AuthProviderConfig)(unsafe.Pointer(in.AuthProvider))
|
||||||
out.Exec = (*ExecConfig)(unsafe.Pointer(in.Exec))
|
if in.Exec != nil {
|
||||||
|
in, out := &in.Exec, &out.Exec
|
||||||
|
*out = new(ExecConfig)
|
||||||
|
if err := Convert_api_ExecConfig_To_v1_ExecConfig(*in, *out, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Exec = nil
|
||||||
|
}
|
||||||
if err := Convert_Map_string_To_runtime_Object_To_Slice_v1_NamedExtension(&in.Extensions, &out.Extensions, s); err != nil {
|
if err := Convert_Map_string_To_runtime_Object_To_Slice_v1_NamedExtension(&in.Extensions, &out.Extensions, s); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -359,6 +375,7 @@ func autoConvert_v1_ExecConfig_To_api_ExecConfig(in *ExecConfig, out *api.ExecCo
|
|||||||
out.Env = *(*[]api.ExecEnvVar)(unsafe.Pointer(&in.Env))
|
out.Env = *(*[]api.ExecEnvVar)(unsafe.Pointer(&in.Env))
|
||||||
out.APIVersion = in.APIVersion
|
out.APIVersion = in.APIVersion
|
||||||
out.InstallHint = in.InstallHint
|
out.InstallHint = in.InstallHint
|
||||||
|
out.ProvideClusterInfo = in.ProvideClusterInfo
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,6 +390,8 @@ func autoConvert_api_ExecConfig_To_v1_ExecConfig(in *api.ExecConfig, out *ExecCo
|
|||||||
out.Env = *(*[]ExecEnvVar)(unsafe.Pointer(&in.Env))
|
out.Env = *(*[]ExecEnvVar)(unsafe.Pointer(&in.Env))
|
||||||
out.APIVersion = in.APIVersion
|
out.APIVersion = in.APIVersion
|
||||||
out.InstallHint = in.InstallHint
|
out.InstallHint = in.InstallHint
|
||||||
|
out.ProvideClusterInfo = in.ProvideClusterInfo
|
||||||
|
// INFO: in.Config opted out of conversion generation
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,6 +267,9 @@ func (in *ExecConfig) DeepCopyInto(out *ExecConfig) {
|
|||||||
*out = make([]ExecEnvVar, len(*in))
|
*out = make([]ExecEnvVar, len(*in))
|
||||||
copy(*out, *in)
|
copy(*out, *in)
|
||||||
}
|
}
|
||||||
|
if in.Config != nil {
|
||||||
|
out.Config = in.Config.DeepCopyObject()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,11 @@ import (
|
|||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// clusterExtensionKey is reserved in the cluster extensions list for exec plugin config.
|
||||||
|
clusterExtensionKey = "client.authentication.k8s.io/exec"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields
|
// ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields
|
||||||
// DEPRECATED will be replaced
|
// DEPRECATED will be replaced
|
||||||
@ -269,9 +274,9 @@ func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthI
|
|||||||
mergedConfig.AuthConfigPersister = persistAuthConfig
|
mergedConfig.AuthConfigPersister = persistAuthConfig
|
||||||
}
|
}
|
||||||
if configAuthInfo.Exec != nil {
|
if configAuthInfo.Exec != nil {
|
||||||
mergedConfig.Exec.ExecProvider = configAuthInfo.Exec
|
mergedConfig.ExecProvider = configAuthInfo.Exec
|
||||||
mergedConfig.ExecProvider.InstallHint = cleanANSIEscapeCodes(mergedConfig.ExecProvider.InstallHint)
|
mergedConfig.ExecProvider.InstallHint = cleanANSIEscapeCodes(mergedConfig.ExecProvider.InstallHint)
|
||||||
mergedConfig.Exec.Config = configClusterInfo.Extensions["exec"] // this key is reserved in the extensions list for exec plugin config
|
mergedConfig.ExecProvider.Config = configClusterInfo.Extensions[clusterExtensionKey]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@ -314,7 +319,7 @@ func canIdentifyUser(config restclient.Config) bool {
|
|||||||
(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 ||
|
config.AuthProvider != nil ||
|
||||||
config.Exec.ExecProvider != nil
|
config.ExecProvider != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanANSIEscapeCodes takes an arbitrary string and ensures that there are no
|
// cleanANSIEscapeCodes takes an arbitrary string and ensures that there are no
|
||||||
|
@ -836,7 +836,7 @@ clusters:
|
|||||||
- cluster:
|
- cluster:
|
||||||
server: https://localhost:8080
|
server: https://localhost:8080
|
||||||
extensions:
|
extensions:
|
||||||
- name: exec
|
- name: client.authentication.k8s.io/exec
|
||||||
extension:
|
extension:
|
||||||
audience: foo
|
audience: foo
|
||||||
other: bar
|
other: bar
|
||||||
@ -858,6 +858,7 @@ users:
|
|||||||
- arg-1
|
- arg-1
|
||||||
- arg-2
|
- arg-2
|
||||||
command: foo-command
|
command: foo-command
|
||||||
|
provideClusterInfo: true
|
||||||
`
|
`
|
||||||
tmpfile, err := ioutil.TempFile("", "kubeconfig")
|
tmpfile, err := ioutil.TempFile("", "kubeconfig")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -871,15 +872,18 @@ users:
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(config.Exec.ExecProvider.Args, []string{"arg-1", "arg-2"}) {
|
if !reflect.DeepEqual(config.ExecProvider.Args, []string{"arg-1", "arg-2"}) {
|
||||||
t.Errorf("Got args %v when they should be %v\n", config.Exec.ExecProvider.Args, []string{"arg-1", "arg-2"})
|
t.Errorf("Got args %v when they should be %v\n", config.ExecProvider.Args, []string{"arg-1", "arg-2"})
|
||||||
|
}
|
||||||
|
if !config.ExecProvider.ProvideClusterInfo {
|
||||||
|
t.Error("Wanted provider cluster info to be true")
|
||||||
}
|
}
|
||||||
want := &runtime.Unknown{
|
want := &runtime.Unknown{
|
||||||
Raw: []byte(`{"audience":"foo","other":"bar"}`),
|
Raw: []byte(`{"audience":"foo","other":"bar"}`),
|
||||||
ContentType: "application/json",
|
ContentType: "application/json",
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(config.Exec.Config, want) {
|
if !reflect.DeepEqual(config.ExecProvider.Config, want) {
|
||||||
t.Errorf("Got config %v when it should be %v\n", config.Exec.Config, want)
|
t.Errorf("Got config %v when it should be %v\n", config.ExecProvider.Config, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user