k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/kube-proxy/app/server.go (about) 1 /* 2 Copyright 2014 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 app does all of the work necessary to configure and run a 18 // Kubernetes app process. 19 package app 20 21 import ( 22 "context" 23 goflag "flag" 24 "fmt" 25 "net" 26 "net/http" 27 "os" 28 "strings" 29 "time" 30 31 "github.com/fsnotify/fsnotify" 32 "github.com/spf13/cobra" 33 "github.com/spf13/pflag" 34 35 v1 "k8s.io/api/core/v1" 36 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 37 "k8s.io/apimachinery/pkg/fields" 38 "k8s.io/apimachinery/pkg/labels" 39 "k8s.io/apimachinery/pkg/runtime" 40 "k8s.io/apimachinery/pkg/runtime/serializer" 41 "k8s.io/apimachinery/pkg/selection" 42 "k8s.io/apimachinery/pkg/types" 43 utilerrors "k8s.io/apimachinery/pkg/util/errors" 44 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 45 "k8s.io/apimachinery/pkg/util/validation/field" 46 "k8s.io/apimachinery/pkg/util/wait" 47 "k8s.io/apiserver/pkg/server/healthz" 48 "k8s.io/apiserver/pkg/server/mux" 49 "k8s.io/apiserver/pkg/server/routes" 50 utilfeature "k8s.io/apiserver/pkg/util/feature" 51 "k8s.io/client-go/informers" 52 clientset "k8s.io/client-go/kubernetes" 53 "k8s.io/client-go/rest" 54 "k8s.io/client-go/tools/clientcmd" 55 clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 56 "k8s.io/client-go/tools/events" 57 cliflag "k8s.io/component-base/cli/flag" 58 componentbaseconfig "k8s.io/component-base/config" 59 "k8s.io/component-base/configz" 60 "k8s.io/component-base/logs" 61 logsapi "k8s.io/component-base/logs/api/v1" 62 "k8s.io/component-base/metrics" 63 metricsfeatures "k8s.io/component-base/metrics/features" 64 "k8s.io/component-base/metrics/legacyregistry" 65 "k8s.io/component-base/metrics/prometheus/slis" 66 "k8s.io/component-base/version" 67 "k8s.io/component-base/version/verflag" 68 nodeutil "k8s.io/component-helpers/node/util" 69 "k8s.io/klog/v2" 70 "k8s.io/kube-proxy/config/v1alpha1" 71 api "k8s.io/kubernetes/pkg/apis/core" 72 "k8s.io/kubernetes/pkg/cluster/ports" 73 "k8s.io/kubernetes/pkg/features" 74 "k8s.io/kubernetes/pkg/kubelet/qos" 75 "k8s.io/kubernetes/pkg/proxy" 76 "k8s.io/kubernetes/pkg/proxy/apis" 77 kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config" 78 proxyconfigscheme "k8s.io/kubernetes/pkg/proxy/apis/config/scheme" 79 kubeproxyconfigv1alpha1 "k8s.io/kubernetes/pkg/proxy/apis/config/v1alpha1" 80 "k8s.io/kubernetes/pkg/proxy/apis/config/validation" 81 "k8s.io/kubernetes/pkg/proxy/config" 82 "k8s.io/kubernetes/pkg/proxy/healthcheck" 83 proxymetrics "k8s.io/kubernetes/pkg/proxy/metrics" 84 proxyutil "k8s.io/kubernetes/pkg/proxy/util" 85 "k8s.io/kubernetes/pkg/util/filesystem" 86 utilflag "k8s.io/kubernetes/pkg/util/flag" 87 utilnode "k8s.io/kubernetes/pkg/util/node" 88 "k8s.io/kubernetes/pkg/util/oom" 89 netutils "k8s.io/utils/net" 90 "k8s.io/utils/ptr" 91 ) 92 93 func init() { 94 utilruntime.Must(metricsfeatures.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) 95 utilruntime.Must(logsapi.AddFeatureGates(utilfeature.DefaultMutableFeatureGate)) 96 } 97 98 // proxyRun defines the interface to run a specified ProxyServer 99 type proxyRun interface { 100 Run(ctx context.Context) error 101 } 102 103 // Options contains everything necessary to create and run a proxy server. 104 type Options struct { 105 // ConfigFile is the location of the proxy server's configuration file. 106 ConfigFile string 107 // WriteConfigTo is the path where the default configuration will be written. 108 WriteConfigTo string 109 // CleanupAndExit, when true, makes the proxy server clean up iptables and ipvs rules, then exit. 110 CleanupAndExit bool 111 // InitAndExit, when true, makes the proxy server makes configurations that need privileged access, then exit. 112 InitAndExit bool 113 // WindowsService should be set to true if kube-proxy is running as a service on Windows. 114 // Its corresponding flag only gets registered in Windows builds 115 WindowsService bool 116 // config is the proxy server's configuration object. 117 config *kubeproxyconfig.KubeProxyConfiguration 118 // watcher is used to watch on the update change of ConfigFile 119 watcher filesystem.FSWatcher 120 // proxyServer is the interface to run the proxy server 121 proxyServer proxyRun 122 // errCh is the channel that errors will be sent 123 errCh chan error 124 125 // The fields below here are placeholders for flags that can't be directly mapped into 126 // config.KubeProxyConfiguration. 127 // 128 // TODO remove these fields once the deprecated flags are removed. 129 130 // master is used to override the kubeconfig's URL to the apiserver. 131 master string 132 // healthzPort is the port to be used by the healthz server. 133 healthzPort int32 134 // metricsPort is the port to be used by the metrics server. 135 metricsPort int32 136 137 // hostnameOverride, if set from the command line flag, takes precedence over the `HostnameOverride` value from the config file 138 hostnameOverride string 139 140 logger klog.Logger 141 } 142 143 // AddFlags adds flags to fs and binds them to options. 144 func (o *Options) AddFlags(fs *pflag.FlagSet) { 145 o.addOSFlags(fs) 146 147 fs.StringVar(&o.ConfigFile, "config", o.ConfigFile, "The path to the configuration file.") 148 fs.StringVar(&o.WriteConfigTo, "write-config-to", o.WriteConfigTo, "If set, write the default configuration values to this file and exit.") 149 150 fs.BoolVar(&o.CleanupAndExit, "cleanup", o.CleanupAndExit, "If true cleanup iptables and ipvs rules and exit.") 151 152 fs.Var(cliflag.NewMapStringBool(&o.config.FeatureGates), "feature-gates", "A set of key=value pairs that describe feature gates for alpha/experimental features. "+ 153 "Options are:\n"+strings.Join(utilfeature.DefaultFeatureGate.KnownFeatures(), "\n")+"\n"+ 154 "This parameter is ignored if a config file is specified by --config.") 155 156 fs.StringVar(&o.config.ClientConnection.Kubeconfig, "kubeconfig", o.config.ClientConnection.Kubeconfig, "Path to kubeconfig file with authorization information (the master location can be overridden by the master flag).") 157 fs.StringVar(&o.master, "master", o.master, "The address of the Kubernetes API server (overrides any value in kubeconfig)") 158 fs.StringVar(&o.config.ClientConnection.ContentType, "kube-api-content-type", o.config.ClientConnection.ContentType, "Content type of requests sent to apiserver.") 159 fs.Int32Var(&o.config.ClientConnection.Burst, "kube-api-burst", o.config.ClientConnection.Burst, "Burst to use while talking with kubernetes apiserver") 160 fs.Float32Var(&o.config.ClientConnection.QPS, "kube-api-qps", o.config.ClientConnection.QPS, "QPS to use while talking with kubernetes apiserver") 161 162 fs.StringVar(&o.hostnameOverride, "hostname-override", o.hostnameOverride, "If non-empty, will be used as the name of the Node that kube-proxy is running on. If unset, the node name is assumed to be the same as the node's hostname.") 163 fs.Var(&utilflag.IPVar{Val: &o.config.BindAddress}, "bind-address", "Overrides kube-proxy's idea of what its node's primary IP is. Note that the name is a historical artifact, and kube-proxy does not actually bind any sockets to this IP. This parameter is ignored if a config file is specified by --config.") 164 fs.Var(&utilflag.IPPortVar{Val: &o.config.HealthzBindAddress}, "healthz-bind-address", "The IP address and port for the health check server to serve on, defaulting to \"0.0.0.0:10256\". This parameter is ignored if a config file is specified by --config.") 165 fs.Var(&utilflag.IPPortVar{Val: &o.config.MetricsBindAddress}, "metrics-bind-address", "The IP address and port for the metrics server to serve on, defaulting to \"127.0.0.1:10249\". (Set to \"0.0.0.0:10249\" / \"[::]:10249\" to bind on all interfaces.) Set empty to disable. This parameter is ignored if a config file is specified by --config.") 166 fs.BoolVar(&o.config.BindAddressHardFail, "bind-address-hard-fail", o.config.BindAddressHardFail, "If true kube-proxy will treat failure to bind to a port as fatal and exit") 167 fs.BoolVar(&o.config.EnableProfiling, "profiling", o.config.EnableProfiling, "If true enables profiling via web interface on /debug/pprof handler. This parameter is ignored if a config file is specified by --config.") 168 fs.StringVar(&o.config.ShowHiddenMetricsForVersion, "show-hidden-metrics-for-version", o.config.ShowHiddenMetricsForVersion, 169 "The previous version for which you want to show hidden metrics. "+ 170 "Only the previous minor version is meaningful, other values will not be allowed. "+ 171 "The format is <major>.<minor>, e.g.: '1.16'. "+ 172 "The purpose of this format is make sure you have the opportunity to notice if the next release hides additional metrics, "+ 173 "rather than being surprised when they are permanently removed in the release after that. "+ 174 "This parameter is ignored if a config file is specified by --config.") 175 fs.BoolVar(&o.InitAndExit, "init-only", o.InitAndExit, "If true, perform any initialization steps that must be done with full root privileges, and then exit. After doing this, you can run kube-proxy again with only the CAP_NET_ADMIN capability.") 176 fs.Var(&o.config.Mode, "proxy-mode", "Which proxy mode to use: on Linux this can be 'iptables' (default) or 'ipvs'. On Windows the only supported value is 'kernelspace'."+ 177 "This parameter is ignored if a config file is specified by --config.") 178 179 fs.Int32Var(o.config.IPTables.MasqueradeBit, "iptables-masquerade-bit", ptr.Deref(o.config.IPTables.MasqueradeBit, 14), "If using the iptables or ipvs proxy mode, the bit of the fwmark space to mark packets requiring SNAT with. Must be within the range [0, 31].") 180 fs.BoolVar(&o.config.IPTables.MasqueradeAll, "masquerade-all", o.config.IPTables.MasqueradeAll, "If using the iptables or ipvs proxy mode, SNAT all traffic sent via Service cluster IPs. This may be required with some CNI plugins.") 181 fs.BoolVar(o.config.IPTables.LocalhostNodePorts, "iptables-localhost-nodeports", ptr.Deref(o.config.IPTables.LocalhostNodePorts, true), "If false, kube-proxy will disable the legacy behavior of allowing NodePort services to be accessed via localhost. (Applies only to iptables mode and IPv4; localhost NodePorts are never allowed with other proxy modes or with IPv6.)") 182 fs.DurationVar(&o.config.IPTables.SyncPeriod.Duration, "iptables-sync-period", o.config.IPTables.SyncPeriod.Duration, "An interval (e.g. '5s', '1m', '2h22m') indicating how frequently various re-synchronizing and cleanup operations are performed. Must be greater than 0.") 183 fs.DurationVar(&o.config.IPTables.MinSyncPeriod.Duration, "iptables-min-sync-period", o.config.IPTables.MinSyncPeriod.Duration, "The minimum period between iptables rule resyncs (e.g. '5s', '1m', '2h22m'). A value of 0 means every Service or EndpointSlice change will result in an immediate iptables resync.") 184 185 fs.DurationVar(&o.config.IPVS.SyncPeriod.Duration, "ipvs-sync-period", o.config.IPVS.SyncPeriod.Duration, "An interval (e.g. '5s', '1m', '2h22m') indicating how frequently various re-synchronizing and cleanup operations are performed. Must be greater than 0.") 186 fs.DurationVar(&o.config.IPVS.MinSyncPeriod.Duration, "ipvs-min-sync-period", o.config.IPVS.MinSyncPeriod.Duration, "The minimum period between IPVS rule resyncs (e.g. '5s', '1m', '2h22m'). A value of 0 means every Service or EndpointSlice change will result in an immediate IPVS resync.") 187 fs.StringVar(&o.config.IPVS.Scheduler, "ipvs-scheduler", o.config.IPVS.Scheduler, "The ipvs scheduler type when proxy mode is ipvs") 188 fs.StringSliceVar(&o.config.IPVS.ExcludeCIDRs, "ipvs-exclude-cidrs", o.config.IPVS.ExcludeCIDRs, "A comma-separated list of CIDRs which the ipvs proxier should not touch when cleaning up IPVS rules.") 189 fs.BoolVar(&o.config.IPVS.StrictARP, "ipvs-strict-arp", o.config.IPVS.StrictARP, "Enable strict ARP by setting arp_ignore to 1 and arp_announce to 2") 190 fs.DurationVar(&o.config.IPVS.TCPTimeout.Duration, "ipvs-tcp-timeout", o.config.IPVS.TCPTimeout.Duration, "The timeout for idle IPVS TCP connections, 0 to leave as-is. (e.g. '5s', '1m', '2h22m').") 191 fs.DurationVar(&o.config.IPVS.TCPFinTimeout.Duration, "ipvs-tcpfin-timeout", o.config.IPVS.TCPFinTimeout.Duration, "The timeout for IPVS TCP connections after receiving a FIN packet, 0 to leave as-is. (e.g. '5s', '1m', '2h22m').") 192 fs.DurationVar(&o.config.IPVS.UDPTimeout.Duration, "ipvs-udp-timeout", o.config.IPVS.UDPTimeout.Duration, "The timeout for IPVS UDP packets, 0 to leave as-is. (e.g. '5s', '1m', '2h22m').") 193 194 fs.Var(&o.config.DetectLocalMode, "detect-local-mode", "Mode to use to detect local traffic. This parameter is ignored if a config file is specified by --config.") 195 fs.StringVar(&o.config.DetectLocal.BridgeInterface, "pod-bridge-interface", o.config.DetectLocal.BridgeInterface, "A bridge interface name. When --detect-local-mode is set to BridgeInterface, kube-proxy will consider traffic to be local if it originates from this bridge.") 196 fs.StringVar(&o.config.DetectLocal.InterfaceNamePrefix, "pod-interface-name-prefix", o.config.DetectLocal.InterfaceNamePrefix, "An interface name prefix. When --detect-local-mode is set to InterfaceNamePrefix, kube-proxy will consider traffic to be local if it originates from any interface whose name begins with this prefix.") 197 fs.StringVar(&o.config.ClusterCIDR, "cluster-cidr", o.config.ClusterCIDR, "The CIDR range of the pods in the cluster. (For dual-stack clusters, this can be a comma-separated dual-stack pair of CIDR ranges.). When --detect-local-mode is set to ClusterCIDR, kube-proxy will consider traffic to be local if its source IP is in this range. (Otherwise it is not used.) "+ 198 "This parameter is ignored if a config file is specified by --config.") 199 200 fs.StringSliceVar(&o.config.NodePortAddresses, "nodeport-addresses", o.config.NodePortAddresses, 201 "A list of CIDR ranges that contain valid node IPs, or alternatively, the single string 'primary'. If set to a list of CIDRs, connections to NodePort services will only be accepted on node IPs in one of the indicated ranges. If set to 'primary', NodePort services will only be accepted on the node's primary IP(s) according to the Node object. If unset, NodePort connections will be accepted on all local IPs. This parameter is ignored if a config file is specified by --config.") 202 203 fs.Int32Var(o.config.OOMScoreAdj, "oom-score-adj", ptr.Deref(o.config.OOMScoreAdj, int32(qos.KubeProxyOOMScoreAdj)), "The oom-score-adj value for kube-proxy process. Values must be within the range [-1000, 1000]. This parameter is ignored if a config file is specified by --config.") 204 fs.Int32Var(o.config.Conntrack.MaxPerCore, "conntrack-max-per-core", *o.config.Conntrack.MaxPerCore, 205 "Maximum number of NAT connections to track per CPU core (0 to leave the limit as-is and ignore conntrack-min).") 206 fs.Int32Var(o.config.Conntrack.Min, "conntrack-min", *o.config.Conntrack.Min, 207 "Minimum number of conntrack entries to allocate, regardless of conntrack-max-per-core (set conntrack-max-per-core=0 to leave the limit as-is).") 208 209 fs.DurationVar(&o.config.Conntrack.TCPEstablishedTimeout.Duration, "conntrack-tcp-timeout-established", o.config.Conntrack.TCPEstablishedTimeout.Duration, "Idle timeout for established TCP connections (0 to leave as-is)") 210 fs.DurationVar( 211 &o.config.Conntrack.TCPCloseWaitTimeout.Duration, "conntrack-tcp-timeout-close-wait", 212 o.config.Conntrack.TCPCloseWaitTimeout.Duration, 213 "NAT timeout for TCP connections in the CLOSE_WAIT state") 214 fs.BoolVar(&o.config.Conntrack.TCPBeLiberal, "conntrack-tcp-be-liberal", o.config.Conntrack.TCPBeLiberal, "Enable liberal mode for tracking TCP packets by setting nf_conntrack_tcp_be_liberal to 1") 215 fs.DurationVar(&o.config.Conntrack.UDPTimeout.Duration, "conntrack-udp-timeout", o.config.Conntrack.UDPTimeout.Duration, "Idle timeout for UNREPLIED UDP connections (0 to leave as-is)") 216 fs.DurationVar(&o.config.Conntrack.UDPStreamTimeout.Duration, "conntrack-udp-timeout-stream", o.config.Conntrack.UDPStreamTimeout.Duration, "Idle timeout for ASSURED UDP connections (0 to leave as-is)") 217 218 fs.DurationVar(&o.config.ConfigSyncPeriod.Duration, "config-sync-period", o.config.ConfigSyncPeriod.Duration, "How often configuration from the apiserver is refreshed. Must be greater than 0.") 219 220 fs.Int32Var(&o.healthzPort, "healthz-port", o.healthzPort, "The port to bind the health check server. Use 0 to disable.") 221 _ = fs.MarkDeprecated("healthz-port", "This flag is deprecated and will be removed in a future release. Please use --healthz-bind-address instead.") 222 fs.Int32Var(&o.metricsPort, "metrics-port", o.metricsPort, "The port to bind the metrics server. Use 0 to disable.") 223 _ = fs.MarkDeprecated("metrics-port", "This flag is deprecated and will be removed in a future release. Please use --metrics-bind-address instead.") 224 fs.Var(utilflag.PortRangeVar{Val: &o.config.PortRange}, "proxy-port-range", "This was previously used to configure the userspace proxy, but is now unused.") 225 _ = fs.MarkDeprecated("proxy-port-range", "This flag has no effect and will be removed in a future release.") 226 227 logsapi.AddFlags(&o.config.Logging, fs) 228 } 229 230 // newKubeProxyConfiguration returns a KubeProxyConfiguration with default values 231 func newKubeProxyConfiguration() *kubeproxyconfig.KubeProxyConfiguration { 232 versionedConfig := &v1alpha1.KubeProxyConfiguration{} 233 proxyconfigscheme.Scheme.Default(versionedConfig) 234 internalConfig, err := proxyconfigscheme.Scheme.ConvertToVersion(versionedConfig, kubeproxyconfig.SchemeGroupVersion) 235 if err != nil { 236 panic(fmt.Sprintf("Unable to create default config: %v", err)) 237 } 238 239 return internalConfig.(*kubeproxyconfig.KubeProxyConfiguration) 240 } 241 242 // NewOptions returns initialized Options 243 func NewOptions() *Options { 244 return &Options{ 245 config: newKubeProxyConfiguration(), 246 healthzPort: ports.ProxyHealthzPort, 247 metricsPort: ports.ProxyStatusPort, 248 errCh: make(chan error), 249 logger: klog.FromContext(context.Background()), 250 } 251 } 252 253 // Complete completes all the required options. 254 func (o *Options) Complete(fs *pflag.FlagSet) error { 255 if len(o.ConfigFile) == 0 && len(o.WriteConfigTo) == 0 { 256 o.config.HealthzBindAddress = addressFromDeprecatedFlags(o.config.HealthzBindAddress, o.healthzPort) 257 o.config.MetricsBindAddress = addressFromDeprecatedFlags(o.config.MetricsBindAddress, o.metricsPort) 258 } 259 260 // Load the config file here in Complete, so that Validate validates the fully-resolved config. 261 if len(o.ConfigFile) > 0 { 262 c, err := o.loadConfigFromFile(o.ConfigFile) 263 if err != nil { 264 return err 265 } 266 267 // Before we overwrite the config which holds the parsed 268 // command line parameters, we need to copy all modified 269 // logging settings over to the loaded config (i.e. logging 270 // command line flags have priority). Otherwise `--config 271 // ... -v=5` doesn't work (config resets verbosity even 272 // when it contains no logging settings). 273 copyLogsFromFlags(fs, &c.Logging) 274 o.config = c 275 276 if err := o.initWatcher(); err != nil { 277 return err 278 } 279 } 280 281 o.platformApplyDefaults(o.config) 282 283 if err := o.processHostnameOverrideFlag(); err != nil { 284 return err 285 } 286 287 return utilfeature.DefaultMutableFeatureGate.SetFromMap(o.config.FeatureGates) 288 } 289 290 // copyLogsFromFlags applies the logging flags from the given flag set to the given 291 // configuration. Fields for which the corresponding flag was not used are left 292 // unmodified. For fields that have multiple values (like vmodule), the values from 293 // the flags get joined so that the command line flags have priority. 294 // 295 // TODO (pohly): move this to logsapi 296 func copyLogsFromFlags(from *pflag.FlagSet, to *logsapi.LoggingConfiguration) error { 297 var cloneFS pflag.FlagSet 298 logsapi.AddFlags(to, &cloneFS) 299 vmodule := to.VModule 300 to.VModule = nil 301 var err error 302 cloneFS.VisitAll(func(f *pflag.Flag) { 303 if err != nil { 304 return 305 } 306 fsFlag := from.Lookup(f.Name) 307 if fsFlag == nil { 308 err = fmt.Errorf("logging flag %s not found in flag set", f.Name) 309 return 310 } 311 if !fsFlag.Changed { 312 return 313 } 314 if setErr := f.Value.Set(fsFlag.Value.String()); setErr != nil { 315 err = fmt.Errorf("copying flag %s value: %v", f.Name, setErr) 316 return 317 } 318 }) 319 to.VModule = append(to.VModule, vmodule...) 320 return err 321 } 322 323 // Creates a new filesystem watcher and adds watches for the config file. 324 func (o *Options) initWatcher() error { 325 fswatcher := filesystem.NewFsnotifyWatcher() 326 err := fswatcher.Init(o.eventHandler, o.errorHandler) 327 if err != nil { 328 return err 329 } 330 err = fswatcher.AddWatch(o.ConfigFile) 331 if err != nil { 332 return err 333 } 334 o.watcher = fswatcher 335 return nil 336 } 337 338 func (o *Options) eventHandler(ent fsnotify.Event) { 339 if ent.Has(fsnotify.Write) || ent.Has(fsnotify.Rename) { 340 // error out when ConfigFile is updated 341 o.errCh <- fmt.Errorf("content of the proxy server's configuration file was updated") 342 return 343 } 344 o.errCh <- nil 345 } 346 347 func (o *Options) errorHandler(err error) { 348 o.errCh <- err 349 } 350 351 // processHostnameOverrideFlag processes hostname-override flag 352 func (o *Options) processHostnameOverrideFlag() error { 353 // Check if hostname-override flag is set and use value since configFile always overrides 354 if len(o.hostnameOverride) > 0 { 355 hostName := strings.TrimSpace(o.hostnameOverride) 356 if len(hostName) == 0 { 357 return fmt.Errorf("empty hostname-override is invalid") 358 } 359 o.config.HostnameOverride = strings.ToLower(hostName) 360 } 361 362 return nil 363 } 364 365 // Validate validates all the required options. 366 func (o *Options) Validate() error { 367 if errs := validation.Validate(o.config); len(errs) != 0 { 368 return errs.ToAggregate() 369 } 370 371 return nil 372 } 373 374 // Run runs the specified ProxyServer. 375 func (o *Options) Run(ctx context.Context) error { 376 defer close(o.errCh) 377 if len(o.WriteConfigTo) > 0 { 378 return o.writeConfigFile() 379 } 380 381 err := platformCleanup(ctx, o.config.Mode, o.CleanupAndExit) 382 if o.CleanupAndExit { 383 return err 384 } 385 // We ignore err otherwise; the cleanup is best-effort, and the backends will have 386 // logged messages if they failed in interesting ways. 387 388 proxyServer, err := newProxyServer(ctx, o.config, o.master, o.InitAndExit) 389 if err != nil { 390 return err 391 } 392 if o.InitAndExit { 393 return nil 394 } 395 396 o.proxyServer = proxyServer 397 return o.runLoop(ctx) 398 } 399 400 // runLoop will watch on the update change of the proxy server's configuration file. 401 // Return an error when updated 402 func (o *Options) runLoop(ctx context.Context) error { 403 if o.watcher != nil { 404 o.watcher.Run() 405 } 406 407 // run the proxy in goroutine 408 go func() { 409 err := o.proxyServer.Run(ctx) 410 o.errCh <- err 411 }() 412 413 for { 414 err := <-o.errCh 415 if err != nil { 416 return err 417 } 418 } 419 } 420 421 func (o *Options) writeConfigFile() (err error) { 422 const mediaType = runtime.ContentTypeYAML 423 info, ok := runtime.SerializerInfoForMediaType(proxyconfigscheme.Codecs.SupportedMediaTypes(), mediaType) 424 if !ok { 425 return fmt.Errorf("unable to locate encoder -- %q is not a supported media type", mediaType) 426 } 427 428 encoder := proxyconfigscheme.Codecs.EncoderForVersion(info.Serializer, v1alpha1.SchemeGroupVersion) 429 430 configFile, err := os.Create(o.WriteConfigTo) 431 if err != nil { 432 return err 433 } 434 435 defer func() { 436 ferr := configFile.Close() 437 if ferr != nil && err == nil { 438 err = ferr 439 } 440 }() 441 442 if err = encoder.Encode(o.config, configFile); err != nil { 443 return err 444 } 445 446 o.logger.Info("Wrote configuration", "file", o.WriteConfigTo) 447 448 return nil 449 } 450 451 // addressFromDeprecatedFlags returns server address from flags 452 // passed on the command line based on the following rules: 453 // 1. If port is 0, disable the server (e.g. set address to empty). 454 // 2. Otherwise, set the port portion of the config accordingly. 455 func addressFromDeprecatedFlags(addr string, port int32) string { 456 if port == 0 { 457 return "" 458 } 459 return proxyutil.AppendPortIfNeeded(addr, port) 460 } 461 462 // newLenientSchemeAndCodecs returns a scheme that has only v1alpha1 registered into 463 // it and a CodecFactory with strict decoding disabled. 464 func newLenientSchemeAndCodecs() (*runtime.Scheme, *serializer.CodecFactory, error) { 465 lenientScheme := runtime.NewScheme() 466 if err := kubeproxyconfig.AddToScheme(lenientScheme); err != nil { 467 return nil, nil, fmt.Errorf("failed to add kube-proxy config API to lenient scheme: %v", err) 468 } 469 if err := kubeproxyconfigv1alpha1.AddToScheme(lenientScheme); err != nil { 470 return nil, nil, fmt.Errorf("failed to add kube-proxy config v1alpha1 API to lenient scheme: %v", err) 471 } 472 lenientCodecs := serializer.NewCodecFactory(lenientScheme, serializer.DisableStrict) 473 return lenientScheme, &lenientCodecs, nil 474 } 475 476 // loadConfigFromFile loads the contents of file and decodes it as a 477 // KubeProxyConfiguration object. 478 func (o *Options) loadConfigFromFile(file string) (*kubeproxyconfig.KubeProxyConfiguration, error) { 479 data, err := os.ReadFile(file) 480 if err != nil { 481 return nil, err 482 } 483 484 return o.loadConfig(data) 485 } 486 487 // loadConfig decodes a serialized KubeProxyConfiguration to the internal type. 488 func (o *Options) loadConfig(data []byte) (*kubeproxyconfig.KubeProxyConfiguration, error) { 489 490 configObj, gvk, err := proxyconfigscheme.Codecs.UniversalDecoder().Decode(data, nil, nil) 491 if err != nil { 492 // Try strict decoding first. If that fails decode with a lenient 493 // decoder, which has only v1alpha1 registered, and log a warning. 494 // The lenient path is to be dropped when support for v1alpha1 is dropped. 495 if !runtime.IsStrictDecodingError(err) { 496 return nil, fmt.Errorf("failed to decode: %w", err) 497 } 498 499 _, lenientCodecs, lenientErr := newLenientSchemeAndCodecs() 500 if lenientErr != nil { 501 return nil, lenientErr 502 } 503 504 configObj, gvk, lenientErr = lenientCodecs.UniversalDecoder().Decode(data, nil, nil) 505 if lenientErr != nil { 506 // Lenient decoding failed with the current version, return the 507 // original strict error. 508 return nil, fmt.Errorf("failed lenient decoding: %v", err) 509 } 510 511 // Continue with the v1alpha1 object that was decoded leniently, but emit a warning. 512 o.logger.Info("Using lenient decoding as strict decoding failed", "err", err) 513 } 514 515 proxyConfig, ok := configObj.(*kubeproxyconfig.KubeProxyConfiguration) 516 if !ok { 517 return nil, fmt.Errorf("got unexpected config type: %v", gvk) 518 } 519 return proxyConfig, nil 520 } 521 522 // NewProxyCommand creates a *cobra.Command object with default parameters 523 func NewProxyCommand() *cobra.Command { 524 opts := NewOptions() 525 526 cmd := &cobra.Command{ 527 Use: "kube-proxy", 528 Long: `The Kubernetes network proxy runs on each node. This 529 reflects services as defined in the Kubernetes API on each node and can do simple 530 TCP, UDP, and SCTP stream forwarding or round robin TCP, UDP, and SCTP forwarding across a set of backends. 531 Service cluster IPs and ports are currently found through Docker-links-compatible 532 environment variables specifying ports opened by the service proxy. There is an optional 533 addon that provides cluster DNS for these cluster IPs. The user must create a service 534 with the apiserver API to configure the proxy.`, 535 RunE: func(cmd *cobra.Command, args []string) error { 536 verflag.PrintAndExitIfRequested() 537 538 if err := initForOS(opts.WindowsService); err != nil { 539 return fmt.Errorf("failed os init: %w", err) 540 } 541 542 if err := opts.Complete(cmd.Flags()); err != nil { 543 return fmt.Errorf("failed complete: %w", err) 544 } 545 546 logs.InitLogs() 547 if err := logsapi.ValidateAndApplyAsField(&opts.config.Logging, utilfeature.DefaultFeatureGate, field.NewPath("logging")); err != nil { 548 return fmt.Errorf("initialize logging: %v", err) 549 } 550 551 cliflag.PrintFlags(cmd.Flags()) 552 553 if err := opts.Validate(); err != nil { 554 return fmt.Errorf("failed validate: %w", err) 555 } 556 // add feature enablement metrics 557 utilfeature.DefaultMutableFeatureGate.AddMetrics() 558 if err := opts.Run(context.Background()); err != nil { 559 opts.logger.Error(err, "Error running ProxyServer") 560 return err 561 } 562 563 return nil 564 }, 565 Args: func(cmd *cobra.Command, args []string) error { 566 for _, arg := range args { 567 if len(arg) > 0 { 568 return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args) 569 } 570 } 571 return nil 572 }, 573 } 574 575 fs := cmd.Flags() 576 opts.AddFlags(fs) 577 fs.AddGoFlagSet(goflag.CommandLine) // for --boot-id-file and --machine-id-file 578 579 _ = cmd.MarkFlagFilename("config", "yaml", "yml", "json") 580 581 return cmd 582 } 583 584 // ProxyServer represents all the parameters required to start the Kubernetes proxy server. All 585 // fields are required. 586 type ProxyServer struct { 587 Config *kubeproxyconfig.KubeProxyConfiguration 588 589 Client clientset.Interface 590 Broadcaster events.EventBroadcaster 591 Recorder events.EventRecorder 592 NodeRef *v1.ObjectReference 593 HealthzServer *healthcheck.ProxierHealthServer 594 Hostname string 595 PrimaryIPFamily v1.IPFamily 596 NodeIPs map[v1.IPFamily]net.IP 597 598 podCIDRs []string // only used for LocalModeNodeCIDR 599 600 Proxier proxy.Provider 601 } 602 603 // newProxyServer creates a ProxyServer based on the given config 604 func newProxyServer(ctx context.Context, config *kubeproxyconfig.KubeProxyConfiguration, master string, initOnly bool) (*ProxyServer, error) { 605 logger := klog.FromContext(ctx) 606 607 s := &ProxyServer{ 608 Config: config, 609 } 610 611 cz, err := configz.New(kubeproxyconfig.GroupName) 612 if err != nil { 613 return nil, fmt.Errorf("unable to register configz: %s", err) 614 } 615 cz.Set(config) 616 617 if len(config.ShowHiddenMetricsForVersion) > 0 { 618 metrics.SetShowHidden() 619 } 620 621 s.Hostname, err = nodeutil.GetHostname(config.HostnameOverride) 622 if err != nil { 623 return nil, err 624 } 625 626 s.Client, err = createClient(ctx, config.ClientConnection, master) 627 if err != nil { 628 return nil, err 629 } 630 631 rawNodeIPs := getNodeIPs(ctx, s.Client, s.Hostname) 632 s.PrimaryIPFamily, s.NodeIPs = detectNodeIPs(ctx, rawNodeIPs, config.BindAddress) 633 634 if len(config.NodePortAddresses) == 1 && config.NodePortAddresses[0] == kubeproxyconfig.NodePortAddressesPrimary { 635 var nodePortAddresses []string 636 if nodeIP := s.NodeIPs[v1.IPv4Protocol]; nodeIP != nil && !nodeIP.IsLoopback() { 637 nodePortAddresses = append(nodePortAddresses, fmt.Sprintf("%s/32", nodeIP.String())) 638 } 639 if nodeIP := s.NodeIPs[v1.IPv6Protocol]; nodeIP != nil && !nodeIP.IsLoopback() { 640 nodePortAddresses = append(nodePortAddresses, fmt.Sprintf("%s/128", nodeIP.String())) 641 } 642 config.NodePortAddresses = nodePortAddresses 643 } 644 645 s.Broadcaster = events.NewBroadcaster(&events.EventSinkImpl{Interface: s.Client.EventsV1()}) 646 s.Recorder = s.Broadcaster.NewRecorder(proxyconfigscheme.Scheme, "kube-proxy") 647 648 s.NodeRef = &v1.ObjectReference{ 649 Kind: "Node", 650 Name: s.Hostname, 651 UID: types.UID(s.Hostname), 652 Namespace: "", 653 } 654 655 if len(config.HealthzBindAddress) > 0 { 656 s.HealthzServer = healthcheck.NewProxierHealthServer(config.HealthzBindAddress, 2*config.IPTables.SyncPeriod.Duration) 657 } 658 659 err = s.platformSetup(ctx) 660 if err != nil { 661 return nil, err 662 } 663 664 err = checkBadConfig(s) 665 if err != nil { 666 logger.Error(err, "Kube-proxy configuration may be incomplete or incorrect") 667 } 668 669 ipv4Supported, ipv6Supported, dualStackSupported, err := s.platformCheckSupported(ctx) 670 if err != nil { 671 return nil, err 672 } else if (s.PrimaryIPFamily == v1.IPv4Protocol && !ipv4Supported) || (s.PrimaryIPFamily == v1.IPv6Protocol && !ipv6Supported) { 673 return nil, fmt.Errorf("no support for primary IP family %q", s.PrimaryIPFamily) 674 } else if dualStackSupported { 675 logger.Info("kube-proxy running in dual-stack mode", "primary ipFamily", s.PrimaryIPFamily) 676 } else { 677 logger.Info("kube-proxy running in single-stack mode", "ipFamily", s.PrimaryIPFamily) 678 } 679 680 err, fatal := checkBadIPConfig(s, dualStackSupported) 681 if err != nil { 682 if fatal { 683 return nil, fmt.Errorf("kube-proxy configuration is incorrect: %v", err) 684 } 685 logger.Error(err, "Kube-proxy configuration may be incomplete or incorrect") 686 } 687 688 s.Proxier, err = s.createProxier(ctx, config, dualStackSupported, initOnly) 689 if err != nil { 690 return nil, err 691 } 692 693 return s, nil 694 } 695 696 // checkBadConfig checks for bad/deprecated configuation 697 func checkBadConfig(s *ProxyServer) error { 698 var errors []error 699 700 // At this point we haven't seen any actual Services or EndpointSlices, so we 701 // don't really know if the cluster is expected to be single- or dual-stack. But 702 // we can at least take note of whether there is any explicitly-dual-stack 703 // configuration. 704 anyDualStackConfig := false 705 clusterCIDRs := strings.Split(s.Config.ClusterCIDR, ",") 706 for _, config := range [][]string{clusterCIDRs, s.Config.NodePortAddresses, s.Config.IPVS.ExcludeCIDRs, s.podCIDRs} { 707 if dual, _ := netutils.IsDualStackCIDRStrings(config); dual { 708 anyDualStackConfig = true 709 break 710 } 711 } 712 713 // Warn if NodePortAddresses does not limit connections on all IP families that 714 // seem to be in use. 715 cidrsByFamily := proxyutil.MapCIDRsByIPFamily(s.Config.NodePortAddresses) 716 if len(s.Config.NodePortAddresses) == 0 { 717 errors = append(errors, fmt.Errorf("nodePortAddresses is unset; NodePort connections will be accepted on all local IPs. Consider using `--nodeport-addresses primary`")) 718 } else if anyDualStackConfig && len(cidrsByFamily[s.PrimaryIPFamily]) == len(s.Config.NodePortAddresses) { 719 errors = append(errors, fmt.Errorf("cluster appears to be dual-stack but nodePortAddresses contains only %s addresses; NodePort connections will be accepted on all local %s IPs", s.PrimaryIPFamily, proxyutil.OtherIPFamily(s.PrimaryIPFamily))) 720 } else if len(cidrsByFamily[s.PrimaryIPFamily]) == 0 { 721 errors = append(errors, fmt.Errorf("cluster appears to be %s-primary but nodePortAddresses contains only %s addresses; NodePort connections will be accepted on all local %s IPs", s.PrimaryIPFamily, proxyutil.OtherIPFamily(s.PrimaryIPFamily), s.PrimaryIPFamily)) 722 } 723 724 return utilerrors.NewAggregate(errors) 725 } 726 727 // checkBadIPConfig checks for bad configuration relative to s.PrimaryIPFamily. 728 // Historically, we did not check most of the config options, so we cannot retroactively 729 // make IP family mismatches in those options be fatal. When we add new options to check 730 // here, we should make problems with those options be fatal. 731 func checkBadIPConfig(s *ProxyServer, dualStackSupported bool) (err error, fatal bool) { 732 var errors []error 733 var badFamily netutils.IPFamily 734 735 if s.PrimaryIPFamily == v1.IPv4Protocol { 736 badFamily = netutils.IPv6 737 } else { 738 badFamily = netutils.IPv4 739 } 740 741 var clusterType string 742 if dualStackSupported { 743 clusterType = fmt.Sprintf("%s-primary", s.PrimaryIPFamily) 744 } else { 745 clusterType = fmt.Sprintf("%s-only", s.PrimaryIPFamily) 746 } 747 748 if s.Config.ClusterCIDR != "" { 749 clusterCIDRs := strings.Split(s.Config.ClusterCIDR, ",") 750 if badCIDRs(clusterCIDRs, badFamily) { 751 errors = append(errors, fmt.Errorf("cluster is %s but clusterCIDRs contains only IPv%s addresses", clusterType, badFamily)) 752 if s.Config.DetectLocalMode == kubeproxyconfig.LocalModeClusterCIDR && !dualStackSupported { 753 // This has always been a fatal error 754 fatal = true 755 } 756 } 757 } 758 759 if badCIDRs(s.podCIDRs, badFamily) { 760 errors = append(errors, fmt.Errorf("cluster is %s but node.spec.podCIDRs contains only IPv%s addresses", clusterType, badFamily)) 761 if s.Config.DetectLocalMode == kubeproxyconfig.LocalModeNodeCIDR { 762 // This has always been a fatal error 763 fatal = true 764 } 765 } 766 767 if netutils.IPFamilyOfString(s.Config.Winkernel.SourceVip) == badFamily { 768 errors = append(errors, fmt.Errorf("cluster is %s but winkernel.sourceVip is IPv%s", clusterType, badFamily)) 769 } 770 771 // In some cases, wrong-IP-family is only a problem when the secondary IP family 772 // isn't present at all. 773 if !dualStackSupported { 774 if badCIDRs(s.Config.IPVS.ExcludeCIDRs, badFamily) { 775 errors = append(errors, fmt.Errorf("cluster is %s but ipvs.excludeCIDRs contains only IPv%s addresses", clusterType, badFamily)) 776 } 777 778 if badBindAddress(s.Config.HealthzBindAddress, badFamily) { 779 errors = append(errors, fmt.Errorf("cluster is %s but healthzBindAddress is IPv%s", clusterType, badFamily)) 780 } 781 if badBindAddress(s.Config.MetricsBindAddress, badFamily) { 782 errors = append(errors, fmt.Errorf("cluster is %s but metricsBindAddress is IPv%s", clusterType, badFamily)) 783 } 784 } 785 786 // Note that s.Config.NodePortAddresses gets checked as part of checkBadConfig() 787 // so it doesn't need to be checked here. 788 789 return utilerrors.NewAggregate(errors), fatal 790 } 791 792 // badCIDRs returns true if cidrs is a non-empty list of CIDRs, all of wrongFamily. 793 func badCIDRs(cidrs []string, wrongFamily netutils.IPFamily) bool { 794 if len(cidrs) == 0 { 795 return false 796 } 797 for _, cidr := range cidrs { 798 if netutils.IPFamilyOfCIDRString(cidr) != wrongFamily { 799 return false 800 } 801 } 802 return true 803 } 804 805 // badBindAddress returns true if bindAddress is an "IP:port" string where IP is a 806 // non-zero IP of wrongFamily. 807 func badBindAddress(bindAddress string, wrongFamily netutils.IPFamily) bool { 808 if host, _, _ := net.SplitHostPort(bindAddress); host != "" { 809 ip := netutils.ParseIPSloppy(host) 810 if ip != nil && netutils.IPFamilyOf(ip) == wrongFamily && !ip.IsUnspecified() { 811 return true 812 } 813 } 814 return false 815 } 816 817 // createClient creates a kube client from the given config and masterOverride. 818 // TODO remove masterOverride when CLI flags are removed. 819 func createClient(ctx context.Context, config componentbaseconfig.ClientConnectionConfiguration, masterOverride string) (clientset.Interface, error) { 820 logger := klog.FromContext(ctx) 821 var kubeConfig *rest.Config 822 var err error 823 824 if len(config.Kubeconfig) == 0 && len(masterOverride) == 0 { 825 logger.Info("Neither kubeconfig file nor master URL was specified, falling back to in-cluster config") 826 kubeConfig, err = rest.InClusterConfig() 827 } else { 828 // This creates a client, first loading any specified kubeconfig 829 // file, and then overriding the Master flag, if non-empty. 830 kubeConfig, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig( 831 &clientcmd.ClientConfigLoadingRules{ExplicitPath: config.Kubeconfig}, 832 &clientcmd.ConfigOverrides{ClusterInfo: clientcmdapi.Cluster{Server: masterOverride}}).ClientConfig() 833 } 834 if err != nil { 835 return nil, err 836 } 837 838 kubeConfig.AcceptContentTypes = config.AcceptContentTypes 839 kubeConfig.ContentType = config.ContentType 840 kubeConfig.QPS = config.QPS 841 kubeConfig.Burst = int(config.Burst) 842 843 client, err := clientset.NewForConfig(kubeConfig) 844 if err != nil { 845 return nil, err 846 } 847 848 return client, nil 849 } 850 851 func serveHealthz(ctx context.Context, hz *healthcheck.ProxierHealthServer, errCh chan error) { 852 logger := klog.FromContext(ctx) 853 if hz == nil { 854 return 855 } 856 857 fn := func() { 858 err := hz.Run() 859 if err != nil { 860 logger.Error(err, "Healthz server failed") 861 if errCh != nil { 862 errCh <- fmt.Errorf("healthz server failed: %v", err) 863 // if in hardfail mode, never retry again 864 blockCh := make(chan error) 865 <-blockCh 866 } 867 } else { 868 logger.Error(nil, "Healthz server returned without error") 869 } 870 } 871 go wait.Until(fn, 5*time.Second, ctx.Done()) 872 } 873 874 func serveMetrics(bindAddress string, proxyMode kubeproxyconfig.ProxyMode, enableProfiling bool, errCh chan error) { 875 if len(bindAddress) == 0 { 876 return 877 } 878 879 proxyMux := mux.NewPathRecorderMux("kube-proxy") 880 healthz.InstallHandler(proxyMux) 881 slis.SLIMetricsWithReset{}.Install(proxyMux) 882 883 proxyMux.HandleFunc("/proxyMode", func(w http.ResponseWriter, r *http.Request) { 884 w.Header().Set("Content-Type", "text/plain; charset=utf-8") 885 w.Header().Set("X-Content-Type-Options", "nosniff") 886 fmt.Fprintf(w, "%s", proxyMode) 887 }) 888 889 proxyMux.Handle("/metrics", legacyregistry.Handler()) 890 891 if enableProfiling { 892 routes.Profiling{}.Install(proxyMux) 893 routes.DebugFlags{}.Install(proxyMux, "v", routes.StringFlagPutHandler(logs.GlogSetter)) 894 } 895 896 configz.InstallHandler(proxyMux) 897 898 fn := func() { 899 err := http.ListenAndServe(bindAddress, proxyMux) 900 if err != nil { 901 err = fmt.Errorf("starting metrics server failed: %v", err) 902 utilruntime.HandleError(err) 903 if errCh != nil { 904 errCh <- err 905 // if in hardfail mode, never retry again 906 blockCh := make(chan error) 907 <-blockCh 908 } 909 } 910 } 911 go wait.Until(fn, 5*time.Second, wait.NeverStop) 912 } 913 914 // Run runs the specified ProxyServer. This should never exit (unless CleanupAndExit is set). 915 // TODO: At the moment, Run() cannot return a nil error, otherwise it's caller will never exit. Update callers of Run to handle nil errors. 916 func (s *ProxyServer) Run(ctx context.Context) error { 917 logger := klog.FromContext(ctx) 918 // To help debugging, immediately log version 919 logger.Info("Version info", "version", version.Get()) 920 921 logger.Info("Golang settings", "GOGC", os.Getenv("GOGC"), "GOMAXPROCS", os.Getenv("GOMAXPROCS"), "GOTRACEBACK", os.Getenv("GOTRACEBACK")) 922 923 proxymetrics.RegisterMetrics(s.Config.Mode) 924 925 // TODO(vmarmol): Use container config for this. 926 var oomAdjuster *oom.OOMAdjuster 927 if s.Config.OOMScoreAdj != nil { 928 oomAdjuster = oom.NewOOMAdjuster() 929 if err := oomAdjuster.ApplyOOMScoreAdj(0, int(*s.Config.OOMScoreAdj)); err != nil { 930 logger.V(2).Info("Failed to apply OOMScore", "err", err) 931 } 932 } 933 934 if s.Broadcaster != nil { 935 stopCh := make(chan struct{}) 936 s.Broadcaster.StartRecordingToSink(stopCh) 937 } 938 939 // TODO(thockin): make it possible for healthz and metrics to be on the same port. 940 941 var healthzErrCh, metricsErrCh chan error 942 if s.Config.BindAddressHardFail { 943 healthzErrCh = make(chan error) 944 metricsErrCh = make(chan error) 945 } 946 947 // Start up a healthz server if requested 948 serveHealthz(ctx, s.HealthzServer, healthzErrCh) 949 950 // Start up a metrics server if requested 951 serveMetrics(s.Config.MetricsBindAddress, s.Config.Mode, s.Config.EnableProfiling, metricsErrCh) 952 953 noProxyName, err := labels.NewRequirement(apis.LabelServiceProxyName, selection.DoesNotExist, nil) 954 if err != nil { 955 return err 956 } 957 958 noHeadlessEndpoints, err := labels.NewRequirement(v1.IsHeadlessService, selection.DoesNotExist, nil) 959 if err != nil { 960 return err 961 } 962 963 labelSelector := labels.NewSelector() 964 labelSelector = labelSelector.Add(*noProxyName, *noHeadlessEndpoints) 965 966 // Make informers that filter out objects that want a non-default service proxy. 967 informerFactory := informers.NewSharedInformerFactoryWithOptions(s.Client, s.Config.ConfigSyncPeriod.Duration, 968 informers.WithTweakListOptions(func(options *metav1.ListOptions) { 969 options.LabelSelector = labelSelector.String() 970 })) 971 972 // Create configs (i.e. Watches for Services, EndpointSlices and ServiceCIDRs) 973 // Note: RegisterHandler() calls need to happen before creation of Sources because sources 974 // only notify on changes, and the initial update (on process start) may be lost if no handlers 975 // are registered yet. 976 serviceConfig := config.NewServiceConfig(ctx, informerFactory.Core().V1().Services(), s.Config.ConfigSyncPeriod.Duration) 977 serviceConfig.RegisterEventHandler(s.Proxier) 978 go serviceConfig.Run(ctx.Done()) 979 980 endpointSliceConfig := config.NewEndpointSliceConfig(ctx, informerFactory.Discovery().V1().EndpointSlices(), s.Config.ConfigSyncPeriod.Duration) 981 endpointSliceConfig.RegisterEventHandler(s.Proxier) 982 go endpointSliceConfig.Run(ctx.Done()) 983 984 if utilfeature.DefaultFeatureGate.Enabled(features.MultiCIDRServiceAllocator) { 985 serviceCIDRConfig := config.NewServiceCIDRConfig(ctx, informerFactory.Networking().V1alpha1().ServiceCIDRs(), s.Config.ConfigSyncPeriod.Duration) 986 serviceCIDRConfig.RegisterEventHandler(s.Proxier) 987 go serviceCIDRConfig.Run(wait.NeverStop) 988 } 989 // This has to start after the calls to NewServiceConfig because that 990 // function must configure its shared informer event handlers first. 991 informerFactory.Start(wait.NeverStop) 992 993 // Make an informer that selects for our nodename. 994 currentNodeInformerFactory := informers.NewSharedInformerFactoryWithOptions(s.Client, s.Config.ConfigSyncPeriod.Duration, 995 informers.WithTweakListOptions(func(options *metav1.ListOptions) { 996 options.FieldSelector = fields.OneTermEqualSelector("metadata.name", s.NodeRef.Name).String() 997 })) 998 nodeConfig := config.NewNodeConfig(ctx, currentNodeInformerFactory.Core().V1().Nodes(), s.Config.ConfigSyncPeriod.Duration) 999 // https://issues.k8s.io/111321 1000 if s.Config.DetectLocalMode == kubeproxyconfig.LocalModeNodeCIDR { 1001 nodeConfig.RegisterEventHandler(proxy.NewNodePodCIDRHandler(ctx, s.podCIDRs)) 1002 } 1003 if utilfeature.DefaultFeatureGate.Enabled(features.KubeProxyDrainingTerminatingNodes) { 1004 nodeConfig.RegisterEventHandler(&proxy.NodeEligibleHandler{ 1005 HealthServer: s.HealthzServer, 1006 }) 1007 } 1008 nodeConfig.RegisterEventHandler(s.Proxier) 1009 1010 go nodeConfig.Run(wait.NeverStop) 1011 1012 // This has to start after the calls to NewNodeConfig because that must 1013 // configure the shared informer event handler first. 1014 currentNodeInformerFactory.Start(wait.NeverStop) 1015 1016 // Birth Cry after the birth is successful 1017 s.birthCry() 1018 1019 go s.Proxier.SyncLoop() 1020 1021 select { 1022 case err = <-healthzErrCh: 1023 s.Recorder.Eventf(s.NodeRef, nil, api.EventTypeWarning, "FailedToStartProxierHealthcheck", "StartKubeProxy", err.Error()) 1024 case err = <-metricsErrCh: 1025 s.Recorder.Eventf(s.NodeRef, nil, api.EventTypeWarning, "FailedToStartMetricServer", "StartKubeProxy", err.Error()) 1026 } 1027 return err 1028 } 1029 1030 func (s *ProxyServer) birthCry() { 1031 s.Recorder.Eventf(s.NodeRef, nil, api.EventTypeNormal, "Starting", "StartKubeProxy", "") 1032 } 1033 1034 // detectNodeIPs returns the proxier's "node IP" or IPs, and the IP family to use if the 1035 // node turns out to be incapable of dual-stack. (Note that kube-proxy normally runs as 1036 // dual-stack if the backend is capable of supporting both IP families, regardless of 1037 // whether the node is *actually* configured as dual-stack or not.) 1038 1039 // (Note that on Linux, the node IPs are used only to determine whether a given 1040 // LoadBalancerSourceRanges value matches the node or not. In particular, they are *not* 1041 // used for NodePort handling.) 1042 // 1043 // The order of precedence is: 1044 // 1. if bindAddress is not 0.0.0.0 or ::, then it is used as the primary IP. 1045 // 2. if rawNodeIPs is not empty, then its address(es) is/are used 1046 // 3. otherwise the node IPs are 127.0.0.1 and ::1 1047 func detectNodeIPs(ctx context.Context, rawNodeIPs []net.IP, bindAddress string) (v1.IPFamily, map[v1.IPFamily]net.IP) { 1048 logger := klog.FromContext(ctx) 1049 primaryFamily := v1.IPv4Protocol 1050 nodeIPs := map[v1.IPFamily]net.IP{ 1051 v1.IPv4Protocol: net.IPv4(127, 0, 0, 1), 1052 v1.IPv6Protocol: net.IPv6loopback, 1053 } 1054 1055 if len(rawNodeIPs) > 0 { 1056 if !netutils.IsIPv4(rawNodeIPs[0]) { 1057 primaryFamily = v1.IPv6Protocol 1058 } 1059 nodeIPs[primaryFamily] = rawNodeIPs[0] 1060 if len(rawNodeIPs) > 1 { 1061 // If more than one address is returned, they are guaranteed to be of different families 1062 family := v1.IPv4Protocol 1063 if !netutils.IsIPv4(rawNodeIPs[1]) { 1064 family = v1.IPv6Protocol 1065 } 1066 nodeIPs[family] = rawNodeIPs[1] 1067 } 1068 } 1069 1070 // If a bindAddress is passed, override the primary IP 1071 bindIP := netutils.ParseIPSloppy(bindAddress) 1072 if bindIP != nil && !bindIP.IsUnspecified() { 1073 if netutils.IsIPv4(bindIP) { 1074 primaryFamily = v1.IPv4Protocol 1075 } else { 1076 primaryFamily = v1.IPv6Protocol 1077 } 1078 nodeIPs[primaryFamily] = bindIP 1079 } 1080 1081 if nodeIPs[primaryFamily].IsLoopback() { 1082 logger.Info("Can't determine this node's IP, assuming loopback; if this is incorrect, please set the --bind-address flag") 1083 } 1084 return primaryFamily, nodeIPs 1085 } 1086 1087 // getNodeIP returns IPs for the node with the provided name. If 1088 // required, it will wait for the node to be created. 1089 func getNodeIPs(ctx context.Context, client clientset.Interface, name string) []net.IP { 1090 logger := klog.FromContext(ctx) 1091 var nodeIPs []net.IP 1092 backoff := wait.Backoff{ 1093 Steps: 6, 1094 Duration: 1 * time.Second, 1095 Factor: 2.0, 1096 Jitter: 0.2, 1097 } 1098 1099 err := wait.ExponentialBackoff(backoff, func() (bool, error) { 1100 node, err := client.CoreV1().Nodes().Get(ctx, name, metav1.GetOptions{}) 1101 if err != nil { 1102 logger.Error(err, "Failed to retrieve node info") 1103 return false, nil 1104 } 1105 nodeIPs, err = utilnode.GetNodeHostIPs(node) 1106 if err != nil { 1107 logger.Error(err, "Failed to retrieve node IPs") 1108 return false, nil 1109 } 1110 return true, nil 1111 }) 1112 if err == nil { 1113 logger.Info("Successfully retrieved node IP(s)", "IPs", nodeIPs) 1114 } 1115 return nodeIPs 1116 }