Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automated cherry pick of #101084: Updating EndpointSlice validation to match Endpoints #101386

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 11 additions & 7 deletions pkg/apis/core/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4239,7 +4239,7 @@ func ValidateService(service *core.Service) field.ErrorList {
allErrs = append(allErrs, field.Invalid(idxPath, ip, msgs[i]))
}
} else {
allErrs = append(allErrs, validateNonSpecialIP(ip, idxPath)...)
allErrs = append(allErrs, ValidateNonSpecialIP(ip, idxPath)...)
}
}

Expand Down Expand Up @@ -5703,15 +5703,19 @@ func validateEndpointAddress(address *core.EndpointAddress, fldPath *field.Path)
allErrs = append(allErrs, field.Invalid(fldPath.Child("nodeName"), *address.NodeName, msg))
}
}
allErrs = append(allErrs, validateNonSpecialIP(address.IP, fldPath.Child("ip"))...)
allErrs = append(allErrs, ValidateNonSpecialIP(address.IP, fldPath.Child("ip"))...)
return allErrs
}

func validateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList {
// We disallow some IPs as endpoints or external-ips. Specifically,
// unspecified and loopback addresses are nonsensical and link-local
// addresses tend to be used for node-centric purposes (e.g. metadata
// service).
// ValidateNonSpecialIP is used to validate Endpoints, EndpointSlices, and
// external IPs. Specifically, this disallows unspecified and loopback addresses
// are nonsensical and link-local addresses tend to be used for node-centric
// purposes (e.g. metadata service).
//
// IPv6 references
// - https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
// - https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml
func ValidateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
ip := net.ParseIP(ipAddress)
if ip == nil {
Expand Down
40 changes: 40 additions & 0 deletions pkg/apis/core/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16915,3 +16915,43 @@ func TestValidatePodTemplateSpecSeccomp(t *testing.T) {
asserttestify.Equal(t, test.expectedErr, err, "TestCase[%d]: %s", i, test.description)
}
}

func TestValidateNonSpecialIP(t *testing.T) {
fp := field.NewPath("ip")

// Valid values.
for _, tc := range []struct {
desc string
ip string
}{
{"ipv4", "10.1.2.3"},
{"ipv6", "2000::1"},
} {
t.Run(tc.desc, func(t *testing.T) {
errs := ValidateNonSpecialIP(tc.ip, fp)
if len(errs) != 0 {
t.Errorf("ValidateNonSpecialIP(%q, ...) = %v; want nil", tc.ip, errs)
}
})
}
// Invalid cases
for _, tc := range []struct {
desc string
ip string
}{
{"ipv4 unspecified", "0.0.0.0"},
{"ipv6 unspecified", "::0"},
{"ipv4 localhost", "127.0.0.0"},
{"ipv4 localhost", "127.255.255.255"},
{"ipv6 localhost", "::1"},
{"ipv6 link local", "fe80::"},
{"ipv6 local multicast", "ff02::"},
} {
t.Run(tc.desc, func(t *testing.T) {
errs := ValidateNonSpecialIP(tc.ip, fp)
if len(errs) == 0 {
t.Errorf("ValidateNonSpecialIP(%q, ...) = nil; want non-nil (errors)", tc.ip)
}
})
}
}
2 changes: 2 additions & 0 deletions pkg/apis/discovery/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.Addres
switch addrType {
case discovery.AddressTypeIPv4:
allErrs = append(allErrs, validation.IsValidIPv4Address(addressPath.Index(i), address)...)
allErrs = append(allErrs, apivalidation.ValidateNonSpecialIP(address, addressPath.Index(i))...)
case discovery.AddressTypeIPv6:
allErrs = append(allErrs, validation.IsValidIPv6Address(addressPath.Index(i), address)...)
allErrs = append(allErrs, apivalidation.ValidateNonSpecialIP(address, addressPath.Index(i))...)
case discovery.AddressTypeFQDN:
allErrs = append(allErrs, validation.IsFullyQualifiedDomainName(addressPath.Index(i), address)...)
}
Expand Down
51 changes: 48 additions & 3 deletions pkg/apis/discovery/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ func TestValidateEndpointSlice(t *testing.T) {
}},
},
},
"good-ipv6": {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIPv6,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: []string{"a00:100::4"},
Hostname: utilpointer.StringPtr("valid-123"),
}},
},
},
"good-fqdns": {
expectedErrors: 0,
endpointSlice: &discovery.EndpointSlice{
Expand Down Expand Up @@ -375,7 +390,7 @@ func TestValidateEndpointSlice(t *testing.T) {
},
},
"bad-ip": {
expectedErrors: 1,
expectedErrors: 2,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIPv4,
Expand All @@ -390,7 +405,7 @@ func TestValidateEndpointSlice(t *testing.T) {
},
},
"bad-ipv4": {
expectedErrors: 2,
expectedErrors: 3,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIPv4,
Expand All @@ -405,7 +420,7 @@ func TestValidateEndpointSlice(t *testing.T) {
},
},
"bad-ipv6": {
expectedErrors: 2,
expectedErrors: 4,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIPv6,
Expand Down Expand Up @@ -454,6 +469,36 @@ func TestValidateEndpointSlice(t *testing.T) {
expectedErrors: 3,
endpointSlice: &discovery.EndpointSlice{},
},
"special-ipv4": {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIPv4,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: []string{"127.0.0.1"},
Hostname: utilpointer.StringPtr("valid-123"),
}},
},
},
"special-ipv6": {
expectedErrors: 1,
endpointSlice: &discovery.EndpointSlice{
ObjectMeta: standardMeta,
AddressType: discovery.AddressTypeIPv6,
Ports: []discovery.EndpointPort{{
Name: utilpointer.StringPtr("http"),
Protocol: protocolPtr(api.ProtocolTCP),
}},
Endpoints: []discovery.Endpoint{{
Addresses: []string{"fe80::9656:d028:8652:66b6"},
Hostname: utilpointer.StringPtr("valid-123"),
}},
},
},
}

for name, testCase := range testCases {
Expand Down