diff --git a/network.go b/network.go index c237ccc86d..efd0736a3e 100644 --- a/network.go +++ b/network.go @@ -76,6 +76,21 @@ func checkRouteOverlaps(networks []*net.IPNet, dockerNetwork *net.IPNet) error { return nil } +func checkNameserverOverlaps(nameservers []string, dockerNetwork *net.IPNet) error { + if len(nameservers) > 0 { + for _, ns := range nameservers { + _, nsNetwork, err := net.ParseCIDR(ns) + if err != nil { + return err + } + if networkOverlaps(dockerNetwork, nsNetwork) { + return fmt.Errorf("%s overlaps nameserver %s", dockerNetwork, nsNetwork) + } + } + } + return nil +} + // CreateBridgeIface creates a network bridge interface on the host system with the name `ifaceName`, // and attempts to configure it with an address which doesn't conflict with any other interface on the host. // If it can't find an address which doesn't conflict, it will return an error. @@ -100,6 +115,16 @@ func CreateBridgeIface(config *DaemonConfig) error { "192.168.44.1/24", } + nameservers := []string{} + resolvConf, _ := utils.GetResolvConf() + // we don't check for an error here, because we don't really care + // if we can't read /etc/resolv.conf. So instead we skip the append + // if resolvConf is nil. It either doesn't exist, or we can't read it + // for some reason. + if resolvConf != nil { + nameservers = append(nameservers, utils.GetNameserversAsCIDR(resolvConf)...) + } + var ifaceAddr string for _, addr := range addrs { _, dockerNetwork, err := net.ParseCIDR(addr) @@ -111,8 +136,10 @@ func CreateBridgeIface(config *DaemonConfig) error { return err } if err := checkRouteOverlaps(routes, dockerNetwork); err == nil { - ifaceAddr = addr - break + if err := checkNameserverOverlaps(nameservers, dockerNetwork); err == nil { + ifaceAddr = addr + break + } } else { utils.Debugf("%s: %s", addr, err) } diff --git a/network_test.go b/network_test.go index 7304e205a5..e2631ddcb7 100644 --- a/network_test.go +++ b/network_test.go @@ -295,3 +295,19 @@ func TestCheckRouteOverlaps(t *testing.T) { t.Fatalf("10.0.2.0/24 and 10.0.2.0 should overlap but it doesn't") } } + +func TestCheckNameserverOverlaps(t *testing.T) { + nameservers := []string{"10.0.2.3/32", "192.168.102.1/32"} + + _, netX, _ := net.ParseCIDR("10.0.2.3/32") + + if err := checkNameserverOverlaps(nameservers, netX); err == nil { + t.Fatalf("%s should overlap 10.0.2.3/32 but doesn't", netX) + } + + _, netX, _ = net.ParseCIDR("192.168.102.2/32") + + if err := checkNameserverOverlaps(nameservers, netX); err != nil { + t.Fatalf("%s should not overlap %v but it does", netX, nameservers) + } +} diff --git a/utils/utils.go b/utils/utils.go index cadd095031..4941eae42d 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -16,6 +16,7 @@ import ( "os/exec" "path/filepath" "runtime" + "regexp" "strconv" "strings" "sync" @@ -903,6 +904,23 @@ func StripComments(input []byte, commentMarker []byte) []byte { return output } +// GetNameserversAsCIDR returns nameservers (if any) listed in +// /etc/resolv.conf as CIDR blocks (e.g., "1.2.3.4/32") +// This function's output is intended for net.ParseCIDR +func GetNameserversAsCIDR(resolvConf []byte) []string { + var parsedResolvConf = StripComments(resolvConf, []byte("#")) + nameservers := []string{} + re := regexp.MustCompile(`^\s*nameserver\s*(([0-9]\.){3}([0-9]))\s*$`) + for _, line := range bytes.Split(parsedResolvConf, []byte("\n")) { + var ns = re.FindSubmatch(line) + if len(ns) > 0 { + nameservers = append(nameservers, string(ns[1])+"/32") + } + } + + return nameservers +} + func ParseHost(host string, port int, addr string) (string, error) { var proto string switch { diff --git a/utils/utils_test.go b/utils/utils_test.go index 49f19bf759..f10bb40d3b 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -444,3 +444,41 @@ func TestParsePortMapping(t *testing.T) { t.Fail() } } + +func TestGetNameserversAsCIDR(t *testing.T) { + for resolv, result := range map[string][]string{` +nameserver 1.2.3.4 +nameserver 4.3.2.1 +search example.com`: {"1.2.3.4/32", "4.3.2.1/32"}, + `search example.com`: {}, + `nameserver 1.2.3.4 +search example.com +nameserver 4.3.2.1`: []string{"1.2.3.4/32", "4.3.2.1/32"}, + ``: []string{}, + ` nameserver 1.2.3.4 `: []string{"1.2.3.4/32"}, + `search example.com +nameserver 1.2.3.4 +#nameserver 4.3.2.1`: []string{"1.2.3.4/32"}, + `search example.com +nameserver 1.2.3.4 # not 4.3.2.1`: []string{"1.2.3.4/32"}, + } { + test := GetNameserversAsCIDR([]byte(resolv)) + if !StrSlicesEqual(test, result) { + t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv) + } + } +} + +func StrSlicesEqual(a, b []string) bool { + if len(a) != len(b) { + return false + } + + for i, v := range a { + if v != b[i] { + return false + } + } + + return true +}