Skip to content

Commit 7fc6b41

Browse files
Antonio OjeaArvinderpal
andcommitted
kubeadm validate maximum service subnet size
Validate that the maximum service subnet size doesn't exceed the limits. Co-authored-by: Arvinderpal Wander <[email protected]>
1 parent 8b52995 commit 7fc6b41

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed

cmd/kubeadm/app/apis/kubeadm/validation/validation.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,25 @@ func ValidateIPNetFromString(subnetStr string, minAddrs int64, isDualStack bool,
403403
return allErrs
404404
}
405405

406+
// ValidateServiceSubnetSize validates that the maximum subnet size is not exceeded
407+
// Should be a small cidr due to how it is stored in etcd.
408+
// bigger cidr (specially those offered by IPv6) will add no value
409+
// and significantly increase snapshotting time.
410+
// NOTE: This is identical to validation performed in the apiserver.
411+
func ValidateServiceSubnetSize(subnetStr string, fldPath *field.Path) field.ErrorList {
412+
allErrs := field.ErrorList{}
413+
// subnets were already validated
414+
subnets, _ := utilnet.ParseCIDRs(strings.Split(subnetStr, ","))
415+
for _, serviceSubnet := range subnets {
416+
ones, bits := serviceSubnet.Mask.Size()
417+
if bits-ones > constants.MaximumBitsForServiceSubnet {
418+
errMsg := fmt.Sprintf("specified service subnet is too large; for %d-bit addresses, the mask must be >= %d", bits, bits-constants.MaximumBitsForServiceSubnet)
419+
allErrs = append(allErrs, field.Invalid(fldPath, serviceSubnet.String(), errMsg))
420+
}
421+
}
422+
return allErrs
423+
}
424+
406425
// ValidatePodSubnetNodeMask validates that the relation between podSubnet and node-masks is correct
407426
func ValidatePodSubnetNodeMask(subnetStr string, c *kubeadm.ClusterConfiguration, fldPath *field.Path) field.ErrorList {
408427
allErrs := field.ErrorList{}
@@ -468,6 +487,8 @@ func ValidateNetworking(c *kubeadm.ClusterConfiguration, fldPath *field.Path) fi
468487

469488
if len(c.Networking.ServiceSubnet) != 0 {
470489
allErrs = append(allErrs, ValidateIPNetFromString(c.Networking.ServiceSubnet, constants.MinimumAddressesInServiceSubnet, isDualStack, field.NewPath("serviceSubnet"))...)
490+
// Service subnet was already validated, we need to validate now the subnet size
491+
allErrs = append(allErrs, ValidateServiceSubnetSize(c.Networking.ServiceSubnet, field.NewPath("serviceSubnet"))...)
471492
}
472493
if len(c.Networking.PodSubnet) != 0 {
473494
allErrs = append(allErrs, ValidateIPNetFromString(c.Networking.PodSubnet, constants.MinimumAddressesInPodSubnet, isDualStack, field.NewPath("podSubnet"))...)

cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,37 @@ func TestValidatePodSubnetNodeMask(t *testing.T) {
287287
}
288288
}
289289

290+
func TestValidateServiceSubnetSize(t *testing.T) {
291+
var tests = []struct {
292+
name string
293+
subnet string
294+
expected bool
295+
}{
296+
{"single IPv4, but mask too large.", "10.0.0.16/2", false},
297+
{"single IPv6, but mask too large.", "2001:db8::1/64", false},
298+
{"single IPv4 CIDR", "10.0.0.16/12", true},
299+
{"single IPv6 CIDR", "2001:db8::/112", true},
300+
// dual-stack:
301+
{"dual, but IPv4 mask too large.", "2001:db8::1/112,10.0.0.16/6", false},
302+
{"dual, but IPv6 mask too large.", "2001:db8::1/12,10.0.0.16/16", false},
303+
{"dual IPv4 IPv6", "10.0.0.16/12,2001:db8::/112", true},
304+
{"dual IPv6 IPv4", "2001:db8::/112,10.0.0.16/12", true},
305+
}
306+
for _, rt := range tests {
307+
308+
actual := ValidateServiceSubnetSize(rt.subnet, nil)
309+
if (len(actual) == 0) != rt.expected {
310+
t.Errorf(
311+
"%s test case failed :\n\texpected: %t\n\t actual: %t\n\t err(s): %v\n\t",
312+
rt.name,
313+
rt.expected,
314+
(len(actual) == 0),
315+
actual,
316+
)
317+
}
318+
}
319+
}
320+
290321
func TestValidateHostPort(t *testing.T) {
291322
var tests = []struct {
292323
name string
@@ -521,7 +552,7 @@ func TestValidateInitConfiguration(t *testing.T) {
521552
},
522553
},
523554
Networking: kubeadm.Networking{
524-
ServiceSubnet: "2001:db8::1/98",
555+
ServiceSubnet: "2001:db8::1/112",
525556
DNSDomain: "cluster.local",
526557
},
527558
CertificatesDir: "/some/other/cert/dir",

cmd/kubeadm/app/constants/constants.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ const (
199199
// We need at least ten, because the DNS service is always at the tenth cluster clusterIP
200200
MinimumAddressesInServiceSubnet = 10
201201

202+
// MaximumBitsForServiceSubnet defines maximum possible size of the service subnet in terms of bits.
203+
// For example, if the value is 20, then the largest supported service subnet is /12 for IPv4 and /108 for IPv6.
204+
// Note however that anything in between /108 and /112 will be clamped to /112 due to the limitations of the underlying allocation logic.
205+
// TODO: https://github.com/kubernetes/enhancements/pull/1881
206+
MaximumBitsForServiceSubnet = 20
207+
202208
// MinimumAddressesInPodSubnet defines minimum amount of pods in the cluster.
203209
// We need at least more than services, an IPv4 /28 or IPv6 /128 subnet means 14 util addresses
204210
MinimumAddressesInPodSubnet = 14

0 commit comments

Comments
 (0)