k8s.io/client-go@v0.31.1/tools/clientcmd/client_config.go (about) 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 }