Add embedded DNS server support for host loopback resolver

Signed-off-by: Santhosh Manohar <santhosh@docker.com>
This commit is contained in:
Santhosh Manohar 2016-12-18 18:27:13 -08:00
parent 3d32070063
commit bf832ec2a7
6 changed files with 55 additions and 17 deletions

View File

@ -918,6 +918,7 @@ func (c *controller) NewSandbox(containerID string, options ...SandboxOption) (s
populatedEndpoints: map[string]struct{}{}, populatedEndpoints: map[string]struct{}{},
config: containerConfig{}, config: containerConfig{},
controller: c, controller: c,
extDNS: []extDNSEntry{},
} }
} }
sBox = sb sBox = sb

View File

@ -4,10 +4,14 @@ import (
"regexp" "regexp"
) )
// IPLocalhost is a regex patter for localhost IP address range. // IPLocalhost is a regex pattern for IPv4 or IPv6 loopback range.
const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)` const IPLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)`
// IPv4Localhost is a regex pattern for IPv4 localhost address range.
const IPv4Localhost = `(127\.([0-9]{1,3}\.){2}[0-9]{1,3})`
var localhostIPRegexp = regexp.MustCompile(IPLocalhost) var localhostIPRegexp = regexp.MustCompile(IPLocalhost)
var localhostIPv4Regexp = regexp.MustCompile(IPv4Localhost)
// IsLocalhost returns true if ip matches the localhost IP regular expression. // IsLocalhost returns true if ip matches the localhost IP regular expression.
// Used for determining if nameserver settings are being passed which are // Used for determining if nameserver settings are being passed which are
@ -15,3 +19,8 @@ var localhostIPRegexp = regexp.MustCompile(IPLocalhost)
func IsLocalhost(ip string) bool { func IsLocalhost(ip string) bool {
return localhostIPRegexp.MatchString(ip) return localhostIPRegexp.MatchString(ip)
} }
// IsIPv4Localhost returns true if ip matches the IPv4 localhost regular expression.
func IsIPv4Localhost(ip string) bool {
return localhostIPv4Regexp.MatchString(ip)
}

View File

