diff --git a/daemon/container_operations_unix.go b/daemon/container_operations_unix.go index 5f2be7cd22..c71f1fb139 100644 --- a/daemon/container_operations_unix.go +++ b/daemon/container_operations_unix.go @@ -391,13 +391,65 @@ func serviceDiscoveryOnDefaultNetwork() bool { func (daemon *Daemon) setupPathsAndSandboxOptions(container *container.Container, sboxOptions *[]libnetwork.SandboxOption) error { var err error - if container.HostConfig.NetworkMode.IsHost() { - // Point to the host files, so that will be copied into the container running in host mode - *sboxOptions = append(*sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) - } + // Set the correct paths for /etc/hosts and /etc/resolv.conf, based on the + // networking-mode of the container. Note that containers with "container" + // networking are already handled in "initializeNetworking()" before we reach + // this function, so do not have to be accounted for here. + switch { + case container.HostConfig.NetworkMode.IsHost(): + // In host-mode networking, the container does not have its own networking + // namespace, so both `/etc/hosts` and `/etc/resolv.conf` should be the same + // as on the host itself. The container gets a copy of these files, but they + // may be symlinked, so resolve the original path first. + etcHosts, err := filepath.EvalSymlinks("/etc/hosts") + if err != nil { + return err + } + resolvConf, err := filepath.EvalSymlinks("/etc/resolv.conf") + if err != nil { + return err + } - // Copy the host's resolv.conf for the container (/etc/resolv.conf or /run/systemd/resolve/resolv.conf) - *sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath(daemon.configStore.GetResolvConf())) + *sboxOptions = append( + *sboxOptions, + libnetwork.OptionOriginHostsPath(etcHosts), + libnetwork.OptionOriginResolvConfPath(resolvConf), + ) + case container.HostConfig.NetworkMode.IsUserDefined(): + // The container uses a user-defined network. We use the embedded DNS + // server for container name resolution and to act as a DNS forwarder + // for external DNS resolution. + // We parse the DNS server(s) that are defined in /etc/resolv.conf on + // the host, which may be a local DNS server (for example, if DNSMasq or + // systemd-resolvd are in use). The embedded DNS server forwards DNS + // resolution to the DNS server configured on the host, which in itself + // may act as a forwarder for external DNS servers. + // If systemd-resolvd is used, the "upstream" DNS servers can be found in + // /run/systemd/resolve/resolv.conf. We do not query those DNS servers + // directly, as they can be dynamically reconfigured. + resolvConf, err := filepath.EvalSymlinks("/etc/resolv.conf") + if err != nil { + return err + } + *sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath(resolvConf)) + default: + // For other situations, such as the default bridge network, container + // discovery / name resolution is handled through /etc/hosts, and no + // embedded DNS server is available. Without the embedded DNS, we + // cannot use local DNS servers on the host (for example, if DNSMasq or + // systemd-resolvd is used). If systemd-resolvd is used, we try to + // determine the external DNS servers that are used on the host. + // This situation is not ideal, because DNS servers configured in the + // container are not updated after the container is created, but the + // DNS servers on the host can be dynamically updated. + // + // Copy the host's resolv.conf for the container (/run/systemd/resolve/resolv.conf or /etc/resolv.conf) + resolvConf, err := filepath.EvalSymlinks(daemon.configStore.GetResolvConf()) + if err != nil { + return err + } + *sboxOptions = append(*sboxOptions, libnetwork.OptionOriginResolvConfPath(resolvConf)) + } container.HostsPath, err = container.GetRootResourcePath("hosts") if err != nil {