소스 검색

Set selinux label on local volumes from mounts API

When using a volume via the `Binds` API, a shared selinux label is
automatically set.
The `Mounts` API is not setting this, which makes volumes specified via
the mounts API useless when selinux is enabled.

This fix adopts the same selinux label for volumes on the mounts API as on
binds.
Note in the case of both the `Binds` API and the `Mounts` API, the
selinux label is only applied when the volume driver is the `local`
driver.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
Brian Goff 7 년 전
부모
커밋
5bbf5cc671
3개의 변경된 파일71개의 추가작업 그리고 34개의 파일을 삭제
  1. 3 0
      daemon/volumes.go
  2. 63 34
      integration-cli/docker_api_containers_test.go
  3. 5 0
      volume/local/local.go

+ 3 - 0
daemon/volumes.go

@@ -207,6 +207,9 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
 			}); ok {
 			}); ok {
 				mp.Source = cv.CachedPath()
 				mp.Source = cv.CachedPath()
 			}
 			}
+			if mp.Driver == volume.DefaultDriverName {
+				setBindModeIfNull(mp)
+			}
 		}
 		}
 
 
 		binds[mp.Destination] = true
 		binds[mp.Destination] = true

+ 63 - 34
integration-cli/docker_api_containers_test.go

@@ -11,6 +11,7 @@ import (
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
 	"regexp"
 	"regexp"
+	"runtime"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
@@ -1901,33 +1902,37 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
 	}
 	}
 
 
 	type testCase struct {
 	type testCase struct {
-		cfg      mounttypes.Mount
+		spec     mounttypes.Mount
 		expected types.MountPoint
 		expected types.MountPoint
 	}
 	}
 
 
+	var selinuxSharedLabel string
+	if runtime.GOOS == "linux" {
+		selinuxSharedLabel = "z"
+	}
+
 	cases := []testCase{
 	cases := []testCase{
 		// use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest
 		// use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest
 		// Validation of the actual `Mount` struct is done in another test is not needed here
 		// Validation of the actual `Mount` struct is done in another test is not needed here
-		{mounttypes.Mount{Type: "volume", Target: destPath}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
-		{mounttypes.Mount{Type: "volume", Target: destPath + slash}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
-		{mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"}, types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath}},
-		{mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"}, types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath}},
 		{
 		{
-			mounttypes.Mount{
-				Type:   "volume",
-				Target: destPath,
-				Source: "test3",
-				VolumeOptions: &mounttypes.VolumeOptions{
-					DriverConfig: &mounttypes.Driver{Name: volume.DefaultDriverName},
-				},
-			},
-			types.MountPoint{
-				Driver:      volume.DefaultDriverName,
-				Type:        "volume",
-				Name:        "test3",
-				RW:          true,
-				Destination: destPath,
-			},
+			spec:     mounttypes.Mount{Type: "volume", Target: destPath},
+			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
+		},
+		{
+			spec:     mounttypes.Mount{Type: "volume", Target: destPath + slash},
+			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
+		},
+		{
+			spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"},
+			expected: types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
+		},
+		{
+			spec:     mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"},
+			expected: types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
+		},
+		{
+			spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test3", VolumeOptions: &mounttypes.VolumeOptions{DriverConfig: &mounttypes.Driver{Name: volume.DefaultDriverName}}},
+			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", Name: "test3", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
 		},
 		},
 	}
 	}
 
 
@@ -1938,19 +1943,22 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
 		defer os.RemoveAll(tmpDir1)
 		defer os.RemoveAll(tmpDir1)
 		cases = append(cases, []testCase{
 		cases = append(cases, []testCase{
 			{
 			{
-				mounttypes.Mount{
+				spec: mounttypes.Mount{
 					Type:   "bind",
 					Type:   "bind",
 					Source: tmpDir1,
 					Source: tmpDir1,
 					Target: destPath,
 					Target: destPath,
 				},
 				},
-				types.MountPoint{
+				expected: types.MountPoint{
 					Type:        "bind",
 					Type:        "bind",
 					RW:          true,
 					RW:          true,
 					Destination: destPath,
 					Destination: destPath,
 					Source:      tmpDir1,
 					Source:      tmpDir1,
 				},
 				},
 			},
 			},
-			{mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true}, types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1}},
+			{
+				spec:     mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true},
+				expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1},
+			},
 		}...)
 		}...)
 
 
 		// for modes only supported on Linux
 		// for modes only supported on Linux
