Browse code

Export all spec generation opts

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>

Michael Crosby authored on 2019/04/11 03:45:14
Showing 6 changed files
... ...
@@ -10,10 +10,7 @@ import (
10 10
 	"testing"
11 11
 
12 12
 	containertypes "github.com/docker/docker/api/types/container"
13
-	"github.com/docker/docker/container"
14 13
 	"github.com/docker/docker/daemon/config"
15
-	"github.com/docker/docker/oci"
16
-	"github.com/docker/docker/pkg/idtools"
17 14
 	"github.com/docker/docker/pkg/mount"
18 15
 	"gotest.tools/assert"
19 16
 	is "gotest.tools/assert/cmp"
... ...
@@ -115,54 +112,6 @@ func TestNotCleanupMounts(t *testing.T) {
115 115
 	}
116 116
 }
117 117
 
118
-// TestTmpfsDevShmSizeOverride checks that user-specified /dev/tmpfs mount
119
-// size is not overridden by the default shmsize (that should only be used
120
-// for default /dev/shm (as in "shareable" and "private" ipc modes).
121
-// https://github.com/moby/moby/issues/35271
122
-func TestTmpfsDevShmSizeOverride(t *testing.T) {
123
-	size := "777m"
124
-	mnt := "/dev/shm"
125
-
126
-	d := Daemon{
127
-		idMapping: &idtools.IdentityMapping{},
128
-	}
129
-	c := &container.Container{
130
-		HostConfig: &containertypes.HostConfig{
131
-			ShmSize: 48 * 1024, // size we should NOT end up with
132
-		},
133
-	}
134
-	ms := []container.Mount{
135
-		{
136
-			Source:      "tmpfs",
137
-			Destination: mnt,
138
-			Data:        "size=" + size,
139
-		},
140
-	}
141
-
142
-	// convert ms to spec
143
-	spec := oci.DefaultSpec()
144
-	err := setMounts(&d, &spec, c, ms)
145
-	assert.Check(t, err)
146
-
147
-	// Check the resulting spec for the correct size
148
-	found := false
149
-	for _, m := range spec.Mounts {
150
-		if m.Destination == mnt {
151
-			for _, o := range m.Options {
152
-				if !strings.HasPrefix(o, "size=") {
153
-					continue
154
-				}
155
-				t.Logf("%+v\n", m.Options)
156
-				assert.Check(t, is.Equal("size="+size, o))
157
-				found = true
158
-			}
159
-		}
160
-	}
161
-	if !found {
162
-		t.Fatal("/dev/shm not found in spec, or size option missing")
163
-	}
164
-}
165
-
166 118
 func TestValidateContainerIsolationLinux(t *testing.T) {
167 119
 	d := Daemon{}
168 120
 
... ...
@@ -1,6 +1,8 @@
1 1
 package daemon // import "github.com/docker/docker/daemon"
2 2
 
3 3
 import (
4
+	"context"
5
+
4 6
 	"github.com/docker/docker/container"
5 7
 	"github.com/docker/docker/daemon/exec"
6 8
 	"github.com/docker/docker/oci/caps"
... ...
@@ -54,6 +56,6 @@ func (daemon *Daemon) execSetPlatformOpt(c *container.Container, ec *exec.Config
54 54
 		}
55 55
 		p.ApparmorProfile = appArmorProfile
56 56
 	}
57
-	daemon.setRlimits(&specs.Spec{Process: p}, c)
58
-	return nil
57
+	s := &specs.Spec{Process: p}
58
+	return WithRlimits(daemon, c)(context.Background(), nil, nil, s)
59 59
 }
... ...
@@ -33,27 +33,121 @@ import (
33 33
 	"golang.org/x/sys/unix"
34 34
 )
35 35
 
36
-const (
37
-	inContainerInitPath = "/sbin/" + daemonconfig.DefaultInitBinary
38
-)
36
+const inContainerInitPath = "/sbin/" + daemonconfig.DefaultInitBinary
39 37
 
40
-func (daemon *Daemon) setRlimits(s *specs.Spec, c *container.Container) error {
41
-	var rlimits []specs.POSIXRlimit
42
-
43
-	// We want to leave the original HostConfig alone so make a copy here
44
-	hostConfig := *c.HostConfig
45
-	// Merge with the daemon defaults
46
-	daemon.mergeUlimits(&hostConfig)
47
-	for _, ul := range hostConfig.Ulimits {
48
-		rlimits = append(rlimits, specs.POSIXRlimit{
49
-			Type: "RLIMIT_" + strings.ToUpper(ul.Name),
50
-			Soft: uint64(ul.Soft),
51
-			Hard: uint64(ul.Hard),
52
-		})
38
+// WithRlimits sets the container's rlimits along with merging the daemon's rlimits
39
+func WithRlimits(daemon *Daemon, c *container.Container) coci.SpecOpts {
40
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
41
+		var rlimits []specs.POSIXRlimit
42
+
43
+		// We want to leave the original HostConfig alone so make a copy here
44
+		hostConfig := *c.HostConfig
45
+		// Merge with the daemon defaults
46
+		daemon.mergeUlimits(&hostConfig)
47
+		for _, ul := range hostConfig.Ulimits {
48
+			rlimits = append(rlimits, specs.POSIXRlimit{
49
+				Type: "RLIMIT_" + strings.ToUpper(ul.Name),
50
+				Soft: uint64(ul.Soft),
51
+				Hard: uint64(ul.Hard),
52
+			})
53
+		}
54
+
55
+		s.Process.Rlimits = rlimits
56
+		return nil
53 57
 	}
58
+}
54 59
 
55
-	s.Process.Rlimits = rlimits
56
-	return nil
60
+// WithLibnetwork sets the libnetwork hook
61
+func WithLibnetwork(daemon *Daemon, c *container.Container) coci.SpecOpts {
62
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
63
+		if s.Hooks == nil {
64
+			s.Hooks = &specs.Hooks{}
65
+		}
66
+		for _, ns := range s.Linux.Namespaces {
67
+			if ns.Type == "network" && ns.Path == "" && !c.Config.NetworkDisabled {
68
+				target := filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe")
69
+				s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
70
+					Path: target,
71
+					Args: []string{
72
+						"libnetwork-setkey",
73
+						"-exec-root=" + daemon.configStore.GetExecRoot(),
74
+						c.ID,
75
+						daemon.netController.ID(),
76
+					},
77
+				})
78
+			}
79
+		}
80
+		return nil
81
+	}
82
+}
83
+
84
+// WithRootless sets the spec to the rootless configuration
85
+func WithRootless(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
86
+	return specconv.ToRootless(s)
87
+}
88
+
89
+// WithOOMScore sets the oom score
90
+func WithOOMScore(score *int) coci.SpecOpts {
91
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
92
+		s.Process.OOMScoreAdj = score
93
+		return nil
94
+	}
95
+}
96
+
97
+// WithSelinux sets the selinux labels
98
+func WithSelinux(c *container.Container) coci.SpecOpts {
99
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
100
+		s.Process.SelinuxLabel = c.GetProcessLabel()
101
+		s.Linux.MountLabel = c.MountLabel
102
+		return nil
103
+	}
104
+}
105
+
106
+// WithApparmor sets the apparmor profile
107
+func WithApparmor(c *container.Container) coci.SpecOpts {
108
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
109
+		if apparmor.IsEnabled() {
110
+			var appArmorProfile string
111
+			if c.AppArmorProfile != "" {
112
+				appArmorProfile = c.AppArmorProfile
113
+			} else if c.HostConfig.Privileged {
114
+				appArmorProfile = "unconfined"
115
+			} else {
116
+				appArmorProfile = "docker-default"
117
+			}
118
+
119
+			if appArmorProfile == "docker-default" {
120
+				// Unattended upgrades and other fun services can unload AppArmor
121
+				// profiles inadvertently. Since we cannot store our profile in
122
+				// /etc/apparmor.d, nor can we practically add other ways of
123
+				// telling the system to keep our profile loaded, in order to make
124
+				// sure that we keep the default profile enabled we dynamically
125
+				// reload it if necessary.
126
+				if err := ensureDefaultAppArmorProfile(); err != nil {
127
+					return err
128
+				}
129
+			}
130
+			s.Process.ApparmorProfile = appArmorProfile
131
+		}
132
+		return nil
133
+	}
134
+}
135
+
136
+// WithCapabilities sets the container's capabilties
137
+func WithCapabilities(c *container.Container) coci.SpecOpts {
138
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
139
+		capabilities, err := caps.TweakCapabilities(
140
+			oci.DefaultCapabilities(),
141
+			c.HostConfig.CapAdd,
142
+			c.HostConfig.CapDrop,
143
+			c.HostConfig.Capabilities,
144
+			c.HostConfig.Privileged,
145
+		)
146
+		if err != nil {
147
+			return err
148
+		}
149
+		return oci.SetCapabilities(s, capabilities)
150
+	}
57 151
 }
