diff --git a/pkg/kubelet/kubelet_network.go b/pkg/kubelet/kubelet_network.go index f9397a0e3be..9b9b588dfbb 100644 --- a/pkg/kubelet/kubelet_network.go +++ b/pkg/kubelet/kubelet_network.go @@ -359,8 +359,13 @@ func (kl *Kubelet) GetClusterDNS(pod *v1.Pod) ([]string, []string, []string, boo // local machine". A nameserver setting of localhost is equivalent to // this documented behavior. if kl.resolverConfig == "" { - hostDNS = []string{"127.0.0.1"} hostSearch = []string{"."} + switch { + case kl.nodeIP == nil || kl.nodeIP.To4() != nil: + hostDNS = []string{"127.0.0.1"} + case kl.nodeIP.To16() != nil: + hostDNS = []string{"::1"} + } } else { hostSearch = kl.formDNSSearchForDNSDefault(hostSearch, pod) } diff --git a/pkg/kubelet/kubelet_network_test.go b/pkg/kubelet/kubelet_network_test.go index 4bffe2c55d2..c0b4ea02886 100644 --- a/pkg/kubelet/kubelet_network_test.go +++ b/pkg/kubelet/kubelet_network_test.go @@ -99,28 +99,56 @@ func TestNoOpHostSupportsLegacyFeatures(t *testing.T) { } func TestNodeIPParam(t *testing.T) { - testKubelet := newTestKubelet(t, false /* controllerAttachDetachEnabled */) - defer testKubelet.Cleanup() - kubelet := testKubelet.kubelet - tests := []struct { + type test struct { nodeIP string success bool testName string - }{ + } + tests := []test{ { nodeIP: "", - success: true, + success: false, testName: "IP not set", }, { nodeIP: "127.0.0.1", success: false, - testName: "loopback address", + testName: "IPv4 loopback address", }, { - nodeIP: "FE80::0202:B3FF:FE1E:8329", + nodeIP: "::1", success: false, - testName: "IPv6 address", + testName: "IPv6 loopback address", + }, + { + nodeIP: "224.0.0.1", + success: false, + testName: "multicast IPv4 address", + }, + { + nodeIP: "ff00::1", + success: false, + testName: "multicast IPv6 address", + }, + { + nodeIP: "169.254.0.1", + success: false, + testName: "IPv4 link-local unicast address", + }, + { + nodeIP: "fe80::0202:b3ff:fe1e:8329", + success: false, + testName: "IPv6 link-local unicast address", + }, + { + nodeIP: "0.0.0.0", + success: false, + testName: "Unspecified IPv4 address", + }, + { + nodeIP: "::", + success: false, + testName: "Unspecified IPv6 address", }, { nodeIP: "1.2.3.4", @@ -128,9 +156,31 @@ func TestNodeIPParam(t *testing.T) { testName: "IPv4 address that doesn't belong to host", }, } + addrs, err := net.InterfaceAddrs() + if err != nil { + assert.Error(t, err, fmt.Sprintf( + "Unable to obtain a list of the node's unicast interface addresses.")) + } + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + if ip.IsLoopback() || ip.IsLinkLocalUnicast() { + break + } + successTest := test{ + nodeIP: ip.String(), + success: true, + testName: fmt.Sprintf("Success test case for address %s", ip.String()), + } + tests = append(tests, successTest) + } for _, test := range tests { - kubelet.nodeIP = net.ParseIP(test.nodeIP) - err := kubelet.validateNodeIP() + err := validateNodeIP(net.ParseIP(test.nodeIP)) if test.success { assert.NoError(t, err, "test %s", test.testName) } else { diff --git a/pkg/kubelet/kubelet_node_status.go b/pkg/kubelet/kubelet_node_status.go index 9b72da72719..5c15d888c98 100644 --- a/pkg/kubelet/kubelet_node_status.go +++ b/pkg/kubelet/kubelet_node_status.go @@ -437,7 +437,7 @@ func (kl *Kubelet) recordNodeStatusEvent(eventType, event string) { // Set IP and hostname addresses for the node. func (kl *Kubelet) setNodeAddress(node *v1.Node) error { if kl.nodeIP != nil { - if err := kl.validateNodeIP(); err != nil { + if err := validateNodeIP(kl.nodeIP); err != nil { return fmt.Errorf("failed to validate nodeIP: %v", err) } glog.V(2).Infof("Using node IP: %q", kl.nodeIP.String()) @@ -503,7 +503,8 @@ func (kl *Kubelet) setNodeAddress(node *v1.Node) error { // 1) Use nodeIP if set // 2) If the user has specified an IP to HostnameOverride, use it - // 3) Lookup the IP from node name by DNS and use the first non-loopback ipv4 address + // 3) Lookup the IP from node name by DNS and use the first valid IPv4 address. + // If the node does not have a valid IPv4 address, use the first valid IPv6 address. // 4) Try to get the IP from the network interface used as default gateway if kl.nodeIP != nil { ipAddr = kl.nodeIP @@ -511,11 +512,16 @@ func (kl *Kubelet) setNodeAddress(node *v1.Node) error { ipAddr = addr } else { var addrs []net.IP - addrs, err = net.LookupIP(node.Name) + addrs, _ = net.LookupIP(node.Name) for _, addr := range addrs { - if !addr.IsLoopback() && addr.To4() != nil { - ipAddr = addr - break + if err = validateNodeIP(addr); err == nil { + if addr.To4() != nil { + ipAddr = addr + break + } + if addr.To16() != nil && ipAddr == nil { + ipAddr = addr + } } } @@ -991,17 +997,22 @@ func (kl *Kubelet) defaultNodeStatusFuncs() []func(*v1.Node) error { } // Validate given node IP belongs to the current host -func (kl *Kubelet) validateNodeIP() error { - if kl.nodeIP == nil { - return nil - } - +func validateNodeIP(nodeIP net.IP) error { // Honor IP limitations set in setNodeStatus() - if kl.nodeIP.IsLoopback() { + if nodeIP.To4() == nil && nodeIP.To16() == nil { + return fmt.Errorf("nodeIP must be a valid IP address") + } + if nodeIP.IsLoopback() { return fmt.Errorf("nodeIP can't be loopback address") } - if kl.nodeIP.To4() == nil { - return fmt.Errorf("nodeIP must be IPv4 address") + if nodeIP.IsMulticast() { + return fmt.Errorf("nodeIP can't be a multicast address") + } + if nodeIP.IsLinkLocalUnicast() { + return fmt.Errorf("nodeIP can't be a link-local unicast address") + } + if nodeIP.IsUnspecified() { + return fmt.Errorf("nodeIP can't be an all zeros address") } addrs, err := net.InterfaceAddrs() @@ -1016,9 +1027,9 @@ func (kl *Kubelet) validateNodeIP() error { case *net.IPAddr: ip = v.IP } - if ip != nil && ip.Equal(kl.nodeIP) { + if ip != nil && ip.Equal(nodeIP) { return nil } } - return fmt.Errorf("Node IP: %q not found in the host's network interfaces", kl.nodeIP.String()) + return fmt.Errorf("Node IP: %q not found in the host's network interfaces", nodeIP.String()) } diff --git a/pkg/kubelet/kubelet_node_status_test.go b/pkg/kubelet/kubelet_node_status_test.go index 87172759441..38636f70e1d 100644 --- a/pkg/kubelet/kubelet_node_status_test.go +++ b/pkg/kubelet/kubelet_node_status_test.go @@ -144,7 +144,7 @@ func TestNodeStatusWithCloudProviderNodeIP(t *testing.T) { Spec: v1.NodeSpec{}, } - // TODO : is it possible to mock kubelet.validateNodeIP() to avoid relying on the host interface addresses ? + // TODO : is it possible to mock validateNodeIP() to avoid relying on the host interface addresses ? addrs, err := net.InterfaceAddrs() assert.NoError(t, err) for _, addr := range addrs {