1
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
38 clusterExtensionKey = "client.authentication.k8s.io/exec"
39 )
40
41 var (
42
43
44 ClusterDefaults = clientcmdapi.Cluster{Server: getDefaultServer()}
45
46
47 DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{
48 ClusterDefaults: ClusterDefaults,
49 }, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}}
50 )
51
52
53
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
62 type ClientConfig interface {
63
64 RawConfig() (clientcmdapi.Config, error)
65
66 ClientConfig() (*restclient.Config, error)
67
68
69
70 Namespace() (string, bool, error)
71
72 ConfigAccess() ConfigAccess
73 }
74
75
76 type OverridingClientConfig interface {
77 ClientConfig
78
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
90 type DirectClientConfig struct {
91 config clientcmdapi.Config
92 contextName string
93 overrides *ConfigOverrides
94 fallbackReader io.Reader
95 configAccess ConfigAccess
96
97 promptedCredentials promptedCredentials
98 }
99
100
101 func NewDefaultClientConfig(config clientcmdapi.Config, overrides *ConfigOverrides) OverridingClientConfig {
102 return &DirectClientConfig{config, config.CurrentContext, overrides, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}}
103 }
104
105
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
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
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
126
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
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
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
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
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
174 func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) {
175
176
177
178
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
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
257
258
259
260
261
262 func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, configClusterInfo clientcmdapi.Cluster) (*restclient.Config, error) {
263 mergedConfig := &restclient.Config{}
264
265
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
277
278
279
280
281
282
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
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
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
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
370
371
372 func cleanANSIEscapeCodes(s string) string {
373
374
375
376 spaceControlCharacters := unicode.RangeTable{
377 R16: []unicode.Range16{
378 {Lo: 0x0009, Hi: 0x000D, Stride: 1},
379 },
380 }
381
382
383
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)
397 } else {
398 fmt.Fprintf(&builder, "%U", roon)
399 }
400 }
401 return builder.String()
402 }
403
404
405 func (config *DirectClientConfig) Namespace() (string, bool, error) {
406 if config.overrides != nil && config.overrides.Context.Namespace != "" {
407
408
409
410
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
431 func (config *DirectClientConfig) ConfigAccess() ConfigAccess {
432 return config.configAccess
433 }
434
435
436
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
461
462 if len(validationErrors) == 1 && validationErrors[0] == ErrEmptyCluster {
463 return newErrConfigurationInvalid([]error{ErrEmptyConfig})
464 }
465 return newErrConfigurationInvalid(validationErrors)
466 }
467
468
469
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
482
483
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
493
494
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
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
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
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
558
559
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
570
571
572
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
582
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
606
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
625
626 if ns := os.Getenv("POD_NAMESPACE"); ns != "" {
627 return ns, false, nil
628 }
629
630
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
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
653
654
655
656
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
672
673 func BuildConfigFromKubeconfigGetter(masterUrl string, kubeconfigGetter KubeconfigGetter) (*restclient.Config, error) {
674
675 cc := NewNonInteractiveDeferredLoadingClientConfig(
676 &ClientConfigGetter{kubeconfigGetter: kubeconfigGetter},
677 &ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterUrl}})
678 return cc.ClientConfig()
679 }
680
View as plain text