k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/controlplane/apiserver/options/options.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 options contains flags and options for initializing an apiserver
    18  package options
    19  
    20  import (
    21  	"fmt"
    22  	"net"
    23  	"os"
    24  	"strings"
    25  	"time"
    26  
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	peerreconcilers "k8s.io/apiserver/pkg/reconcilers"
    29  	genericoptions "k8s.io/apiserver/pkg/server/options"
    30  	"k8s.io/apiserver/pkg/storage/storagebackend"
    31  	"k8s.io/client-go/util/keyutil"
    32  	cliflag "k8s.io/component-base/cli/flag"
    33  	"k8s.io/component-base/logs"
    34  	logsapi "k8s.io/component-base/logs/api/v1"
    35  	"k8s.io/component-base/metrics"
    36  	"k8s.io/klog/v2"
    37  	netutil "k8s.io/utils/net"
    38  
    39  	_ "k8s.io/kubernetes/pkg/features"
    40  	kubeauthenticator "k8s.io/kubernetes/pkg/kubeapiserver/authenticator"
    41  	kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
    42  	"k8s.io/kubernetes/pkg/serviceaccount"
    43  )
    44  
    45  // Options define the flags and validation for a generic controlplane. If the
    46  // structs are nil, the options are not added to the command line and not validated.
    47  type Options struct {
    48  	GenericServerRunOptions *genericoptions.ServerRunOptions
    49  	Etcd                    *genericoptions.EtcdOptions
    50  	SecureServing           *genericoptions.SecureServingOptionsWithLoopback
    51  	Audit                   *genericoptions.AuditOptions
    52  	Features                *genericoptions.FeatureOptions
    53  	Admission               *kubeoptions.AdmissionOptions
    54  	Authentication          *kubeoptions.BuiltInAuthenticationOptions
    55  	Authorization           *kubeoptions.BuiltInAuthorizationOptions
    56  	APIEnablement           *genericoptions.APIEnablementOptions
    57  	EgressSelector          *genericoptions.EgressSelectorOptions
    58  	Metrics                 *metrics.Options
    59  	Logs                    *logs.Options
    60  	Traces                  *genericoptions.TracingOptions
    61  
    62  	EnableLogsHandler        bool
    63  	EventTTL                 time.Duration
    64  	MaxConnectionBytesPerSec int64
    65  
    66  	ProxyClientCertFile string
    67  	ProxyClientKeyFile  string
    68  
    69  	// PeerCAFile is the ca bundle used by this kube-apiserver to verify peer apiservers'
    70  	// serving certs when routing a request to the peer in the case the request can not be served
    71  	// locally due to version skew.
    72  	PeerCAFile string
    73  
    74  	// PeerAdvertiseAddress is the IP for this kube-apiserver which is used by peer apiservers to route a request
    75  	// to this apiserver. This happens in cases where the peer is not able to serve the request due to
    76  	// version skew.
    77  	PeerAdvertiseAddress peerreconcilers.PeerAdvertiseAddress
    78  
    79  	EnableAggregatorRouting             bool
    80  	AggregatorRejectForwardingRedirects bool
    81  
    82  	ServiceAccountSigningKeyFile     string
    83  	ServiceAccountIssuer             serviceaccount.TokenGenerator
    84  	ServiceAccountTokenMaxExpiration time.Duration
    85  
    86  	ShowHiddenMetricsForVersion string
    87  
    88  	SystemNamespaces []string
    89  }
    90  
    91  // completedServerRunOptions is a private wrapper that enforces a call of Complete() before Run can be invoked.
    92  type completedOptions struct {
    93  	Options
    94  }
    95  
    96  type CompletedOptions struct {
    97  	// Embed a private pointer that cannot be instantiated outside of this package.
    98  	*completedOptions
    99  }
   100  
   101  // NewOptions creates a new ServerRunOptions object with default parameters
   102  func NewOptions() *Options {
   103  	s := Options{
   104  		GenericServerRunOptions: genericoptions.NewServerRunOptions(),
   105  		Etcd:                    genericoptions.NewEtcdOptions(storagebackend.NewDefaultConfig(kubeoptions.DefaultEtcdPathPrefix, nil)),
   106  		SecureServing:           kubeoptions.NewSecureServingOptions(),
   107  		Audit:                   genericoptions.NewAuditOptions(),
   108  		Features:                genericoptions.NewFeatureOptions(),
   109  		Admission:               kubeoptions.NewAdmissionOptions(),
   110  		Authentication:          kubeoptions.NewBuiltInAuthenticationOptions().WithAll(),
   111  		Authorization:           kubeoptions.NewBuiltInAuthorizationOptions(),
   112  		APIEnablement:           genericoptions.NewAPIEnablementOptions(),
   113  		EgressSelector:          genericoptions.NewEgressSelectorOptions(),
   114  		Metrics:                 metrics.NewOptions(),
   115  		Logs:                    logs.NewOptions(),
   116  		Traces:                  genericoptions.NewTracingOptions(),
   117  
   118  		EnableLogsHandler:                   true,
   119  		EventTTL:                            1 * time.Hour,
   120  		AggregatorRejectForwardingRedirects: true,
   121  		SystemNamespaces:                    []string{metav1.NamespaceSystem, metav1.NamespacePublic, metav1.NamespaceDefault},
   122  	}
   123  
   124  	// Overwrite the default for storage data format.
   125  	s.Etcd.DefaultStorageMediaType = "application/vnd.kubernetes.protobuf"
   126  
   127  	return &s
   128  }
   129  
   130  func (s *Options) AddFlags(fss *cliflag.NamedFlagSets) {
   131  	// Add the generic flags.
   132  	s.GenericServerRunOptions.AddUniversalFlags(fss.FlagSet("generic"))
   133  	s.Etcd.AddFlags(fss.FlagSet("etcd"))
   134  	s.SecureServing.AddFlags(fss.FlagSet("secure serving"))
   135  	s.Audit.AddFlags(fss.FlagSet("auditing"))
   136  	s.Features.AddFlags(fss.FlagSet("features"))
   137  	s.Authentication.AddFlags(fss.FlagSet("authentication"))
   138  	s.Authorization.AddFlags(fss.FlagSet("authorization"))
   139  	s.APIEnablement.AddFlags(fss.FlagSet("API enablement"))
   140  	s.EgressSelector.AddFlags(fss.FlagSet("egress selector"))
   141  	s.Admission.AddFlags(fss.FlagSet("admission"))
   142  	s.Metrics.AddFlags(fss.FlagSet("metrics"))
   143  	logsapi.AddFlags(s.Logs, fss.FlagSet("logs"))
   144  	s.Traces.AddFlags(fss.FlagSet("traces"))
   145  
   146  	// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
   147  	// arrange these text blocks sensibly. Grrr.
   148  	fs := fss.FlagSet("misc")
   149  	fs.DurationVar(&s.EventTTL, "event-ttl", s.EventTTL,
   150  		"Amount of time to retain events.")
   151  
   152  	fs.BoolVar(&s.EnableLogsHandler, "enable-logs-handler", s.EnableLogsHandler,
   153  		"If true, install a /logs handler for the apiserver logs.")
   154  	fs.MarkDeprecated("enable-logs-handler", "This flag will be removed in v1.19")
   155  
   156  	fs.Int64Var(&s.MaxConnectionBytesPerSec, "max-connection-bytes-per-sec", s.MaxConnectionBytesPerSec, ""+
   157  		"If non-zero, throttle each user connection to this number of bytes/sec. "+
   158  		"Currently only applies to long-running requests.")
   159  
   160  	fs.StringVar(&s.ProxyClientCertFile, "proxy-client-cert-file", s.ProxyClientCertFile, ""+
   161  		"Client certificate used to prove the identity of the aggregator or kube-apiserver "+
   162  		"when it must call out during a request. This includes proxying requests to a user "+
   163  		"api-server and calling out to webhook admission plugins. It is expected that this "+
   164  		"cert includes a signature from the CA in the --requestheader-client-ca-file flag. "+
   165  		"That CA is published in the 'extension-apiserver-authentication' configmap in "+
   166  		"the kube-system namespace. Components receiving calls from kube-aggregator should "+
   167  		"use that CA to perform their half of the mutual TLS verification.")
   168  	fs.StringVar(&s.ProxyClientKeyFile, "proxy-client-key-file", s.ProxyClientKeyFile, ""+
   169  		"Private key for the client certificate used to prove the identity of the aggregator or kube-apiserver "+
   170  		"when it must call out during a request. This includes proxying requests to a user "+
   171  		"api-server and calling out to webhook admission plugins.")
   172  
   173  	fs.StringVar(&s.PeerCAFile, "peer-ca-file", s.PeerCAFile,
   174  		"If set and the UnknownVersionInteroperabilityProxy feature gate is enabled, this file will be used to verify serving certificates of peer kube-apiservers. "+
   175  			"This flag is only used in clusters configured with multiple kube-apiservers for high availability.")
   176  
   177  	fs.StringVar(&s.PeerAdvertiseAddress.PeerAdvertiseIP, "peer-advertise-ip", s.PeerAdvertiseAddress.PeerAdvertiseIP,
   178  		"If set and the UnknownVersionInteroperabilityProxy feature gate is enabled, this IP will be used by peer kube-apiservers to proxy requests to this kube-apiserver "+
   179  			"when the request cannot be handled by the peer due to version skew between the kube-apiservers. "+
   180  			"This flag is only used in clusters configured with multiple kube-apiservers for high availability. ")
   181  
   182  	fs.StringVar(&s.PeerAdvertiseAddress.PeerAdvertisePort, "peer-advertise-port", s.PeerAdvertiseAddress.PeerAdvertisePort,
   183  		"If set and the UnknownVersionInteroperabilityProxy feature gate is enabled, this port will be used by peer kube-apiservers to proxy requests to this kube-apiserver "+
   184  			"when the request cannot be handled by the peer due to version skew between the kube-apiservers. "+
   185  			"This flag is only used in clusters configured with multiple kube-apiservers for high availability. ")
   186  
   187  	fs.BoolVar(&s.EnableAggregatorRouting, "enable-aggregator-routing", s.EnableAggregatorRouting,
   188  		"Turns on aggregator routing requests to endpoints IP rather than cluster IP.")
   189  
   190  	fs.BoolVar(&s.AggregatorRejectForwardingRedirects, "aggregator-reject-forwarding-redirect", s.AggregatorRejectForwardingRedirects,
   191  		"Aggregator reject forwarding redirect response back to client.")
   192  
   193  	fs.StringVar(&s.ServiceAccountSigningKeyFile, "service-account-signing-key-file", s.ServiceAccountSigningKeyFile, ""+
   194  		"Path to the file that contains the current private key of the service account token issuer. The issuer will sign issued ID tokens with this private key.")
   195  }
   196  
   197  func (o *Options) Complete(alternateDNS []string, alternateIPs []net.IP) (CompletedOptions, error) {
   198  	if o == nil {
   199  		return CompletedOptions{completedOptions: &completedOptions{}}, nil
   200  	}
   201  
   202  	completed := completedOptions{
   203  		Options: *o,
   204  	}
   205  
   206  	// set defaults
   207  	if err := completed.GenericServerRunOptions.DefaultAdvertiseAddress(completed.SecureServing.SecureServingOptions); err != nil {
   208  		return CompletedOptions{}, err
   209  	}
   210  
   211  	if err := completed.SecureServing.MaybeDefaultWithSelfSignedCerts(completed.GenericServerRunOptions.AdvertiseAddress.String(), alternateDNS, alternateIPs); err != nil {
   212  		return CompletedOptions{}, fmt.Errorf("error creating self-signed certificates: %v", err)
   213  	}
   214  
   215  	if len(completed.GenericServerRunOptions.ExternalHost) == 0 {
   216  		if len(completed.GenericServerRunOptions.AdvertiseAddress) > 0 {
   217  			completed.GenericServerRunOptions.ExternalHost = completed.GenericServerRunOptions.AdvertiseAddress.String()
   218  		} else {
   219  			hostname, err := os.Hostname()
   220  			if err != nil {
   221  				return CompletedOptions{}, fmt.Errorf("error finding host name: %v", err)
   222  			}
   223  			completed.GenericServerRunOptions.ExternalHost = hostname
   224  		}
   225  		klog.Infof("external host was not specified, using %v", completed.GenericServerRunOptions.ExternalHost)
   226  	}
   227  
   228  	// put authorization options in final state
   229  	completed.Authorization.Complete()
   230  	// adjust authentication for completed authorization
   231  	completed.Authentication.ApplyAuthorization(completed.Authorization)
   232  
   233  	// Use (ServiceAccountSigningKeyFile != "") as a proxy to the user enabling
   234  	// TokenRequest functionality. This defaulting was convenient, but messed up
   235  	// a lot of people when they rotated their serving cert with no idea it was
   236  	// connected to their service account keys. We are taking this opportunity to
   237  	// remove this problematic defaulting.
   238  	if completed.ServiceAccountSigningKeyFile == "" {
   239  		// Default to the private server key for service account token signing
   240  		if len(completed.Authentication.ServiceAccounts.KeyFiles) == 0 && completed.SecureServing.ServerCert.CertKey.KeyFile != "" {
   241  			if kubeauthenticator.IsValidServiceAccountKeyFile(completed.SecureServing.ServerCert.CertKey.KeyFile) {
   242  				completed.Authentication.ServiceAccounts.KeyFiles = []string{completed.SecureServing.ServerCert.CertKey.KeyFile}
   243  			} else {
   244  				klog.Warning("No TLS key provided, service account token authentication disabled")
   245  			}
   246  		}
   247  	}
   248  
   249  	if completed.ServiceAccountSigningKeyFile != "" && len(completed.Authentication.ServiceAccounts.Issuers) != 0 && completed.Authentication.ServiceAccounts.Issuers[0] != "" {
   250  		sk, err := keyutil.PrivateKeyFromFile(completed.ServiceAccountSigningKeyFile)
   251  		if err != nil {
   252  			return CompletedOptions{}, fmt.Errorf("failed to parse service-account-issuer-key-file: %v", err)
   253  		}
   254  		if completed.Authentication.ServiceAccounts.MaxExpiration != 0 {
   255  			lowBound := time.Hour
   256  			upBound := time.Duration(1<<32) * time.Second
   257  			if completed.Authentication.ServiceAccounts.MaxExpiration < lowBound ||
   258  				completed.Authentication.ServiceAccounts.MaxExpiration > upBound {
   259  				return CompletedOptions{}, fmt.Errorf("the service-account-max-token-expiration must be between 1 hour and 2^32 seconds")
   260  			}
   261  			if completed.Authentication.ServiceAccounts.ExtendExpiration {
   262  				if completed.Authentication.ServiceAccounts.MaxExpiration < serviceaccount.WarnOnlyBoundTokenExpirationSeconds*time.Second {
   263  					klog.Warningf("service-account-extend-token-expiration is true, in order to correctly trigger safe transition logic, service-account-max-token-expiration must be set longer than %d seconds (currently %s)", serviceaccount.WarnOnlyBoundTokenExpirationSeconds, completed.Authentication.ServiceAccounts.MaxExpiration)
   264  				}
   265  				if completed.Authentication.ServiceAccounts.MaxExpiration < serviceaccount.ExpirationExtensionSeconds*time.Second {
   266  					klog.Warningf("service-account-extend-token-expiration is true, enabling tokens valid up to %d seconds, which is longer than service-account-max-token-expiration set to %s seconds", serviceaccount.ExpirationExtensionSeconds, completed.Authentication.ServiceAccounts.MaxExpiration)
   267  				}
   268  			}
   269  		}
   270  
   271  		completed.ServiceAccountIssuer, err = serviceaccount.JWTTokenGenerator(completed.Authentication.ServiceAccounts.Issuers[0], sk)
   272  		if err != nil {
   273  			return CompletedOptions{}, fmt.Errorf("failed to build token generator: %v", err)
   274  		}
   275  		completed.ServiceAccountTokenMaxExpiration = completed.Authentication.ServiceAccounts.MaxExpiration
   276  	}
   277  
   278  	for key, value := range completed.APIEnablement.RuntimeConfig {
   279  		if key == "v1" || strings.HasPrefix(key, "v1/") ||
   280  			key == "api/v1" || strings.HasPrefix(key, "api/v1/") {
   281  			delete(completed.APIEnablement.RuntimeConfig, key)
   282  			completed.APIEnablement.RuntimeConfig["/v1"] = value
   283  		}
   284  		if key == "api/legacy" {
   285  			delete(completed.APIEnablement.RuntimeConfig, key)
   286  		}
   287  	}
   288  
   289  	return CompletedOptions{
   290  		completedOptions: &completed,
   291  	}, nil
   292  }
   293  
   294  // ServiceIPRange checks if the serviceClusterIPRange flag is nil, raising a warning if so and
   295  // setting service ip range to the default value in kubeoptions.DefaultServiceIPCIDR
   296  // for now until the default is removed per the deprecation timeline guidelines.
   297  // Returns service ip range, api server service IP, and an error
   298  func ServiceIPRange(passedServiceClusterIPRange net.IPNet) (net.IPNet, net.IP, error) {
   299  	serviceClusterIPRange := passedServiceClusterIPRange
   300  	if passedServiceClusterIPRange.IP == nil {
   301  		klog.Warningf("No CIDR for service cluster IPs specified. Default value which was %s is deprecated and will be removed in future releases. Please specify it using --service-cluster-ip-range on kube-apiserver.", kubeoptions.DefaultServiceIPCIDR.String())
   302  		serviceClusterIPRange = kubeoptions.DefaultServiceIPCIDR
   303  	}
   304  
   305  	size := min(netutil.RangeSize(&serviceClusterIPRange), 1<<16)
   306  	if size < 8 {
   307  		return net.IPNet{}, net.IP{}, fmt.Errorf("the service cluster IP range must be at least %d IP addresses", 8)
   308  	}
   309  
   310  	// Select the first valid IP from ServiceClusterIPRange to use as the GenericAPIServer service IP.
   311  	apiServerServiceIP, err := netutil.GetIndexedIP(&serviceClusterIPRange, 1)
   312  	if err != nil {
   313  		return net.IPNet{}, net.IP{}, err
   314  	}
   315  	klog.V(4).Infof("Setting service IP to %q (read-write).", apiServerServiceIP)
   316  
   317  	return serviceClusterIPRange, apiServerServiceIP, nil
   318  }