client_config.go 25 KB

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