mirror of https://github.com/k3s-io/k3s
271 lines
10 KiB
Go
271 lines
10 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"
|
|
|
|
apimachineryconfig "k8s.io/apimachinery/pkg/apis/config"
|
|
utilnet "k8s.io/apimachinery/pkg/util/net"
|
|
"k8s.io/apimachinery/pkg/util/sets"
|
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
|
kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config"
|
|
)
|
|
|
|
// Validate validates the configuration of kube-proxy
|
|
func Validate(config *kubeproxyconfig.KubeProxyConfiguration) field.ErrorList {
|
|
allErrs := field.ErrorList{}
|
|
|
|
newPath := field.NewPath("KubeProxyConfiguration")
|
|
|
|
allErrs = append(allErrs, validateKubeProxyIPTablesConfiguration(config.IPTables, newPath.Child("KubeProxyIPTablesConfiguration"))...)
|
|
if config.Mode == kubeproxyconfig.ProxyModeIPVS {
|
|
allErrs = append(allErrs, validateKubeProxyIPVSConfiguration(config.IPVS, newPath.Child("KubeProxyIPVSConfiguration"))...)
|
|
}
|
|
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.UDPIdleTimeout.Duration <= 0 {
|
|
allErrs = append(allErrs, field.Invalid(newPath.Child("UDPIdleTimeout"), config.UDPIdleTimeout, "must be greater than 0"))
|
|
}
|
|
|
|
if config.ConfigSyncPeriod.Duration <= 0 {
|
|
allErrs = append(allErrs, field.Invalid(newPath.Child("ConfigSyncPeriod"), config.ConfigSyncPeriod, "must be greater than 0"))
|
|
}
|
|
|
|
if net.ParseIP(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 != "" {
|
|
if _, _, err := net.ParseCIDR(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)"))
|
|
}
|
|
}
|
|
|
|
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"))...)
|
|
|
|
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, validateIPVSSchedulerMethod(kubeproxyconfig.IPVSSchedulerMethod(config.Scheduler), fldPath.Child("Scheduler"))...)
|
|
allErrs = append(allErrs, validateIPVSExcludeCIDRs(config.ExcludeCIDRs, fldPath.Child("ExcludeCidrs"))...)
|
|
|
|
return allErrs
|
|
}
|
|
|
|
func validateKubeProxyConntrackConfiguration(config kubeproxyconfig.KubeProxyConntrackConfiguration, fldPath *field.Path) field.ErrorList {
|
|
allErrs := field.ErrorList{}
|
|
|
|
if config.Max != nil && *config.Max < 0 {
|
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("Max"), config.Max, "must be greater than or equal to 0"))
|
|
}
|
|
|
|
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"))
|
|
}
|
|
|
|
if config.TCPEstablishedTimeout.Duration < 0 {
|
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("TCPEstablishedTimeout"), config.TCPEstablishedTimeout, "must be greater than or equal to 0"))
|
|
}
|
|
|
|
if config.TCPCloseWaitTimeout.Duration < 0 {
|
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("TCPCloseWaitTimeout"), config.TCPCloseWaitTimeout, "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.NewString(
|
|
string(kubeproxyconfig.ProxyModeUserspace),
|
|
string(kubeproxyconfig.ProxyModeIPTables),
|
|
string(kubeproxyconfig.ProxyModeIPVS),
|
|
)
|
|
|
|
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(validModes.List(), ","))
|
|
return field.ErrorList{field.Invalid(fldPath.Child("ProxyMode"), string(mode), errMsg)}
|
|
}
|
|
|
|
func validateProxyModeWindows(mode kubeproxyconfig.ProxyMode, fldPath *field.Path) field.ErrorList {
|
|
validModes := sets.NewString(
|
|
string(kubeproxyconfig.ProxyModeUserspace),
|
|
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 userspace])", strings.Join(validModes.List(), ","))
|
|
return field.ErrorList{field.Invalid(fldPath.Child("ProxyMode"), string(mode), errMsg)}
|
|
}
|
|
|
|
func validateClientConnectionConfiguration(config apimachineryconfig.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 := net.ParseIP(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 validateIPVSSchedulerMethod(scheduler kubeproxyconfig.IPVSSchedulerMethod, fldPath *field.Path) field.ErrorList {
|
|
supportedMethod := []kubeproxyconfig.IPVSSchedulerMethod{
|
|
kubeproxyconfig.RoundRobin,
|
|
kubeproxyconfig.WeightedRoundRobin,
|
|
kubeproxyconfig.LeastConnection,
|
|
kubeproxyconfig.WeightedLeastConnection,
|
|
kubeproxyconfig.LocalityBasedLeastConnection,
|
|
kubeproxyconfig.LocalityBasedLeastConnectionWithReplication,
|
|
kubeproxyconfig.SourceHashing,
|
|
kubeproxyconfig.DestinationHashing,
|
|
kubeproxyconfig.ShortestExpectedDelay,
|
|
kubeproxyconfig.NeverQueue,
|
|
"",
|
|
}
|
|
allErrs := field.ErrorList{}
|
|
var found bool
|
|
for i := range supportedMethod {
|
|
if scheduler == supportedMethod[i] {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
// Not found
|
|
if !found {
|
|
errMsg := fmt.Sprintf("must be in %v, blank means the default algorithm method (currently rr)", supportedMethod)
|
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("Scheduler"), string(scheduler), errMsg))
|
|
}
|
|
return allErrs
|
|
}
|
|
|
|
func validateKubeProxyNodePortAddress(nodePortAddresses []string, fldPath *field.Path) field.ErrorList {
|
|
allErrs := field.ErrorList{}
|
|
|
|
for i := range nodePortAddresses {
|
|
if _, _, err := net.ParseCIDR(nodePortAddresses[i]); err != nil {
|
|
allErrs = append(allErrs, field.Invalid(fldPath, nodePortAddresses, "must be a valid IP block"))
|
|
break
|
|
}
|
|
}
|
|
|
|
return allErrs
|
|
}
|
|
|
|
func validateIPVSExcludeCIDRs(excludeCIDRs []string, fldPath *field.Path) field.ErrorList {
|
|
allErrs := field.ErrorList{}
|
|
|
|
for i := range excludeCIDRs {
|
|
if _, _, err := net.ParseCIDR(excludeCIDRs[i]); err != nil {
|
|
allErrs = append(allErrs, field.Invalid(fldPath, excludeCIDRs, "must be a valid IP block"))
|
|
}
|
|
}
|
|
return allErrs
|
|
}
|