From 6403215635633c90d2ac256cd85cfeaa1eb52863 Mon Sep 17 00:00:00 2001 From: Justin Cormack Date: Tue, 25 Jul 2017 14:40:40 +0100 Subject: [PATCH] In the init section use a symlink for /etc/resolv.conf Unfortunately there are a lot of issues with resolv.conf as we cannot actually write it into the image from any docker image, as docker will always have something bind mounted in. In addition, normally we expect the filesystem to br read only for images that moby generates, so the actual etc/resolv.conf is likely not to be writeable. Previously we were adding in a default resolv.conf into every image pointing at Google's name servers but that is really a bad idea. Instead, normal images now get an empty default, while images in the `init` section will get a symlink, currently hard coded to `/run/resolvconf/resolv.conf` but you can override this with the `files` section to be static or a different link. In future, if we have an easy way to build and extract images with user control of this, we can drop this. Signed-off-by: Justin Cormack --- src/moby/build.go | 10 ++++++++-- src/moby/image.go | 49 +++++++++++++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/moby/build.go b/src/moby/build.go index 15f0ec04d..d71d9ad4e 100644 --- a/src/moby/build.go +++ b/src/moby/build.go @@ -40,6 +40,12 @@ RUN rm -f Dockerfile ENTRYPOINT ["/sbin/tini", "--", "/bin/rc.init"] ` +// For now this is a constant that we use in init section only to make +// resolv.conf point at somewhere writeable. In future whe we are not using +// Docker to extract images we can read this directly from image, but now Docker +// will overwrite anything we put in the image. +const resolvconfSymlink = "/run/resolvconf/resolv.conf" + var additions = map[string]addFun{ "docker": func(tw *tar.Writer) error { log.Infof(" Adding Dockerfile") @@ -146,7 +152,7 @@ func Build(m Moby, w io.Writer, pull bool, tp string) error { // get kernel and initrd tarball from container log.Infof("Extract kernel image: %s", m.Kernel.Image) kf := newKernelFilter(iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar) - err := ImageTar(m.Kernel.Image, "", kf, enforceContentTrust(m.Kernel.Image, &m.Trust), pull) + err := ImageTar(m.Kernel.Image, "", kf, enforceContentTrust(m.Kernel.Image, &m.Trust), pull, "") if err != nil { return fmt.Errorf("Failed to extract kernel image and tarball: %v", err) } @@ -162,7 +168,7 @@ func Build(m Moby, w io.Writer, pull bool, tp string) error { } for _, ii := range m.Init { log.Infof("Process init image: %s", ii) - err := ImageTar(ii, "", iw, enforceContentTrust(ii, &m.Trust), pull) + err := ImageTar(ii, "", iw, enforceContentTrust(ii, &m.Trust), pull, resolvconfSymlink) if err != nil { return fmt.Errorf("Failed to build init tarball from %s: %v", ii, err) } diff --git a/src/moby/image.go b/src/moby/image.go index 52aaa95cb..98d841c31 100644 --- a/src/moby/image.go +++ b/src/moby/image.go @@ -22,6 +22,11 @@ type tarWriter interface { // used the containerd libraries to do this instead locally direct from a local image // cache as it would be much simpler. +// Unfortunately there are some files that Docker always makes appear in a running image and +// export shows them. In particular we have no way for a user to specify their own resolv.conf. +// Even if we were not using docker export to get the image, users of docker build cannot override +// the resolv.conf either, as it is not writeable and bind mounted in. + var exclude = map[string]bool{ ".dockerenv": true, "Dockerfile": true, @@ -39,10 +44,8 @@ ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters `, - "etc/resolv.conf": `nameserver 8.8.8.8 -nameserver 8.8.4.4 -nameserver 2001:4860:4860::8888 -nameserver 2001:4860:4860::8844 + "etc/resolv.conf": ` +# no resolv.conf configured `, } @@ -75,7 +78,7 @@ func tarPrefix(path string, tw tarWriter) error { } // ImageTar takes a Docker image and outputs it to a tar stream -func ImageTar(image, prefix string, tw tarWriter, trust bool, pull bool) error { +func ImageTar(image, prefix string, tw tarWriter, trust bool, pull bool, resolv string) error { log.Debugf("image tar: %s %s", image, prefix) if prefix != "" && prefix[len(prefix)-1] != byte('/') { return fmt.Errorf("prefix does not end with /: %s", prefix) @@ -137,17 +140,29 @@ func ImageTar(image, prefix string, tw tarWriter, trust bool, pull bool) error { return err } } else if replace[hdr.Name] != "" { - contents := replace[hdr.Name] - hdr.Size = int64(len(contents)) - hdr.Name = prefix + hdr.Name - log.Debugf("image tar: %s %s add %s", image, prefix, hdr.Name) - if err := tw.WriteHeader(hdr); err != nil { - return err - } - buf := bytes.NewBufferString(contents) - _, err = io.Copy(tw, buf) - if err != nil { - return err + if hdr.Name != "etc/resolv.conf" || resolv == "" { + contents := replace[hdr.Name] + hdr.Size = int64(len(contents)) + hdr.Name = prefix + hdr.Name + log.Debugf("image tar: %s %s add %s", image, prefix, hdr.Name) + if err := tw.WriteHeader(hdr); err != nil { + return err + } + buf := bytes.NewBufferString(contents) + _, err = io.Copy(tw, buf) + if err != nil { + return err + } + } else { + // replace resolv.conf with specified symlink + hdr.Name = prefix + hdr.Name + hdr.Size = 0 + hdr.Typeflag = tar.TypeSymlink + hdr.Linkname = resolv + log.Debugf("image tar: %s %s add resolv symlink /etc/resolv.conf -> %s", image, prefix, resolv) + if err := tw.WriteHeader(hdr); err != nil { + return err + } } _, err = io.Copy(ioutil.Discard, tr) if err != nil { @@ -171,7 +186,7 @@ func ImageTar(image, prefix string, tw tarWriter, trust bool, pull bool) error { // ImageBundle produces an OCI bundle at the given path in a tarball, given an image and a config.json func ImageBundle(path string, image string, config []byte, tw tarWriter, trust bool, pull bool) error { log.Debugf("image bundle: %s %s cfg: %s", path, image, string(config)) - err := ImageTar(image, path+"/rootfs/", tw, trust, pull) + err := ImageTar(image, path+"/rootfs/", tw, trust, pull, "") if err != nil { return err }