diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go index 15f24edc451..2c78435a5f0 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation.go @@ -372,33 +372,32 @@ func ValidateHostPort(endpoint string, fldPath *field.Path) field.ErrorList { // ValidateIPNetFromString validates network portion of ip address func ValidateIPNetFromString(subnetStr string, minAddrs int64, isDualStack bool, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} - if isDualStack { - subnets, err := utilnet.ParseCIDRs(strings.Split(subnetStr, ",")) + subnets, err := utilnet.ParseCIDRs(strings.Split(subnetStr, ",")) + if err != nil { + allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "couldn't parse subnet")) + return allErrs + } + switch { + // if DualStack only 2 CIDRs allowed + case isDualStack && len(subnets) > 2: + allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "expected one (IPv4 or IPv6) CIDR or two CIDRs from each family for dual-stack networking")) + // if DualStack and there are 2 CIDRs validate if there is at least one of each IP family + case isDualStack && len(subnets) == 2: + areDualStackCIDRs, err := utilnet.IsDualStackCIDRs(subnets) if err != nil { allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, err.Error())) - } else { - areDualStackCIDRs, err := utilnet.IsDualStackCIDRs(subnets) - if err != nil { - allErrs = append(allErrs, field.Invalid(fldPath, subnets, err.Error())) - } else if !areDualStackCIDRs { - allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "expected at least one IP from each family (v4 or v6) for dual-stack networking")) - } - for _, s := range subnets { - numAddresses := utilnet.RangeSize(s) - if numAddresses < minAddrs { - allErrs = append(allErrs, field.Invalid(fldPath, s, "subnet is too small")) - } - } + } else if !areDualStackCIDRs { + allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "expected one (IPv4 or IPv6) CIDR or two CIDRs from each family for dual-stack networking")) } - } else { - _, svcSubnet, err := net.ParseCIDR(subnetStr) - if err != nil { - allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "couldn't parse subnet")) - return allErrs - } - numAddresses := utilnet.RangeSize(svcSubnet) + // if not DualStack only one CIDR allowed + case !isDualStack && len(subnets) > 1: + allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "only one CIDR allowed for single-stack networking")) + } + // validate the subnet/s + for _, s := range subnets { + numAddresses := utilnet.RangeSize(s) if numAddresses < minAddrs { - allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "subnet is too small")) + allErrs = append(allErrs, field.Invalid(fldPath, s, "subnet is too small")) } } return allErrs diff --git a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go index 0d5ff65aabb..71cd3f5c8b9 100644 --- a/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go +++ b/cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go @@ -197,18 +197,21 @@ func TestValidateIPNetFromString(t *testing.T) { expected bool }{ {"invalid missing CIDR", "", 0, false, false}, + {"invalid CIDR", "a", 0, false, false}, {"invalid CIDR missing decimal points in IPv4 address and / mask", "1234", 0, false, false}, {"invalid CIDR use of letters instead of numbers and / mask", "abc", 0, false, false}, {"invalid IPv4 address provided instead of CIDR representation", "1.2.3.4", 0, false, false}, {"invalid IPv6 address provided instead of CIDR representation", "2001:db8::1", 0, false, false}, + {"invalid multiple CIDR provided in a single stack cluster", "2001:db8::1/64,1.2.3.4/24", 0, false, false}, + {"invalid multiple CIDR provided in a single stack cluster and one invalid subnet", "2001:db8::1/64,a", 0, false, false}, {"valid, but IPv4 CIDR too small. At least 10 addresses needed", "10.0.0.16/29", 10, false, false}, {"valid, but IPv6 CIDR too small. At least 10 addresses needed", "2001:db8::/125", 10, false, false}, {"valid IPv4 CIDR", "10.0.0.16/12", 10, false, true}, {"valid IPv6 CIDR", "2001:db8::/98", 10, false, true}, // dual-stack: {"invalid missing CIDR", "", 0, true, false}, - {"invalid only an IPv4 CIDR specified", "10.0.0.16/12", 10, true, false}, - {"invalid only an IPv6 CIDR specified", "2001:db8::/98", 10, true, false}, + {"valid dual-stack enabled but only an IPv4 CIDR specified", "10.0.0.16/12", 10, true, true}, + {"valid dual-stack enabled but only an IPv6 CIDR specified", "2001:db8::/98", 10, true, true}, {"invalid IPv4 address provided instead of CIDR representation", "1.2.3.4,2001:db8::/98", 0, true, false}, {"invalid IPv6 address provided instead of CIDR representation", "2001:db8::1,10.0.0.16/12", 0, true, false}, {"valid, but IPv4 CIDR too small. At least 10 addresses needed", "10.0.0.16/29,2001:db8::/98", 10, true, false}, @@ -217,6 +220,8 @@ func TestValidateIPNetFromString(t *testing.T) { {"valid, but only IPv6 family addresses specified. IPv4 CIDR is necessary.", "2001:db8::/98,2005:db8::/98", 10, true, false}, {"valid IPv4 and IPv6 CIDR", "10.0.0.16/12,2001:db8::/98", 10, true, true}, {"valid IPv6 and IPv4 CIDR", "10.0.0.16/12,2001:db8::/98", 10, true, true}, + {"invalid IPv6 and IPv4 CIDR with more than 2 subnets", "10.0.0.16/12,2001:db8::/98,192.168.0.0/16", 10, true, false}, + {"invalid IPv6 and IPv4 CIDR with more than 2 subnets", "10.0.0.16/12,2001:db8::/98,192.168.0.0/16,a.b.c.d/24", 10, true, false}, } for _, rt := range tests { actual := ValidateIPNetFromString(rt.subnet, rt.minaddrs, rt.checkDualStack, nil)