...

Source file src/k8s.io/client-go/tools/clientcmd/client_config.go

Documentation: k8s.io/client-go/tools/clientcmd

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package clientcmd
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"net/http"
    23  	"net/url"
    24  	"os"
    25  	"strings"
    26  	"unicode"
    27  
    28  	restclient "k8s.io/client-go/rest"
    29  	clientauth "k8s.io/client-go/tools/auth"
    30  	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
    31  	"k8s.io/klog/v2"
    32  
    33  	"github.com/imdario/mergo"
    34  )
    35  
    36  const (
    37  	// clusterExtensionKey is reserved in the cluster extensions list for exec plugin config.
    38  	clusterExtensionKey = "client.authentication.k8s.io/exec"
    39  )
    40  
    41  var (
    42  	// ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields
    43  	// DEPRECATED will be replaced
    44  	ClusterDefaults = clientcmdapi.Cluster{Server: getDefaultServer()}
    45  	// DefaultClientConfig represents the legacy behavior of this package for defaulting
    46  	// DEPRECATED will be replace
    47  	DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{
    48  		ClusterDefaults: ClusterDefaults,
    49  	}, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}}
    50  )
    51  
    52  // getDefaultServer returns a default setting for DefaultClientConfig
    53  // DEPRECATED
    54  func getDefaultServer() string {
    55  	if server := os.Getenv("KUBERNETES_MASTER"); len(server) > 0 {
    56  		return server
    57  	}
    58  	return "http://localhost:8080"
    59  }
    60  
    61  // ClientConfig is used to make it easy to get an api server client
    62  type ClientConfig interface {
    63  	// RawConfig returns the merged result of all overrides
    64  	RawConfig() (clientcmdapi.Config, error)
    65  	// ClientConfig returns a complete client config
    66  	ClientConfig() (*restclient.Config, error)
    67  	// Namespace returns the namespace resulting from the merged
    68  	// result of all overrides and a boolean indicating if it was
    69  	// overridden
    70  	Namespace() (string, bool, error)
    71  	// ConfigAccess returns the rules for loading/persisting the config.
    72  	ConfigAccess() ConfigAccess
    73  }
    74  
    75  // OverridingClientConfig is used to enable overrriding the raw KubeConfig
    76  type OverridingClientConfig interface {
    77  	ClientConfig
    78  	// MergedRawConfig return the RawConfig merged with all overrides.
    79  	MergedRawConfig() (clientcmdapi.Config, error)
    80  }
    81  
    82  type PersistAuthProviderConfigForUser func(user string) restclient.AuthProviderConfigPersister
    83  
    84  type promptedCredentials struct {
    85  	username string
    86  	password string `datapolicy:"password"`
    87  }
    88  
    89  // DirectClientConfig is a ClientConfig interface that is backed by a clientcmdapi.Config, options overrides, and an optional fallbackReader for auth information
    90  type DirectClientConfig struct {
    91  	config         clientcmdapi.Config
    92  	contextName    string
    93  	overrides      *ConfigOverrides
    94  	fallbackReader io.Reader
    95  	configAccess   ConfigAccess
    96  	// promptedCredentials store the credentials input by the user
    97  	promptedCredentials promptedCredentials
    98  }
    99  
   100  // NewDefaultClientConfig creates a DirectClientConfig using the config.CurrentContext as the context name
   101  func NewDefaultClientConfig(config clientcmdapi.Config, overrides *ConfigOverrides) OverridingClientConfig {
   102  	return &DirectClientConfig{config, config.CurrentContext, overrides, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}}
   103  }
   104  
   105  // NewNonInteractiveClientConfig creates a DirectClientConfig using the passed context name and does not have a fallback reader for auth information
   106  func NewNonInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, configAccess ConfigAccess) OverridingClientConfig {
   107  	return &DirectClientConfig{config, contextName, overrides, nil, configAccess, promptedCredentials{}}
   108  }
   109  
   110  // NewInteractiveClientConfig creates a DirectClientConfig using the passed context name and a reader in case auth information is not provided via files or flags
   111  func NewInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, fallbackReader io.Reader, configAccess ConfigAccess) OverridingClientConfig {
   112  	return &DirectClientConfig{config, contextName, overrides, fallbackReader, configAccess, promptedCredentials{}}
   113  }
   114  
   115  // NewClientConfigFromBytes takes your kubeconfig and gives you back a ClientConfig
   116  func NewClientConfigFromBytes(configBytes []byte) (OverridingClientConfig, error) {
   117  	config, err := Load(configBytes)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	return &DirectClientConfig{*config, "", &ConfigOverrides{}, nil, nil, promptedCredentials{}}, nil
   123  }
   124  
   125  // RESTConfigFromKubeConfig is a convenience method to give back a restconfig from your kubeconfig bytes.
   126  // For programmatic access, this is what you want 80% of the time
   127  func RESTConfigFromKubeConfig(configBytes []byte) (*restclient.Config, error) {
   128  	clientConfig, err := NewClientConfigFromBytes(configBytes)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	return clientConfig.ClientConfig()
   133  }
   134  
   135  func (config *DirectClientConfig) RawConfig() (clientcmdapi.Config, error) {
   136  	return config.config, nil
   137  }
   138  
   139  // MergedRawConfig returns the raw kube config merged with the overrides
   140  func (config *DirectClientConfig) MergedRawConfig() (clientcmdapi.Config, error) {
   141  	if err := config.ConfirmUsable(); err != nil {
   142  		return clientcmdapi.Config{}, err
   143  	}
   144  	merged := config.config.DeepCopy()
   145  
   146  	// set the AuthInfo merged with overrides in the merged config
   147  	mergedAuthInfo, err := config.getAuthInfo()
   148  	if err != nil {
   149  		return clientcmdapi.Config{}, err
   150  	}
   151  	mergedAuthInfoName, _ := config.getAuthInfoName()
   152  	merged.AuthInfos[mergedAuthInfoName] = &mergedAuthInfo
   153  
   154  	// set the Context merged with overrides in the merged config
   155  	mergedContext, err := config.getContext()
   156  	if err != nil {
   157  		return clientcmdapi.Config{}, err
   158  	}
   159  	mergedContextName, _ := config.getContextName()
   160  	merged.Contexts[mergedContextName] = &mergedContext
   161  	merged.CurrentContext = mergedContextName
   162  
   163  	// set the Cluster merged with overrides in the merged config
   164  	configClusterInfo, err := config.getCluster()
   165  	if err != nil {
   166  		return clientcmdapi.Config{}, err
   167  	}
   168  	configClusterName, _ := config.getClusterName()
   169  	merged.Clusters[configClusterName] = &configClusterInfo
   170  	return *merged, nil
   171  }
   172  
   173  // ClientConfig implements ClientConfig
   174  func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
   175  	// check that getAuthInfo, getContext, and getCluster do not return an error.
   176  	// Do this before checking if the current config is usable in the event that an
   177  	// AuthInfo, Context, or Cluster config with user-defined names are not found.
   178  	// This provides a user with the immediate cause for error if one is found
   179  	configAuthInfo, err := config.getAuthInfo()
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	_, err = config.getContext()
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	configClusterInfo, err := config.getCluster()
   190  	if err != nil {
   191  		return nil, err
   192  	}
   193  
   194  	if err := config.ConfirmUsable(); err != nil {
   195  		return nil, err
   196  	}
   197  
   198  	clientConfig := &restclient.Config{}
   199  	clientConfig.Host = configClusterInfo.Server
   200  	if configClusterInfo.ProxyURL != "" {
   201  		u, err := parseProxyURL(configClusterInfo.ProxyURL)
   202  		if err != nil {
   203  			return nil, err
   204  		}
   205  		clientConfig.Proxy = http.ProxyURL(u)
   206  	}
   207  
   208  	clientConfig.DisableCompression = configClusterInfo.DisableCompression
   209  
   210  	if config.overrides != nil && len(config.overrides.Timeout) > 0 {
   211  		timeout, err := ParseTimeout(config.overrides.Timeout)
   212  		if err != nil {
   213  			return nil, err
   214  		}
   215  		clientConfig.Timeout = timeout
   216  	}
   217  
   218  	if u, err := url.ParseRequestURI(clientConfig.Host); err == nil && u.Opaque == "" && len(u.Path) > 1 {
   219  		u.RawQuery = ""
   220  		u.Fragment = ""
   221  		clientConfig.Host = u.String()
   222  	}
   223  	if len(configAuthInfo.Impersonate) > 0 {
   224  		clientConfig.Impersonate = restclient.ImpersonationConfig{
   225  			UserName: configAuthInfo.Impersonate,
   226  			UID:      configAuthInfo.ImpersonateUID,
   227  			Groups:   configAuthInfo.ImpersonateGroups,
   228  			Extra:    configAuthInfo.ImpersonateUserExtra,
   229  		}
   230  	}
   231  
   232  	// only try to read the auth information if we are secure
   233  	if restclient.IsConfigTransportTLS(*clientConfig) {
   234  		var err error
   235  		var persister restclient.AuthProviderConfigPersister
   236  		if config.configAccess != nil {
   237  			authInfoName, _ := config.getAuthInfoName()
   238  			persister = PersisterForUser(config.configAccess, authInfoName)
   239  		}
   240  		userAuthPartialConfig, err := config.getUserIdentificationPartialConfig(configAuthInfo, config.fallbackReader, persister, configClusterInfo)
   241  		if err != nil {
   242  			return nil, err
   243  		}
   244  		mergo.Merge(clientConfig, userAuthPartialConfig, mergo.WithOverride)
   245  
   246  		serverAuthPartialConfig, err := getServerIdentificationPartialConfig(configAuthInfo, configClusterInfo)
   247  		if err != nil {
   248  			return nil, err
   249  		}
   250  		mergo.Merge(clientConfig, serverAuthPartialConfig, mergo.WithOverride)
   251  	}
   252  
   253  	return clientConfig, nil
   254  }
   255  
   256  // clientauth.Info object contain both user identification and server identification.  We want different precedence orders for
   257  // both, so we have to split the objects and merge them separately
   258  // we want this order of precedence for the server identification
   259  // 1.  configClusterInfo (the final result of command line flags and merged .kubeconfig files)
   260  // 2.  configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority)
   261  // 3.  load the ~/.kubernetes_auth file as a default
   262  func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, configClusterInfo clientcmdapi.Cluster) (*restclient.Config, error) {
   263  	mergedConfig := &restclient.Config{}
   264  
   265  	// configClusterInfo holds the information identify the server provided by .kubeconfig
   266  	configClientConfig := &restclient.Config{}
   267  	configClientConfig.CAFile = configClusterInfo.CertificateAuthority
   268  	configClientConfig.CAData = configClusterInfo.CertificateAuthorityData
   269  	configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify
   270  	configClientConfig.ServerName = configClusterInfo.TLSServerName
   271  	mergo.Merge(mergedConfig, configClientConfig, mergo.WithOverride)
   272  
   273  	return mergedConfig, nil
   274  }
   275  
   276  // clientauth.Info object contain both user identification and server identification.  We want different precedence orders for
   277  // both, so we have to split the objects and merge them separately
   278  // we want this order of precedence for user identification
   279  // 1.  configAuthInfo minus auth-path (the final result of command line flags and merged .kubeconfig files)
   280  // 2.  configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority)
   281  // 3.  if there is not enough information to identify the user, load try the ~/.kubernetes_auth file
   282  // 4.  if there is not enough information to identify the user, prompt if possible
   283  func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fallbackReader io.Reader, persistAuthConfig restclient.AuthProviderConfigPersister, configClusterInfo clientcmdapi.Cluster) (*restclient.Config, error) {
   284  	mergedConfig := &restclient.Config{}
   285  
   286  	// blindly overwrite existing values based on precedence
   287  	if len(configAuthInfo.Token) > 0 {
   288  		mergedConfig.BearerToken = configAuthInfo.Token
   289  		mergedConfig.BearerTokenFile = configAuthInfo.TokenFile
   290  	} else if len(configAuthInfo.TokenFile) > 0 {
   291  		tokenBytes, err := os.ReadFile(configAuthInfo.TokenFile)
   292  		if err != nil {
   293  			return nil, err
   294  		}
   295  		mergedConfig.BearerToken = string(tokenBytes)
   296  		mergedConfig.BearerTokenFile = configAuthInfo.TokenFile
   297  	}
   298  	if len(configAuthInfo.Impersonate) > 0 {
   299  		mergedConfig.Impersonate = restclient.ImpersonationConfig{
   300  			UserName: configAuthInfo.Impersonate,
   301  			UID:      configAuthInfo.ImpersonateUID,
   302  			Groups:   configAuthInfo.ImpersonateGroups,
   303  			Extra:    configAuthInfo.ImpersonateUserExtra,
   304  		}
   305  	}
   306  	if len(configAuthInfo.ClientCertificate) > 0 || len(configAuthInfo.ClientCertificateData) > 0 {
   307  		mergedConfig.CertFile = configAuthInfo.ClientCertificate
   308  		mergedConfig.CertData = configAuthInfo.ClientCertificateData
   309  		mergedConfig.KeyFile = configAuthInfo.ClientKey
   310  		mergedConfig.KeyData = configAuthInfo.ClientKeyData
   311  	}
   312  	if len(configAuthInfo.Username) > 0 || len(configAuthInfo.Password) > 0 {
   313  		mergedConfig.Username = configAuthInfo.Username
   314  		mergedConfig.Password = configAuthInfo.Password
   315  	}
   316  	if configAuthInfo.AuthProvider != nil {
   317  		mergedConfig.AuthProvider = configAuthInfo.AuthProvider
   318  		mergedConfig.AuthConfigPersister = persistAuthConfig
   319  	}
   320  	if configAuthInfo.Exec != nil {
   321  		mergedConfig.ExecProvider = configAuthInfo.Exec
   322  		mergedConfig.ExecProvider.InstallHint = cleanANSIEscapeCodes(mergedConfig.ExecProvider.InstallHint)
   323  		mergedConfig.ExecProvider.Config = configClusterInfo.Extensions[clusterExtensionKey]
   324  	}
   325  
   326  	// if there still isn't enough information to authenticate the user, try prompting
   327  	if !canIdentifyUser(*mergedConfig) && (fallbackReader != nil) {
   328  		if len(config.promptedCredentials.username) > 0 && len(config.promptedCredentials.password) > 0 {
   329  			mergedConfig.Username = config.promptedCredentials.username
   330  			mergedConfig.Password = config.promptedCredentials.password
   331  			return mergedConfig, nil
   332  		}
   333  		prompter := NewPromptingAuthLoader(fallbackReader)
   334  		promptedAuthInfo, err := prompter.Prompt()
   335  		if err != nil {
   336  			return nil, err
   337  		}
   338  		promptedConfig := makeUserIdentificationConfig(*promptedAuthInfo)
   339  		previouslyMergedConfig := mergedConfig
   340  		mergedConfig = &restclient.Config{}
   341  		mergo.Merge(mergedConfig, promptedConfig, mergo.WithOverride)
   342  		mergo.Merge(mergedConfig, previouslyMergedConfig, mergo.WithOverride)
   343  		config.promptedCredentials.username = mergedConfig.Username
   344  		config.promptedCredentials.password = mergedConfig.Password
   345  	}
   346  
   347  	return mergedConfig, nil
   348  }
   349  
   350  // makeUserIdentificationFieldsConfig returns a client.Config capable of being merged using mergo for only user identification information
   351  func makeUserIdentificationConfig(info clientauth.Info) *restclient.Config {
   352  	config := &restclient.Config{}
   353  	config.Username = info.User
   354  	config.Password = info.Password
   355  	config.CertFile = info.CertFile
   356  	config.KeyFile = info.KeyFile
   357  	config.BearerToken = info.BearerToken
   358  	return config
   359  }
   360  
   361  func canIdentifyUser(config restclient.Config) bool {
   362  	return len(config.Username) > 0 ||
   363  		(len(config.CertFile) > 0 || len(config.CertData) > 0) ||
   364  		len(config.BearerToken) > 0 ||
   365  		config.AuthProvider != nil ||
   366  		config.ExecProvider != nil
   367  }
   368  
   369  // cleanANSIEscapeCodes takes an arbitrary string and ensures that there are no
   370  // ANSI escape sequences that could put the terminal in a weird state (e.g.,
   371  // "\e[1m" bolds text)
   372  func cleanANSIEscapeCodes(s string) string {
   373  	// spaceControlCharacters includes tab, new line, vertical tab, new page, and
   374  	// carriage return. These are in the unicode.Cc category, but that category also
   375  	// contains ESC (U+001B) which we don't want.
   376  	spaceControlCharacters := unicode.RangeTable{
   377  		R16: []unicode.Range16{
   378  			{Lo: 0x0009, Hi: 0x000D, Stride: 1},
   379  		},
   380  	}
   381  
   382  	// Why not make this deny-only (instead of allow-only)? Because unicode.C
   383  	// contains newline and tab characters that we want.
   384  	allowedRanges := []*unicode.RangeTable{
   385  		unicode.L,
   386  		unicode.M,
   387  		unicode.N,
   388  		unicode.P,
   389  		unicode.S,
   390  		unicode.Z,
   391  		&spaceControlCharacters,
   392  	}
   393  	builder := strings.Builder{}
   394  	for _, roon := range s {
   395  		if unicode.IsOneOf(allowedRanges, roon) {
   396  			builder.WriteRune(roon) // returns nil error, per go doc
   397  		} else {
   398  			fmt.Fprintf(&builder, "%U", roon)
   399  		}
   400  	}
   401  	return builder.String()
   402  }
   403  
   404  // Namespace implements ClientConfig
   405  func (config *DirectClientConfig) Namespace() (string, bool, error) {
   406  	if config.overrides != nil && config.overrides.Context.Namespace != "" {
   407  		// In the event we have an empty config but we do have a namespace override, we should return
   408  		// the namespace override instead of having config.ConfirmUsable() return an error. This allows
   409  		// things like in-cluster clients to execute `kubectl get pods --namespace=foo` and have the
   410  		// --namespace flag honored instead of being ignored.
   411  		return config.overrides.Context.Namespace, true, nil
   412  	}
   413  
   414  	if err := config.ConfirmUsable(); err != nil {
   415  		return "", false, err
   416  	}
   417  
   418  	configContext, err := config.getContext()
   419  	if err != nil {
   420  		return "", false, err
   421  	}
   422  
   423  	if len(configContext.Namespace) == 0 {
   424  		return "default", false, nil
   425  	}
   426  
   427  	return configContext.Namespace, false, nil
   428  }
   429  
   430  // ConfigAccess implements ClientConfig
   431  func (config *DirectClientConfig) ConfigAccess() ConfigAccess {
   432  	return config.configAccess
   433  }
   434  
   435  // ConfirmUsable looks a particular context and determines if that particular part of the config is useable.  There might still be errors in the config,
   436  // but no errors in the sections requested or referenced.  It does not return early so that it can find as many errors as possible.
   437  func (config *DirectClientConfig) ConfirmUsable() error {
   438  	validationErrors := make([]error, 0)
   439  
   440  	var contextName string
   441  	if len(config.contextName) != 0 {
   442  		contextName = config.contextName
   443  	} else {
   444  		contextName = config.config.CurrentContext
   445  	}
   446  
   447  	if len(contextName) > 0 {
   448  		_, exists := config.config.Contexts[contextName]
   449  		if !exists {
   450  			validationErrors = append(validationErrors, &errContextNotFound{contextName})
   451  		}
   452  	}
   453  
   454  	authInfoName, _ := config.getAuthInfoName()
   455  	authInfo, _ := config.getAuthInfo()
   456  	validationErrors = append(validationErrors, validateAuthInfo(authInfoName, authInfo)...)
   457  	clusterName, _ := config.getClusterName()
   458  	cluster, _ := config.getCluster()
   459  	validationErrors = append(validationErrors, validateClusterInfo(clusterName, cluster)...)
   460  	// when direct client config is specified, and our only error is that no server is defined, we should
   461  	// return a standard "no config" error
   462  	if len(validationErrors) == 1 && validationErrors[0] == ErrEmptyCluster {
   463  		return newErrConfigurationInvalid([]error{ErrEmptyConfig})
   464  	}
   465  	return newErrConfigurationInvalid(validationErrors)
   466  }
   467  
   468  // getContextName returns the default, or user-set context name, and a boolean that indicates
   469  // whether the default context name has been overwritten by a user-set flag, or left as its default value
   470  func (config *DirectClientConfig) getContextName() (string, bool) {
   471  	if config.overrides != nil && len(config.overrides.CurrentContext) != 0 {
   472  		return config.overrides.CurrentContext, true
   473  	}
   474  	if len(config.contextName) != 0 {
   475  		return config.contextName, false
   476  	}
   477  
   478  	return config.config.CurrentContext, false
   479  }
   480  
   481  // getAuthInfoName returns a string containing the current authinfo name for the current context,
   482  // and a boolean indicating  whether the default authInfo name is overwritten by a user-set flag, or
   483  // left as its default value
   484  func (config *DirectClientConfig) getAuthInfoName() (string, bool) {
   485  	if config.overrides != nil && len(config.overrides.Context.AuthInfo) != 0 {
   486  		return config.overrides.Context.AuthInfo, true
   487  	}
   488  	context, _ := config.getContext()
   489  	return context.AuthInfo, false
   490  }
   491  
   492  // getClusterName returns a string containing the default, or user-set cluster name, and a boolean
   493  // indicating whether the default clusterName has been overwritten by a user-set flag, or left as
   494  // its default value
   495  func (config *DirectClientConfig) getClusterName() (string, bool) {
   496  	if config.overrides != nil && len(config.overrides.Context.Cluster) != 0 {
   497  		return config.overrides.Context.Cluster, true
   498  	}
   499  	context, _ := config.getContext()
   500  	return context.Cluster, false
   501  }
   502  
   503  // getContext returns the clientcmdapi.Context, or an error if a required context is not found.
   504  func (config *DirectClientConfig) getContext() (clientcmdapi.Context, error) {
   505  	contexts := config.config.Contexts
   506  	contextName, required := config.getContextName()
   507  
   508  	mergedContext := clientcmdapi.NewContext()
   509  	if configContext, exists := contexts[contextName]; exists {
   510  		mergo.Merge(mergedContext, configContext, mergo.WithOverride)
   511  	} else if required {
   512  		return clientcmdapi.Context{}, fmt.Errorf("context %q does not exist", contextName)
   513  	}
   514  	if config.overrides != nil {
   515  		mergo.Merge(mergedContext, config.overrides.Context, mergo.WithOverride)
   516  	}
   517  
   518  	return *mergedContext, nil
   519  }
   520  
   521  // getAuthInfo returns the clientcmdapi.AuthInfo, or an error if a required auth info is not found.
   522  func (config *DirectClientConfig) getAuthInfo() (clientcmdapi.AuthInfo, error) {
   523  	authInfos := config.config.AuthInfos
   524  	authInfoName, required := config.getAuthInfoName()
   525  
   526  	mergedAuthInfo := clientcmdapi.NewAuthInfo()
   527  	if configAuthInfo, exists := authInfos[authInfoName]; exists {
   528  		mergo.Merge(mergedAuthInfo, configAuthInfo, mergo.WithOverride)
   529  	} else if required {
   530  		return clientcmdapi.AuthInfo{}, fmt.Errorf("auth info %q does not exist", authInfoName)
   531  	}
   532  	if config.overrides != nil {
   533  		mergo.Merge(mergedAuthInfo, config.overrides.AuthInfo, mergo.WithOverride)
   534  	}
   535  
   536  	return *mergedAuthInfo, nil
   537  }
   538  
   539  // getCluster returns the clientcmdapi.Cluster, or an error if a required cluster is not found.
   540  func (config *DirectClientConfig) getCluster() (clientcmdapi.Cluster, error) {
   541  	clusterInfos := config.config.Clusters
   542  	clusterInfoName, required := config.getClusterName()
   543  
   544  	mergedClusterInfo := clientcmdapi.NewCluster()
   545  	if config.overrides != nil {
   546  		mergo.Merge(mergedClusterInfo, config.overrides.ClusterDefaults, mergo.WithOverride)
   547  	}
   548  	if configClusterInfo, exists := clusterInfos[clusterInfoName]; exists {
   549  		mergo.Merge(mergedClusterInfo, configClusterInfo, mergo.WithOverride)
   550  	} else if required {
   551  		return clientcmdapi.Cluster{}, fmt.Errorf("cluster %q does not exist", clusterInfoName)
   552  	}
   553  	if config.overrides != nil {
   554  		mergo.Merge(mergedClusterInfo, config.overrides.ClusterInfo, mergo.WithOverride)
   555  	}
   556  
   557  	// * An override of --insecure-skip-tls-verify=true and no accompanying CA/CA data should clear already-set CA/CA data
   558  	// otherwise, a kubeconfig containing a CA reference would return an error that "CA and insecure-skip-tls-verify couldn't both be set".
   559  	// * An override of --certificate-authority should also override TLS skip settings and CA data, otherwise existing CA data will take precedence.
   560  	if config.overrides != nil {
   561  		caLen := len(config.overrides.ClusterInfo.CertificateAuthority)
   562  		caDataLen := len(config.overrides.ClusterInfo.CertificateAuthorityData)
   563  		if config.overrides.ClusterInfo.InsecureSkipTLSVerify || caLen > 0 || caDataLen > 0 {
   564  			mergedClusterInfo.InsecureSkipTLSVerify = config.overrides.ClusterInfo.InsecureSkipTLSVerify
   565  			mergedClusterInfo.CertificateAuthority = config.overrides.ClusterInfo.CertificateAuthority
   566  			mergedClusterInfo.CertificateAuthorityData = config.overrides.ClusterInfo.CertificateAuthorityData
   567  		}
   568  
   569  		// if the --tls-server-name has been set in overrides, use that value.
   570  		// if the --server has been set in overrides, then use the value of --tls-server-name specified on the CLI too.  This gives the property
   571  		// that setting a --server will effectively clear the KUBECONFIG value of tls-server-name if it is specified on the command line which is
   572  		// usually correct.
   573  		if config.overrides.ClusterInfo.TLSServerName != "" || config.overrides.ClusterInfo.Server != "" {
   574  			mergedClusterInfo.TLSServerName = config.overrides.ClusterInfo.TLSServerName
   575  		}
   576  	}
   577  
   578  	return *mergedClusterInfo, nil
   579  }
   580  
   581  // inClusterClientConfig makes a config that will work from within a kubernetes cluster container environment.
   582  // Can take options overrides for flags explicitly provided to the command inside the cluster container.
   583  type inClusterClientConfig struct {
   584  	overrides               *ConfigOverrides
   585  	inClusterConfigProvider func() (*restclient.Config, error)
   586  }
   587  
   588  var _ ClientConfig = &inClusterClientConfig{}
   589  
   590  func (config *inClusterClientConfig) RawConfig() (clientcmdapi.Config, error) {
   591  	return clientcmdapi.Config{}, fmt.Errorf("inCluster environment config doesn't support multiple clusters")
   592  }
   593  
   594  func (config *inClusterClientConfig) ClientConfig() (*restclient.Config, error) {
   595  	inClusterConfigProvider := config.inClusterConfigProvider
   596  	if inClusterConfigProvider == nil {
   597  		inClusterConfigProvider = restclient.InClusterConfig
   598  	}
   599  
   600  	icc, err := inClusterConfigProvider()
   601  	if err != nil {
   602  		return nil, err
   603  	}
   604  
   605  	// in-cluster configs only takes a host, token, or CA file
   606  	// if any of them were individually provided, overwrite anything else
   607  	if config.overrides != nil {
   608  		if server := config.overrides.ClusterInfo.Server; len(server) > 0 {
   609  			icc.Host = server
   610  		}
   611  		if len(config.overrides.AuthInfo.Token) > 0 || len(config.overrides.AuthInfo.TokenFile) > 0 {
   612  			icc.BearerToken = config.overrides.AuthInfo.Token
   613  			icc.BearerTokenFile = config.overrides.AuthInfo.TokenFile
   614  		}
   615  		if certificateAuthorityFile := config.overrides.ClusterInfo.CertificateAuthority; len(certificateAuthorityFile) > 0 {
   616  			icc.TLSClientConfig.CAFile = certificateAuthorityFile
   617  		}
   618  	}
   619  
   620  	return icc, nil
   621  }
   622  
   623  func (config *inClusterClientConfig) Namespace() (string, bool, error) {
   624  	// This way assumes you've set the POD_NAMESPACE environment variable using the downward API.
   625  	// This check has to be done first for backwards compatibility with the way InClusterConfig was originally set up
   626  	if ns := os.Getenv("POD_NAMESPACE"); ns != "" {
   627  		return ns, false, nil
   628  	}
   629  
   630  	// Fall back to the namespace associated with the service account token, if available
   631  	if data, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
   632  		if ns := strings.TrimSpace(string(data)); len(ns) > 0 {
   633  			return ns, false, nil
   634  		}
   635  	}
   636  
   637  	return "default", false, nil
   638  }
   639  
   640  func (config *inClusterClientConfig) ConfigAccess() ConfigAccess {
   641  	return NewDefaultClientConfigLoadingRules()
   642  }
   643  
   644  // Possible returns true if loading an inside-kubernetes-cluster is possible.
   645  func (config *inClusterClientConfig) Possible() bool {
   646  	fi, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount/token")
   647  	return os.Getenv("KUBERNETES_SERVICE_HOST") != "" &&
   648  		os.Getenv("KUBERNETES_SERVICE_PORT") != "" &&
   649  		err == nil && !fi.IsDir()
   650  }
   651  
   652  // BuildConfigFromFlags is a helper function that builds configs from a master
   653  // url or a kubeconfig filepath. These are passed in as command line flags for cluster
   654  // components. Warnings should reflect this usage. If neither masterUrl or kubeconfigPath
   655  // are passed in we fallback to inClusterConfig. If inClusterConfig fails, we fallback
   656  // to the default config.
   657  func BuildConfigFromFlags(masterUrl, kubeconfigPath string) (*restclient.Config, error) {
   658  	if kubeconfigPath == "" && masterUrl == "" {
   659  		klog.Warning("Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.")
   660  		kubeconfig, err := restclient.InClusterConfig()
   661  		if err == nil {
   662  			return kubeconfig, nil
   663  		}
   664  		klog.Warning("error creating inClusterConfig, falling back to default config: ", err)
   665  	}
   666  	return NewNonInteractiveDeferredLoadingClientConfig(
   667  		&ClientConfigLoadingRules{ExplicitPath: kubeconfigPath},
   668  		&ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterUrl}}).ClientConfig()
   669  }
   670  
   671  // BuildConfigFromKubeconfigGetter is a helper function that builds configs from a master
   672  // url and a kubeconfigGetter.
   673  func BuildConfigFromKubeconfigGetter(masterUrl string, kubeconfigGetter KubeconfigGetter) (*restclient.Config, error) {
   674  	// TODO: We do not need a DeferredLoader here. Refactor code and see if we can use DirectClientConfig here.
   675  	cc := NewNonInteractiveDeferredLoadingClientConfig(
   676  		&ClientConfigGetter{kubeconfigGetter: kubeconfigGetter},
   677  		&ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterUrl}})
   678  	return cc.ClientConfig()
   679  }
   680  

View as plain text