diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go index 9470a174b65..d9ebd9f9399 100644 --- a/pkg/apis/core/validation/validation.go +++ b/pkg/apis/core/validation/validation.go @@ -4266,7 +4266,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)...) } } @@ -5754,19 +5754,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). - // - // 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 +// 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 { diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go index f5c0d2ae0c4..7256cb1df86 100644 --- a/pkg/apis/core/validation/validation_test.go +++ b/pkg/apis/core/validation/validation_test.go @@ -17379,9 +17379,9 @@ func TestValidateNonSpecialIP(t *testing.T) { {"ipv6", "2000::1"}, } { t.Run(tc.desc, func(t *testing.T) { - errs := validateNonSpecialIP(tc.ip, fp) + errs := ValidateNonSpecialIP(tc.ip, fp) if len(errs) != 0 { - t.Errorf("validateNonSpecialIP(%q, ...) = %v; want nil", tc.ip, errs) + t.Errorf("ValidateNonSpecialIP(%q, ...) = %v; want nil", tc.ip, errs) } }) } @@ -17399,9 +17399,9 @@ func TestValidateNonSpecialIP(t *testing.T) { {"ipv6 local multicast", "ff02::"}, } { t.Run(tc.desc, func(t *testing.T) { - errs := validateNonSpecialIP(tc.ip, fp) + errs := ValidateNonSpecialIP(tc.ip, fp) if len(errs) == 0 { - t.Errorf("validateNonSpecialIP(%q, ...) = nil; want non-nil (errors)", tc.ip) + t.Errorf("ValidateNonSpecialIP(%q, ...) = nil; want non-nil (errors)", tc.ip) } }) } diff --git a/pkg/apis/discovery/validation/validation.go b/pkg/apis/discovery/validation/validation.go index c0a13ee48ea..4754c1449e8 100644 --- a/pkg/apis/discovery/validation/validation.go +++ b/pkg/apis/discovery/validation/validation.go @@ -100,8 +100,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)...) } diff --git a/pkg/apis/discovery/validation/validation_test.go b/pkg/apis/discovery/validation/validation_test.go index ea62422efa0..f9de0d29d0d 100644 --- a/pkg/apis/discovery/validation/validation_test.go +++ b/pkg/apis/discovery/validation/validation_test.go @@ -53,6 +53,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{ @@ -393,7 +408,7 @@ func TestValidateEndpointSlice(t *testing.T) { }, }, "bad-ip": { - expectedErrors: 1, + expectedErrors: 2, endpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, @@ -408,7 +423,7 @@ func TestValidateEndpointSlice(t *testing.T) { }, }, "bad-ipv4": { - expectedErrors: 2, + expectedErrors: 3, endpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv4, @@ -423,7 +438,7 @@ func TestValidateEndpointSlice(t *testing.T) { }, }, "bad-ipv6": { - expectedErrors: 2, + expectedErrors: 4, endpointSlice: &discovery.EndpointSlice{ ObjectMeta: standardMeta, AddressType: discovery.AddressTypeIPv6, @@ -552,6 +567,36 @@ func TestValidateEndpointSlice(t *testing.T) { }}, }, }, + "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 {