Browse Source

resolvconf: use /run/systemd/resolve/resolv.conf if systemd-resolved manages DNS

Signed-off-by: Tibor Vass <tibor@docker.com>
Tibor Vass 6 years ago
parent
commit
c575631528
2 changed files with 40 additions and 6 deletions
  1. 38 4
      libnetwork/resolvconf/resolvconf.go
  2. 2 2
      libnetwork/sandbox_dns_unix.go

+ 38 - 4
libnetwork/resolvconf/resolvconf.go

@@ -15,10 +15,44 @@ import (
 )
 
 const (
-	// DefaultResolvConf points to the default file used for dns configuration on a linux machine
-	DefaultResolvConf = "/etc/resolv.conf"
+	// defaultPath is the default path to the resolv.conf that contains information to resolve DNS. See Path().
+	defaultPath = "/etc/resolv.conf"
+	// alternatePath is a path different from defaultPath, that may be used to resolve DNS. See Path().
+	alternatePath = "/run/systemd/resolve/resolv.conf"
 )
 
+var (
+	detectSystemdResolvConfOnce sync.Once
+	pathAfterSystemdDetection   = defaultPath
+)
+
+// Path returns the path to the resolv.conf file that libnetwork should use.
+//
+// When /etc/resolv.conf contains 127.0.0.53 as the only nameserver, then
+// it is assumed systemd-resolved manages DNS. Because inside the container 127.0.0.53
+// is not a valid DNS server, Path() returns /run/systemd/resolve/resolv.conf
+// which is the resolv.conf that systemd-resolved generates and manages.
+// Otherwise Path() returns /etc/resolv.conf.
+//
+// Errors are silenced as they will inevitably resurface at future open/read calls.
+//
+// More information at https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html#/etc/resolv.conf
+func Path() string {
+	detectSystemdResolvConfOnce.Do(func() {
+		candidateResolvConf, err := ioutil.ReadFile(defaultPath)
+		if err != nil {
+			// silencing error as it will resurface at next calls trying to read defaultPath
+			return
+		}
+		ns := GetNameservers(candidateResolvConf, types.IP)
+		if len(ns) == 1 && ns[0] == "127.0.0.53" {
+			pathAfterSystemdDetection = alternatePath
+			logrus.Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath)
+		}
+	})
+	return pathAfterSystemdDetection
+}
+
 var (
 	// Note: the default IPv4 & IPv6 resolvers are set to Google's Public DNS
 	defaultIPv4Dns = []string{"nameserver 8.8.8.8", "nameserver 8.8.4.4"}
@@ -55,7 +89,7 @@ type File struct {
 
 // Get returns the contents of /etc/resolv.conf and its hash
 func Get() (*File, error) {
-	return GetSpecific(DefaultResolvConf)
+	return GetSpecific(Path())
 }
 
 // GetSpecific returns the contents of the user specified resolv.conf file and its hash
@@ -78,7 +112,7 @@ func GetIfChanged() (*File, error) {
 	lastModified.Lock()
 	defer lastModified.Unlock()
 
-	resolv, err := ioutil.ReadFile("/etc/resolv.conf")
+	resolv, err := ioutil.ReadFile(Path())
 	if err != nil {
 		return nil, err
 	}

+ 2 - 2
libnetwork/sandbox_dns_unix.go

@@ -213,8 +213,8 @@ func (sb *sandbox) setupDNS() error {
 
 	originResolvConfPath := sb.config.originResolvConfPath
 	if originResolvConfPath == "" {
-		// if not specified fallback to default /etc/resolv.conf
-		originResolvConfPath = resolvconf.DefaultResolvConf
+		// fallback if not specified
+		originResolvConfPath = resolvconf.Path()
 	}
 	currRC, err := resolvconf.GetSpecific(originResolvConfPath)
 	if err != nil {