client_config.go 26 KB

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