Fix IP/CIDR validation to allow updates to existing invalid objects

Ignore pre-existing bad IP/CIDR values in:
  - pod.spec.podIP(s)
  - pod.spec.hostIP(s)
  - service.spec.externalIPs
  - service.spec.clusterIP(s)
  - service.spec.loadBalancerSourceRanges (and corresponding annotation)
  - service.status.loadBalancer.ingress[].ip
  - endpoints.subsets
  - endpointslice.endpoints
  - networkpolicy.spec.{ingress[].from[],egress[].to[]}.ipBlock
  - ingress.status.loadBalancer.ingress[].ip

In the Endpoints and EndpointSlice case, if *any* endpoint IP is
changed, then the entire object must be valid; invalid IPs are only
allowed to remain in place for updates that don't change any IPs.
(e.g., changing the labels or annotations).

In most of the other cases, when the invalid IP is part of an array,
it can be moved around within the array without triggering
revalidation.
This commit is contained in:
Dan Winship
2023-12-28 11:30:45 -05:00
parent 692785d25b
commit ad22c0d495
9 changed files with 693 additions and 99 deletions

View File

@@ -442,13 +442,95 @@ func TestValidateNetworkPolicyUpdate(t *testing.T) {
},
},
},
"fix pre-existing invalid CIDR": {
old: networking.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: networking.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
Ingress: []networking.NetworkPolicyIngressRule{{
From: []networking.NetworkPolicyPeer{{
IPBlock: &networking.IPBlock{
CIDR: "192.168.0.0/16",
Except: []string{
"192.168.2.0/24",
"192.168.3.1/24",
},
},
}},
}},
},
},
update: networking.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: networking.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
Ingress: []networking.NetworkPolicyIngressRule{{
From: []networking.NetworkPolicyPeer{{
IPBlock: &networking.IPBlock{
CIDR: "192.168.0.0/16",
Except: []string{
"192.168.2.0/24",
"192.168.3.0/24",
},
},
}},
}},
},
},
},
"fail to fix pre-existing invalid CIDR": {
old: networking.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: networking.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
Ingress: []networking.NetworkPolicyIngressRule{{
From: []networking.NetworkPolicyPeer{{
IPBlock: &networking.IPBlock{
CIDR: "192.168.0.0/16",
Except: []string{
"192.168.2.0/24",
"192.168.3.1/24",
},
},
}},
}},
},
},
update: networking.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: networking.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
Ingress: []networking.NetworkPolicyIngressRule{{
From: []networking.NetworkPolicyPeer{{
IPBlock: &networking.IPBlock{
CIDR: "192.168.0.0/16",
Except: []string{
"192.168.1.0/24",
"192.168.2.0/24",
"192.168.3.1/24",
},
},
}},
}},
},
},
},
}
for testName, successCase := range successCases {
t.Run(testName, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StrictIPCIDRValidation, true)
successCase.old.ObjectMeta.ResourceVersion = "1"
successCase.update.ObjectMeta.ResourceVersion = "1"
if errs := ValidateNetworkPolicyUpdate(&successCase.update, &successCase.old, NetworkPolicyValidationOptions{false}); len(errs) != 0 {
if errs := ValidateNetworkPolicyUpdate(&successCase.update, &successCase.old, NetworkPolicyValidationOptions{}); len(errs) != 0 {
t.Errorf("expected success, but got %v", errs)
}
})
@@ -471,13 +553,55 @@ func TestValidateNetworkPolicyUpdate(t *testing.T) {
},
},
},
"add new invalid CIDR": {
old: networking.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: networking.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
Ingress: []networking.NetworkPolicyIngressRule{{
From: []networking.NetworkPolicyPeer{{
IPBlock: &networking.IPBlock{
CIDR: "192.168.0.0/16",
Except: []string{
"192.168.2.0/24",
"192.168.3.0/24",
},
},
}},
}},
},
},
update: networking.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar"},
Spec: networking.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: map[string]string{"a": "b"},
},
Ingress: []networking.NetworkPolicyIngressRule{{
From: []networking.NetworkPolicyPeer{{
IPBlock: &networking.IPBlock{
CIDR: "192.168.0.0/16",
Except: []string{
"192.168.1.1/24",
"192.168.2.0/24",
"192.168.3.0/24",
},
},
}},
}},
},
},
},
}
for testName, errorCase := range errorCases {
t.Run(testName, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StrictIPCIDRValidation, true)
errorCase.old.ObjectMeta.ResourceVersion = "1"
errorCase.update.ObjectMeta.ResourceVersion = "1"
if errs := ValidateNetworkPolicyUpdate(&errorCase.update, &errorCase.old, NetworkPolicyValidationOptions{false}); len(errs) == 0 {
if errs := ValidateNetworkPolicyUpdate(&errorCase.update, &errorCase.old, NetworkPolicyValidationOptions{}); len(errs) == 0 {
t.Errorf("expected failure")
}
})
@@ -1865,6 +1989,10 @@ func TestValidateIngressStatusUpdate(t *testing.T) {
newValue: legacyIP,
legacyIPs: true,
},
"legacy IPs unchanged in update": {
oldValue: legacyIP,
newValue: legacyIP,
},
}
for k, tc := range successCases {
t.Run(k, func(t *testing.T) {