1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

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>
This commit is contained in:
Brian Goff 2017-12-14 10:27:10 -05:00
parent a53930a04f
commit 0e5eaf8ee3
11 changed files with 60 additions and 68 deletions

View file

@ -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 {

View file

@ -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
} }

View file

@ -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(logRoot, id),
fifoPath: filepath.Join(root, 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")
} }

View file

@ -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
} }

View file

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

View file

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

View file

@ -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)
} }

View file

@ -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. // ScopedPath returns the path scoped to the plugin rootfs
// For Plugin objects this returns the host path of the plugin container's rootfs. func (p *Plugin) ScopedPath(s string) string {
func (p *Plugin) BasePath() string { if p.PluginObj.Config.PropagatedMount != "" && strings.HasPrefix(s, p.PluginObj.Config.PropagatedMount) {
return p.Rootfs // 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.

View file

@ -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
} }
@ -33,7 +32,7 @@ func (a *volumeDriverAdapter) Create(name string, opts map[string]string) (volum
proxy: a.proxy, proxy: a.proxy,
name: name, name: name,
driverName: a.name, driverName: a.name,
baseHostPath: a.baseHostPath, 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 {
@ -59,9 +51,9 @@ func (a *volumeDriverAdapter) List() ([]volume.Volume, error) {
out = append(out, &volumeAdapter{ out = append(out, &volumeAdapter{
proxy: a.proxy, proxy: a.proxy,
name: vp.Name, name: vp.Name,
baseHostPath: a.baseHostPath, scopePath: a.scopePath,
driverName: a.name, driverName: a.name,
eMount: hostPath(a.baseHostPath, vp.Mountpoint), eMount: a.scopePath(vp.Mountpoint),
}) })
} }
return out, nil return out, nil
@ -85,7 +77,7 @@ func (a *volumeDriverAdapter) Get(name string) (volume.Volume, error) {
eMount: v.Mountpoint, eMount: v.Mountpoint,
createdAt: v.CreatedAt, createdAt: v.CreatedAt,
status: v.Status, status: v.Status,
baseHostPath: a.baseHostPath, scopePath: a.scopePath,
}, nil }, nil
} }
@ -124,7 +116,7 @@ func (a *volumeDriverAdapter) getCapabilities() volume.Capability {
type volumeAdapter struct { type volumeAdapter struct {
proxy *volumeDriverProxy proxy *volumeDriverProxy
name string name string
baseHostPath string scopePath func(string) string
driverName string driverName string
eMount string // ephemeral host volume path eMount string // ephemeral host volume path
createdAt time.Time // time the directory was created createdAt time.Time // time the directory was created
@ -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
} }

View file

@ -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
} }

View file

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