@ -29,7 +29,7 @@ type Resolver interface {
NameServer() string NameServer() string
// SetExtServers configures the external nameservers the resolver // SetExtServers configures the external nameservers the resolver
// should use to forward queries // should use to forward queries
SetExtServers([]string) SetExtServers([]extDNSEntry)
// ResolverOptions returns resolv.conf options that should be set // ResolverOptions returns resolv.conf options that should be set
ResolverOptions() []string ResolverOptions() []string
} }
@ -69,7 +69,8 @@ const (
) )
type extDNSEntry struct { type extDNSEntry struct {
ipStr string ipStr string
hostLoopback bool
} }
// resolver implements the Resolver interface // resolver implements the Resolver interface
@ -182,13 +183,13 @@ func (r *resolver) Stop() {
r.queryLock = sync.Mutex{} r.queryLock = sync.Mutex{}
} }
func (r *resolver) SetExtServers(dns []string) { func (r *resolver) SetExtServers(extDNS []extDNSEntry) {
l := len(dns) l := len(extDNS)
if l > maxExtDNS { if l > maxExtDNS {
l = maxExtDNS l = maxExtDNS
} }
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
r.extDNSList[i].ipStr = dns[i] r.extDNSList[i] = extDNS[i]
} }
} }
@ -417,10 +418,14 @@ func (r *resolver) ServeDNS(w dns.ResponseWriter, query *dns.Msg) {
extConn, err = net.DialTimeout(proto, addr, extIOTimeout) extConn, err = net.DialTimeout(proto, addr, extIOTimeout)
} }
execErr := r.backend.ExecFunc(extConnect) if extDNS.hostLoopback {
if execErr != nil { extConnect()
logrus.Warn(execErr) } else {
continue execErr := r.backend.ExecFunc(extConnect)
if execErr != nil {
logrus.Warn(execErr)
continue
}
} }
if err != nil { if err != nil {
logrus.Warnf("Connect failed: %s", err) logrus.Warnf("Connect failed: %s", err)

View File

@ -69,7 +69,7 @@ type sandbox struct {
id string id string
containerID string containerID string
config containerConfig config containerConfig
extDNS []string extDNS []extDNSEntry
osSbox osl.Sandbox osSbox osl.Sandbox
controller *controller controller *controller
resolver Resolver resolver Resolver

View File

@ -14,6 +14,7 @@ import (
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/etchosts" "github.com/docker/libnetwork/etchosts"
"github.com/docker/libnetwork/resolvconf" "github.com/docker/libnetwork/resolvconf"
"github.com/docker/libnetwork/resolvconf/dns"
"github.com/docker/libnetwork/types" "github.com/docker/libnetwork/types"
) )
@ -161,6 +162,20 @@ func (sb *sandbox) restorePath() {
} }
} }
func (sb *sandbox) setExternalResolvers(content []byte, addrType int, checkLoopback bool) {
servers := resolvconf.GetNameservers(content, addrType)
for _, ip := range servers {
hostLoopback := false
if checkLoopback {
hostLoopback = dns.IsIPv4Localhost(ip)
}
sb.extDNS = append(sb.extDNS, extDNSEntry{
ipStr: ip,
hostLoopback: hostLoopback,
})
}
}
func (sb *sandbox) setupDNS() error { func (sb *sandbox) setupDNS() error {
var newRC *resolvconf.File var newRC *resolvconf.File
@ -208,7 +223,17 @@ func (sb *sandbox) setupDNS() error {
if err != nil { if err != nil {
return err return err
} }
// After building the resolv.conf from the user config save the
// external resolvers in the sandbox. Note that --dns 127.0.0.x
// config refers to the loopback in the container namespace
sb.setExternalResolvers(newRC.Content, types.IPv4, false)
} else { } else {
// If the host resolv.conf file has 127.0.0.x container should
// use the host restolver for queries. This is supported by the
// docker embedded DNS server. Hence save the external resolvers
// before filtering it out.
sb.setExternalResolvers(currRC.Content, types.IPv4, true)
// Replace any localhost/127.* (at this point we have no info about ipv6, pass it as true) // Replace any localhost/127.* (at this point we have no info about ipv6, pass it as true)
if newRC, err = resolvconf.FilterResolvDNS(currRC.Content, true); err != nil { if newRC, err = resolvconf.FilterResolvDNS(currRC.Content, true); err != nil {
return err return err
@ -297,7 +322,6 @@ func (sb *sandbox) updateDNS(ipv6Enabled bool) error {
// Embedded DNS server has to be enabled for this sandbox. Rebuild the container's // Embedded DNS server has to be enabled for this sandbox. Rebuild the container's
// resolv.conf by doing the following // resolv.conf by doing the following
// - Save the external name servers in resolv.conf in the sandbox
// - Add only the embedded server's IP to container's resolv.conf // - Add only the embedded server's IP to container's resolv.conf
// - If the embedded server needs any resolv.conf options add it to the current list // - If the embedded server needs any resolv.conf options add it to the current list
func (sb *sandbox) rebuildDNS() error { func (sb *sandbox) rebuildDNS() error {
@ -306,10 +330,9 @@ func (sb *sandbox) rebuildDNS() error {
return err return err
} }
// localhost entries have already been filtered out from the list if len(sb.extDNS) == 0 {
// retain only the v4 servers in sb for forwarding the DNS queries sb.setExternalResolvers(currRC.Content, types.IPv4, false)
sb.extDNS = resolvconf.GetNameservers(currRC.Content, types.IPv4) }
var ( var (
dnsList = []string{sb.resolver.NameServer()} dnsList = []string{sb.resolver.NameServer()}
dnsOptionsList = resolvconf.GetOptions(currRC.Content) dnsOptionsList = resolvconf.GetOptions(currRC.Content)

View File

@ -27,7 +27,7 @@ type sbState struct {
dbExists bool dbExists bool
Eps []epState Eps []epState
EpPriority map[string]int EpPriority map[string]int
ExtDNS []string ExtDNS []extDNSEntry
} }
func (sbs *sbState) Key() []string { func (sbs *sbState) Key() []string {