58 152
 
59 153
 func readUserFile(c *container.Container, p string) (io.ReadCloser, error) {
... ...
@@ -119,99 +213,102 @@ func setNamespace(s *specs.Spec, ns specs.LinuxNamespace) {
119 119
 	s.Linux.Namespaces = append(s.Linux.Namespaces, ns)
120 120
 }
121 121
 
122
-func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error {
123
-	userNS := false
124
-	// user
125
-	if c.HostConfig.UsernsMode.IsPrivate() {
126
-		uidMap := daemon.idMapping.UIDs()
127
-		if uidMap != nil {
128
-			userNS = true
129
-			ns := specs.LinuxNamespace{Type: "user"}
122
+// WithNamespaces sets the container's namespaces
123
+func WithNamespaces(daemon *Daemon, c *container.Container) coci.SpecOpts {
124
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
125
+		userNS := false
126
+		// user
127
+		if c.HostConfig.UsernsMode.IsPrivate() {
128
+			uidMap := daemon.idMapping.UIDs()
129
+			if uidMap != nil {
130
+				userNS = true
131
+				ns := specs.LinuxNamespace{Type: "user"}
132
+				setNamespace(s, ns)
133
+				s.Linux.UIDMappings = specMapping(uidMap)
134
+				s.Linux.GIDMappings = specMapping(daemon.idMapping.GIDs())
135
+			}
136
+		}
137
+		// network
138
+		if !c.Config.NetworkDisabled {
139
+			ns := specs.LinuxNamespace{Type: "network"}
140
+			parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2)
141
+			if parts[0] == "container" {
142
+				nc, err := daemon.getNetworkedContainer(c.ID, c.HostConfig.NetworkMode.ConnectedContainer())
143
+				if err != nil {
144
+					return err
145
+				}
146
+				ns.Path = fmt.Sprintf("/proc/%d/ns/net", nc.State.GetPID())
147
+				if userNS {
148
+					// to share a net namespace, they must also share a user namespace
149
+					nsUser := specs.LinuxNamespace{Type: "user"}
150
+					nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", nc.State.GetPID())
151
+					setNamespace(s, nsUser)
152
+				}
153
+			} else if c.HostConfig.NetworkMode.IsHost() {
154
+				ns.Path = c.NetworkSettings.SandboxKey
155
+			}
130 156
 			setNamespace(s, ns)
131
-			s.Linux.UIDMappings = specMapping(uidMap)
132
-			s.Linux.GIDMappings = specMapping(daemon.idMapping.GIDs())
133 157
 		}
134
-	}
135
-	// network
136
-	if !c.Config.NetworkDisabled {
137
-		ns := specs.LinuxNamespace{Type: "network"}
138
-		parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2)
139
-		if parts[0] == "container" {
140
-			nc, err := daemon.getNetworkedContainer(c.ID, c.HostConfig.NetworkMode.ConnectedContainer())
158
+
159
+		// ipc
160
+		ipcMode := c.HostConfig.IpcMode
161
+		switch {
162
+		case ipcMode.IsContainer():
163
+			ns := specs.LinuxNamespace{Type: "ipc"}
164
+			ic, err := daemon.getIpcContainer(ipcMode.Container())
141 165
 			if err != nil {
142 166
 				return err
143 167
 			}
144
-			ns.Path = fmt.Sprintf("/proc/%d/ns/net", nc.State.GetPID())
168
+			ns.Path = fmt.Sprintf("/proc/%d/ns/ipc", ic.State.GetPID())
169
+			setNamespace(s, ns)
145 170
 			if userNS {
146
-				// to share a net namespace, they must also share a user namespace
171
+				// to share an IPC namespace, they must also share a user namespace
147 172
 				nsUser := specs.LinuxNamespace{Type: "user"}
148
-				nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", nc.State.GetPID())
173
+				nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", ic.State.GetPID())
149 174
 				setNamespace(s, nsUser)
150 175
 			}
151
-		} else if c.HostConfig.NetworkMode.IsHost() {
152
-			ns.Path = c.NetworkSettings.SandboxKey
176
+		case ipcMode.IsHost():
177
+			oci.RemoveNamespace(s, specs.LinuxNamespaceType("ipc"))
178
+		case ipcMode.IsEmpty():
179
+			// A container was created by an older version of the daemon.
180
+			// The default behavior used to be what is now called "shareable".
181
+			fallthrough
182
+		case ipcMode.IsPrivate(), ipcMode.IsShareable(), ipcMode.IsNone():
183
+			ns := specs.LinuxNamespace{Type: "ipc"}
184
+			setNamespace(s, ns)
185
+		default:
186
+			return fmt.Errorf("Invalid IPC mode: %v", ipcMode)
153 187
 		}
154
-		setNamespace(s, ns)
155
-	}
156 188
 
157
-	// ipc
158
-	ipcMode := c.HostConfig.IpcMode
159
-	switch {
160
-	case ipcMode.IsContainer():
161
-		ns := specs.LinuxNamespace{Type: "ipc"}
162
-		ic, err := daemon.getIpcContainer(ipcMode.Container())
163
-		if err != nil {
164
-			return err
165
-		}
166
-		ns.Path = fmt.Sprintf("/proc/%d/ns/ipc", ic.State.GetPID())
167
-		setNamespace(s, ns)
168
-		if userNS {
169
-			// to share an IPC namespace, they must also share a user namespace
170
-			nsUser := specs.LinuxNamespace{Type: "user"}
171
-			nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", ic.State.GetPID())
172
-			setNamespace(s, nsUser)
173
-		}
174
-	case ipcMode.IsHost():
175
-		oci.RemoveNamespace(s, specs.LinuxNamespaceType("ipc"))
176
-	case ipcMode.IsEmpty():
177
-		// A container was created by an older version of the daemon.
178
-		// The default behavior used to be what is now called "shareable".
179
-		fallthrough
180
-	case ipcMode.IsPrivate(), ipcMode.IsShareable(), ipcMode.IsNone():
181
-		ns := specs.LinuxNamespace{Type: "ipc"}
182
-		setNamespace(s, ns)
183
-	default:
184
-		return fmt.Errorf("Invalid IPC mode: %v", ipcMode)
185
-	}
186
-
187
-	// pid
188
-	if c.HostConfig.PidMode.IsContainer() {
189
-		ns := specs.LinuxNamespace{Type: "pid"}
190
-		pc, err := daemon.getPidContainer(c)
191
-		if err != nil {
192
-			return err
189
+		// pid
190
+		if c.HostConfig.PidMode.IsContainer() {
191
+			ns := specs.LinuxNamespace{Type: "pid"}
192
+			pc, err := daemon.getPidContainer(c)
193
+			if err != nil {
194
+				return err
195
+			}
196
+			ns.Path = fmt.Sprintf("/proc/%d/ns/pid", pc.State.GetPID())
197
+			setNamespace(s, ns)
198
+			if userNS {
199
+				// to share a PID namespace, they must also share a user namespace
200
+				nsUser := specs.LinuxNamespace{Type: "user"}
201
+				nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", pc.State.GetPID())
202
+				setNamespace(s, nsUser)
203
+			}
204
+		} else if c.HostConfig.PidMode.IsHost() {
205
+			oci.RemoveNamespace(s, specs.LinuxNamespaceType("pid"))
206
+		} else {
207
+			ns := specs.LinuxNamespace{Type: "pid"}
208
+			setNamespace(s, ns)
193 209
 		}
194
-		ns.Path = fmt.Sprintf("/proc/%d/ns/pid", pc.State.GetPID())
195
-		setNamespace(s, ns)
196
-		if userNS {
197
-			// to share a PID namespace, they must also share a user namespace
198
-			nsUser := specs.LinuxNamespace{Type: "user"}
199
-			nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", pc.State.GetPID())
200
-			setNamespace(s, nsUser)
210
+		// uts
211
+		if c.HostConfig.UTSMode.IsHost() {
212
+			oci.RemoveNamespace(s, specs.LinuxNamespaceType("uts"))
213
+			s.Hostname = ""
201 214
 		}
202
-	} else if c.HostConfig.PidMode.IsHost() {
203
-		oci.RemoveNamespace(s, specs.LinuxNamespaceType("pid"))
204
-	} else {
205
-		ns := specs.LinuxNamespace{Type: "pid"}
206
-		setNamespace(s, ns)
207
-	}
208
-	// uts
209
-	if c.HostConfig.UTSMode.IsHost() {
210
-		oci.RemoveNamespace(s, specs.LinuxNamespaceType("uts"))
211
-		s.Hostname = ""
212
-	}
213 215
 
214
-	return nil
216
+		return nil
217
+	}
215 218
 }
216 219
 
217 220
 func specMapping(s []idtools.IDMap) []specs.LinuxIDMapping {
... ...
@@ -361,233 +458,284 @@ func inSlice(slice []string, s string) bool {
361 361
 	return false
362 362
 }
363 363
 
364
-func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []container.Mount) error {
365
-	userMounts := make(map[string]struct{})
366
-	for _, m := range mounts {
367
-		userMounts[m.Destination] = struct{}{}
368
-	}
369
-
370
-	// Copy all mounts from spec to defaultMounts, except for
371
-	//  - mounts overridden by a user supplied mount;
372
-	//  - all mounts under /dev if a user supplied /dev is present;
373
-	//  - /dev/shm, in case IpcMode is none.
374
-	// While at it, also
375
-	//  - set size for /dev/shm from shmsize.
376
-	defaultMounts := s.Mounts[:0]
377
-	_, mountDev := userMounts["/dev"]
378
-	for _, m := range s.Mounts {
379
-		if _, ok := userMounts[m.Destination]; ok {
380
-			// filter out mount overridden by a user supplied mount
381
-			continue
382
-		}
383
-		if mountDev && strings.HasPrefix(m.Destination, "/dev/") {
384
-			// filter out everything under /dev if /dev is user-mounted
385
-			continue
386
-		}
387
-
388
-		if m.Destination == "/dev/shm" {
389
-			if c.HostConfig.IpcMode.IsNone() {
390
-				// filter out /dev/shm for "none" IpcMode
391
-				continue
392
-			}
393
-			// set size for /dev/shm mount from spec
394
-			sizeOpt := "size=" + strconv.FormatInt(c.HostConfig.ShmSize, 10)
395
-			m.Options = append(m.Options, sizeOpt)
364
+// WithMounts sets the container's mounts
365
+func WithMounts(daemon *Daemon, c *container.Container) coci.SpecOpts {
366
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) (err error) {
367
+		if err := daemon.setupContainerMountsRoot(c); err != nil {
368
+			return err
396 369
 		}
397 370
 
398
-		defaultMounts = append(defaultMounts, m)
399
-	}
400
-
401
-	s.Mounts = defaultMounts
402
-	for _, m := range mounts {
403
-		if m.Source == "tmpfs" {
404
-			data := m.Data
405
-			parser := volumemounts.NewParser("linux")
406
-			options := []string{"noexec", "nosuid", "nodev", string(parser.DefaultPropagationMode())}
407
-			if data != "" {
408
-				options = append(options, strings.Split(data, ",")...)
409
-			}
371
+		if err := daemon.setupIpcDirs(c); err != nil {
372
+			return err
373
+		}
410 374
 
411
-			merged, err := mount.MergeTmpfsOptions(options)
375
+		defer func() {
412 376
 			if err != nil {
413
-				return err
377
+				daemon.cleanupSecretDir(c)
414 378
 			}
379
+		}()
415 380
 
416
-			s.Mounts = append(s.Mounts, specs.Mount{Destination: m.Destination, Source: m.Source, Type: "tmpfs", Options: merged})
417
-			continue
381
+		if err := daemon.setupSecretDir(c); err != nil {
382
+			return err
418 383
 		}
419 384
 
420
-		mt := specs.Mount{Destination: m.Destination, Source: m.Source, Type: "bind"}
385
+		ms, err := daemon.setupMounts(c)
386
+		if err != nil {
387
+			return err
388
+		}
421 389
 
422
-		// Determine property of RootPropagation based on volume
423
-		// properties. If a volume is shared, then keep root propagation
424
-		// shared. This should work for slave and private volumes too.
425
-		//
426
-		// For slave volumes, it can be either [r]shared/[r]slave.
427
-		//
428
-		// For private volumes any root propagation value should work.
429
-		pFlag := mountPropagationMap[m.Propagation]
430
-		switch pFlag {
431
-		case mount.SHARED, mount.RSHARED:
432
-			if err := ensureShared(m.Source); err != nil {
433
-				return err
390
+		if !c.HostConfig.IpcMode.IsPrivate() && !c.HostConfig.IpcMode.IsEmpty() {
391
+			ms = append(ms, c.IpcMounts()...)
392
+		}
393
+
394
+		tmpfsMounts, err := c.TmpfsMounts()
395
+		if err != nil {
396
+			return err
397
+		}
398
+		ms = append(ms, tmpfsMounts...)
399
+
400
+		secretMounts, err := c.SecretMounts()
401
+		if err != nil {
402
+			return err
403
+		}
404
+		ms = append(ms, secretMounts...)
405
+
406
+		sort.Sort(mounts(ms))
407
+
408
+		mounts := ms
409
+
410
+		userMounts := make(map[string]struct{})
411
+		for _, m := range mounts {
412
+			userMounts[m.Destination] = struct{}{}
413
+		}
414
+
415
+		// Copy all mounts from spec to defaultMounts, except for
416
+		//  - mounts overridden by a user supplied mount;
417
+		//  - all mounts under /dev if a user supplied /dev is present;
418
+		//  - /dev/shm, in case IpcMode is none.
419
+		// While at it, also
420
+		//  - set size for /dev/shm from shmsize.
421
+		defaultMounts := s.Mounts[:0]
422
+		_, mountDev := userMounts["/dev"]
423
+		for _, m := range s.Mounts {
424
+			if _, ok := userMounts[m.Destination]; ok {
425
+				// filter out mount overridden by a user supplied mount
426
+				continue
434 427
 			}
435
-			rootpg := mountPropagationMap[s.Linux.RootfsPropagation]
436
-			if rootpg != mount.SHARED && rootpg != mount.RSHARED {
437
-				s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.SHARED]
438
-			}
439
-		case mount.SLAVE, mount.RSLAVE:
440
-			var fallback bool
441
-			if err := ensureSharedOrSlave(m.Source); err != nil {
442
-				// For backwards compatibility purposes, treat mounts from the daemon root
443
-				// as special since we automatically add rslave propagation to these mounts
444
-				// when the user did not set anything, so we should fallback to the old
445
-				// behavior which is to use private propagation which is normally the
446
-				// default.
447
-				if !strings.HasPrefix(m.Source, daemon.root) && !strings.HasPrefix(daemon.root, m.Source) {
448
-					return err
428
+			if mountDev && strings.HasPrefix(m.Destination, "/dev/") {
429
+				// filter out everything under /dev if /dev is user-mounted
430
+				continue
431
+			}
432
+
433
+			if m.Destination == "/dev/shm" {
434
+				if c.HostConfig.IpcMode.IsNone() {
435
+					// filter out /dev/shm for "none" IpcMode
436
+					continue
449 437
 				}
438
+				// set size for /dev/shm mount from spec
439
+				sizeOpt := "size=" + strconv.FormatInt(c.HostConfig.ShmSize, 10)
440
+				m.Options = append(m.Options, sizeOpt)
441
+			}
450 442
 
451
-				cm, ok := c.MountPoints[m.Destination]
452
-				if !ok {
453
-					return err
443
+			defaultMounts = append(defaultMounts, m)
444
+		}
445
+
446
+		s.Mounts = defaultMounts
447
+		for _, m := range mounts {
448
+			if m.Source == "tmpfs" {
449
+				data := m.Data
450
+				parser := volumemounts.NewParser("linux")
451
+				options := []string{"noexec", "nosuid", "nodev", string(parser.DefaultPropagationMode())}
452
+				if data != "" {
453
+					options = append(options, strings.Split(data, ",")...)
454 454
 				}
455
-				if cm.Spec.BindOptions != nil && cm.Spec.BindOptions.Propagation != "" {
456
-					// This means the user explicitly set a propagation, do not fallback in that case.
455
+
456
+				merged, err := mount.MergeTmpfsOptions(options)
457
+				if err != nil {
457 458
 					return err
458 459
 				}
459
-				fallback = true
460
-				logrus.WithField("container", c.ID).WithField("source", m.Source).Warn("Falling back to default propagation for bind source in daemon root")
460
+
461
+				s.Mounts = append(s.Mounts, specs.Mount{Destination: m.Destination, Source: m.Source, Type: "tmpfs", Options: merged})
462
+				continue
461 463
 			}
462
-			if !fallback {
464
+
465
+			mt := specs.Mount{Destination: m.Destination, Source: m.Source, Type: "bind"}
466
+
467
+			// Determine property of RootPropagation based on volume
468
+			// properties. If a volume is shared, then keep root propagation
469
+			// shared. This should work for slave and private volumes too.
470
+			//
471
+			// For slave volumes, it can be either [r]shared/[r]slave.
472
+			//
473
+			// For private volumes any root propagation value should work.
474
+			pFlag := mountPropagationMap[m.Propagation]
475
+			switch pFlag {
476
+			case mount.SHARED, mount.RSHARED:
477
+				if err := ensureShared(m.Source); err != nil {
478
+					return err
479
+				}
463 480
 				rootpg := mountPropagationMap[s.Linux.RootfsPropagation]
464
-				if rootpg != mount.SHARED && rootpg != mount.RSHARED && rootpg != mount.SLAVE && rootpg != mount.RSLAVE {
465
-					s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.RSLAVE]
481
+				if rootpg != mount.SHARED && rootpg != mount.RSHARED {
482
+					s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.SHARED]
483
+				}
484
+			case mount.SLAVE, mount.RSLAVE:
485
+				var fallback bool
486
+				if err := ensureSharedOrSlave(m.Source); err != nil {
487
+					// For backwards compatibility purposes, treat mounts from the daemon root
488
+					// as special since we automatically add rslave propagation to these mounts
489
+					// when the user did not set anything, so we should fallback to the old
490
+					// behavior which is to use private propagation which is normally the
491
+					// default.
492
+					if !strings.HasPrefix(m.Source, daemon.root) && !strings.HasPrefix(daemon.root, m.Source) {
493
+						return err
494
+					}
495
+
496
+					cm, ok := c.MountPoints[m.Destination]
497
+					if !ok {
498
+						return err
499
+					}
500
+					if cm.Spec.BindOptions != nil && cm.Spec.BindOptions.Propagation != "" {
501
+						// This means the user explicitly set a propagation, do not fallback in that case.
502
+						return err
503
+					}
504
+					fallback = true
505
+					logrus.WithField("container", c.ID).WithField("source", m.Source).Warn("Falling back to default propagation for bind source in daemon root")
506
+				}
507
+				if !fallback {
508
+					rootpg := mountPropagationMap[s.Linux.RootfsPropagation]
509
+					if rootpg != mount.SHARED && rootpg != mount.RSHARED && rootpg != mount.SLAVE && rootpg != mount.RSLAVE {
510
+						s.Linux.RootfsPropagation = mountPropagationReverseMap[mount.RSLAVE]
511
+					}
466 512
 				}
467 513
 			}
468
-		}
469 514
 
470
-		bindMode := "rbind"
471
-		if m.NonRecursive {
472
-			bindMode = "bind"
473
-		}
474
-		opts := []string{bindMode}
475
-		if !m.Writable {
476
-			opts = append(opts, "ro")
477
-		}
478
-		if pFlag != 0 {
479
-			opts = append(opts, mountPropagationReverseMap[pFlag])
480
-		}
515
+			bindMode := "rbind"
516
+			if m.NonRecursive {
517
+				bindMode = "bind"
518
+			}
519
+			opts := []string{bindMode}
520
+			if !m.Writable {
521
+				opts = append(opts, "ro")
522
+			}
523
+			if pFlag != 0 {
524
+				opts = append(opts, mountPropagationReverseMap[pFlag])
525
+			}
481 526
 
482
-		// If we are using user namespaces, then we must make sure that we
483
-		// don't drop any of the CL_UNPRIVILEGED "locked" flags of the source
484
-		// "mount" when we bind-mount. The reason for this is that at the point
485
-		// when runc sets up the root filesystem, it is already inside a user
486
-		// namespace, and thus cannot change any flags that are locked.
487
-		if daemon.configStore.RemappedRoot != "" {
488
-			unprivOpts, err := getUnprivilegedMountFlags(m.Source)
489
-			if err != nil {
490
-				return err
527
+			// If we are using user namespaces, then we must make sure that we
528
+			// don't drop any of the CL_UNPRIVILEGED "locked" flags of the source
529
+			// "mount" when we bind-mount. The reason for this is that at the point
530
+			// when runc sets up the root filesystem, it is already inside a user
531
+			// namespace, and thus cannot change any flags that are locked.
532
+			if daemon.configStore.RemappedRoot != "" {
533
+				unprivOpts, err := getUnprivilegedMountFlags(m.Source)
534
+				if err != nil {
535
+					return err
536
+				}
537
+				opts = append(opts, unprivOpts...)
491 538
 			}
492
-			opts = append(opts, unprivOpts...)
493
-		}
494 539
 
495
-		mt.Options = opts
496
-		s.Mounts = append(s.Mounts, mt)
497
-	}
540
+			mt.Options = opts
541
+			s.Mounts = append(s.Mounts, mt)
542
+		}
498 543
 
499
-	if s.Root.Readonly {
500
-		for i, m := range s.Mounts {
501
-			switch m.Destination {
502
-			case "/proc", "/dev/pts", "/dev/shm", "/dev/mqueue", "/dev":
503
-				continue
504
-			}
505
-			if _, ok := userMounts[m.Destination]; !ok {
506
-				if !inSlice(m.Options, "ro") {
507
-					s.Mounts[i].Options = append(s.Mounts[i].Options, "ro")
544
+		if s.Root.Readonly {
545
+			for i, m := range s.Mounts {
546
+				switch m.Destination {
547
+				case "/proc", "/dev/pts", "/dev/shm", "/dev/mqueue", "/dev":
548
+					continue
549
+				}
550
+				if _, ok := userMounts[m.Destination]; !ok {
551
+					if !inSlice(m.Options, "ro") {
552
+						s.Mounts[i].Options = append(s.Mounts[i].Options, "ro")
553
+					}
508 554
 				}
509 555
 			}
510 556
 		}
511
-	}
512 557
 
513
-	if c.HostConfig.Privileged {
514
-		// clear readonly for /sys
515
-		for i := range s.Mounts {
516
-			if s.Mounts[i].Destination == "/sys" {
517
-				clearReadOnly(&s.Mounts[i])
558
+		if c.HostConfig.Privileged {
559
+			// clear readonly for /sys
560
+			for i := range s.Mounts {
561
+				if s.Mounts[i].Destination == "/sys" {
562
+					clearReadOnly(&s.Mounts[i])
563
+				}
518 564
 			}
565
+			s.Linux.ReadonlyPaths = nil
566
+			s.Linux.MaskedPaths = nil
519 567
 		}
520
-		s.Linux.ReadonlyPaths = nil
521
-		s.Linux.MaskedPaths = nil
522
-	}
523 568
 
524
-	// TODO: until a kernel/mount solution exists for handling remount in a user namespace,
525
-	// we must clear the readonly flag for the cgroups mount (@mrunalp concurs)
526
-	if uidMap := daemon.idMapping.UIDs(); uidMap != nil || c.HostConfig.Privileged {
527
-		for i, m := range s.Mounts {
528
-			if m.Type == "cgroup" {
529
-				clearReadOnly(&s.Mounts[i])
569
+		// TODO: until a kernel/mount solution exists for handling remount in a user namespace,
570
+		// we must clear the readonly flag for the cgroups mount (@mrunalp concurs)
571
+		if uidMap := daemon.idMapping.UIDs(); uidMap != nil || c.HostConfig.Privileged {
572
+			for i, m := range s.Mounts {
573
+				if m.Type == "cgroup" {
574
+					clearReadOnly(&s.Mounts[i])
575
+				}
530 576
 			}
531 577
 		}
532
-	}
533 578
 
534
-	return nil
535
-}
579
+		return nil
536 580
 
537
-func (daemon *Daemon) populateCommonSpec(s *specs.Spec, c *container.Container) error {
538
-	if c.BaseFS == nil {
539
-		return errors.New("populateCommonSpec: BaseFS of container " + c.ID + " is unexpectedly nil")
540
-	}
541
-	linkedEnv, err := daemon.setupLinkedContainers(c)
542
-	if err != nil {
543
-		return err
544
-	}
545
-	s.Root = &specs.Root{
546
-		Path:     c.BaseFS.Path(),
547
-		Readonly: c.HostConfig.ReadonlyRootfs,
548
-	}
549
-	if err := c.SetupWorkingDirectory(daemon.idMapping.RootPair()); err != nil {
550
-		return err
551 581
 	}
552
-	cwd := c.Config.WorkingDir
553
-	if len(cwd) == 0 {
554
-		cwd = "/"
555
-	}
556
-	s.Process.Args = append([]string{c.Path}, c.Args...)
557
-
558
-	// only add the custom init if it is specified and the container is running in its
559
-	// own private pid namespace.  It does not make sense to add if it is running in the
560
-	// host namespace or another container's pid namespace where we already have an init
561
-	if c.HostConfig.PidMode.IsPrivate() {
562
-		if (c.HostConfig.Init != nil && *c.HostConfig.Init) ||
563
-			(c.HostConfig.Init == nil && daemon.configStore.Init) {
564
-			s.Process.Args = append([]string{inContainerInitPath, "--", c.Path}, c.Args...)
565
-			path := daemon.configStore.InitPath
566
-			if path == "" {
567
-				path, err = exec.LookPath(daemonconfig.DefaultInitBinary)
568
-				if err != nil {
569
-					return err
582
+}
583
+
584
+// WithCommonOptions sets common docker options
585
+func WithCommonOptions(daemon *Daemon, c *container.Container) coci.SpecOpts {
586
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
587
+		if c.BaseFS == nil {
588
+			return errors.New("populateCommonSpec: BaseFS of container " + c.ID + " is unexpectedly nil")
589
+		}
590
+		linkedEnv, err := daemon.setupLinkedContainers(c)
591
+		if err != nil {
592
+			return err
593
+		}
594
+		s.Root = &specs.Root{
595
+			Path:     c.BaseFS.Path(),
596
+			Readonly: c.HostConfig.ReadonlyRootfs,
597
+		}
598
+		if err := c.SetupWorkingDirectory(daemon.idMapping.RootPair()); err != nil {
599
+			return err
600
+		}
601
+		cwd := c.Config.WorkingDir
602
+		if len(cwd) == 0 {
603
+			cwd = "/"
604
+		}
605
+		s.Process.Args = append([]string{c.Path}, c.Args...)
606
+
607
+		// only add the custom init if it is specified and the container is running in its
608
+		// own private pid namespace.  It does not make sense to add if it is running in the
609
+		// host namespace or another container's pid namespace where we already have an init
610
+		if c.HostConfig.PidMode.IsPrivate() {
611
+			if (c.HostConfig.Init != nil && *c.HostConfig.Init) ||
612
+				(c.HostConfig.Init == nil && daemon.configStore.Init) {
613
+				s.Process.Args = append([]string{inContainerInitPath, "--", c.Path}, c.Args...)
614
+				path := daemon.configStore.InitPath
615
+				if path == "" {
616
+					path, err = exec.LookPath(daemonconfig.DefaultInitBinary)
617
+					if err != nil {
618
+						return err
619
+					}
570 620
 				}
621
+				s.Mounts = append(s.Mounts, specs.Mount{
622
+					Destination: inContainerInitPath,
623
+					Type:        "bind",
624
+					Source:      path,
625
+					Options:     []string{"bind", "ro"},
626
+				})
571 627
 			}
572
-			s.Mounts = append(s.Mounts, specs.Mount{
573
-				Destination: inContainerInitPath,
574
-				Type:        "bind",
575
-				Source:      path,
576
-				Options:     []string{"bind", "ro"},
577
-			})
578 628
 		}
579
-	}
580
-	s.Process.Cwd = cwd
581
-	s.Process.Env = c.CreateDaemonEnvironment(c.Config.Tty, linkedEnv)
582
-	s.Process.Terminal = c.Config.Tty
629
+		s.Process.Cwd = cwd
630
+		s.Process.Env = c.CreateDaemonEnvironment(c.Config.Tty, linkedEnv)
631
+		s.Process.Terminal = c.Config.Tty
583 632
 
584
-	s.Hostname = c.Config.Hostname
585
-	setLinuxDomainname(c, s)
633
+		s.Hostname = c.Config.Hostname
634
+		setLinuxDomainname(c, s)
586 635
 
587
-	return nil
636
+		return nil
637
+	}
588 638
 }
589 639
 
590
-func withCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
640
+// WithCgroups sets the container's cgroups
641
+func WithCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
591 642
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
592 643
 		var cgroupsPath string
593 644
 		scopePrefix := "docker"
... ...
@@ -636,7 +784,8 @@ func withCgroups(daemon *Daemon, c *container.Container) coci.SpecOpts {
636 636
 	}
637 637
 }
638 638
 
639
-func withContainerDevices(daemon *Daemon, c *container.Container) coci.SpecOpts {
639
+// WithDevices sets the container's devices
640
+func WithDevices(daemon *Daemon, c *container.Container) coci.SpecOpts {
640 641
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
641 642
 		// Build lists of devices allowed and created within the container.
642 643
 		var devs []specs.LinuxDevice
... ...
@@ -684,7 +833,8 @@ func withContainerDevices(daemon *Daemon, c *container.Container) coci.SpecOpts
684 684
 	}
685 685
 }
686 686
 
687
-func withResources(c *container.Container) coci.SpecOpts {
687
+// WithResources applies the container resources
688
+func WithResources(c *container.Container) coci.SpecOpts {
688 689
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
689 690
 		r := c.HostConfig.Resources
690 691
 		weightDevices, err := getBlkioWeightDevices(r)
... ...
@@ -738,7 +888,8 @@ func withResources(c *container.Container) coci.SpecOpts {
738 738
 	}
739 739
 }
740 740
 
741
-func withSysctls(c *container.Container) coci.SpecOpts {
741
+// WithSysctls sets the container's sysctls
742
+func WithSysctls(c *container.Container) coci.SpecOpts {
742 743
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
743 744
 		// We merge the sysctls injected above with the HostConfig (latter takes
744 745
 		// precedence for backwards-compatibility reasons).
... ...
@@ -749,7 +900,8 @@ func withSysctls(c *container.Container) coci.SpecOpts {
749 749
 	}
750 750
 }
751 751
 
752
-func withUser(c *container.Container) coci.SpecOpts {
752
+// WithUser sets the container's user
753
+func WithUser(c *container.Container) coci.SpecOpts {
753 754
 	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
754 755
 		uid, gid, additionalGids, err := getUser(c, c.Config.User)
755 756
 		if err != nil {
... ...
@@ -767,133 +919,36 @@ func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, e
767 767
 		opts []coci.SpecOpts
768 768
 		s    = oci.DefaultSpec()
769 769
 	)
770
-	if err := daemon.populateCommonSpec(&s, c); err != nil {
771
-		return nil, err
772
-	}
773
-
774 770
 	opts = append(opts,
775
-		withCgroups(daemon, c),
776
-		withResources(c),
777
-		withSysctls(c),
778
-		withContainerDevices(daemon, c),
779
-		withUser(c),
771
+		WithCommonOptions(daemon, c),
772
+		WithCgroups(daemon, c),
773
+		WithResources(c),
774
+		WithSysctls(c),
775
+		WithDevices(daemon, c),
776
+		WithUser(c),
777
+		WithRlimits(daemon, c),
778
+		WithNamespaces(daemon, c),
779
+		WithCapabilities(c),
780
+		WithSeccomp(daemon, c),
781
+		WithMounts(daemon, c),
782
+		WithLibnetwork(daemon, c),
783
+		WithApparmor(c),
784
+		WithSelinux(c),
785
+		WithOOMScore(&c.HostConfig.OomScoreAdj),
780 786
 	)
781
-
782
-	if err := daemon.setRlimits(&s, c); err != nil {
783
-		return nil, fmt.Errorf("linux runtime spec rlimits: %v", err)
784
-	}
785
-	if err := setNamespaces(daemon, &s, c); err != nil {
786
-		return nil, fmt.Errorf("linux spec namespaces: %v", err)
787
-	}
788
-	capabilities, err := caps.TweakCapabilities(oci.DefaultCapabilities(), c.HostConfig.CapAdd, c.HostConfig.CapDrop, c.HostConfig.Capabilities, c.HostConfig.Privileged)
789
-	if err != nil {
790
-		return nil, fmt.Errorf("linux spec capabilities: %v", err)
791
-	}
792
-	if err := oci.SetCapabilities(&s, capabilities); err != nil {
793
-		return nil, fmt.Errorf("linux spec capabilities: %v", err)
794
-	}
795
-	if err := setSeccomp(daemon, &s, c); err != nil {
796
-		return nil, fmt.Errorf("linux seccomp: %v", err)
797
-	}
798
-
799
-	if err := daemon.setupContainerMountsRoot(c); err != nil {
800
-		return nil, err
801
-	}
802
-
803
-	if err := daemon.setupIpcDirs(c); err != nil {
804
-		return nil, err
805
-	}
806
-
807
-	defer func() {
808
-		if err != nil {
809
-			daemon.cleanupSecretDir(c)
810
-		}
811
-	}()
812
-
813
-	if err := daemon.setupSecretDir(c); err != nil {
814
-		return nil, err
815
-	}
816
-
817
-	ms, err := daemon.setupMounts(c)
818
-	if err != nil {
819
-		return nil, err
820
-	}
821
-
822
-	if !c.HostConfig.IpcMode.IsPrivate() && !c.HostConfig.IpcMode.IsEmpty() {
823
-		ms = append(ms, c.IpcMounts()...)
824
-	}
825
-
826
-	tmpfsMounts, err := c.TmpfsMounts()
827
-	if err != nil {
828
-		return nil, err
829
-	}
830
-	ms = append(ms, tmpfsMounts...)
831
-
832
-	secretMounts, err := c.SecretMounts()
833
-	if err != nil {
834
-		return nil, err
835
-	}
836
-	ms = append(ms, secretMounts...)
837
-
838
-	sort.Sort(mounts(ms))
839
-	if err := setMounts(daemon, &s, c, ms); err != nil {
840
-		return nil, fmt.Errorf("linux mounts: %v", err)
841
-	}
842
-
843
-	if s.Hooks == nil {
844
-		s.Hooks = &specs.Hooks{}
845
-	}
846
-	for _, ns := range s.Linux.Namespaces {
847
-		if ns.Type == "network" && ns.Path == "" && !c.Config.NetworkDisabled {
848
-			target := filepath.Join("/proc", strconv.Itoa(os.Getpid()), "exe")
849
-			s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
850
-				Path: target,
851
-				Args: []string{"libnetwork-setkey", "-exec-root=" + daemon.configStore.GetExecRoot(), c.ID, daemon.netController.ID()},
852
-			})
853
-		}
854
-	}
855
-
856
-	if apparmor.IsEnabled() {
857
-		var appArmorProfile string
858
-		if c.AppArmorProfile != "" {
859
-			appArmorProfile = c.AppArmorProfile
860
-		} else if c.HostConfig.Privileged {
861
-			appArmorProfile = "unconfined"
862
-		} else {
863
-			appArmorProfile = "docker-default"
864
-		}
865
-
866
-		if appArmorProfile == "docker-default" {
867
-			// Unattended upgrades and other fun services can unload AppArmor
868
-			// profiles inadvertently. Since we cannot store our profile in
869
-			// /etc/apparmor.d, nor can we practically add other ways of
870
-			// telling the system to keep our profile loaded, in order to make
871
-			// sure that we keep the default profile enabled we dynamically
872
-			// reload it if necessary.
873
-			if err := ensureDefaultAppArmorProfile(); err != nil {
874
-				return nil, err
875
-			}
876
-		}
877
-
878
-		s.Process.ApparmorProfile = appArmorProfile
787
+	if c.NoNewPrivileges {
788
+		opts = append(opts, coci.WithNoNewPrivileges)
879 789
 	}
880
-	s.Process.SelinuxLabel = c.GetProcessLabel()
881
-	s.Process.NoNewPrivileges = c.NoNewPrivileges
882
-	s.Process.OOMScoreAdj = &c.HostConfig.OomScoreAdj
883
-	s.Linux.MountLabel = c.MountLabel
884 790
 
885 791
 	// Set the masked and readonly paths with regard to the host config options if they are set.
886 792
 	if c.HostConfig.MaskedPaths != nil {
887
-		s.Linux.MaskedPaths = c.HostConfig.MaskedPaths
793
+		opts = append(opts, coci.WithMaskedPaths(c.HostConfig.MaskedPaths))
888 794
 	}
889 795
 	if c.HostConfig.ReadonlyPaths != nil {
890
-		s.Linux.ReadonlyPaths = c.HostConfig.ReadonlyPaths
796
+		opts = append(opts, coci.WithReadonlyPaths(c.HostConfig.ReadonlyPaths))
891 797
 	}
892
-
893 798
 	if daemon.configStore.Rootless {
894
-		if err := specconv.ToRootless(&s); err != nil {
895
-			return nil, err
896
-		}
799
+		opts = append(opts, WithRootless)
897 800
 	}
898 801
 	return &s, coci.ApplyOpts(context.Background(), nil, &containers.Container{
899 802
 		ID: c.ID,
... ...
@@ -3,17 +3,22 @@
3 3
 package daemon // import "github.com/docker/docker/daemon"
4 4
 
5 5
 import (
6
+	"context"
6 7
 	"fmt"
7 8
 
9
+	"github.com/containerd/containerd/containers"
10
+	coci "github.com/containerd/containerd/oci"
8 11
 	"github.com/docker/docker/container"
9
-	"github.com/opencontainers/runtime-spec/specs-go"
10 12
 )
11 13
 
12 14
 var supportsSeccomp = false
13 15
 
14
-func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error {
15
-	if c.SeccompProfile != "" && c.SeccompProfile != "unconfined" {
16
-		return fmt.Errorf("seccomp profiles are not supported on this daemon, you cannot specify a custom seccomp profile")
16
+// WithSeccomp sets the seccomp profile
17
+func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts {
18
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
19
+		if c.SeccompProfile != "" && c.SeccompProfile != "unconfined" {
20
+			return fmt.Errorf("seccomp profiles are not supported on this daemon, you cannot specify a custom seccomp profile")
21
+		}
22
+		return nil
17 23
 	}
18
-	return nil
19 24
 }
... ...
@@ -3,8 +3,11 @@
3 3
 package daemon // import "github.com/docker/docker/daemon"
4 4
 
5 5
 import (
6
+	"context"
6 7
 	"fmt"
7 8
 
9
+	"github.com/containerd/containerd/containers"
10
+	coci "github.com/containerd/containerd/oci"
8 11
 	"github.com/docker/docker/container"
9 12
 	"github.com/docker/docker/profiles/seccomp"
10 13
 	"github.com/opencontainers/runtime-spec/specs-go"
... ...
@@ -13,43 +16,46 @@ import (
13 13
 
14 14
 var supportsSeccomp = true
15 15
 
16
-func setSeccomp(daemon *Daemon, rs *specs.Spec, c *container.Container) error {
17
-	var profile *specs.LinuxSeccomp
18
-	var err error
16
+// WithSeccomp sets the seccomp profile
17
+func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts {
18
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
19
+		var profile *specs.LinuxSeccomp
20
+		var err error
19 21
 
20
-	if c.HostConfig.Privileged {
21
-		return nil
22
-	}
22
+		if c.HostConfig.Privileged {
23
+			return nil
24
+		}
23 25
 
24
-	if !daemon.seccompEnabled {
25
-		if c.SeccompProfile != "" && c.SeccompProfile != "unconfined" {
26
-			return fmt.Errorf("Seccomp is not enabled in your kernel, cannot run a custom seccomp profile.")
26
+		if !daemon.seccompEnabled {
27
+			if c.SeccompProfile != "" && c.SeccompProfile != "unconfined" {
28
+				return fmt.Errorf("Seccomp is not enabled in your kernel, cannot run a custom seccomp profile.")
29
+			}
30
+			logrus.Warn("Seccomp is not enabled in your kernel, running container without default profile.")
31
+			c.SeccompProfile = "unconfined"
27 32
 		}
28
-		logrus.Warn("Seccomp is not enabled in your kernel, running container without default profile.")
29
-		c.SeccompProfile = "unconfined"
30
-	}
31
-	if c.SeccompProfile == "unconfined" {
32
-		return nil
33
-	}
34
-	if c.SeccompProfile != "" {
35
-		profile, err = seccomp.LoadProfile(c.SeccompProfile, rs)
36
-		if err != nil {
37
-			return err
33
+		if c.SeccompProfile == "unconfined" {
34
+			return nil
38 35
 		}
39
-	} else {
40
-		if daemon.seccompProfile != nil {
41
-			profile, err = seccomp.LoadProfile(string(daemon.seccompProfile), rs)
36
+		if c.SeccompProfile != "" {
37
+			profile, err = seccomp.LoadProfile(c.SeccompProfile, s)
42 38
 			if err != nil {
43 39
 				return err
44 40
 			}
45 41
 		} else {
46
-			profile, err = seccomp.GetDefaultProfile(rs)
47
-			if err != nil {
48
-				return err
42
+			if daemon.seccompProfile != nil {
43
+				profile, err = seccomp.LoadProfile(string(daemon.seccompProfile), s)
44
+				if err != nil {
45
+					return err
46
+				}
47
+			} else {
48
+				profile, err = seccomp.GetDefaultProfile(s)
49
+				if err != nil {
50
+					return err
51
+				}
49 52
 			}
50 53
 		}
51
-	}
52 54
 
53
-	rs.Linux.Seccomp = profile
54
-	return nil
55
+		s.Linux.Seccomp = profile
56
+		return nil
57
+	}
55 58
 }
... ...
@@ -2,4 +2,19 @@
2 2
 
3 3
 package daemon // import "github.com/docker/docker/daemon"
4 4
 
5
+import (
6
+	"context"
7
+
8
+	"github.com/containerd/containerd/containers"
9
+	coci "github.com/containerd/containerd/oci"
10
+	"github.com/docker/docker/container"
11
+)
12
+
5 13
 var supportsSeccomp = false
14
+
15
+// WithSeccomp sets the seccomp profile
16
+func WithSeccomp(daemon *Daemon, c *container.Container) coci.SpecOpts {
17
+	return func(ctx context.Context, _ coci.Client, _ *containers.Container, s *coci.Spec) error {
18
+		return nil
19
+	}
20
+}