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 }