diff --git a/daemon/graphdriver/overlay/copy.go b/daemon/graphdriver/overlay/copy.go index 7d81a83abd..666a5c0e04 100644 --- a/daemon/graphdriver/overlay/copy.go +++ b/daemon/graphdriver/overlay/copy.go @@ -11,6 +11,7 @@ import ( "github.com/docker/docker/pkg/pools" "github.com/docker/docker/pkg/system" + rsystem "github.com/opencontainers/runc/libcontainer/system" ) type copyFlags int @@ -105,6 +106,10 @@ func copyDir(srcDir, dstDir string, flags copyFlags) error { case os.ModeNamedPipe: fallthrough case os.ModeSocket: + if rsystem.RunningInUserNS() { + // cannot create a device if running in user namespace + return nil + } if err := syscall.Mkfifo(dstPath, stat.Mode); err != nil { return err } diff --git a/pkg/archive/archive.go b/pkg/archive/archive.go index ad3d65b2fc..2a2166d439 100644 --- a/pkg/archive/archive.go +++ b/pkg/archive/archive.go @@ -59,6 +59,7 @@ type ( // For each include when creating an archive, the included name will be // replaced with the matching name from this map. RebaseNames map[string]string + InUserNS bool } // Archiver allows the reuse of most utility functions of this package @@ -381,7 +382,7 @@ func (ta *tarAppender) addTarFile(path, name string) error { return nil } -func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *TarChownOptions) error { +func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *TarChownOptions, inUserns bool) error { // hdr.Mode is in linux format, which we can use for sycalls, // but for os.Foo() calls we need the mode converted to os.FileMode, // so use hdrInfo.Mode() (they differ for e.g. setuid bits) @@ -409,7 +410,16 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L } file.Close() - case tar.TypeBlock, tar.TypeChar, tar.TypeFifo: + case tar.TypeBlock, tar.TypeChar: + if inUserns { // cannot create devices in a userns + return nil + } + // Handle this is an OS-specific way + if err := handleTarTypeBlockCharFifo(hdr, path); err != nil { + return err + } + + case tar.TypeFifo: // Handle this is an OS-specific way if err := handleTarTypeBlockCharFifo(hdr, path); err != nil { return err @@ -817,7 +827,7 @@ loop: } } - if err := createTarFile(path, dest, hdr, trBuf, !options.NoLchown, options.ChownOpts); err != nil { + if err := createTarFile(path, dest, hdr, trBuf, !options.NoLchown, options.ChownOpts, options.InUserNS); err != nil { return err } diff --git a/pkg/archive/archive_test.go b/pkg/archive/archive_test.go index 05db3d7ed9..6c6ac28a02 100644 --- a/pkg/archive/archive_test.go +++ b/pkg/archive/archive_test.go @@ -802,7 +802,7 @@ func TestTypeXGlobalHeaderDoesNotFail(t *testing.T) { t.Fatal(err) } defer os.RemoveAll(tmpDir) - err = createTarFile(filepath.Join(tmpDir, "pax_global_header"), tmpDir, &hdr, nil, true, nil) + err = createTarFile(filepath.Join(tmpDir, "pax_global_header"), tmpDir, &hdr, nil, true, nil, false) if err != nil { t.Fatal(err) } diff --git a/pkg/archive/archive_unix.go b/pkg/archive/archive_unix.go index fbc3bb8c4d..7083f2fa53 100644 --- a/pkg/archive/archive_unix.go +++ b/pkg/archive/archive_unix.go @@ -10,6 +10,7 @@ import ( "syscall" "github.com/docker/docker/pkg/system" + rsystem "github.com/opencontainers/runc/libcontainer/system" ) // fixVolumePathPrefix does platform specific processing to ensure that if @@ -80,6 +81,11 @@ func minor(device uint64) uint64 { // handleTarTypeBlockCharFifo is an OS-specific helper function used by // createTarFile to handle the following types of header: Block; Char; Fifo func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error { + if rsystem.RunningInUserNS() { + // cannot create a device if running in user namespace + return nil + } + mode := uint32(hdr.Mode & 07777) switch hdr.Typeflag { case tar.TypeBlock: diff --git a/pkg/archive/diff.go b/pkg/archive/diff.go index 1b08ad33ab..26da677c1f 100644 --- a/pkg/archive/diff.go +++ b/pkg/archive/diff.go @@ -111,7 +111,7 @@ func UnpackLayer(dest string, layer Reader, options *TarOptions) (size int64, er } defer os.RemoveAll(aufsTempdir) } - if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr, true, nil); err != nil { + if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr, true, nil, options.InUserNS); err != nil { return 0, err } } @@ -219,7 +219,7 @@ func UnpackLayer(dest string, layer Reader, options *TarOptions) (size int64, er } srcHdr.Gid = xGID } - if err := createTarFile(path, dest, srcHdr, srcData, true, nil); err != nil { + if err := createTarFile(path, dest, srcHdr, srcData, true, nil, options.InUserNS); err != nil { return 0, err } diff --git a/pkg/chrootarchive/chroot_linux.go b/pkg/chrootarchive/chroot_linux.go index cefbef9df4..7153b48798 100644 --- a/pkg/chrootarchive/chroot_linux.go +++ b/pkg/chrootarchive/chroot_linux.go @@ -8,6 +8,7 @@ import ( "syscall" "github.com/docker/docker/pkg/mount" + rsystem "github.com/opencontainers/runc/libcontainer/system" ) // chroot on linux uses pivot_root instead of chroot @@ -17,6 +18,10 @@ import ( // Old root is removed after the call to pivot_root so it is no longer available under the new root. // This is similar to how libcontainer sets up a container's rootfs func chroot(path string) (err error) { + // if the engine is running in a user namespace we need to use actual chroot + if rsystem.RunningInUserNS() { + return realChroot(path) + } if err := syscall.Unshare(syscall.CLONE_NEWNS); err != nil { return fmt.Errorf("Error creating mount namespace before pivot: %v", err) } diff --git a/pkg/chrootarchive/diff_unix.go b/pkg/chrootarchive/diff_unix.go index a4adb74d58..066eda5fa7 100644 --- a/pkg/chrootarchive/diff_unix.go +++ b/pkg/chrootarchive/diff_unix.go @@ -15,6 +15,7 @@ import ( "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/system" + rsystem "github.com/opencontainers/runc/libcontainer/system" ) type applyLayerResponse struct { @@ -34,6 +35,7 @@ func applyLayer() { runtime.LockOSThread() flag.Parse() + inUserns := rsystem.RunningInUserNS() if err := chroot(flag.Arg(0)); err != nil { fatal(err) } @@ -49,6 +51,10 @@ func applyLayer() { fatal(err) } + if inUserns { + options.InUserNS = true + } + if tmpDir, err = ioutil.TempDir("/", "temp-docker-extract"); err != nil { fatal(err) } @@ -88,6 +94,9 @@ func applyLayerHandler(dest string, layer archive.Reader, options *archive.TarOp } if options == nil { options = &archive.TarOptions{} + if rsystem.RunningInUserNS() { + options.InUserNS = true + } } if options.ExcludePatterns == nil { options.ExcludePatterns = []string{}