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 <justin.cormack@docker.com>
This commit is contained in:
Justin Cormack 2017-07-25 14:40:40 +01:00
parent 2c4d567781
commit 6403215635
2 changed files with 40 additions and 19 deletions

View File

@ -40,6 +40,12 @@ RUN rm -f Dockerfile
ENTRYPOINT ["/sbin/tini", "--", "/bin/rc.init"] 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{ var additions = map[string]addFun{
"docker": func(tw *tar.Writer) error { "docker": func(tw *tar.Writer) error {
log.Infof(" Adding Dockerfile") 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 // get kernel and initrd tarball from container
log.Infof("Extract kernel image: %s", m.Kernel.Image) log.Infof("Extract kernel image: %s", m.Kernel.Image)
kf := newKernelFilter(iw, m.Kernel.Cmdline, m.Kernel.Binary, m.Kernel.Tar) 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 { if err != nil {
return fmt.Errorf("Failed to extract kernel image and tarball: %v", err) 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 { for _, ii := range m.Init {
log.Infof("Process init image: %s", ii) 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 { if err != nil {
return fmt.Errorf("Failed to build init tarball from %s: %v", ii, err) return fmt.Errorf("Failed to build init tarball from %s: %v", ii, err)
} }

View File

@ -22,6 +22,11 @@ type tarWriter interface {
// used the containerd libraries to do this instead locally direct from a local image // used the containerd libraries to do this instead locally direct from a local image
// cache as it would be much simpler. // 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{ var exclude = map[string]bool{
".dockerenv": true, ".dockerenv": true,
"Dockerfile": true, "Dockerfile": true,
@ -39,10 +44,8 @@ ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes ff02::1 ip6-allnodes
ff02::2 ip6-allrouters ff02::2 ip6-allrouters
`, `,
"etc/resolv.conf": `nameserver 8.8.8.8 "etc/resolv.conf": `
nameserver 8.8.4.4 # no resolv.conf configured
nameserver 2001:4860:4860::8888
nameserver 2001:4860:4860::8844
`, `,
} }
@ -75,7 +78,7 @@ func tarPrefix(path string, tw tarWriter) error {
} }
// ImageTar takes a Docker image and outputs it to a tar stream // 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) log.Debugf("image tar: %s %s", image, prefix)
if prefix != "" && prefix[len(prefix)-1] != byte('/') { if prefix != "" && prefix[len(prefix)-1] != byte('/') {
return fmt.Errorf("prefix does not end with /: %s", prefix) 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 return err
} }
} else if replace[hdr.Name] != "" { } else if replace[hdr.Name] != "" {
contents := replace[hdr.Name] if hdr.Name != "etc/resolv.conf" || resolv == "" {
hdr.Size = int64(len(contents)) contents := replace[hdr.Name]
hdr.Name = prefix + hdr.Name hdr.Size = int64(len(contents))
log.Debugf("image tar: %s %s add %s", image, prefix, hdr.Name) hdr.Name = prefix + hdr.Name
if err := tw.WriteHeader(hdr); err != nil { log.Debugf("image tar: %s %s add %s", image, prefix, hdr.Name)
return err if err := tw.WriteHeader(hdr); err != nil {
} return err
buf := bytes.NewBufferString(contents) }
_, err = io.Copy(tw, buf) buf := bytes.NewBufferString(contents)
if err != nil { _, err = io.Copy(tw, buf)
return err 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) _, err = io.Copy(ioutil.Discard, tr)
if err != nil { 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 // 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 { 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)) 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 { if err != nil {
return err return err
} }