mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			354 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			354 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2017 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package validation
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"net"
 | 
						|
	"runtime"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	utilnet "k8s.io/apimachinery/pkg/util/net"
 | 
						|
	"k8s.io/apimachinery/pkg/util/sets"
 | 
						|
	"k8s.io/apimachinery/pkg/util/validation/field"
 | 
						|
	utilfeature "k8s.io/apiserver/pkg/util/feature"
 | 
						|
	componentbaseconfig "k8s.io/component-base/config"
 | 
						|
	logsapi "k8s.io/component-base/logs/api/v1"
 | 
						|
	"k8s.io/component-base/metrics"
 | 
						|
	apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
 | 
						|
	"k8s.io/kubernetes/pkg/features"
 | 
						|
	kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config"
 | 
						|
	netutils "k8s.io/utils/net"
 | 
						|
)
 | 
						|
 | 
						|
// Validate validates the configuration of kube-proxy
 | 
						|
func Validate(config *kubeproxyconfig.KubeProxyConfiguration) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
 | 
						|
	newPath := field.NewPath("KubeProxyConfiguration")
 | 
						|
 | 
						|
	effectiveFeatures := utilfeature.DefaultFeatureGate.DeepCopy()
 | 
						|
	if err := effectiveFeatures.SetFromMap(config.FeatureGates); err != nil {
 | 
						|
		allErrs = append(allErrs, field.Invalid(newPath.Child("featureGates"), config.FeatureGates, err.Error()))
 | 
						|
	}
 | 
						|
 | 
						|
	allErrs = append(allErrs, validateKubeProxyIPTablesConfiguration(config.IPTables, newPath.Child("KubeProxyIPTablesConfiguration"))...)
 | 
						|
	switch config.Mode {
 | 
						|
	case kubeproxyconfig.ProxyModeIPVS:
 | 
						|
		allErrs = append(allErrs, validateKubeProxyIPVSConfiguration(config.IPVS, newPath.Child("KubeProxyIPVSConfiguration"))...)
 | 
						|
	case kubeproxyconfig.ProxyModeNFTables:
 | 
						|
		allErrs = append(allErrs, validateKubeProxyNFTablesConfiguration(config.NFTables, newPath.Child("KubeProxyNFTablesConfiguration"))...)
 | 
						|
	}
 | 
						|
	allErrs = append(allErrs, validateKubeProxyConntrackConfiguration(config.Conntrack, newPath.Child("KubeProxyConntrackConfiguration"))...)
 | 
						|
	allErrs = append(allErrs, validateProxyMode(config.Mode, newPath.Child("Mode"))...)
 | 
						|
	allErrs = append(allErrs, validateClientConnectionConfiguration(config.ClientConnection, newPath.Child("ClientConnection"))...)
 | 
						|
 | 
						|
	if config.OOMScoreAdj != nil && (*config.OOMScoreAdj < -1000 || *config.OOMScoreAdj > 1000) {
 | 
						|
		allErrs = append(allErrs, field.Invalid(newPath.Child("OOMScoreAdj"), *config.OOMScoreAdj, "must be within the range [-1000, 1000]"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.ConfigSyncPeriod.Duration <= 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(newPath.Child("ConfigSyncPeriod"), config.ConfigSyncPeriod, "must be greater than 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if netutils.ParseIPSloppy(config.BindAddress) == nil {
 | 
						|
		allErrs = append(allErrs, field.Invalid(newPath.Child("BindAddress"), config.BindAddress, "not a valid textual representation of an IP address"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.HealthzBindAddress != "" {
 | 
						|
		allErrs = append(allErrs, validateHostPort(config.HealthzBindAddress, newPath.Child("HealthzBindAddress"))...)
 | 
						|
	}
 | 
						|
	allErrs = append(allErrs, validateHostPort(config.MetricsBindAddress, newPath.Child("MetricsBindAddress"))...)
 | 
						|
 | 
						|
	if config.ClusterCIDR != "" {
 | 
						|
		cidrs := strings.Split(config.ClusterCIDR, ",")
 | 
						|
		switch {
 | 
						|
		case len(cidrs) > 2:
 | 
						|
			allErrs = append(allErrs, field.Invalid(newPath.Child("ClusterCIDR"), config.ClusterCIDR, "only one CIDR allowed or a valid DualStack CIDR (e.g. 10.100.0.0/16,fde4:8dba:82e1::/48)"))
 | 
						|
		// if DualStack and two cidrs validate if there is at least one of each IP family
 | 
						|
		case len(cidrs) == 2:
 | 
						|
			isDual, err := netutils.IsDualStackCIDRStrings(cidrs)
 | 
						|
			if err != nil || !isDual {
 | 
						|
				allErrs = append(allErrs, field.Invalid(newPath.Child("ClusterCIDR"), config.ClusterCIDR, "must be a valid DualStack CIDR (e.g. 10.100.0.0/16,fde4:8dba:82e1::/48)"))
 | 
						|
			}
 | 
						|
		// if we are here means that len(cidrs) == 1, we need to validate it
 | 
						|
		default:
 | 
						|
			if _, _, err := netutils.ParseCIDRSloppy(config.ClusterCIDR); err != nil {
 | 
						|
				allErrs = append(allErrs, field.Invalid(newPath.Child("ClusterCIDR"), config.ClusterCIDR, "must be a valid CIDR block (e.g. 10.100.0.0/16 or fde4:8dba:82e1::/48)"))
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if _, err := utilnet.ParsePortRange(config.PortRange); err != nil {
 | 
						|
		allErrs = append(allErrs, field.Invalid(newPath.Child("PortRange"), config.PortRange, "must be a valid port range (e.g. 300-2000)"))
 | 
						|
	}
 | 
						|
 | 
						|
	allErrs = append(allErrs, validateKubeProxyNodePortAddress(config.NodePortAddresses, newPath.Child("NodePortAddresses"))...)
 | 
						|
	allErrs = append(allErrs, validateShowHiddenMetricsVersion(config.ShowHiddenMetricsForVersion, newPath.Child("ShowHiddenMetricsForVersion"))...)
 | 
						|
 | 
						|
	allErrs = append(allErrs, validateDetectLocalMode(config.DetectLocalMode, newPath.Child("DetectLocalMode"))...)
 | 
						|
	if config.DetectLocalMode == kubeproxyconfig.LocalModeBridgeInterface {
 | 
						|
		allErrs = append(allErrs, validateInterface(config.DetectLocal.BridgeInterface, newPath.Child("InterfaceName"))...)
 | 
						|
	}
 | 
						|
	if config.DetectLocalMode == kubeproxyconfig.LocalModeInterfaceNamePrefix {
 | 
						|
		allErrs = append(allErrs, validateInterface(config.DetectLocal.InterfaceNamePrefix, newPath.Child("InterfacePrefix"))...)
 | 
						|
	}
 | 
						|
	allErrs = append(allErrs, logsapi.Validate(&config.Logging, effectiveFeatures, newPath.Child("logging"))...)
 | 
						|
 | 
						|
	return allErrs
 | 
						|
}
 | 
						|
 | 
						|
func validateKubeProxyIPTablesConfiguration(config kubeproxyconfig.KubeProxyIPTablesConfiguration, fldPath *field.Path) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
 | 
						|
	if config.MasqueradeBit != nil && (*config.MasqueradeBit < 0 || *config.MasqueradeBit > 31) {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("MasqueradeBit"), config.MasqueradeBit, "must be within the range [0, 31]"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.SyncPeriod.Duration <= 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("SyncPeriod"), config.SyncPeriod, "must be greater than 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.MinSyncPeriod.Duration < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("MinSyncPeriod"), config.MinSyncPeriod, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.MinSyncPeriod.Duration > config.SyncPeriod.Duration {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("SyncPeriod"), config.MinSyncPeriod, fmt.Sprintf("must be greater than or equal to %s", fldPath.Child("MinSyncPeriod").String())))
 | 
						|
	}
 | 
						|
 | 
						|
	return allErrs
 | 
						|
}
 | 
						|
 | 
						|
func validateKubeProxyIPVSConfiguration(config kubeproxyconfig.KubeProxyIPVSConfiguration, fldPath *field.Path) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
 | 
						|
	if config.SyncPeriod.Duration <= 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("SyncPeriod"), config.SyncPeriod, "must be greater than 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.MinSyncPeriod.Duration < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("MinSyncPeriod"), config.MinSyncPeriod, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.MinSyncPeriod.Duration > config.SyncPeriod.Duration {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("SyncPeriod"), config.MinSyncPeriod, fmt.Sprintf("must be greater than or equal to %s", fldPath.Child("MinSyncPeriod").String())))
 | 
						|
	}
 | 
						|
 | 
						|
	allErrs = append(allErrs, validateIPVSTimeout(config, fldPath)...)
 | 
						|
	allErrs = append(allErrs, validateIPVSExcludeCIDRs(config.ExcludeCIDRs, fldPath.Child("ExcludeCidrs"))...)
 | 
						|
 | 
						|
	return allErrs
 | 
						|
}
 | 
						|
 | 
						|
func validateKubeProxyNFTablesConfiguration(config kubeproxyconfig.KubeProxyNFTablesConfiguration, fldPath *field.Path) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
 | 
						|
	if config.MasqueradeBit != nil && (*config.MasqueradeBit < 0 || *config.MasqueradeBit > 31) {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("MasqueradeBit"), config.MasqueradeBit, "must be within the range [0, 31]"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.SyncPeriod.Duration <= 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("SyncPeriod"), config.SyncPeriod, "must be greater than 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.MinSyncPeriod.Duration < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("MinSyncPeriod"), config.MinSyncPeriod, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.MinSyncPeriod.Duration > config.SyncPeriod.Duration {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("SyncPeriod"), config.MinSyncPeriod, fmt.Sprintf("must be greater than or equal to %s", fldPath.Child("MinSyncPeriod").String())))
 | 
						|
	}
 | 
						|
 | 
						|
	return allErrs
 | 
						|
}
 | 
						|
 | 
						|
func validateKubeProxyConntrackConfiguration(config kubeproxyconfig.KubeProxyConntrackConfiguration, fldPath *field.Path) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
 | 
						|
	if config.MaxPerCore != nil && *config.MaxPerCore < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("MaxPerCore"), config.MaxPerCore, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.Min != nil && *config.Min < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("Min"), config.Min, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	// config.TCPEstablishedTimeout has a default value, so can't be nil.
 | 
						|
	if config.TCPEstablishedTimeout.Duration < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("TCPEstablishedTimeout"), config.TCPEstablishedTimeout, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	// config.TCPCloseWaitTimeout has a default value, so can't be nil.
 | 
						|
	if config.TCPCloseWaitTimeout.Duration < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("TCPCloseWaitTimeout"), config.TCPCloseWaitTimeout, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.UDPTimeout.Duration < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("UDPTimeout"), config.UDPTimeout, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.UDPStreamTimeout.Duration < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("UDPStreamTimeout"), config.UDPStreamTimeout, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	return allErrs
 | 
						|
}
 | 
						|
 | 
						|
func validateProxyMode(mode kubeproxyconfig.ProxyMode, fldPath *field.Path) field.ErrorList {
 | 
						|
	if runtime.GOOS == "windows" {
 | 
						|
		return validateProxyModeWindows(mode, fldPath)
 | 
						|
	}
 | 
						|
 | 
						|
	return validateProxyModeLinux(mode, fldPath)
 | 
						|
}
 | 
						|
 | 
						|
func validateProxyModeLinux(mode kubeproxyconfig.ProxyMode, fldPath *field.Path) field.ErrorList {
 | 
						|
	validModes := sets.New[string](
 | 
						|
		string(kubeproxyconfig.ProxyModeIPTables),
 | 
						|
		string(kubeproxyconfig.ProxyModeIPVS),
 | 
						|
	)
 | 
						|
 | 
						|
	if utilfeature.DefaultFeatureGate.Enabled(features.NFTablesProxyMode) {
 | 
						|
		validModes.Insert(string(kubeproxyconfig.ProxyModeNFTables))
 | 
						|
	}
 | 
						|
 | 
						|
	if mode == "" || validModes.Has(string(mode)) {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	errMsg := fmt.Sprintf("must be %s or blank (blank means the best-available proxy [currently iptables])", strings.Join(sets.List(validModes), ", "))
 | 
						|
	return field.ErrorList{field.Invalid(fldPath.Child("ProxyMode"), string(mode), errMsg)}
 | 
						|
}
 | 
						|
 | 
						|
func validateProxyModeWindows(mode kubeproxyconfig.ProxyMode, fldPath *field.Path) field.ErrorList {
 | 
						|
	validModes := sets.New[string](
 | 
						|
		string(kubeproxyconfig.ProxyModeKernelspace),
 | 
						|
	)
 | 
						|
 | 
						|
	if mode == "" || validModes.Has(string(mode)) {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	errMsg := fmt.Sprintf("must be %s or blank (blank means the most-available proxy [currently 'kernelspace'])", strings.Join(sets.List(validModes), ", "))
 | 
						|
	return field.ErrorList{field.Invalid(fldPath.Child("ProxyMode"), string(mode), errMsg)}
 | 
						|
}
 | 
						|
 | 
						|
func validateDetectLocalMode(mode kubeproxyconfig.LocalMode, fldPath *field.Path) field.ErrorList {
 | 
						|
	validModes := []string{
 | 
						|
		string(kubeproxyconfig.LocalModeClusterCIDR),
 | 
						|
		string(kubeproxyconfig.LocalModeNodeCIDR),
 | 
						|
		string(kubeproxyconfig.LocalModeBridgeInterface),
 | 
						|
		string(kubeproxyconfig.LocalModeInterfaceNamePrefix),
 | 
						|
		"",
 | 
						|
	}
 | 
						|
 | 
						|
	if sets.New(validModes...).Has(string(mode)) {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	return field.ErrorList{field.NotSupported(fldPath, string(mode), validModes)}
 | 
						|
}
 | 
						|
 | 
						|
func validateClientConnectionConfiguration(config componentbaseconfig.ClientConnectionConfiguration, fldPath *field.Path) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
	allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(config.Burst), fldPath.Child("Burst"))...)
 | 
						|
	return allErrs
 | 
						|
}
 | 
						|
 | 
						|
func validateHostPort(input string, fldPath *field.Path) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
 | 
						|
	hostIP, port, err := net.SplitHostPort(input)
 | 
						|
	if err != nil {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath, input, "must be IP:port"))
 | 
						|
		return allErrs
 | 
						|
	}
 | 
						|
 | 
						|
	if ip := netutils.ParseIPSloppy(hostIP); ip == nil {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath, hostIP, "must be a valid IP"))
 | 
						|
	}
 | 
						|
 | 
						|
	if p, err := strconv.Atoi(port); err != nil {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath, port, "must be a valid port"))
 | 
						|
	} else if p < 1 || p > 65535 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath, port, "must be a valid port"))
 | 
						|
	}
 | 
						|
 | 
						|
	return allErrs
 | 
						|
}
 | 
						|
 | 
						|
