...

Source file src/k8s.io/kubernetes/cmd/kubeadm/app/discovery/file/file.go

Documentation: k8s.io/kubernetes/cmd/kubeadm/app/discovery/file

     1  /*
     2  Copyright 2016 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 file
    18  
    19  import (
    20  	"context"
    21  	"time"
    22  
    23  	"github.com/pkg/errors"
    24  
    25  	v1 "k8s.io/api/core/v1"
    26  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/util/wait"
    29  	"k8s.io/client-go/tools/clientcmd"
    30  	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
    31  	bootstrapapi "k8s.io/cluster-bootstrap/token/api"
    32  	"k8s.io/klog/v2"
    33  
    34  	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
    35  	kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
    36  )
    37  
    38  // RetrieveValidatedConfigInfo connects to the API Server and makes sure it can talk
    39  // securely to the API Server using the provided CA cert and
    40  // optionally refreshes the cluster-info information from the cluster-info ConfigMap
    41  func RetrieveValidatedConfigInfo(filepath string, discoveryTimeout time.Duration) (*clientcmdapi.Config, error) {
    42  	config, err := clientcmd.LoadFromFile(filepath)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	return ValidateConfigInfo(config, discoveryTimeout)
    47  }
    48  
    49  // ValidateConfigInfo connects to the API Server and makes sure it can talk
    50  // securely to the API Server using the provided CA cert/client certificates  and
    51  // optionally refreshes the cluster-info information from the cluster-info ConfigMap
    52  func ValidateConfigInfo(config *clientcmdapi.Config, discoveryTimeout time.Duration) (*clientcmdapi.Config, error) {
    53  	if len(config.Clusters) < 1 {
    54  		return nil, errors.New("the provided kubeconfig file must have at least one Cluster defined")
    55  	}
    56  	currentClusterName, currentCluster := kubeconfigutil.GetClusterFromKubeConfig(config)
    57  	if currentCluster == nil {
    58  		return nil, errors.New("the provided kubeconfig file must have a unnamed Cluster or a CurrentContext that specifies a non-nil Cluster")
    59  	}
    60  	if err := clientcmd.Validate(*config); err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	// If the kubeconfig points to a file for the CA, make sure the CA file contents are embedded
    65  	if err := kubeconfigutil.EnsureCertificateAuthorityIsEmbedded(currentCluster); err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	// If the discovery file config contains authentication credentials
    70  	if kubeconfigutil.HasAuthenticationCredentials(config) {
    71  		klog.V(1).Info("[discovery] Using authentication credentials from the discovery file for validating TLS connection")
    72  
    73  		// We should ensure that all the authentication info is embedded in config file, so everything will work also when
    74  		// the kubeconfig file will be stored in /etc/kubernetes/boostrap-kubelet.conf
    75  		if err := kubeconfigutil.EnsureAuthenticationInfoAreEmbedded(config); err != nil {
    76  			return nil, err
    77  		}
    78  	} else {
    79  		// If the discovery file config does not contains authentication credentials
    80  		klog.V(1).Info("[discovery] Discovery file does not contains authentication credentials, using unauthenticated request for validating TLS connection")
    81  	}
    82  
    83  	// Try to read the cluster-info config map; this step was required by the original design in order
    84  	// to validate the TLS connection to the server early in the process
    85  	client, err := kubeconfigutil.ToClientSet(config)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	klog.V(1).Infof("[discovery] Created cluster-info discovery client, requesting info from %q\n", currentCluster.Server)
    91  
    92  	var clusterinfoCM *v1.ConfigMap
    93  
    94  	var lastError error
    95  	err = wait.PollUntilContextTimeout(context.Background(),
    96  		constants.DiscoveryRetryInterval, discoveryTimeout,
    97  		true, func(_ context.Context) (bool, error) {
    98  			clusterinfoCM, lastError = client.CoreV1().ConfigMaps(metav1.NamespacePublic).Get(context.TODO(), bootstrapapi.ConfigMapClusterInfo, metav1.GetOptions{})
    99  			if lastError != nil {
   100  				if apierrors.IsForbidden(lastError) {
   101  					// If the request is unauthorized, the cluster admin has not granted access to the cluster info configmap for unauthenticated users
   102  					// In that case, trust the cluster admin and do not refresh the cluster-info data
   103  					klog.Warningf("[discovery] Could not access the %s ConfigMap for refreshing the cluster-info information, but the TLS cert is valid so proceeding...\n", bootstrapapi.ConfigMapClusterInfo)
   104  					return true, nil
   105  				}
   106  				klog.V(1).Infof("[discovery] Error reading the %s ConfigMap, will try again: %v\n", bootstrapapi.ConfigMapClusterInfo, err)
   107  				return false, nil
   108  			}
   109  			return true, nil
   110  		})
   111  	if err != nil {
   112  		return nil, errors.Wrapf(lastError, "Abort reading the %s ConfigMap after timeout of %v",
   113  			bootstrapapi.ConfigMapClusterInfo, discoveryTimeout)
   114  	}
   115  
   116  	// If we couldn't fetch the cluster-info ConfigMap, just return the cluster-info object the user provided
   117  	if clusterinfoCM == nil {
   118  		return config, nil
   119  	}
   120  
   121  	// We somehow got hold of the ConfigMap, try to read some data from it. If we can't, fallback on the user-provided file
   122  	refreshedBaseKubeConfig, err := tryParseClusterInfoFromConfigMap(clusterinfoCM)
   123  	if err != nil {
   124  		klog.V(1).Infof("[discovery] The %s ConfigMap isn't set up properly (%v), but the TLS cert is valid so proceeding...\n", bootstrapapi.ConfigMapClusterInfo, err)
   125  		return config, nil
   126  	}
   127  
   128  	_, refreshedCluster := kubeconfigutil.GetClusterFromKubeConfig(refreshedBaseKubeConfig)
   129  	if currentCluster.Server != refreshedCluster.Server {
   130  		klog.Warningf("[discovery] the API Server endpoint %q in use is different from the endpoint %q which defined in the %s ConfigMap", currentCluster.Server, refreshedCluster.Server, bootstrapapi.ConfigMapClusterInfo)
   131  	}
   132  
   133  	if len(currentCluster.CertificateAuthorityData) == 0 && len(refreshedCluster.CertificateAuthorityData) > 0 {
   134  		config.Clusters[currentClusterName].CertificateAuthorityData = refreshedCluster.CertificateAuthorityData
   135  		klog.V(1).Infof("[discovery] Synced CertificateAuthorityData from the %s ConfigMap", bootstrapapi.ConfigMapClusterInfo)
   136  	}
   137  
   138  	return config, nil
   139  }
   140  
   141  // tryParseClusterInfoFromConfigMap tries to parse a kubeconfig file from a ConfigMap key
   142  func tryParseClusterInfoFromConfigMap(cm *v1.ConfigMap) (*clientcmdapi.Config, error) {
   143  	kubeConfigString, ok := cm.Data[bootstrapapi.KubeConfigKey]
   144  	if !ok || len(kubeConfigString) == 0 {
   145  		return nil, errors.Errorf("no %s key in ConfigMap", bootstrapapi.KubeConfigKey)
   146  	}
   147  	parsedKubeConfig, err := clientcmd.Load([]byte(kubeConfigString))
   148  	if err != nil {
   149  		return nil, errors.Wrapf(err, "couldn't parse the kubeconfig file in the %s ConfigMap", bootstrapapi.ConfigMapClusterInfo)
   150  	}
   151  	return parsedKubeConfig, nil
   152  }
   153  

View as plain text