Browse code

Don't create devices if in a user namespace

If we are running in a user namespace, don't try to mknod as
it won't be allowed. libcontainer will bind-mount the host's
devices over files in the container anyway, so it's not needed.

The chrootarchive package does a chroot (without mounting /proc) before
its work, so we cannot check /proc/self/uid_map when we need to. So
compute it in advance and pass it along with the tar options.

Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>

Serge Hallyn authored on 2016/02/13 09:05:50
Showing 6 changed files
... ...
@@ -11,6 +11,7 @@ import (
11 11
 
12 12
 	"github.com/docker/docker/pkg/pools"
13 13
 	"github.com/docker/docker/pkg/system"
14
+	rsystem "github.com/opencontainers/runc/libcontainer/system"
14 15
 )
15 16
 
16 17
 type copyFlags int
... ...
@@ -105,6 +106,10 @@ func copyDir(srcDir, dstDir string, flags copyFlags) error {
105 105
 		case os.ModeNamedPipe:
106 106
 			fallthrough
107 107
 		case os.ModeSocket:
108
+			if rsystem.RunningInUserNS() {
109
+				// cannot create a device if running in user namespace
110
+				return nil
111
+			}
108 112
 			if err := syscall.Mkfifo(dstPath, stat.Mode); err != nil {
109 113
 				return err
110 114
 			}
... ...
@@ -59,6 +59,7 @@ type (
59 59
 		// For each include when creating an archive, the included name will be
60 60
 		// replaced with the matching name from this map.
61 61
 		RebaseNames map[string]string
62
+		InUserNS    bool
62 63
 	}
63 64
 
64 65
 	// Archiver allows the reuse of most utility functions of this package
... ...
@@ -381,7 +382,7 @@ func (ta *tarAppender) addTarFile(path, name string) error {
381 381
 	return nil
382 382
 }
383 383
 
384
-func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *TarChownOptions) error {
384
+func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, Lchown bool, chownOpts *TarChownOptions, inUserns bool) error {
385 385
 	// hdr.Mode is in linux format, which we can use for sycalls,
386 386
 	// but for os.Foo() calls we need the mode converted to os.FileMode,
387 387
 	// 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
409 409
 		}
410 410
 		file.Close()
411 411
 
412
-	case tar.TypeBlock, tar.TypeChar, tar.TypeFifo:
412
+	case tar.TypeBlock, tar.TypeChar:
413
+		if inUserns { // cannot create devices in a userns
414
+			return nil
415
+		}
416
+		// Handle this is an OS-specific way
417
+		if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
418
+			return err
419
+		}
420
+
421
+	case tar.TypeFifo:
413 422
 		// Handle this is an OS-specific way
414 423
 		if err := handleTarTypeBlockCharFifo(hdr, path); err != nil {
415 424
 			return err
... ...
@@ -817,7 +827,7 @@ loop:
817 817
 			}
818 818
 		}
819 819
 
820
-		if err := createTarFile(path, dest, hdr, trBuf, !options.NoLchown, options.ChownOpts); err != nil {
820
+		if err := createTarFile(path, dest, hdr, trBuf, !options.NoLchown, options.ChownOpts, options.InUserNS); err != nil {
821 821
 			return err
822 822
 		}
823 823
 
... ...
@@ -802,7 +802,7 @@ func TestTypeXGlobalHeaderDoesNotFail(t *testing.T) {
802 802
 		t.Fatal(err)
803 803
 	}
804 804
 	defer os.RemoveAll(tmpDir)
805
-	err = createTarFile(filepath.Join(tmpDir, "pax_global_header"), tmpDir, &hdr, nil, true, nil)
805
+	err = createTarFile(filepath.Join(tmpDir, "pax_global_header"), tmpDir, &hdr, nil, true, nil, false)
806 806
 	if err != nil {
807 807
 		t.Fatal(err)
808 808
 	}
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	"syscall"
11 11
 
12 12
 	"github.com/docker/docker/pkg/system"
13
+	rsystem "github.com/opencontainers/runc/libcontainer/system"
13 14
 )
14 15
 
15 16
 // fixVolumePathPrefix does platform specific processing to ensure that if
... ...
@@ -80,6 +81,11 @@ func minor(device uint64) uint64 {
80 80
 // handleTarTypeBlockCharFifo is an OS-specific helper function used by
81 81
 // createTarFile to handle the following types of header: Block; Char; Fifo
82 82
 func handleTarTypeBlockCharFifo(hdr *tar.Header, path string) error {
83
+	if rsystem.RunningInUserNS() {
84
+		// cannot create a device if running in user namespace
85
+		return nil
86
+	}
87
+
83 88
 	mode := uint32(hdr.Mode & 07777)
84 89
 	switch hdr.Typeflag {
85 90
 	case tar.TypeBlock:
... ...
@@ -111,7 +111,7 @@ func UnpackLayer(dest string, layer Reader, options *TarOptions) (size int64, er
111 111
 					}
112 112
 					defer os.RemoveAll(aufsTempdir)
113 113
 				}
114
-				if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr, true, nil); err != nil {
114
+				if err := createTarFile(filepath.Join(aufsTempdir, basename), dest, hdr, tr, true, nil, options.InUserNS); err != nil {
115 115
 					return 0, err
116 116
 				}
117 117
 			}
... ...
@@ -219,7 +219,7 @@ func UnpackLayer(dest string, layer Reader, options *TarOptions) (size int64, er
219 219
 				}
220 220
 				srcHdr.Gid = xGID
221 221
 			}
222
-			if err := createTarFile(path, dest, srcHdr, srcData, true, nil); err != nil {
222
+			if err := createTarFile(path, dest, srcHdr, srcData, true, nil, options.InUserNS); err != nil {
223 223
 				return 0, err
224 224
 			}
225 225
 
... ...
@@ -15,6 +15,7 @@ import (
15 15
 	"github.com/docker/docker/pkg/archive"
16 16
 	"github.com/docker/docker/pkg/reexec"
17 17
 	"github.com/docker/docker/pkg/system"
18
+	rsystem "github.com/opencontainers/runc/libcontainer/system"
18 19
 )
19 20
 
20 21
 type applyLayerResponse struct {
... ...
@@ -34,6 +35,7 @@ func applyLayer() {
34 34
 	runtime.LockOSThread()
35 35
 	flag.Parse()
36 36
 
37
+	inUserns := rsystem.RunningInUserNS()
37 38
 	if err := chroot(flag.Arg(0)); err != nil {
38 39
 		fatal(err)
39 40
 	}
... ...
@@ -49,6 +51,10 @@ func applyLayer() {
49 49
 		fatal(err)
50 50
 	}
51 51
 
52
+	if inUserns {
53
+		options.InUserNS = true
54
+	}
55
+
52 56
 	if tmpDir, err = ioutil.TempDir("/", "temp-docker-extract"); err != nil {
53 57
 		fatal(err)
54 58
 	}
... ...
@@ -88,6 +94,9 @@ func applyLayerHandler(dest string, layer archive.Reader, options *archive.TarOp
88 88
 	}
89 89
 	if options == nil {
90 90
 		options = &archive.TarOptions{}
91
+		if rsystem.RunningInUserNS() {
92
+			options.InUserNS = true
93
+		}
91 94
 	}
92 95
 	if options.ExcludePatterns == nil {
93 96
 		options.ExcludePatterns = []string{}