...

Source file src/k8s.io/client-go/tools/clientcmd/api/helpers.go

Documentation: k8s.io/client-go/tools/clientcmd/api

     1  /*
     2  Copyright 2015 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 api
    18  
    19  import (
    20  	"encoding/base64"
    21  	"errors"
    22  	"fmt"
    23  	"os"
    24  	"path"
    25  	"path/filepath"
    26  	"reflect"
    27  	"strings"
    28  )
    29  
    30  func init() {
    31  	sDec, _ := base64.StdEncoding.DecodeString("REDACTED+")
    32  	redactedBytes = []byte(string(sDec))
    33  	sDec, _ = base64.StdEncoding.DecodeString("DATA+OMITTED")
    34  	dataOmittedBytes = []byte(string(sDec))
    35  }
    36  
    37  // IsConfigEmpty returns true if the config is empty.
    38  func IsConfigEmpty(config *Config) bool {
    39  	return len(config.AuthInfos) == 0 && len(config.Clusters) == 0 && len(config.Contexts) == 0 &&
    40  		len(config.CurrentContext) == 0 &&
    41  		len(config.Preferences.Extensions) == 0 && !config.Preferences.Colors &&
    42  		len(config.Extensions) == 0
    43  }
    44  
    45  // MinifyConfig read the current context and uses that to keep only the relevant pieces of config
    46  // This is useful for making secrets based on kubeconfig files
    47  func MinifyConfig(config *Config) error {
    48  	if len(config.CurrentContext) == 0 {
    49  		return errors.New("current-context must exist in order to minify")
    50  	}
    51  
    52  	currContext, exists := config.Contexts[config.CurrentContext]
    53  	if !exists {
    54  		return fmt.Errorf("cannot locate context %v", config.CurrentContext)
    55  	}
    56  
    57  	newContexts := map[string]*Context{}
    58  	newContexts[config.CurrentContext] = currContext
    59  
    60  	newClusters := map[string]*Cluster{}
    61  	if len(currContext.Cluster) > 0 {
    62  		if _, exists := config.Clusters[currContext.Cluster]; !exists {
    63  			return fmt.Errorf("cannot locate cluster %v", currContext.Cluster)
    64  		}
    65  
    66  		newClusters[currContext.Cluster] = config.Clusters[currContext.Cluster]
    67  	}
    68  
    69  	newAuthInfos := map[string]*AuthInfo{}
    70  	if len(currContext.AuthInfo) > 0 {
    71  		if _, exists := config.AuthInfos[currContext.AuthInfo]; !exists {
    72  			return fmt.Errorf("cannot locate user %v", currContext.AuthInfo)
    73  		}
    74  
    75  		newAuthInfos[currContext.AuthInfo] = config.AuthInfos[currContext.AuthInfo]
    76  	}
    77  
    78  	config.AuthInfos = newAuthInfos
    79  	config.Clusters = newClusters
    80  	config.Contexts = newContexts
    81  
    82  	return nil
    83  }
    84  
    85  var (
    86  	dataOmittedBytes []byte
    87  	redactedBytes    []byte
    88  )
    89  
    90  // ShortenConfig redacts raw data entries from the config object for a human-readable view.
    91  func ShortenConfig(config *Config) {
    92  	// trick json encoder into printing a human-readable string in the raw data
    93  	// by base64 decoding what we want to print. Relies on implementation of
    94  	// http://golang.org/pkg/encoding/json/#Marshal using base64 to encode []byte
    95  	for key, authInfo := range config.AuthInfos {
    96  		if len(authInfo.ClientKeyData) > 0 {
    97  			authInfo.ClientKeyData = dataOmittedBytes
    98  		}
    99  		if len(authInfo.ClientCertificateData) > 0 {
   100  			authInfo.ClientCertificateData = dataOmittedBytes
   101  		}
   102  		if len(authInfo.Token) > 0 {
   103  			authInfo.Token = "REDACTED"
   104  		}
   105  		config.AuthInfos[key] = authInfo
   106  	}
   107  	for key, cluster := range config.Clusters {
   108  		if len(cluster.CertificateAuthorityData) > 0 {
   109  			cluster.CertificateAuthorityData = dataOmittedBytes
   110  		}
   111  		config.Clusters[key] = cluster
   112  	}
   113  }
   114  
   115  // FlattenConfig changes the config object into a self-contained config (useful for making secrets)
   116  func FlattenConfig(config *Config) error {
   117  	for key, authInfo := range config.AuthInfos {
   118  		baseDir, err := MakeAbs(path.Dir(authInfo.LocationOfOrigin), "")
   119  		if err != nil {
   120  			return err
   121  		}
   122  
   123  		if err := FlattenContent(&authInfo.ClientCertificate, &authInfo.ClientCertificateData, baseDir); err != nil {
   124  			return err
   125  		}
   126  		if err := FlattenContent(&authInfo.ClientKey, &authInfo.ClientKeyData, baseDir); err != nil {
   127  			return err
   128  		}
   129  
   130  		config.AuthInfos[key] = authInfo
   131  	}
   132  	for key, cluster := range config.Clusters {
   133  		baseDir, err := MakeAbs(path.Dir(cluster.LocationOfOrigin), "")
   134  		if err != nil {
   135  			return err
   136  		}
   137  
   138  		if err := FlattenContent(&cluster.CertificateAuthority, &cluster.CertificateAuthorityData, baseDir); err != nil {
   139  			return err
   140  		}
   141  
   142  		config.Clusters[key] = cluster
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  func FlattenContent(path *string, contents *[]byte, baseDir string) error {
   149  	if len(*path) != 0 {
   150  		if len(*contents) > 0 {
   151  			return errors.New("cannot have values for both path and contents")
   152  		}
   153  
   154  		var err error
   155  		absPath := ResolvePath(*path, baseDir)
   156  		*contents, err = os.ReadFile(absPath)
   157  		if err != nil {
   158  			return err
   159  		}
   160  
   161  		*path = ""
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  // ResolvePath returns the path as an absolute paths, relative to the given base directory
   168  func ResolvePath(path string, base string) string {
   169  	// Don't resolve empty paths
   170  	if len(path) > 0 {
   171  		// Don't resolve absolute paths
   172  		if !filepath.IsAbs(path) {
   173  			return filepath.Join(base, path)
   174  		}
   175  	}
   176  
   177  	return path
   178  }
   179  
   180  func MakeAbs(path, base string) (string, error) {
   181  	if filepath.IsAbs(path) {
   182  		return path, nil
   183  	}
   184  	if len(base) == 0 {
   185  		cwd, err := os.Getwd()
   186  		if err != nil {
   187  			return "", err
   188  		}
   189  		base = cwd
   190  	}
   191  	return filepath.Join(base, path), nil
   192  }
   193  
   194  // RedactSecrets replaces any sensitive values with REDACTED
   195  func RedactSecrets(config *Config) error {
   196  	return redactSecrets(reflect.ValueOf(config), false)
   197  }
   198  
   199  func redactSecrets(curr reflect.Value, redact bool) error {
   200  	redactedBytes = []byte("REDACTED")
   201  	if !curr.IsValid() {
   202  		return nil
   203  	}
   204  
   205  	actualCurrValue := curr
   206  	if curr.Kind() == reflect.Ptr {
   207  		actualCurrValue = curr.Elem()
   208  	}
   209  
   210  	switch actualCurrValue.Kind() {
   211  	case reflect.Map:
   212  		for _, v := range actualCurrValue.MapKeys() {
   213  			err := redactSecrets(actualCurrValue.MapIndex(v), false)
   214  			if err != nil {
   215  				return err
   216  			}
   217  		}
   218  		return nil
   219  
   220  	case reflect.String:
   221  		if redact {
   222  			if !actualCurrValue.IsZero() {
   223  				actualCurrValue.SetString("REDACTED")
   224  			}
   225  		}
   226  		return nil
   227  
   228  	case reflect.Slice:
   229  		if actualCurrValue.Type() == reflect.TypeOf([]byte{}) && redact {
   230  			if !actualCurrValue.IsNil() {
   231  				actualCurrValue.SetBytes(redactedBytes)
   232  			}
   233  			return nil
   234  		}
   235  		for i := 0; i < actualCurrValue.Len(); i++ {
   236  			err := redactSecrets(actualCurrValue.Index(i), false)
   237  			if err != nil {
   238  				return err
   239  			}
   240  		}
   241  		return nil
   242  
   243  	case reflect.Struct:
   244  		for fieldIndex := 0; fieldIndex < actualCurrValue.NumField(); fieldIndex++ {
   245  			currFieldValue := actualCurrValue.Field(fieldIndex)
   246  			currFieldType := actualCurrValue.Type().Field(fieldIndex)
   247  			currYamlTag := currFieldType.Tag.Get("datapolicy")
   248  			currFieldTypeYamlName := strings.Split(currYamlTag, ",")[0]
   249  			if currFieldTypeYamlName != "" {
   250  				err := redactSecrets(currFieldValue, true)
   251  				if err != nil {
   252  					return err
   253  				}
   254  			} else {
   255  				err := redactSecrets(currFieldValue, false)
   256  				if err != nil {
   257  					return err
   258  				}
   259  			}
   260  		}
   261  		return nil
   262  
   263  	default:
   264  		return nil
   265  	}
   266  }
   267  

View as plain text