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