@@ -1963,19 +1971,40 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
 			c.Assert(mount.ForceMount("", tmpDir3, "none", "shared"), checker.IsNil)
 			c.Assert(mount.ForceMount("", tmpDir3, "none", "shared"), checker.IsNil)
 
 
 			cases = append(cases, []testCase{
 			cases = append(cases, []testCase{
-				{mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath}, types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3}},
-				{mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true}, types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3}},
-				{mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true, BindOptions: &mounttypes.BindOptions{Propagation: "shared"}}, types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3, Propagation: "shared"}},
+				{
+					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath},
+					expected: types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3},
+				},
+				{
+					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true},
+					expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3},
+				},
+				{
+					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true, BindOptions: &mounttypes.BindOptions{Propagation: "shared"}},
+					expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3, Propagation: "shared"},
+				},
 			}...)
 			}...)
 		}
 		}
 	}
 	}
 
 
 	if testEnv.DaemonPlatform() != "windows" { // Windows does not support volume populate
 	if testEnv.DaemonPlatform() != "windows" { // Windows does not support volume populate
 		cases = append(cases, []testCase{
 		cases = append(cases, []testCase{
-			{mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
-			{mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
-			{mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath}},
-			{mounttypes.Mount{Type: "volume", Target: destPath, Source: "test5", ReadOnly: true, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Type: "volume", Name: "test5", RW: false, Destination: destPath}},
+			{
+				spec:     mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
+				expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
+			},
+			{
+				spec:     mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
+				expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
+			},
+			{
+				spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
+				expected: types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
+			},
+			{
+				spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test5", ReadOnly: true, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
+				expected: types.MountPoint{Type: "volume", Name: "test5", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
+			},
 		}...)
 		}...)
 	}
 	}
 
 
@@ -1990,11 +2019,11 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
 	ctx := context.Background()
 	ctx := context.Background()
 	apiclient := testEnv.APIClient()
 	apiclient := testEnv.APIClient()
 	for i, x := range cases {
 	for i, x := range cases {
-		c.Logf("case %d - config: %v", i, x.cfg)
+		c.Logf("case %d - config: %v", i, x.spec)
 		container, err := apiclient.ContainerCreate(
 		container, err := apiclient.ContainerCreate(
 			ctx,
 			ctx,
 			&containertypes.Config{Image: testImg},
 			&containertypes.Config{Image: testImg},
-			&containertypes.HostConfig{Mounts: []mounttypes.Mount{x.cfg}},
+			&containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}},
 			&networktypes.NetworkingConfig{},
 			&networktypes.NetworkingConfig{},
 			"")
 			"")
 		require.NoError(c, err)
 		require.NoError(c, err)
@@ -2035,12 +2064,12 @@ func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
 		switch {
 		switch {
 
 
 		// Named volumes still exist after the container is removed
 		// Named volumes still exist after the container is removed
-		case x.cfg.Type == "volume" && len(x.cfg.Source) > 0:
+		case x.spec.Type == "volume" && len(x.spec.Source) > 0:
 			_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
 			_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
 			require.NoError(c, err)
 			require.NoError(c, err)
 
 
 		// Bind mounts are never removed with the container
 		// Bind mounts are never removed with the container
-		case x.cfg.Type == "bind":
+		case x.spec.Type == "bind":
 
 
 		// anonymous volumes are removed
 		// anonymous volumes are removed
 		default:
 		default:

+ 5 - 0
volume/local/local.go

@@ -334,6 +334,11 @@ func (v *localVolume) Path() string {
 	return v.path
 	return v.path
 }
 }
 
 
+// CachedPath returns the data location
+func (v *localVolume) CachedPath() string {
+	return v.path
+}
+
 // Mount implements the localVolume interface, returning the data location.
 // Mount implements the localVolume interface, returning the data location.
 // If there are any provided mount options, the resources will be mounted at this point
 // If there are any provided mount options, the resources will be mounted at this point
 func (v *localVolume) Mount(id string) (string, error) {
 func (v *localVolume) Mount(id string) (string, error) {