diff --git a/test/e2e/network/funny_ips.go b/test/e2e/network/funny_ips.go deleted file mode 100644 index 0bf9f290d28..00000000000 --- a/test/e2e/network/funny_ips.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -Copyright 2022 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package network - -import ( - "context" - "fmt" - "strconv" - "strings" - "time" - - "github.com/onsi/ginkgo/v2" - - "k8s.io/kubernetes/test/e2e/framework" - e2epod "k8s.io/kubernetes/test/e2e/framework/pod" - e2eoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" - e2eservice "k8s.io/kubernetes/test/e2e/framework/service" - e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper" - "k8s.io/kubernetes/test/e2e/network/common" - - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/wait" - clientset "k8s.io/client-go/kubernetes" - admissionapi "k8s.io/pod-security-admission/api" - netutils "k8s.io/utils/net" -) - -// What are funny IPs: -// The adjective is because of the curl blog that explains the history and the problem of liberal -// parsing of IP addresses and the consequences and security risks caused the lack of normalization, -// mainly due to the use of different notations to abuse parsers misalignment to bypass filters. -// xref: https://daniel.haxx.se/blog/2021/04/19/curl-those-funny-ipv4-addresses/ -// -// Since golang 1.17, IPv4 addresses with leading zeros are rejected by the standard library. -// xref: https://github.com/golang/go/issues/30999 -// -// Because this change on the parsers can cause that previous valid data become invalid, Kubernetes -// forked the old parsers allowing leading zeros on IPv4 address to not break the compatibility. -// -// Kubernetes interprets leading zeros on IPv4 addresses as decimal, users must not rely on parser -// alignment to not being impacted by the associated security advisory: CVE-2021-29923 golang -// standard library "net" - Improper Input Validation of octal literals in golang 1.16.2 and below -// standard library "net" results in indeterminate SSRF & RFI vulnerabilities. xref: -// https://nvd.nist.gov/vuln/detail/CVE-2021-29923 -// -// Kubernetes -// ip := net.ParseIPSloppy("192.168.0.011") -// ip.String() yields 192.168.0.11 -// -// BSD stacks -// ping 192.168.0.011 -// PING 192.168.0.011 (192.168.0.9) 56(84) bytes of data. - -var _ = common.SIGDescribe("CVE-2021-29923", func() { - var ( - ns string - cs clientset.Interface - ) - - f := framework.NewDefaultFramework("funny-ips") - f.NamespacePodSecurityLevel = admissionapi.LevelBaseline - - ginkgo.BeforeEach(func() { - if framework.TestContext.ClusterIsIPv6() { - e2eskipper.Skipf("The test doesn't apply to IPv6 addresses, only IPv4 addresses are affected by CVE-2021-29923") - } - ns = f.Namespace.Name - cs = f.ClientSet - }) - - /* - Create a ClusterIP service with a ClusterIP with leading zeros and check that it is reachable in the IP interpreted as decimal. - If the Service is not reachable only FAIL if it is reachable in the IP interpreted as binary, because it will be exposed to CVE-2021-29923. - IMPORTANT: CoreDNS since version 1.8.5 discard IPs with leading zeros so Services are not resolvable, and is probably that - most of the ecosystem has done the same, however, Kubernetes doesn't impose any restriction, users should migrate their IPs. - */ - ginkgo.It("IPv4 Service Type ClusterIP with leading zeros should work interpreted as decimal", func(ctx context.Context) { - serviceName := "funny-ip" - // Use a very uncommon port to reduce the risk of conflicts with other tests that create services. - servicePort := 7180 - jig := e2eservice.NewTestJig(cs, ns, serviceName) - - clusterIPZero, clusterIPOctal := getServiceIPWithLeadingZeros(ctx, cs) - if clusterIPZero == "" { - e2eskipper.Skipf("Couldn't find a free ClusterIP") - } - - ginkgo.By("creating service " + serviceName + " with type=ClusterIP and ip " + clusterIPZero + " in namespace " + ns) - _, err := jig.CreateTCPService(ctx, func(svc *v1.Service) { - svc.Spec.ClusterIP = clusterIPZero // IP with a leading zero - svc.Spec.Type = v1.ServiceTypeClusterIP - svc.Spec.Ports = []v1.ServicePort{ - {Port: int32(servicePort), Name: "http", Protocol: v1.ProtocolTCP, TargetPort: intstr.FromInt32(9376)}, - } - }) - framework.ExpectNoError(err) - - err = jig.CreateServicePods(ctx, 1) - framework.ExpectNoError(err) - - execPod := e2epod.CreateExecPodOrFail(ctx, cs, ns, "execpod", nil) - ip := netutils.ParseIPSloppy(clusterIPZero) - cmd := fmt.Sprintf("echo hostName | nc -v -t -w 2 %s %v", ip.String(), servicePort) - err = wait.PollImmediate(1*time.Second, e2eservice.ServiceReachabilityShortPollTimeout, func() (bool, error) { - stdout, err := e2eoutput.RunHostCmd(execPod.Namespace, execPod.Name, cmd) - if err != nil { - framework.Logf("Service reachability failing with error: %v\nRetrying...", err) - return false, nil - } - trimmed := strings.TrimSpace(stdout) - if trimmed != "" { - return true, nil - } - return false, nil - }) - // Service is working on the expected IP. - if err == nil { - return - } - // It may happen that the component implementing Services discard the IP. - // We have to check that the Service is not reachable in the address interpreted as decimal. - cmd = fmt.Sprintf("echo hostName | nc -v -t -w 2 %s %v", clusterIPOctal, servicePort) - err = wait.PollImmediate(1*time.Second, e2eservice.ServiceReachabilityShortPollTimeout, func() (bool, error) { - stdout, err := e2eoutput.RunHostCmd(execPod.Namespace, execPod.Name, cmd) - if err != nil { - framework.Logf("Service reachability failing with error: %v\nRetrying...", err) - return false, nil - } - trimmed := strings.TrimSpace(stdout) - if trimmed != "" { - return true, nil - } - return false, nil - }) - // Ouch, Service has worked on IP interpreted as octal. - if err == nil { - framework.Failf("WARNING: Your Cluster interprets Service ClusterIP %s as %s, please see https://nvd.nist.gov/vuln/detail/CVE-2021-29923", clusterIPZero, clusterIPOctal) - } - framework.Logf("Service reachability failing for Service against ClusterIP %s and %s, most probably leading zeros on IPs are not supported by the cluster proxy", clusterIPZero, clusterIPOctal) - }) - -}) - -// Try to get a free IP that has different decimal and binary interpretation with leading zeros. -// Return both IPs, the one interpretad as binary and the one interpreted as decimal. -// Return empty if not IPs are found. -func getServiceIPWithLeadingZeros(ctx context.Context, cs clientset.Interface) (string, string) { - clusterIPMap := map[string]struct{}{} - var clusterIPPrefix string - // Dump all the IPs and look for the ones we want. - list, err := cs.CoreV1().Services(metav1.NamespaceAll).List(ctx, metav1.ListOptions{}) - framework.ExpectNoError(err) - for _, svc := range list.Items { - if len(svc.Spec.ClusterIP) == 0 || svc.Spec.ClusterIP == v1.ClusterIPNone { - continue - } - // Get the clusterIP prefix, needed later for building the IPs with octal format. - if clusterIPPrefix == "" { - // split the IP by "." - // get the first 3 values - // join them again using dots, dropping the last field - clusterIPPrefix = strings.Join(strings.Split(svc.Spec.ClusterIP, ".")[:3], ".") - } - // Canonicalize IP. - ip := netutils.ParseIPSloppy(svc.Spec.ClusterIP) - clusterIPMap[ip.String()] = struct{}{} - } - - // Try to get a free IP between x.x.x.11 and x.x.x.31, this assumes a /27 Service IP range that should be safe. - for i := 11; i < 31; i++ { - ip := fmt.Sprintf("%s.%d", clusterIPPrefix, i) - // Check the IP in decimal format is free. - if _, ok := clusterIPMap[ip]; !ok { - o, err := strconv.ParseInt(fmt.Sprintf("%d", i), 8, 64) - // Omit ips without binary representation i.e. x.x.x.018, x.x.x.019 ... - if err != nil { - continue - } - ipOctal := fmt.Sprintf("%s.%d", clusterIPPrefix, o) - // Check the same IP in octal format is free. - if _, ok := clusterIPMap[ipOctal]; !ok { - // Add a leading zero to the decimal format. - return fmt.Sprintf("%s.0%d", clusterIPPrefix, i), ipOctal - } - } - } - return "", "" -}