func validateKubeProxyNodePortAddress(nodePortAddresses []string, fldPath *field.Path) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
 | 
						|
	for i := range nodePortAddresses {
 | 
						|
		if _, _, err := netutils.ParseCIDRSloppy(nodePortAddresses[i]); err != nil {
 | 
						|
			allErrs = append(allErrs, field.Invalid(fldPath.Index(i), nodePortAddresses[i], "must be a valid CIDR"))
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return allErrs
 | 
						|
}
 | 
						|
 | 
						|
func validateIPVSTimeout(config kubeproxyconfig.KubeProxyIPVSConfiguration, fldPath *field.Path) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
 | 
						|
	if config.TCPTimeout.Duration < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("TCPTimeout"), config.TCPTimeout, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.TCPFinTimeout.Duration < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("TCPFinTimeout"), config.TCPFinTimeout, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	if config.UDPTimeout.Duration < 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath.Child("UDPTimeout"), config.UDPTimeout, "must be greater than or equal to 0"))
 | 
						|
	}
 | 
						|
 | 
						|
	return allErrs
 | 
						|
}
 | 
						|
 | 
						|
func validateIPVSExcludeCIDRs(excludeCIDRs []string, fldPath *field.Path) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
 | 
						|
	for i := range excludeCIDRs {
 | 
						|
		if _, _, err := netutils.ParseCIDRSloppy(excludeCIDRs[i]); err != nil {
 | 
						|
			allErrs = append(allErrs, field.Invalid(fldPath.Index(i), excludeCIDRs[i], "must be a valid CIDR"))
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return allErrs
 | 
						|
}
 | 
						|
 | 
						|
func validateShowHiddenMetricsVersion(version string, fldPath *field.Path) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
	errs := metrics.ValidateShowHiddenMetricsVersion(version)
 | 
						|
	for _, e := range errs {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath, version, e.Error()))
 | 
						|
	}
 | 
						|
 | 
						|
	return allErrs
 | 
						|
}
 | 
						|
 | 
						|
func validateInterface(iface string, fldPath *field.Path) field.ErrorList {
 | 
						|
	allErrs := field.ErrorList{}
 | 
						|
	if len(iface) == 0 {
 | 
						|
		allErrs = append(allErrs, field.Invalid(fldPath, iface, "must not be empty"))
 | 
						|
	}
 | 
						|
	return allErrs
 | 
						|
}
 |