k8s.io/kubernetes@v1.29.3/pkg/controlplane/apiserver/config.go (about)

     1  /*
     2  Copyright 2023 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 apiserver
    18  
    19  import (
    20  	"fmt"
    21  	"time"
    22  
    23  	oteltrace "go.opentelemetry.io/otel/trace"
    24  
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	"k8s.io/apimachinery/pkg/util/sets"
    27  	"k8s.io/apiserver/pkg/authorization/authorizer"
    28  	"k8s.io/apiserver/pkg/endpoints/discovery/aggregated"
    29  	openapinamer "k8s.io/apiserver/pkg/endpoints/openapi"
    30  	genericfeatures "k8s.io/apiserver/pkg/features"
    31  	"k8s.io/apiserver/pkg/reconcilers"
    32  	genericapiserver "k8s.io/apiserver/pkg/server"
    33  	"k8s.io/apiserver/pkg/server/egressselector"
    34  	"k8s.io/apiserver/pkg/server/filters"
    35  	serverstorage "k8s.io/apiserver/pkg/server/storage"
    36  	"k8s.io/apiserver/pkg/storageversion"
    37  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    38  	"k8s.io/apiserver/pkg/util/openapi"
    39  	utilpeerproxy "k8s.io/apiserver/pkg/util/peerproxy"
    40  	clientgoinformers "k8s.io/client-go/informers"
    41  	clientgoclientset "k8s.io/client-go/kubernetes"
    42  	"k8s.io/client-go/transport"
    43  	"k8s.io/component-base/version"
    44  	"k8s.io/klog/v2"
    45  	openapicommon "k8s.io/kube-openapi/pkg/common"
    46  
    47  	"k8s.io/kubernetes/pkg/api/legacyscheme"
    48  	api "k8s.io/kubernetes/pkg/apis/core"
    49  	"k8s.io/kubernetes/pkg/controlplane"
    50  	controlplaneapiserver "k8s.io/kubernetes/pkg/controlplane/apiserver/options"
    51  	"k8s.io/kubernetes/pkg/kubeapiserver"
    52  	"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
    53  	rbacrest "k8s.io/kubernetes/pkg/registry/rbac/rest"
    54  )
    55  
    56  // BuildGenericConfig takes the master server options and produces the genericapiserver.Config associated with it
    57  func BuildGenericConfig(
    58  	s controlplaneapiserver.CompletedOptions,
    59  	schemes []*runtime.Scheme,
    60  	getOpenAPIDefinitions func(ref openapicommon.ReferenceCallback) map[string]openapicommon.OpenAPIDefinition,
    61  ) (
    62  	genericConfig *genericapiserver.Config,
    63  	versionedInformers clientgoinformers.SharedInformerFactory,
    64  	storageFactory *serverstorage.DefaultStorageFactory,
    65  
    66  	lastErr error,
    67  ) {
    68  	genericConfig = genericapiserver.NewConfig(legacyscheme.Codecs)
    69  	genericConfig.MergedResourceConfig = controlplane.DefaultAPIResourceConfigSource()
    70  
    71  	if lastErr = s.GenericServerRunOptions.ApplyTo(genericConfig); lastErr != nil {
    72  		return
    73  	}
    74  
    75  	if lastErr = s.SecureServing.ApplyTo(&genericConfig.SecureServing, &genericConfig.LoopbackClientConfig); lastErr != nil {
    76  		return
    77  	}
    78  
    79  	// Use protobufs for self-communication.
    80  	// Since not every generic apiserver has to support protobufs, we
    81  	// cannot default to it in generic apiserver and need to explicitly
    82  	// set it in kube-apiserver.
    83  	genericConfig.LoopbackClientConfig.ContentConfig.ContentType = "application/vnd.kubernetes.protobuf"
    84  	// Disable compression for self-communication, since we are going to be
    85  	// on a fast local network
    86  	genericConfig.LoopbackClientConfig.DisableCompression = true
    87  
    88  	kubeClientConfig := genericConfig.LoopbackClientConfig
    89  	clientgoExternalClient, err := clientgoclientset.NewForConfig(kubeClientConfig)
    90  	if err != nil {
    91  		lastErr = fmt.Errorf("failed to create real external clientset: %v", err)
    92  		return
    93  	}
    94  	versionedInformers = clientgoinformers.NewSharedInformerFactory(clientgoExternalClient, 10*time.Minute)
    95  
    96  	if lastErr = s.Features.ApplyTo(genericConfig, clientgoExternalClient, versionedInformers); lastErr != nil {
    97  		return
    98  	}
    99  	if lastErr = s.APIEnablement.ApplyTo(genericConfig, controlplane.DefaultAPIResourceConfigSource(), legacyscheme.Scheme); lastErr != nil {
   100  		return
   101  	}
   102  	if lastErr = s.EgressSelector.ApplyTo(genericConfig); lastErr != nil {
   103  		return
   104  	}
   105  	if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) {
   106  		if lastErr = s.Traces.ApplyTo(genericConfig.EgressSelector, genericConfig); lastErr != nil {
   107  			return
   108  		}
   109  	}
   110  	// wrap the definitions to revert any changes from disabled features
   111  	getOpenAPIDefinitions = openapi.GetOpenAPIDefinitionsWithoutDisabledFeatures(getOpenAPIDefinitions)
   112  	namer := openapinamer.NewDefinitionNamer(schemes...)
   113  	genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(getOpenAPIDefinitions, namer)
   114  	genericConfig.OpenAPIConfig.Info.Title = "Kubernetes"
   115  	genericConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(getOpenAPIDefinitions, namer)
   116  	genericConfig.OpenAPIV3Config.Info.Title = "Kubernetes"
   117  
   118  	genericConfig.LongRunningFunc = filters.BasicLongRunningRequestCheck(
   119  		sets.NewString("watch", "proxy"),
   120  		sets.NewString("attach", "exec", "proxy", "log", "portforward"),
   121  	)
   122  
   123  	kubeVersion := version.Get()
   124  	genericConfig.Version = &kubeVersion
   125  
   126  	if genericConfig.EgressSelector != nil {
   127  		s.Etcd.StorageConfig.Transport.EgressLookup = genericConfig.EgressSelector.Lookup
   128  	}
   129  	if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIServerTracing) {
   130  		s.Etcd.StorageConfig.Transport.TracerProvider = genericConfig.TracerProvider
   131  	} else {
   132  		s.Etcd.StorageConfig.Transport.TracerProvider = oteltrace.NewNoopTracerProvider()
   133  	}
   134  
   135  	storageFactoryConfig := kubeapiserver.NewStorageFactoryConfig()
   136  	storageFactoryConfig.APIResourceConfig = genericConfig.MergedResourceConfig
   137  	storageFactory, lastErr = storageFactoryConfig.Complete(s.Etcd).New()
   138  	if lastErr != nil {
   139  		return
   140  	}
   141  	if lastErr = s.Etcd.ApplyWithStorageFactoryTo(storageFactory, genericConfig); lastErr != nil {
   142  		return
   143  	}
   144  
   145  	// Authentication.ApplyTo requires already applied OpenAPIConfig and EgressSelector if present
   146  	if lastErr = s.Authentication.ApplyTo(&genericConfig.Authentication, genericConfig.SecureServing, genericConfig.EgressSelector, genericConfig.OpenAPIConfig, genericConfig.OpenAPIV3Config, clientgoExternalClient, versionedInformers); lastErr != nil {
   147  		return
   148  	}
   149  
   150  	var enablesRBAC bool
   151  	genericConfig.Authorization.Authorizer, genericConfig.RuleResolver, enablesRBAC, err = BuildAuthorizer(s, genericConfig.EgressSelector, versionedInformers)
   152  	if err != nil {
   153  		lastErr = fmt.Errorf("invalid authorization config: %v", err)
   154  		return
   155  	}
   156  	if s.Authorization != nil && !enablesRBAC {
   157  		genericConfig.DisabledPostStartHooks.Insert(rbacrest.PostStartHookName)
   158  	}
   159  
   160  	lastErr = s.Audit.ApplyTo(genericConfig)
   161  	if lastErr != nil {
   162  		return
   163  	}
   164  
   165  	if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.AggregatedDiscoveryEndpoint) {
   166  		genericConfig.AggregatedDiscoveryGroupManager = aggregated.NewResourceManager("apis")
   167  	}
   168  
   169  	return
   170  }
   171  
   172  // BuildAuthorizer constructs the authorizer. If authorization is not set in s, it returns nil, nil, false, nil
   173  func BuildAuthorizer(s controlplaneapiserver.CompletedOptions, egressSelector *egressselector.EgressSelector, versionedInformers clientgoinformers.SharedInformerFactory) (authorizer.Authorizer, authorizer.RuleResolver, bool, error) {
   174  	authorizationConfig, err := s.Authorization.ToAuthorizationConfig(versionedInformers)
   175  	if err != nil {
   176  		return nil, nil, false, err
   177  	}
   178  	if authorizationConfig == nil {
   179  		return nil, nil, false, nil
   180  	}
   181  
   182  	if egressSelector != nil {
   183  		egressDialer, err := egressSelector.Lookup(egressselector.ControlPlane.AsNetworkContext())
   184  		if err != nil {
   185  			return nil, nil, false, err
   186  		}
   187  		authorizationConfig.CustomDial = egressDialer
   188  	}
   189  
   190  	enablesRBAC := false
   191  	for _, a := range authorizationConfig.AuthorizationConfiguration.Authorizers {
   192  		if string(a.Type) == modes.ModeRBAC {
   193  			enablesRBAC = true
   194  			break
   195  		}
   196  	}
   197  
   198  	authorizer, ruleResolver, err := authorizationConfig.New()
   199  
   200  	return authorizer, ruleResolver, enablesRBAC, err
   201  }
   202  
   203  // CreatePeerEndpointLeaseReconciler creates a apiserver endpoint lease reconciliation loop
   204  // The peer endpoint leases are used to find network locations of apiservers for peer proxy
   205  func CreatePeerEndpointLeaseReconciler(c genericapiserver.Config, storageFactory serverstorage.StorageFactory) (reconcilers.PeerEndpointLeaseReconciler, error) {
   206  	ttl := controlplane.DefaultEndpointReconcilerTTL
   207  	config, err := storageFactory.NewConfig(api.Resource("apiServerPeerIPInfo"))
   208  	if err != nil {
   209  		return nil, fmt.Errorf("error creating storage factory config: %w", err)
   210  	}
   211  	reconciler, err := reconcilers.NewPeerEndpointLeaseReconciler(config, "/peerserverleases/", ttl)
   212  	return reconciler, err
   213  }
   214  
   215  func BuildPeerProxy(versionedInformer clientgoinformers.SharedInformerFactory, svm storageversion.Manager,
   216  	proxyClientCertFile string, proxyClientKeyFile string, peerCAFile string, peerAdvertiseAddress reconcilers.PeerAdvertiseAddress,
   217  	apiServerID string, reconciler reconcilers.PeerEndpointLeaseReconciler, serializer runtime.NegotiatedSerializer) (utilpeerproxy.Interface, error) {
   218  	if proxyClientCertFile == "" {
   219  		return nil, fmt.Errorf("error building peer proxy handler, proxy-cert-file not specified")
   220  	}
   221  	if proxyClientKeyFile == "" {
   222  		return nil, fmt.Errorf("error building peer proxy handler, proxy-key-file not specified")
   223  	}
   224  	// create proxy client config
   225  	clientConfig := &transport.Config{
   226  		TLS: transport.TLSConfig{
   227  			Insecure:   false,
   228  			CertFile:   proxyClientCertFile,
   229  			KeyFile:    proxyClientKeyFile,
   230  			CAFile:     peerCAFile,
   231  			ServerName: "kubernetes.default.svc",
   232  		}}
   233  
   234  	// build proxy transport
   235  	proxyRoundTripper, transportBuildingError := transport.New(clientConfig)
   236  	if transportBuildingError != nil {
   237  		klog.Error(transportBuildingError.Error())
   238  		return nil, transportBuildingError
   239  	}
   240  	return utilpeerproxy.NewPeerProxyHandler(
   241  		versionedInformer,
   242  		svm,
   243  		proxyRoundTripper,
   244  		apiServerID,
   245  		reconciler,
   246  		serializer,
   247  	), nil
   248  }