2018-12-19 14:29:25 +00:00
/ *
Copyright 2014 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package clientcmd
import (
"fmt"
"io"
2020-12-17 12:28:29 +00:00
"net/http"
2018-12-19 14:29:25 +00:00
"net/url"
"os"
"strings"
2020-12-17 12:28:29 +00:00
"unicode"
2018-12-19 14:29:25 +00:00
restclient "k8s.io/client-go/rest"
clientauth "k8s.io/client-go/tools/auth"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
2020-12-17 12:28:29 +00:00
"k8s.io/klog/v2"
"github.com/imdario/mergo"
)
const (
// clusterExtensionKey is reserved in the cluster extensions list for exec plugin config.
clusterExtensionKey = "client.authentication.k8s.io/exec"
2018-12-19 14:29:25 +00:00
)
var (
// ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields
// DEPRECATED will be replaced
2020-07-24 14:20:51 +00:00
ClusterDefaults = clientcmdapi . Cluster { Server : getDefaultServer ( ) }
2018-12-19 14:29:25 +00:00
// DefaultClientConfig represents the legacy behavior of this package for defaulting
// DEPRECATED will be replace
DefaultClientConfig = DirectClientConfig { * clientcmdapi . NewConfig ( ) , "" , & ConfigOverrides {
ClusterDefaults : ClusterDefaults ,
} , nil , NewDefaultClientConfigLoadingRules ( ) , promptedCredentials { } }
)
2020-07-24 14:20:51 +00:00
// getDefaultServer returns a default setting for DefaultClientConfig
// DEPRECATED
func getDefaultServer ( ) string {
if server := os . Getenv ( "KUBERNETES_MASTER" ) ; len ( server ) > 0 {
return server
}
return "http://localhost:8080"
}
2018-12-19 14:29:25 +00:00
// ClientConfig is used to make it easy to get an api server client
type ClientConfig interface {
// RawConfig returns the merged result of all overrides
RawConfig ( ) ( clientcmdapi . Config , error )
// ClientConfig returns a complete client config
ClientConfig ( ) ( * restclient . Config , error )
// Namespace returns the namespace resulting from the merged
// result of all overrides and a boolean indicating if it was
// overridden
Namespace ( ) ( string , bool , error )
// ConfigAccess returns the rules for loading/persisting the config.
ConfigAccess ( ) ConfigAccess
}
2024-05-15 06:54:18 +00:00
// OverridingClientConfig is used to enable overrriding the raw KubeConfig
type OverridingClientConfig interface {
ClientConfig
// MergedRawConfig return the RawConfig merged with all overrides.
MergedRawConfig ( ) ( clientcmdapi . Config , error )
}
2018-12-19 14:29:25 +00:00
type PersistAuthProviderConfigForUser func ( user string ) restclient . AuthProviderConfigPersister
type promptedCredentials struct {
username string
2020-12-17 12:28:29 +00:00
password string ` datapolicy:"password" `
2018-12-19 14:29:25 +00:00
}
// DirectClientConfig is a ClientConfig interface that is backed by a clientcmdapi.Config, options overrides, and an optional fallbackReader for auth information
type DirectClientConfig struct {
config clientcmdapi . Config
contextName string
overrides * ConfigOverrides
fallbackReader io . Reader
configAccess ConfigAccess
// promptedCredentials store the credentials input by the user
promptedCredentials promptedCredentials
}
// NewDefaultClientConfig creates a DirectClientConfig using the config.CurrentContext as the context name
2024-05-15 06:54:18 +00:00
func NewDefaultClientConfig ( config clientcmdapi . Config , overrides * ConfigOverrides ) OverridingClientConfig {
2018-12-19 14:29:25 +00:00
return & DirectClientConfig { config , config . CurrentContext , overrides , nil , NewDefaultClientConfigLoadingRules ( ) , promptedCredentials { } }
}
// NewNonInteractiveClientConfig creates a DirectClientConfig using the passed context name and does not have a fallback reader for auth information
2024-05-15 06:54:18 +00:00
func NewNonInteractiveClientConfig ( config clientcmdapi . Config , contextName string , overrides * ConfigOverrides , configAccess ConfigAccess ) OverridingClientConfig {
2018-12-19 14:29:25 +00:00
return & DirectClientConfig { config , contextName , overrides , nil , configAccess , promptedCredentials { } }
}
// NewInteractiveClientConfig creates a DirectClientConfig using the passed context name and a reader in case auth information is not provided via files or flags
2024-05-15 06:54:18 +00:00
func NewInteractiveClientConfig ( config clientcmdapi . Config , contextName string , overrides * ConfigOverrides , fallbackReader io . Reader , configAccess ConfigAccess ) OverridingClientConfig {
2018-12-19 14:29:25 +00:00
return & DirectClientConfig { config , contextName , overrides , fallbackReader , configAccess , promptedCredentials { } }
}
2019-01-15 16:20:41 +00:00
// NewClientConfigFromBytes takes your kubeconfig and gives you back a ClientConfig
2024-05-15 06:54:18 +00:00
func NewClientConfigFromBytes ( configBytes [ ] byte ) ( OverridingClientConfig , error ) {
2019-01-15 16:20:41 +00:00
config , err := Load ( configBytes )
if err != nil {
return nil , err
}
return & DirectClientConfig { * config , "" , & ConfigOverrides { } , nil , nil , promptedCredentials { } } , nil
}
// RESTConfigFromKubeConfig is a convenience method to give back a restconfig from your kubeconfig bytes.
// For programmatic access, this is what you want 80% of the time
func RESTConfigFromKubeConfig ( configBytes [ ] byte ) ( * restclient . Config , error ) {
clientConfig , err := NewClientConfigFromBytes ( configBytes )
if err != nil {
return nil , err
}
return clientConfig . ClientConfig ( )
}
2018-12-19 14:29:25 +00:00
func ( config * DirectClientConfig ) RawConfig ( ) ( clientcmdapi . Config , error ) {
return config . config , nil
}
2024-05-15 06:54:18 +00:00
// MergedRawConfig returns the raw kube config merged with the overrides
func ( config * DirectClientConfig ) MergedRawConfig ( ) ( clientcmdapi . Config , error ) {
if err := config . ConfirmUsable ( ) ; err != nil {
return clientcmdapi . Config { } , err
}
merged := config . config . DeepCopy ( )
// set the AuthInfo merged with overrides in the merged config
mergedAuthInfo , err := config . getAuthInfo ( )
if err != nil {
return clientcmdapi . Config { } , err
}
mergedAuthInfoName , _ := config . getAuthInfoName ( )
merged . AuthInfos [ mergedAuthInfoName ] = & mergedAuthInfo
// set the Context merged with overrides in the merged config
mergedContext , err := config . getContext ( )
if err != nil {
return clientcmdapi . Config { } , err
}
mergedContextName , _ := config . getContextName ( )
merged . Contexts [ mergedContextName ] = & mergedContext
merged . CurrentContext = mergedContextName
// set the Cluster merged with overrides in the merged config
configClusterInfo , err := config . getCluster ( )
if err != nil {
return clientcmdapi . Config { } , err
}
configClusterName , _ := config . getClusterName ( )
merged . Clusters [ configClusterName ] = & configClusterInfo
return * merged , nil
}
2018-12-19 14:29:25 +00:00
// ClientConfig implements ClientConfig
func ( config * DirectClientConfig ) ClientConfig ( ) ( * restclient . Config , error ) {
// check that getAuthInfo, getContext, and getCluster do not return an error.
// Do this before checking if the current config is usable in the event that an
// AuthInfo, Context, or Cluster config with user-defined names are not found.
// This provides a user with the immediate cause for error if one is found
configAuthInfo , err := config . getAuthInfo ( )
if err != nil {
return nil , err
}
_ , err = config . getContext ( )
if err != nil {
return nil , err
}
configClusterInfo , err := config . getCluster ( )
if err != nil {
return nil , err
}
if err := config . ConfirmUsable ( ) ; err != nil {
return nil , err
}
clientConfig := & restclient . Config { }
clientConfig . Host = configClusterInfo . Server
2020-12-17 12:28:29 +00:00
if configClusterInfo . ProxyURL != "" {
u , err := parseProxyURL ( configClusterInfo . ProxyURL )
if err != nil {
return nil , err
}
clientConfig . Proxy = http . ProxyURL ( u )
}
2018-12-19 14:29:25 +00:00
2023-02-01 17:06:36 +00:00
clientConfig . DisableCompression = configClusterInfo . DisableCompression
2020-12-17 12:28:29 +00:00
if config . overrides != nil && len ( config . overrides . Timeout ) > 0 {
2018-12-19 14:29:25 +00:00
timeout , err := ParseTimeout ( config . overrides . Timeout )
if err != nil {
return nil , err
}
clientConfig . Timeout = timeout
}
if u , err := url . ParseRequestURI ( clientConfig . Host ) ; err == nil && u . Opaque == "" && len ( u . Path ) > 1 {
u . RawQuery = ""
u . Fragment = ""
clientConfig . Host = u . String ( )
}
if len ( configAuthInfo . Impersonate ) > 0 {
clientConfig . Impersonate = restclient . ImpersonationConfig {
UserName : configAuthInfo . Impersonate ,
2021-12-08 13:50:47 +00:00
UID : configAuthInfo . ImpersonateUID ,
2018-12-19 14:29:25 +00:00
Groups : configAuthInfo . ImpersonateGroups ,
Extra : configAuthInfo . ImpersonateUserExtra ,
}
}
// only try to read the auth information if we are secure
if restclient . IsConfigTransportTLS ( * clientConfig ) {
var err error
var persister restclient . AuthProviderConfigPersister
if config . configAccess != nil {
authInfoName , _ := config . getAuthInfoName ( )
persister = PersisterForUser ( config . configAccess , authInfoName )
}
2020-12-17 12:28:29 +00:00
userAuthPartialConfig , err := config . getUserIdentificationPartialConfig ( configAuthInfo , config . fallbackReader , persister , configClusterInfo )
2018-12-19 14:29:25 +00:00
if err != nil {
return nil , err
}
2021-06-25 04:59:51 +00:00
mergo . Merge ( clientConfig , userAuthPartialConfig , mergo . WithOverride )
2018-12-19 14:29:25 +00:00
serverAuthPartialConfig , err := getServerIdentificationPartialConfig ( configAuthInfo , configClusterInfo )
if err != nil {
return nil , err
}
2021-06-25 04:59:51 +00:00
mergo . Merge ( clientConfig , serverAuthPartialConfig , mergo . WithOverride )
2018-12-19 14:29:25 +00:00
}
return clientConfig , nil
}
// clientauth.Info object contain both user identification and server identification. We want different precedence orders for
// both, so we have to split the objects and merge them separately
// we want this order of precedence for the server identification
// 1. configClusterInfo (the final result of command line flags and merged .kubeconfig files)
// 2. configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority)
// 3. load the ~/.kubernetes_auth file as a default
func getServerIdentificationPartialConfig ( configAuthInfo clientcmdapi . AuthInfo , configClusterInfo clientcmdapi . Cluster ) ( * restclient . Config , error ) {
mergedConfig := & restclient . Config { }
// configClusterInfo holds the information identify the server provided by .kubeconfig
configClientConfig := & restclient . Config { }
configClientConfig . CAFile = configClusterInfo . CertificateAuthority
configClientConfig . CAData = configClusterInfo . CertificateAuthorityData
configClientConfig . Insecure = configClusterInfo . InsecureSkipTLSVerify
2020-04-14 07:04:33 +00:00
configClientConfig . ServerName = configClusterInfo . TLSServerName
2021-06-25 04:59:51 +00:00
mergo . Merge ( mergedConfig , configClientConfig , mergo . WithOverride )
2018-12-19 14:29:25 +00:00
return mergedConfig , nil
}
// clientauth.Info object contain both user identification and server identification. We want different precedence orders for
// both, so we have to split the objects and merge them separately
// we want this order of precedence for user identification
// 1. configAuthInfo minus auth-path (the final result of command line flags and merged .kubeconfig files)
// 2. configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority)
// 3. if there is not enough information to identify the user, load try the ~/.kubernetes_auth file
// 4. if there is not enough information to identify the user, prompt if possible
2020-12-17 12:28:29 +00:00
func ( config * DirectClientConfig ) getUserIdentificationPartialConfig ( configAuthInfo clientcmdapi . AuthInfo , fallbackReader io . Reader , persistAuthConfig restclient . AuthProviderConfigPersister , configClusterInfo clientcmdapi . Cluster ) ( * restclient . Config , error ) {
2018-12-19 14:29:25 +00:00
mergedConfig := & restclient . Config { }
// blindly overwrite existing values based on precedence
if len ( configAuthInfo . Token ) > 0 {
mergedConfig . BearerToken = configAuthInfo . Token
2019-06-24 09:08:09 +00:00
mergedConfig . BearerTokenFile = configAuthInfo . TokenFile
2018-12-19 14:29:25 +00:00
} else if len ( configAuthInfo . TokenFile ) > 0 {
2023-02-01 17:06:36 +00:00
tokenBytes , err := os . ReadFile ( configAuthInfo . TokenFile )
2019-01-25 10:30:04 +00:00
if err != nil {
2018-12-19 14:29:25 +00:00
return nil , err
}
2019-01-25 10:30:04 +00:00
mergedConfig . BearerToken = string ( tokenBytes )
mergedConfig . BearerTokenFile = configAuthInfo . TokenFile
2018-12-19 14:29:25 +00:00
}
if len ( configAuthInfo . Impersonate ) > 0 {
mergedConfig . Impersonate = restclient . ImpersonationConfig {
UserName : configAuthInfo . Impersonate ,
2021-12-08 13:50:47 +00:00
UID : configAuthInfo . ImpersonateUID ,
2018-12-19 14:29:25 +00:00
Groups : configAuthInfo . ImpersonateGroups ,
Extra : configAuthInfo . ImpersonateUserExtra ,
}
}
if len ( configAuthInfo . ClientCertificate ) > 0 || len ( configAuthInfo . ClientCertificateData ) > 0 {
mergedConfig . CertFile = configAuthInfo . ClientCertificate
mergedConfig . CertData = configAuthInfo . ClientCertificateData
mergedConfig . KeyFile = configAuthInfo . ClientKey
mergedConfig . KeyData = configAuthInfo . ClientKeyData
}
if len ( configAuthInfo . Username ) > 0 || len ( configAuthInfo . Password ) > 0 {
mergedConfig . Username = configAuthInfo . Username
mergedConfig . Password = configAuthInfo . Password
}
if configAuthInfo . AuthProvider != nil {
mergedConfig . AuthProvider = configAuthInfo . AuthProvider
mergedConfig . AuthConfigPersister = persistAuthConfig
}
2019-01-15 16:20:41 +00:00
if configAuthInfo . Exec != nil {
mergedConfig . ExecProvider = configAuthInfo . Exec
2020-12-17 12:28:29 +00:00
mergedConfig . ExecProvider . InstallHint = cleanANSIEscapeCodes ( mergedConfig . ExecProvider . InstallHint )
mergedConfig . ExecProvider . Config = configClusterInfo . Extensions [ clusterExtensionKey ]
2019-01-15 16:20:41 +00:00
}
2018-12-19 14:29:25 +00:00
// if there still isn't enough information to authenticate the user, try prompting
if ! canIdentifyUser ( * mergedConfig ) && ( fallbackReader != nil ) {
if len ( config . promptedCredentials . username ) > 0 && len ( config . promptedCredentials . password ) > 0 {
mergedConfig . Username = config . promptedCredentials . username
mergedConfig . Password = config . promptedCredentials . password
return mergedConfig , nil
}
prompter := NewPromptingAuthLoader ( fallbackReader )
promptedAuthInfo , err := prompter . Prompt ( )
if err != nil {
return nil , err
}
promptedConfig := makeUserIdentificationConfig ( * promptedAuthInfo )
previouslyMergedConfig := mergedConfig
mergedConfig = & restclient . Config { }
2021-06-25 04:59:51 +00:00
mergo . Merge ( mergedConfig , promptedConfig , mergo . WithOverride )
mergo . Merge ( mergedConfig , previouslyMergedConfig , mergo . WithOverride )
2018-12-19 14:29:25 +00:00
config . promptedCredentials . username = mergedConfig . Username
config . promptedCredentials . password = mergedConfig . Password
}
return mergedConfig , nil
}
// makeUserIdentificationFieldsConfig returns a client.Config capable of being merged using mergo for only user identification information
func makeUserIdentificationConfig ( info clientauth . Info ) * restclient . Config {
config := & restclient . Config { }
config . Username = info . User
config . Password = info . Password
config . CertFile = info . CertFile
config . KeyFile = info . KeyFile
config . BearerToken = info . BearerToken
return config
}
func canIdentifyUser ( config restclient . Config ) bool {
return len ( config . Username ) > 0 ||
( len ( config . CertFile ) > 0 || len ( config . CertData ) > 0 ) ||
len ( config . BearerToken ) > 0 ||
2019-01-15 16:20:41 +00:00
config . AuthProvider != nil ||
config . ExecProvider != nil
2018-12-19 14:29:25 +00:00
}
2020-12-17 12:28:29 +00:00
// cleanANSIEscapeCodes takes an arbitrary string and ensures that there are no
// ANSI escape sequences that could put the terminal in a weird state (e.g.,
// "\e[1m" bolds text)
func cleanANSIEscapeCodes ( s string ) string {
// spaceControlCharacters includes tab, new line, vertical tab, new page, and
// carriage return. These are in the unicode.Cc category, but that category also
// contains ESC (U+001B) which we don't want.
spaceControlCharacters := unicode . RangeTable {
R16 : [ ] unicode . Range16 {
{ Lo : 0x0009 , Hi : 0x000D , Stride : 1 } ,
} ,
}
// Why not make this deny-only (instead of allow-only)? Because unicode.C
// contains newline and tab characters that we want.
allowedRanges := [ ] * unicode . RangeTable {
unicode . L ,
unicode . M ,
unicode . N ,
unicode . P ,
unicode . S ,
unicode . Z ,
& spaceControlCharacters ,
}
builder := strings . Builder { }
for _ , roon := range s {
if unicode . IsOneOf ( allowedRanges , roon ) {
builder . WriteRune ( roon ) // returns nil error, per go doc
} else {
fmt . Fprintf ( & builder , "%U" , roon )
}
}
return builder . String ( )
}
2018-12-19 14:29:25 +00:00
// Namespace implements ClientConfig
func ( config * DirectClientConfig ) Namespace ( ) ( string , bool , error ) {
if config . overrides != nil && config . overrides . Context . Namespace != "" {
// In the event we have an empty config but we do have a namespace override, we should return
// the namespace override instead of having config.ConfirmUsable() return an error. This allows
// things like in-cluster clients to execute `kubectl get pods --namespace=foo` and have the
// --namespace flag honored instead of being ignored.
return config . overrides . Context . Namespace , true , nil
}
if err := config . ConfirmUsable ( ) ; err != nil {
return "" , false , err
}
configContext , err := config . getContext ( )
if err != nil {
return "" , false , err
}
if len ( configContext . Namespace ) == 0 {
2019-01-15 16:20:41 +00:00
return "default" , false , nil
2018-12-19 14:29:25 +00:00
}
return configContext . Namespace , false , nil
}
// ConfigAccess implements ClientConfig
func ( config * DirectClientConfig ) ConfigAccess ( ) ConfigAccess {
return config . configAccess
}
// ConfirmUsable looks a particular context and determines if that particular part of the config is useable. There might still be errors in the config,
// but no errors in the sections requested or referenced. It does not return early so that it can find as many errors as possible.
func ( config * DirectClientConfig ) ConfirmUsable ( ) error {
validationErrors := make ( [ ] error , 0 )
var contextName string
if len ( config . contextName ) != 0 {
contextName = config . contextName
} else {
contextName = config . config . CurrentContext
}
if len ( contextName ) > 0 {
_ , exists := config . config . Contexts [ contextName ]
if ! exists {
validationErrors = append ( validationErrors , & errContextNotFound { contextName } )
}
}
authInfoName , _ := config . getAuthInfoName ( )
authInfo , _ := config . getAuthInfo ( )
validationErrors = append ( validationErrors , validateAuthInfo ( authInfoName , authInfo ) ... )
clusterName , _ := config . getClusterName ( )
cluster , _ := config . getCluster ( )
validationErrors = append ( validationErrors , validateClusterInfo ( clusterName , cluster ) ... )
// when direct client config is specified, and our only error is that no server is defined, we should
// return a standard "no config" error
if len ( validationErrors ) == 1 && validationErrors [ 0 ] == ErrEmptyCluster {
return newErrConfigurationInvalid ( [ ] error { ErrEmptyConfig } )
}
return newErrConfigurationInvalid ( validationErrors )
}
// getContextName returns the default, or user-set context name, and a boolean that indicates
// whether the default context name has been overwritten by a user-set flag, or left as its default value
func ( config * DirectClientConfig ) getContextName ( ) ( string , bool ) {
2020-12-17 12:28:29 +00:00
if config . overrides != nil && len ( config . overrides . CurrentContext ) != 0 {
2018-12-19 14:29:25 +00:00
return config . overrides . CurrentContext , true
}
if len ( config . contextName ) != 0 {
return config . contextName , false
}
return config . config . CurrentContext , false
}
// getAuthInfoName returns a string containing the current authinfo name for the current context,
// and a boolean indicating whether the default authInfo name is overwritten by a user-set flag, or
// left as its default value
func ( config * DirectClientConfig ) getAuthInfoName ( ) ( string , bool ) {
2020-12-17 12:28:29 +00:00
if config . overrides != nil && len ( config . overrides . Context . AuthInfo ) != 0 {
2018-12-19 14:29:25 +00:00
return config . overrides . Context . AuthInfo , true
}
context , _ := config . getContext ( )
return context . AuthInfo , false
}
// getClusterName returns a string containing the default, or user-set cluster name, and a boolean
// indicating whether the default clusterName has been overwritten by a user-set flag, or left as
// its default value
func ( config * DirectClientConfig ) getClusterName ( ) ( string , bool ) {
2020-12-17 12:28:29 +00:00
if config . overrides != nil && len ( config . overrides . Context . Cluster ) != 0 {
2018-12-19 14:29:25 +00:00
return config . overrides . Context . Cluster , true
}
context , _ := config . getContext ( )
return context . Cluster , false
}
// getContext returns the clientcmdapi.Context, or an error if a required context is not found.
func ( config * DirectClientConfig ) getContext ( ) ( clientcmdapi . Context , error ) {
contexts := config . config . Contexts
contextName , required := config . getContextName ( )
mergedContext := clientcmdapi . NewContext ( )
if configContext , exists := contexts [ contextName ] ; exists {
2021-06-25 04:59:51 +00:00
mergo . Merge ( mergedContext , configContext , mergo . WithOverride )
2018-12-19 14:29:25 +00:00
} else if required {
return clientcmdapi . Context { } , fmt . Errorf ( "context %q does not exist" , contextName )
}
2020-12-17 12:28:29 +00:00
if config . overrides != nil {
2021-06-25 04:59:51 +00:00
mergo . Merge ( mergedContext , config . overrides . Context , mergo . WithOverride )
2020-12-17 12:28:29 +00:00
}
2018-12-19 14:29:25 +00:00
return * mergedContext , nil
}
// getAuthInfo returns the clientcmdapi.AuthInfo, or an error if a required auth info is not found.
func ( config * DirectClientConfig ) getAuthInfo ( ) ( clientcmdapi . AuthInfo , error ) {
authInfos := config . config . AuthInfos
authInfoName , required := config . getAuthInfoName ( )
mergedAuthInfo := clientcmdapi . NewAuthInfo ( )
if configAuthInfo , exists := authInfos [ authInfoName ] ; exists {
2021-06-25 04:59:51 +00:00
mergo . Merge ( mergedAuthInfo , configAuthInfo , mergo . WithOverride )
2018-12-19 14:29:25 +00:00
} else if required {
return clientcmdapi . AuthInfo { } , fmt . Errorf ( "auth info %q does not exist" , authInfoName )
}
2020-12-17 12:28:29 +00:00
if config . overrides != nil {
2021-06-25 04:59:51 +00:00
mergo . Merge ( mergedAuthInfo , config . overrides . AuthInfo , mergo . WithOverride )
2020-12-17 12:28:29 +00:00
}
2018-12-19 14:29:25 +00:00
return * mergedAuthInfo , nil
}
// getCluster returns the clientcmdapi.Cluster, or an error if a required cluster is not found.
func ( config * DirectClientConfig ) getCluster ( ) ( clientcmdapi . Cluster , error ) {
clusterInfos := config . config . Clusters
clusterInfoName , required := config . getClusterName ( )
mergedClusterInfo := clientcmdapi . NewCluster ( )
2020-12-17 12:28:29 +00:00
if config . overrides != nil {
2021-06-25 04:59:51 +00:00
mergo . Merge ( mergedClusterInfo , config . overrides . ClusterDefaults , mergo . WithOverride )
2020-12-17 12:28:29 +00:00
}
2018-12-19 14:29:25 +00:00
if configClusterInfo , exists := clusterInfos [ clusterInfoName ] ; exists {
2021-06-25 04:59:51 +00:00
mergo . Merge ( mergedClusterInfo , configClusterInfo , mergo . WithOverride )
2018-12-19 14:29:25 +00:00
} else if required {
return clientcmdapi . Cluster { } , fmt . Errorf ( "cluster %q does not exist" , clusterInfoName )
}
2020-12-17 12:28:29 +00:00
if config . overrides != nil {
2021-06-25 04:59:51 +00:00
mergo . Merge ( mergedClusterInfo , config . overrides . ClusterInfo , mergo . WithOverride )
2020-12-17 12:28:29 +00:00
}
2020-01-14 10:38:55 +00:00
// * An override of --insecure-skip-tls-verify=true and no accompanying CA/CA data should clear already-set CA/CA data
// otherwise, a kubeconfig containing a CA reference would return an error that "CA and insecure-skip-tls-verify couldn't both be set".
// * An override of --certificate-authority should also override TLS skip settings and CA data, otherwise existing CA data will take precedence.
2020-12-17 12:28:29 +00:00
if config . overrides != nil {
caLen := len ( config . overrides . ClusterInfo . CertificateAuthority )
caDataLen := len ( config . overrides . ClusterInfo . CertificateAuthorityData )
if config . overrides . ClusterInfo . InsecureSkipTLSVerify || caLen > 0 || caDataLen > 0 {
mergedClusterInfo . InsecureSkipTLSVerify = config . overrides . ClusterInfo . InsecureSkipTLSVerify
mergedClusterInfo . CertificateAuthority = config . overrides . ClusterInfo . CertificateAuthority
mergedClusterInfo . CertificateAuthorityData = config . overrides . ClusterInfo . CertificateAuthorityData
}
// if the --tls-server-name has been set in overrides, use that value.
// 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
// that setting a --server will effectively clear the KUBECONFIG value of tls-server-name if it is specified on the command line which is
// usually correct.
if config . overrides . ClusterInfo . TLSServerName != "" || config . overrides . ClusterInfo . Server != "" {
mergedClusterInfo . TLSServerName = config . overrides . ClusterInfo . TLSServerName
}
2020-04-14 07:04:33 +00:00
}
2018-12-19 14:29:25 +00:00
return * mergedClusterInfo , nil
}
// inClusterClientConfig makes a config that will work from within a kubernetes cluster container environment.
// Can take options overrides for flags explicitly provided to the command inside the cluster container.
type inClusterClientConfig struct {
overrides * ConfigOverrides
inClusterConfigProvider func ( ) ( * restclient . Config , error )
}
var _ ClientConfig = & inClusterClientConfig { }
func ( config * inClusterClientConfig ) RawConfig ( ) ( clientcmdapi . Config , error ) {
return clientcmdapi . Config { } , fmt . Errorf ( "inCluster environment config doesn't support multiple clusters" )
}
func ( config * inClusterClientConfig ) ClientConfig ( ) ( * restclient . Config , error ) {
2021-06-25 04:59:51 +00:00
inClusterConfigProvider := config . inClusterConfigProvider
if inClusterConfigProvider == nil {
inClusterConfigProvider = restclient . InClusterConfig
2018-12-19 14:29:25 +00:00
}
2021-06-25 04:59:51 +00:00
icc , err := inClusterConfigProvider ( )
2018-12-19 14:29:25 +00:00
if err != nil {
return nil , err
}
// in-cluster configs only takes a host, token, or CA file
// if any of them were individually provided, overwrite anything else
if config . overrides != nil {
if server := config . overrides . ClusterInfo . Server ; len ( server ) > 0 {
icc . Host = server
}
2019-06-24 09:08:09 +00:00
if len ( config . overrides . AuthInfo . Token ) > 0 || len ( config . overrides . AuthInfo . TokenFile ) > 0 {
icc . BearerToken = config . overrides . AuthInfo . Token
icc . BearerTokenFile = config . overrides . AuthInfo . TokenFile
2018-12-19 14:29:25 +00:00
}
if certificateAuthorityFile := config . overrides . ClusterInfo . CertificateAuthority ; len ( certificateAuthorityFile ) > 0 {
icc . TLSClientConfig . CAFile = certificateAuthorityFile
}
}
2021-06-25 04:59:51 +00:00
return icc , nil
2018-12-19 14:29:25 +00:00
}
func ( config * inClusterClientConfig ) Namespace ( ) ( string , bool , error ) {
// This way assumes you've set the POD_NAMESPACE environment variable using the downward API.
// This check has to be done first for backwards compatibility with the way InClusterConfig was originally set up
if ns := os . Getenv ( "POD_NAMESPACE" ) ; ns != "" {
return ns , false , nil
}
// Fall back to the namespace associated with the service account token, if available
2023-02-01 17:06:36 +00:00
if data , err := os . ReadFile ( "/var/run/secrets/kubernetes.io/serviceaccount/namespace" ) ; err == nil {
2018-12-19 14:29:25 +00:00
if ns := strings . TrimSpace ( string ( data ) ) ; len ( ns ) > 0 {
return ns , false , nil
}
}
return "default" , false , nil
}
func ( config * inClusterClientConfig ) ConfigAccess ( ) ConfigAccess {
return NewDefaultClientConfigLoadingRules ( )
}
// Possible returns true if loading an inside-kubernetes-cluster is possible.
func ( config * inClusterClientConfig ) Possible ( ) bool {
fi , err := os . Stat ( "/var/run/secrets/kubernetes.io/serviceaccount/token" )
return os . Getenv ( "KUBERNETES_SERVICE_HOST" ) != "" &&
os . Getenv ( "KUBERNETES_SERVICE_PORT" ) != "" &&
err == nil && ! fi . IsDir ( )
}
// BuildConfigFromFlags is a helper function that builds configs from a master
// url or a kubeconfig filepath. These are passed in as command line flags for cluster
// components. Warnings should reflect this usage. If neither masterUrl or kubeconfigPath
// are passed in we fallback to inClusterConfig. If inClusterConfig fails, we fallback
// to the default config.
func BuildConfigFromFlags ( masterUrl , kubeconfigPath string ) ( * restclient . Config , error ) {
if kubeconfigPath == "" && masterUrl == "" {
2021-06-25 04:59:51 +00:00
klog . Warning ( "Neither --kubeconfig nor --master was specified. Using the inClusterConfig. This might not work." )
2018-12-19 14:29:25 +00:00
kubeconfig , err := restclient . InClusterConfig ( )
if err == nil {
return kubeconfig , nil
}
2019-01-15 16:20:41 +00:00
klog . Warning ( "error creating inClusterConfig, falling back to default config: " , err )
2018-12-19 14:29:25 +00:00
}
return NewNonInteractiveDeferredLoadingClientConfig (
& ClientConfigLoadingRules { ExplicitPath : kubeconfigPath } ,
& ConfigOverrides { ClusterInfo : clientcmdapi . Cluster { Server : masterUrl } } ) . ClientConfig ( )
}
// BuildConfigFromKubeconfigGetter is a helper function that builds configs from a master
// url and a kubeconfigGetter.
func BuildConfigFromKubeconfigGetter ( masterUrl string , kubeconfigGetter KubeconfigGetter ) ( * restclient . Config , error ) {
// TODO: We do not need a DeferredLoader here. Refactor code and see if we can use DirectClientConfig here.
cc := NewNonInteractiveDeferredLoadingClientConfig (
& ClientConfigGetter { kubeconfigGetter : kubeconfigGetter } ,
& ConfigOverrides { ClusterInfo : clientcmdapi . Cluster { Server : masterUrl } } )
return cc . ClientConfig ( )
}