Parcourir la source

Ensure plugin returns correctly scoped paths

Before this change, volume management was relying on the fact that
everything the plugin mounts is visible on the host within the plugin's
rootfs. In practice this caused some issues with mount leaks, so we
changed the behavior such that mounts are not visible on the plugin's
rootfs, but available outside of it, which breaks volume management.

To fix the issue, allow the plugin to scope the path correctly rather
than assuming that everything is visible in `p.Rootfs`.
In practice this is just scoping the `PropagatedMount` paths to the
correct host path.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
Brian Goff il y a 7 ans
Parent
commit
0e5eaf8ee3

+ 1 - 2
daemon/graphdriver/proxy.go

@@ -4,7 +4,6 @@ import (
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
-	"path/filepath"
 
 
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/containerfs"
 	"github.com/docker/docker/pkg/containerfs"
@@ -143,7 +142,7 @@ func (d *graphDriverProxy) Get(id, mountLabel string) (containerfs.ContainerFS,
 	if ret.Err != "" {
 	if ret.Err != "" {
 		err = errors.New(ret.Err)
 		err = errors.New(ret.Err)
 	}
 	}
-	return containerfs.NewLocalContainerFS(filepath.Join(d.p.BasePath(), ret.Dir)), err
+	return containerfs.NewLocalContainerFS(d.p.ScopedPath(ret.Dir)), err
 }
 }
 
 
 func (d *graphDriverProxy) Put(id string) error {
 func (d *graphDriverProxy) Put(id string) error {

+ 2 - 3
daemon/logger/adapter.go

@@ -3,7 +3,7 @@ package logger
 import (
 import (
 	"io"
 	"io"
 	"os"
 	"os"
-	"strings"
+	"path/filepath"
 	"sync"
 	"sync"
 	"time"
 	"time"
 
 
@@ -19,7 +19,6 @@ type pluginAdapter struct {
 	driverName   string
 	driverName   string
 	id           string
 	id           string
 	plugin       logPlugin
 	plugin       logPlugin
-	basePath     string
 	fifoPath     string
 	fifoPath     string
 	capabilities Capability
 	capabilities Capability
 	logInfo      Info
 	logInfo      Info
@@ -58,7 +57,7 @@ func (a *pluginAdapter) Close() error {
 	a.mu.Lock()
 	a.mu.Lock()
 	defer a.mu.Unlock()
 	defer a.mu.Unlock()
 
 
-	if err := a.plugin.StopLogging(strings.TrimPrefix(a.fifoPath, a.basePath)); err != nil {
+	if err := a.plugin.StopLogging(filepath.Join("/", "run", "docker", "logging", a.id)); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 8 - 8
daemon/logger/plugin.go

@@ -5,7 +5,6 @@ import (
 	"io"
 	"io"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
-	"strings"
 
 
 	"github.com/docker/docker/api/types/plugins/logdriver"
 	"github.com/docker/docker/api/types/plugins/logdriver"
 	getter "github.com/docker/docker/pkg/plugingetter"
 	getter "github.com/docker/docker/pkg/plugingetter"
@@ -39,18 +38,20 @@ func getPlugin(name string, mode int) (Creator, error) {
 	}
 	}
 
 
 	d := &logPluginProxy{p.Client()}
 	d := &logPluginProxy{p.Client()}
-	return makePluginCreator(name, d, p.BasePath()), nil
+	return makePluginCreator(name, d, p.ScopedPath), nil
 }
 }
 
 
-func makePluginCreator(name string, l *logPluginProxy, basePath string) Creator {
+func makePluginCreator(name string, l *logPluginProxy, scopePath func(s string) string) Creator {
 	return func(logCtx Info) (logger Logger, err error) {
 	return func(logCtx Info) (logger Logger, err error) {
 		defer func() {
 		defer func() {
 			if err != nil {
 			if err != nil {
 				pluginGetter.Get(name, extName, getter.Release)
 				pluginGetter.Get(name, extName, getter.Release)
 			}
 			}
 		}()
 		}()
-		root := filepath.Join(basePath, "run", "docker", "logging")
-		if err := os.MkdirAll(root, 0700); err != nil {
+
+		unscopedPath := filepath.Join("/", "run", "docker", "logging")
+		logRoot := scopePath(unscopedPath)
+		if err := os.MkdirAll(logRoot, 0700); err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 
 
@@ -59,8 +60,7 @@ func makePluginCreator(name string, l *logPluginProxy, basePath string) Creator
 			driverName: name,
 			driverName: name,
 			id:         id,
 			id:         id,
 			plugin:     l,
 			plugin:     l,
-			basePath:   basePath,
-			fifoPath:   filepath.Join(root, id),
+			fifoPath:   filepath.Join(logRoot, id),
 			logInfo:    logCtx,
 			logInfo:    logCtx,
 		}
 		}
 
 
@@ -77,7 +77,7 @@ func makePluginCreator(name string, l *logPluginProxy, basePath string) Creator
 		a.stream = stream
 		a.stream = stream
 		a.enc = logdriver.NewLogEntryEncoder(a.stream)
 		a.enc = logdriver.NewLogEntryEncoder(a.stream)
 
 
-		if err := l.StartLogging(strings.TrimPrefix(a.fifoPath, basePath), logCtx); err != nil {
+		if err := l.StartLogging(filepath.Join(unscopedPath, id), logCtx); err != nil {
 			return nil, errors.Wrapf(err, "error creating logger")
 			return nil, errors.Wrapf(err, "error creating logger")
 		}
 		}
 
 

+ 1 - 1
pkg/plugingetter/getter.go

@@ -17,7 +17,7 @@ const (
 type CompatPlugin interface {
 type CompatPlugin interface {
 	Client() *plugins.Client
 	Client() *plugins.Client
 	Name() string
 	Name() string
-	BasePath() string
+	ScopedPath(string) string
 	IsV1() bool
 	IsV1() bool
 }
 }
 
 

+ 4 - 4
pkg/plugins/plugins_unix.go

@@ -2,8 +2,8 @@
 
 
 package plugins
 package plugins
 
 
-// BasePath returns the path to which all paths returned by the plugin are relative to.
-// For v1 plugins, this always returns the host's root directory.
-func (p *Plugin) BasePath() string {
-	return "/"
+// ScopedPath returns the path scoped to the plugin's rootfs.
+// For v1 plugins, this always returns the path unchanged as v1 plugins run directly on the host.
+func (p *Plugin) ScopedPath(s string) string {
+	return s
 }
 }

+ 4 - 5
pkg/plugins/plugins_windows.go

@@ -1,8 +1,7 @@
 package plugins
 package plugins
 
 
-// BasePath returns the path to which all paths returned by the plugin are relative to.
-// For Windows v1 plugins, this returns an empty string, since the plugin is already aware
-// of the absolute path of the mount.
-func (p *Plugin) BasePath() string {
-	return ""
+// ScopedPath returns the path scoped to the plugin's rootfs.
+// For v1 plugins, this always returns the path unchanged as v1 plugins run directly on the host.
+func (p *Plugin) ScopedPath(s string) string {
+	return s
 }
 }

+ 0 - 1
plugin/manager_linux.go

@@ -65,7 +65,6 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
 			}
 			}
 		}
 		}
 	}
 	}
-
 	return pm.pluginPostStart(p, c)
 	return pm.pluginPostStart(p, c)
 }
 }
 
 

+ 8 - 4
plugin/v2/plugin.go

@@ -2,6 +2,7 @@ package v2
 
 
 import (
 import (
 	"fmt"
 	"fmt"
+	"path/filepath"
 	"strings"
 	"strings"
 	"sync"
 	"sync"
 
 
@@ -39,10 +40,13 @@ func (e ErrInadequateCapability) Error() string {
 	return fmt.Sprintf("plugin does not provide %q capability", e.cap)
 	return fmt.Sprintf("plugin does not provide %q capability", e.cap)
 }
 }
 
 
-// BasePath returns the path to which all paths returned by the plugin are relative to.
-// For Plugin objects this returns the host path of the plugin container's rootfs.
-func (p *Plugin) BasePath() string {
-	return p.Rootfs
+// ScopedPath returns the path scoped to the plugin rootfs
+func (p *Plugin) ScopedPath(s string) string {
+	if p.PluginObj.Config.PropagatedMount != "" && strings.HasPrefix(s, p.PluginObj.Config.PropagatedMount) {
+		// re-scope to the propagated mount path on the host
+		return filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount", strings.TrimPrefix(s, p.PluginObj.Config.PropagatedMount))
+	}
+	return filepath.Join(p.Rootfs, s)
 }
 }
 
 
 // Client returns the plugin client.
 // Client returns the plugin client.

+ 26 - 34
volume/drivers/adapter.go

@@ -2,7 +2,6 @@ package volumedrivers
 
 
 import (
 import (
 	"errors"
 	"errors"
-	"path/filepath"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
@@ -16,7 +15,7 @@ var (
 
 
 type volumeDriverAdapter struct {
 type volumeDriverAdapter struct {
 	name         string
 	name         string
-	baseHostPath string
+	scopePath    func(s string) string
 	capabilities *volume.Capability
 	capabilities *volume.Capability
 	proxy        *volumeDriverProxy
 	proxy        *volumeDriverProxy
 }
 }
@@ -30,10 +29,10 @@ func (a *volumeDriverAdapter) Create(name string, opts map[string]string) (volum
 		return nil, err
 		return nil, err
 	}
 	}
 	return &volumeAdapter{
 	return &volumeAdapter{
-		proxy:        a.proxy,
-		name:         name,
-		driverName:   a.name,
-		baseHostPath: a.baseHostPath,
+		proxy:      a.proxy,
+		name:       name,
+		driverName: a.name,
+		scopePath:  a.scopePath,
 	}, nil
 	}, nil
 }
 }
 
 
@@ -41,13 +40,6 @@ func (a *volumeDriverAdapter) Remove(v volume.Volume) error {
 	return a.proxy.Remove(v.Name())
 	return a.proxy.Remove(v.Name())
 }
 }
 
 
-func hostPath(baseHostPath, path string) string {
-	if baseHostPath != "" {
-		path = filepath.Join(baseHostPath, path)
-	}
-	return path
-}
-
 func (a *volumeDriverAdapter) List() ([]volume.Volume, error) {
 func (a *volumeDriverAdapter) List() ([]volume.Volume, error) {
 	ls, err := a.proxy.List()
 	ls, err := a.proxy.List()
 	if err != nil {
 	if err != nil {
@@ -57,11 +49,11 @@ func (a *volumeDriverAdapter) List() ([]volume.Volume, error) {
 	var out []volume.Volume
 	var out []volume.Volume
 	for _, vp := range ls {
 	for _, vp := range ls {
 		out = append(out, &volumeAdapter{
 		out = append(out, &volumeAdapter{
-			proxy:        a.proxy,
-			name:         vp.Name,
-			baseHostPath: a.baseHostPath,
-			driverName:   a.name,
-			eMount:       hostPath(a.baseHostPath, vp.Mountpoint),
+			proxy:      a.proxy,
+			name:       vp.Name,
+			scopePath:  a.scopePath,
+			driverName: a.name,
+			eMount:     a.scopePath(vp.Mountpoint),
 		})
 		})
 	}
 	}
 	return out, nil
 	return out, nil
@@ -79,13 +71,13 @@ func (a *volumeDriverAdapter) Get(name string) (volume.Volume, error) {
 	}
 	}
 
 
 	return &volumeAdapter{
 	return &volumeAdapter{
-		proxy:        a.proxy,
-		name:         v.Name,
-		driverName:   a.Name(),
-		eMount:       v.Mountpoint,
-		createdAt:    v.CreatedAt,
-		status:       v.Status,
-		baseHostPath: a.baseHostPath,
+		proxy:      a.proxy,
+		name:       v.Name,
+		driverName: a.Name(),
+		eMount:     v.Mountpoint,
+		createdAt:  v.CreatedAt,
+		status:     v.Status,
+		scopePath:  a.scopePath,
 	}, nil
 	}, nil
 }
 }
 
 
@@ -122,13 +114,13 @@ func (a *volumeDriverAdapter) getCapabilities() volume.Capability {
 }
 }
 
 
 type volumeAdapter struct {
 type volumeAdapter struct {
-	proxy        *volumeDriverProxy
-	name         string
-	baseHostPath string
-	driverName   string
-	eMount       string    // ephemeral host volume path
-	createdAt    time.Time // time the directory was created
-	status       map[string]interface{}
+	proxy      *volumeDriverProxy
+	name       string
+	scopePath  func(string) string
+	driverName string
+	eMount     string    // ephemeral host volume path
+	createdAt  time.Time // time the directory was created
+	status     map[string]interface{}
 }
 }
 
 
 type proxyVolume struct {
 type proxyVolume struct {
@@ -149,7 +141,7 @@ func (a *volumeAdapter) DriverName() string {
 func (a *volumeAdapter) Path() string {
 func (a *volumeAdapter) Path() string {
 	if len(a.eMount) == 0 {
 	if len(a.eMount) == 0 {
 		mountpoint, _ := a.proxy.Path(a.name)
 		mountpoint, _ := a.proxy.Path(a.name)
-		a.eMount = hostPath(a.baseHostPath, mountpoint)
+		a.eMount = a.scopePath(mountpoint)
 	}
 	}
 	return a.eMount
 	return a.eMount
 }
 }
@@ -160,7 +152,7 @@ func (a *volumeAdapter) CachedPath() string {
 
 
 func (a *volumeAdapter) Mount(id string) (string, error) {
 func (a *volumeAdapter) Mount(id string) (string, error) {
 	mountpoint, err := a.proxy.Mount(a.name, id)
 	mountpoint, err := a.proxy.Mount(a.name, id)
-	a.eMount = hostPath(a.baseHostPath, mountpoint)
+	a.eMount = a.scopePath(mountpoint)
 	return a.eMount, err
 	return a.eMount, err
 }
 }
 
 

+ 4 - 4
volume/drivers/extpoint.go

@@ -25,9 +25,9 @@ var drivers = &driverExtpoint{
 const extName = "VolumeDriver"
 const extName = "VolumeDriver"
 
 
 // NewVolumeDriver returns a driver has the given name mapped on the given client.
 // NewVolumeDriver returns a driver has the given name mapped on the given client.
-func NewVolumeDriver(name string, baseHostPath string, c client) volume.Driver {
+func NewVolumeDriver(name string, scopePath func(string) string, c client) volume.Driver {
 	proxy := &volumeDriverProxy{c}
 	proxy := &volumeDriverProxy{c}
-	return &volumeDriverAdapter{name: name, baseHostPath: baseHostPath, proxy: proxy}
+	return &volumeDriverAdapter{name: name, scopePath: scopePath, proxy: proxy}
 }
 }
 
 
 // volumeDriver defines the available functions that volume plugins must implement.
 // volumeDriver defines the available functions that volume plugins must implement.
@@ -129,7 +129,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
 			return nil, errors.Wrap(err, "error looking up volume plugin "+name)
 			return nil, errors.Wrap(err, "error looking up volume plugin "+name)
 		}
 		}
 
 
-		d := NewVolumeDriver(p.Name(), p.BasePath(), p.Client())
+		d := NewVolumeDriver(p.Name(), p.ScopedPath, p.Client())
 		if err := validateDriver(d); err != nil {
 		if err := validateDriver(d); err != nil {
 			if mode > 0 {
 			if mode > 0 {
 				// Undo any reference count changes from the initial `Get`
 				// Undo any reference count changes from the initial `Get`
@@ -224,7 +224,7 @@ func GetAllDrivers() ([]volume.Driver, error) {
 			continue
 			continue
 		}
 		}
 
 
-		ext := NewVolumeDriver(name, p.BasePath(), p.Client())
+		ext := NewVolumeDriver(name, p.ScopedPath, p.Client())
 		if p.IsV1() {
 		if p.IsV1() {
 			drivers.extensions[name] = ext
 			drivers.extensions[name] = ext
 		}
 		}

+ 2 - 2
volume/testutils/testutils.go

@@ -178,8 +178,8 @@ func (p *fakePlugin) IsV1() bool {
 	return false
 	return false
 }
 }
 
 
-func (p *fakePlugin) BasePath() string {
-	return ""
+func (p *fakePlugin) ScopedPath(s string) string {
+	return s
 }
 }
 
 
 type fakePluginGetter struct {
 type fakePluginGetter struct {