mirror of
https://github.com/moby/moby.git
synced 2026-08-09 01:21:37 +00:00
Merge pull request #42755 from thaJeztah/move_resolvconf_consts
libnetwork: make resolvconf more self-contained
This commit is contained in:
@@ -28,7 +28,6 @@ import (
|
||||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/cli/build"
|
||||
"github.com/docker/docker/libnetwork/resolvconf"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/runconfig"
|
||||
"github.com/docker/docker/testutil"
|
||||
@@ -1326,13 +1325,13 @@ func (s *DockerSuite) TestRunDNSOptionsBasedOnHostResolvConf(c *testing.T) {
|
||||
c.Fatalf("/etc/resolv.conf does not exist")
|
||||
}
|
||||
|
||||
hostNameservers := resolvconf.GetNameservers(origResolvConf, types.IP)
|
||||
hostNameservers := resolvconf.GetNameservers(origResolvConf, resolvconf.IP)
|
||||
hostSearch := resolvconf.GetSearchDomains(origResolvConf)
|
||||
|
||||
var out string
|
||||
out, _ = dockerCmd(c, "run", "--dns=127.0.0.1", "busybox", "cat", "/etc/resolv.conf")
|
||||
|
||||
if actualNameservers := resolvconf.GetNameservers([]byte(out), types.IP); actualNameservers[0] != "127.0.0.1" {
|
||||
if actualNameservers := resolvconf.GetNameservers([]byte(out), resolvconf.IP); actualNameservers[0] != "127.0.0.1" {
|
||||
c.Fatalf("expected '127.0.0.1', but says: %q", actualNameservers[0])
|
||||
}
|
||||
|
||||
@@ -1348,7 +1347,7 @@ func (s *DockerSuite) TestRunDNSOptionsBasedOnHostResolvConf(c *testing.T) {
|
||||
|
||||
out, _ = dockerCmd(c, "run", "--dns-search=mydomain", "busybox", "cat", "/etc/resolv.conf")
|
||||
|
||||
actualNameservers := resolvconf.GetNameservers([]byte(out), types.IP)
|
||||
actualNameservers := resolvconf.GetNameservers([]byte(out), resolvconf.IP)
|
||||
if len(actualNameservers) != len(hostNameservers) {
|
||||
c.Fatalf("expected %q nameserver(s), but it has: %q", len(hostNameservers), len(actualNameservers))
|
||||
}
|
||||
@@ -1382,7 +1381,7 @@ func (s *DockerSuite) TestRunDNSOptionsBasedOnHostResolvConf(c *testing.T) {
|
||||
hostSearch = resolvconf.GetSearchDomains(resolvConf)
|
||||
|
||||
out, _ = dockerCmd(c, "run", "busybox", "cat", "/etc/resolv.conf")
|
||||
if actualNameservers = resolvconf.GetNameservers([]byte(out), types.IP); actualNameservers[0] != "12.34.56.78" || len(actualNameservers) != 1 {
|
||||
if actualNameservers = resolvconf.GetNameservers([]byte(out), resolvconf.IP); actualNameservers[0] != "12.34.56.78" || len(actualNameservers) != 1 {
|
||||
c.Fatalf("expected '12.34.56.78', but has: %v", actualNameservers)
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Package resolvconf provides utility code to query and update DNS configuration in /etc/resolv.conf
|
||||
@@ -1,26 +0,0 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// IPLocalhost is a regex pattern for IPv4 or IPv6 loopback range.
|
||||
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 localhostIPv4Regexp = regexp.MustCompile(IPv4Localhost)
|
||||
|
||||
// IsLocalhost returns true if ip matches the localhost IP regular expression.
|
||||
// Used for determining if nameserver settings are being passed which are
|
||||
// localhost addresses
|
||||
func IsLocalhost(ip string) bool {
|
||||
return localhostIPRegexp.MatchString(ip)
|
||||
}
|
||||
|
||||
// IsIPv4Localhost returns true if ip matches the IPv4 localhost regular expression.
|
||||
func IsIPv4Localhost(ip string) bool {
|
||||
return localhostIPv4Regexp.MatchString(ip)
|
||||
}
|
||||
@@ -8,9 +8,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/docker/libnetwork/resolvconf/dns"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -21,6 +18,13 @@ const (
|
||||
alternatePath = "/run/systemd/resolve/resolv.conf"
|
||||
)
|
||||
|
||||
// constants for the IP address type
|
||||
const (
|
||||
IP = iota // IPv4 and IPv6
|
||||
IPv4
|
||||
IPv6
|
||||
)
|
||||
|
||||
var (
|
||||
detectSystemdResolvConfOnce sync.Once
|
||||
pathAfterSystemdDetection = defaultPath
|
||||
@@ -44,7 +48,7 @@ func Path() string {
|
||||
// silencing error as it will resurface at next calls trying to read defaultPath
|
||||
return
|
||||
}
|
||||
ns := GetNameservers(candidateResolvConf, types.IP)
|
||||
ns := GetNameservers(candidateResolvConf, 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)
|
||||
@@ -53,20 +57,26 @@ func Path() string {
|
||||
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"}
|
||||
defaultIPv6Dns = []string{"nameserver 2001:4860:4860::8888", "nameserver 2001:4860:4860::8844"}
|
||||
ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)`
|
||||
ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock
|
||||
const (
|
||||
// ipLocalhost is a regex pattern for IPv4 or IPv6 loopback range.
|
||||
ipLocalhost = `((127\.([0-9]{1,3}\.){2}[0-9]{1,3})|(::1)$)`
|
||||
ipv4NumBlock = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)`
|
||||
ipv4Address = `(` + ipv4NumBlock + `\.){3}` + ipv4NumBlock
|
||||
|
||||
// This is not an IPv6 address verifier as it will accept a super-set of IPv6, and also
|
||||
// will *not match* IPv4-Embedded IPv6 Addresses (RFC6052), but that and other variants
|
||||
// -- e.g. other link-local types -- either won't work in containers or are unnecessary.
|
||||
// For readability and sufficiency for Docker purposes this seemed more reasonable than a
|
||||
// 1000+ character regexp with exact and complete IPv6 validation
|
||||
ipv6Address = `([0-9A-Fa-f]{0,4}:){2,7}([0-9A-Fa-f]{0,4})(%\w+)?`
|
||||
)
|
||||
|
||||
localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + dns.IPLocalhost + `\s*\n*`)
|
||||
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"}
|
||||
defaultIPv6Dns = []string{"nameserver 2001:4860:4860::8888", "nameserver 2001:4860:4860::8844"}
|
||||
|
||||
localhostNSRegexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipLocalhost + `\s*\n*`)
|
||||
nsIPv6Regexp = regexp.MustCompile(`(?m)^nameserver\s+` + ipv6Address + `\s*\n*`)
|
||||
nsRegexp = regexp.MustCompile(`^\s*nameserver\s*((` + ipv4Address + `)|(` + ipv6Address + `))\s*$`)
|
||||
nsIPv6Regexpmatch = regexp.MustCompile(`^\s*nameserver\s*((` + ipv6Address + `))\s*$`)
|
||||
@@ -98,7 +108,7 @@ func GetSpecific(path string) (*File, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hash, err := ioutils.HashData(bytes.NewReader(resolv))
|
||||
hash, err := hashData(bytes.NewReader(resolv))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -116,7 +126,7 @@ func GetIfChanged() (*File, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newHash, err := ioutils.HashData(bytes.NewReader(resolv))
|
||||
newHash, err := hashData(bytes.NewReader(resolv))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -153,7 +163,7 @@ func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) {
|
||||
}
|
||||
// if the resulting resolvConf has no more nameservers defined, add appropriate
|
||||
// default DNS servers for IPv4 and (optionally) IPv6
|
||||
if len(GetNameservers(cleanedResolvConf, types.IP)) == 0 {
|
||||
if len(GetNameservers(cleanedResolvConf, IP)) == 0 {
|
||||
logrus.Infof("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers: %v", defaultIPv4Dns)
|
||||
dns := defaultIPv4Dns
|
||||
if ipv6Enabled {
|
||||
@@ -162,7 +172,7 @@ func FilterResolvDNS(resolvConf []byte, ipv6Enabled bool) (*File, error) {
|
||||
}
|
||||
cleanedResolvConf = append(cleanedResolvConf, []byte("\n"+strings.Join(dns, "\n"))...)
|
||||
}
|
||||
hash, err := ioutils.HashData(bytes.NewReader(cleanedResolvConf))
|
||||
hash, err := hashData(bytes.NewReader(cleanedResolvConf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -189,11 +199,11 @@ func GetNameservers(resolvConf []byte, kind int) []string {
|
||||
nameservers := []string{}
|
||||
for _, line := range getLines(resolvConf, []byte("#")) {
|
||||
var ns [][]byte
|
||||
if kind == types.IP {
|
||||
if kind == IP {
|
||||
ns = nsRegexp.FindSubmatch(line)
|
||||
} else if kind == types.IPv4 {
|
||||
} else if kind == IPv4 {
|
||||
ns = nsIPv4Regexpmatch.FindSubmatch(line)
|
||||
} else if kind == types.IPv6 {
|
||||
} else if kind == IPv6 {
|
||||
ns = nsIPv6Regexpmatch.FindSubmatch(line)
|
||||
}
|
||||
if len(ns) > 0 {
|
||||
@@ -208,7 +218,7 @@ func GetNameservers(resolvConf []byte, kind int) []string {
|
||||
// This function's output is intended for net.ParseCIDR
|
||||
func GetNameserversAsCIDR(resolvConf []byte) []string {
|
||||
nameservers := []string{}
|
||||
for _, nameserver := range GetNameservers(resolvConf, types.IP) {
|
||||
for _, nameserver := range GetNameservers(resolvConf, IP) {
|
||||
var address string
|
||||
// If IPv6, strip zone if present
|
||||
if strings.Contains(nameserver, ":") {
|
||||
@@ -276,7 +286,7 @@ func Build(path string, dns, dnsSearch, dnsOptions []string) (*File, error) {
|
||||
}
|
||||
}
|
||||
|
||||
hash, err := ioutils.HashData(bytes.NewReader(content.Bytes()))
|
||||
hash, err := hashData(bytes.NewReader(content.Bytes()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -5,9 +5,6 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
@@ -22,7 +19,7 @@ func TestGet(t *testing.T) {
|
||||
if string(resolvConfUtils.Content) != string(resolvConfSystem) {
|
||||
t.Fatalf("/etc/resolv.conf and GetResolvConf have different content.")
|
||||
}
|
||||
hashSystem, err := ioutils.HashData(bytes.NewReader(resolvConfSystem))
|
||||
hashSystem, err := hashData(bytes.NewReader(resolvConfSystem))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -48,7 +45,7 @@ nameserver 1.2.3.4
|
||||
`search example.com
|
||||
nameserver 1.2.3.4 # not 4.3.2.1`: {"1.2.3.4"},
|
||||
} {
|
||||
test := GetNameservers([]byte(resolv), types.IP)
|
||||
test := GetNameservers([]byte(resolv), IP)
|
||||
if !strSlicesEqual(test, result) {
|
||||
t.Fatalf("Wrong nameserver string {%s} should be %v. Input: %s", test, result, resolv)
|
||||
}
|
||||
|
||||
16
libnetwork/resolvconf/utils.go
Normal file
16
libnetwork/resolvconf/utils.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package resolvconf
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
)
|
||||
|
||||
// hashData returns the sha256 sum of src.
|
||||
func hashData(src io.Reader) (string, error) {
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, src); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
18
libnetwork/resolvconf/utils_test.go
Normal file
18
libnetwork/resolvconf/utils_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package resolvconf
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHashData(t *testing.T) {
|
||||
reader := strings.NewReader("hash-me")
|
||||
actual, err := hashData(reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := "sha256:4d11186aed035cc624d553e10db358492c84a7cd6b9670d92123c144930450aa"
|
||||
if actual != expected {
|
||||
t.Fatalf("Expecting %s, got %s", expected, actual)
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package libnetwork
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -13,7 +14,6 @@ import (
|
||||
|
||||
"github.com/docker/docker/libnetwork/etchosts"
|
||||
"github.com/docker/docker/libnetwork/resolvconf"
|
||||
"github.com/docker/docker/libnetwork/resolvconf/dns"
|
||||
"github.com/docker/docker/libnetwork/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -171,8 +171,8 @@ func (sb *sandbox) setExternalResolvers(content []byte, addrType int, checkLoopb
|
||||
servers := resolvconf.GetNameservers(content, addrType)
|
||||
for _, ip := range servers {
|
||||
hostLoopback := false
|
||||
if checkLoopback {
|
||||
hostLoopback = dns.IsIPv4Localhost(ip)
|
||||
if checkLoopback && isIPv4Loopback(ip) {
|
||||
hostLoopback = true
|
||||
}
|
||||
sb.extDNS = append(sb.extDNS, extDNSEntry{
|
||||
IPStr: ip,
|
||||
@@ -181,6 +181,18 @@ func (sb *sandbox) setExternalResolvers(content []byte, addrType int, checkLoopb
|
||||
}
|
||||
}
|
||||
|
||||
// isIPv4Loopback checks if the given IP address is an IPv4 loopback address.
|
||||
// It's based on the logic in Go's net.IP.IsLoopback(), but only the IPv4 part:
|
||||
// https://github.com/golang/go/blob/go1.16.6/src/net/ip.go#L120-L126
|
||||
func isIPv4Loopback(ipAddress string) bool {
|
||||
if ip := net.ParseIP(ipAddress); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return ip4[0] == 127
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (sb *sandbox) setupDNS() error {
|
||||
var newRC *resolvconf.File
|
||||
|
||||
@@ -233,7 +245,7 @@ func (sb *sandbox) setupDNS() error {
|
||||
if len(sb.config.dnsList) > 0 || len(sb.config.dnsSearchList) > 0 || len(sb.config.dnsOptionsList) > 0 {
|
||||
var (
|
||||
err error
|
||||
dnsList = resolvconf.GetNameservers(currRC.Content, types.IP)
|
||||
dnsList = resolvconf.GetNameservers(currRC.Content, resolvconf.IP)
|
||||
dnsSearchList = resolvconf.GetSearchDomains(currRC.Content)
|
||||
dnsOptionsList = resolvconf.GetOptions(currRC.Content)
|
||||
)
|
||||
@@ -253,13 +265,13 @@ func (sb *sandbox) setupDNS() error {
|
||||
// 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)
|
||||
sb.setExternalResolvers(newRC.Content, resolvconf.IPv4, false)
|
||||
} else {
|
||||
// If the host resolv.conf file has 127.0.0.x container should
|
||||
// use the host resolver 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)
|
||||
sb.setExternalResolvers(currRC.Content, resolvconf.IPv4, 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 {
|
||||
@@ -358,7 +370,7 @@ func (sb *sandbox) rebuildDNS() error {
|
||||
}
|
||||
|
||||
if len(sb.extDNS) == 0 {
|
||||
sb.setExternalResolvers(currRC.Content, types.IPv4, false)
|
||||
sb.setExternalResolvers(currRC.Content, resolvconf.IPv4, false)
|
||||
}
|
||||
var (
|
||||
dnsList = []string{sb.resolver.NameServer()}
|
||||
@@ -367,7 +379,7 @@ func (sb *sandbox) rebuildDNS() error {
|
||||
)
|
||||
|
||||
// external v6 DNS servers has to be listed in resolv.conf
|
||||
dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, types.IPv6)...)
|
||||
dnsList = append(dnsList, resolvconf.GetNameservers(currRC.Content, resolvconf.IPv6)...)
|
||||
|
||||
// If the user config and embedded DNS server both have ndots option set,
|
||||
// remember the user's config so that unqualified names not in the docker
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// constants for the IP address type
|
||||
// Deprecated: use the consts defined in github.com/docker/docker/libnetwork/resolvconf
|
||||
const (
|
||||
IP = iota // IPv4 and IPv6
|
||||
IPv4
|
||||
|
||||
@@ -2,9 +2,12 @@ package ioutils // import "github.com/docker/docker/pkg/ioutils"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
|
||||
// make sure crypto.SHA256, crypto.sha512 and crypto.SHA384 are registered
|
||||
// TODO remove once https://github.com/opencontainers/go-digest/pull/64 is merged.
|
||||
_ "crypto/sha256"
|
||||
_ "crypto/sha512"
|
||||
)
|
||||
|
||||
// ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser
|
||||
@@ -49,15 +52,6 @@ func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader {
|
||||
}
|
||||
}
|
||||
|
||||
// HashData returns the sha256 sum of src.
|
||||
func HashData(src io.Reader) (string, error) {
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, src); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "sha256:" + hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// OnEOFReader wraps an io.ReadCloser and a function
|
||||
// the function will run at the end of file or close the file.
|
||||
type OnEOFReader struct {
|
||||
|
||||
@@ -58,18 +58,6 @@ func TestReaderErrWrapperRead(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashData(t *testing.T) {
|
||||
reader := strings.NewReader("hash-me")
|
||||
actual, err := HashData(reader)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := "sha256:4d11186aed035cc624d553e10db358492c84a7cd6b9670d92123c144930450aa"
|
||||
if actual != expected {
|
||||
t.Fatalf("Expecting %s, got %s", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
type perpetualReader struct{}
|
||||
|
||||
func (p *perpetualReader) Read(buf []byte) (n int, err error) {
|
||||
|
||||
Reference in New Issue
Block a user