Browse code

Plugins perform propagated mount in runtime spec

Setting up the mounts on the host increases chances of mount leakage and
makes for more cleanup after the plugin has stopped.
With this change all mounts for the plugin are performed by the
container runtime and automatically cleaned up when the container exits.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>

Brian Goff authored on 2017/12/14 23:29:11
Showing 5 changed files
... ...
@@ -23,7 +23,7 @@ func newPluginDriver(name string, pl plugingetter.CompatPlugin, config Options)
23 23
 	home := config.Root
24 24
 	if !pl.IsV1() {
25 25
 		if p, ok := pl.(*v2.Plugin); ok {
26
-			if p.PropagatedMount != "" {
26
+			if p.PluginObj.Config.PropagatedMount != "" {
27 27
 				home = p.PluginObj.Config.PropagatedMount
28 28
 			}
29 29
 		}
... ...
@@ -19,7 +19,6 @@ import (
19 19
 	"github.com/docker/docker/layer"
20 20
 	"github.com/docker/docker/pkg/authorization"
21 21
 	"github.com/docker/docker/pkg/ioutils"
22
-	"github.com/docker/docker/pkg/mount"
23 22
 	"github.com/docker/docker/pkg/pubsub"
24 23
 	"github.com/docker/docker/pkg/system"
25 24
 	"github.com/docker/docker/plugin/v2"
... ...
@@ -151,16 +150,6 @@ func (pm *Manager) HandleExitEvent(id string) error {
151 151
 
152 152
 	os.RemoveAll(filepath.Join(pm.config.ExecRoot, id))
153 153
 
154
-	if p.PropagatedMount != "" {
155
-		if err := mount.Unmount(p.PropagatedMount); err != nil {
156
-			logrus.Warnf("Could not unmount %s: %v", p.PropagatedMount, err)
157
-		}
158
-		propRoot := filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
159
-		if err := mount.Unmount(propRoot); err != nil {
160
-			logrus.Warn("Could not unmount %s: %v", propRoot, err)
161
-		}
162
-	}
163
-
164 154
 	pm.mu.RLock()
165 155
 	c := pm.cMap[p]
166 156
 	if c.exitChan != nil {
... ...
@@ -239,28 +228,17 @@ func (pm *Manager) reload() error { // todo: restore
239 239
 						// check if we need to migrate an older propagated mount from before
240 240
 						// these mounts were stored outside the plugin rootfs
241 241
 						if _, err := os.Stat(propRoot); os.IsNotExist(err) {
242
-							if _, err := os.Stat(p.PropagatedMount); err == nil {
243
-								// make sure nothing is mounted here
244
-								// don't care about errors
245
-								mount.Unmount(p.PropagatedMount)
246
-								if err := os.Rename(p.PropagatedMount, propRoot); err != nil {
242
+							rootfsProp := filepath.Join(p.Rootfs, p.PluginObj.Config.PropagatedMount)
243
+							if _, err := os.Stat(rootfsProp); err == nil {
244
+								if err := os.Rename(rootfsProp, propRoot); err != nil {
247 245
 									logrus.WithError(err).WithField("dir", propRoot).Error("error migrating propagated mount storage")
248 246
 								}
249
-								if err := os.MkdirAll(p.PropagatedMount, 0755); err != nil {
250
-									logrus.WithError(err).WithField("dir", p.PropagatedMount).Error("error migrating propagated mount storage")
251
-								}
252 247
 							}
253 248
 						}
254 249
 
255 250
 						if err := os.MkdirAll(propRoot, 0755); err != nil {
256 251
 							logrus.Errorf("failed to create PropagatedMount directory at %s: %v", propRoot, err)
257 252
 						}
258
-						// TODO: sanitize PropagatedMount and prevent breakout
259
-						p.PropagatedMount = filepath.Join(p.Rootfs, p.PluginObj.Config.PropagatedMount)
260
-						if err := os.MkdirAll(p.PropagatedMount, 0755); err != nil {
261
-							logrus.Errorf("failed to create PropagatedMount directory at %s: %v", p.PropagatedMount, err)
262
-							return
263
-						}
264 253
 					}
265 254
 				}
266 255
 			}
... ...
@@ -22,7 +22,7 @@ import (
22 22
 	"golang.org/x/sys/unix"
23 23
 )
24 24
 
25
-func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) (err error) {
25
+func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
26 26
 	p.Rootfs = filepath.Join(pm.config.Root, p.PluginObj.ID, "rootfs")
27 27
 	if p.IsEnabled() && !force {
28 28
 		return errors.Wrap(enabledError(p.Name()), "plugin already enabled")
... ...
@@ -40,20 +40,16 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) (err error) {
40 40
 	pm.mu.Unlock()
41 41
 
42 42
 	var propRoot string
43
-	if p.PropagatedMount != "" {
43
+	if p.PluginObj.Config.PropagatedMount != "" {
44 44
 		propRoot = filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
45 45
 
46
-		if err = os.MkdirAll(propRoot, 0755); err != nil {
46
+		if err := os.MkdirAll(propRoot, 0755); err != nil {
47 47
 			logrus.Errorf("failed to create PropagatedMount directory at %s: %v", propRoot, err)
48 48
 		}
49 49
 
50
-		if err = mount.MakeRShared(propRoot); err != nil {
50
+		if err := mount.MakeRShared(propRoot); err != nil {
51 51
 			return errors.Wrap(err, "error setting up propagated mount dir")
52 52
 		}
53
-
54
-		if err = mount.Mount(propRoot, p.PropagatedMount, "none", "rbind"); err != nil {
55
-			return errors.Wrap(err, "error creating mount for propagated mount")
56
-		}
57 53
 	}
58 54
 
59 55
 	rootFS := containerfs.NewLocalContainerFS(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName))
... ...
@@ -63,10 +59,7 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) (err error) {
63 63
 
64 64
 	stdout, stderr := makeLoggerStreams(p.GetID())
65 65
 	if err := pm.executor.Create(p.GetID(), *spec, stdout, stderr); err != nil {
66
-		if p.PropagatedMount != "" {
67
-			if err := mount.Unmount(p.PropagatedMount); err != nil {
68
-				logrus.Warnf("Could not unmount %s: %v", p.PropagatedMount, err)
69
-			}
66
+		if p.PluginObj.Config.PropagatedMount != "" {
70 67
 			if err := mount.Unmount(propRoot); err != nil {
71 68
 				logrus.Warnf("Could not unmount %s: %v", propRoot, err)
72 69
 			}
... ...
@@ -14,12 +14,11 @@ import (
14 14
 
15 15
 // Plugin represents an individual plugin.
16 16
 type Plugin struct {
17
-	mu              sync.RWMutex
18
-	PluginObj       types.Plugin `json:"plugin"` // todo: embed struct
19
-	pClient         *plugins.Client
20
-	refCount        int
21
-	PropagatedMount string // TODO: make private
22
-	Rootfs          string // TODO: make private
17
+	mu        sync.RWMutex
18
+	PluginObj types.Plugin `json:"plugin"` // todo: embed struct
19
+	pClient   *plugins.Client
20
+	refCount  int
21
+	Rootfs    string // TODO: make private
23 22
 
24 23
 	Config   digest.Digest
25 24
 	Blobsums []digest.Digest
... ...
@@ -4,6 +4,7 @@ import (
4 4
 	"os"
5 5
 	"path/filepath"
6 6
 	"runtime"
7
+	"sort"
7 8
 	"strings"
8 9
 
9 10
 	"github.com/docker/docker/api/types"
... ...
@@ -32,6 +33,17 @@ func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
32 32
 		return nil, errors.WithStack(err)
33 33
 	}
34 34
 
35
+	if p.PluginObj.Config.PropagatedMount != "" {
36
+		pRoot := filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
37
+		s.Mounts = append(s.Mounts, specs.Mount{
38
+			Source:      pRoot,
39
+			Destination: p.PluginObj.Config.PropagatedMount,
40
+			Type:        "bind",
41
+			Options:     []string{"rbind", "rw", "rshared"},
42
+		})
43
+		s.Linux.RootfsPropagation = "rshared"
44
+	}
45
+
35 46
 	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
36 47
 		Source:      &execRoot,
37 48
 		Destination: defaultPluginRuntimeDestination,
... ...
@@ -89,11 +101,6 @@ func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
89 89
 		}
90 90
 	}
91 91
 
92
-	if p.PluginObj.Config.PropagatedMount != "" {
93
-		p.PropagatedMount = filepath.Join(p.Rootfs, p.PluginObj.Config.PropagatedMount)
94
-		s.Linux.RootfsPropagation = "rshared"
95
-	}
96
-
97 92
 	if p.PluginObj.Config.Linux.AllowAllDevices {
98 93
 		s.Linux.Resources.Devices = []specs.LinuxDeviceCgroup{{Allow: true, Access: "rwm"}}
99 94
 	}
... ...
@@ -131,5 +138,9 @@ func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
131 131
 		p.modifyRuntimeSpec(&s)
132 132
 	}
133 133
 
134
+	sort.Slice(s.Mounts, func(i, j int) bool {
135
+		return s.Mounts[i].Destination < s.Mounts[j].Destination
136
+	})
137
+
134 138
 	return &s, nil
135 139
 }