Skip to content

Commit 38f8b0e

Browse files
committed
Validate mount paths on task create
This is intended as a minor fix for 1.12.1 so that task creation doesn't do unexpected things when the user supplies erroneous paths. In particular, because we're currently using hostConfig.Binds to setup mounts, if a user uses an absolute path for a volume mount source, or a non-absolute path for a bind mount source, the engine will do the opposite of what the user requested since all absolute paths are treated as binds and all non-absolute paths are treated as named volumes. Fixes moby#25253 Signed-off-by: Brian Goff <[email protected]>
1 parent ccbdc16 commit 38f8b0e

File tree

5 files changed

+173
-0
lines changed

5 files changed

+173
-0
lines changed

daemon/cluster/executor/container/container.go

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ func (c *containerConfig) setTask(t *api.Task) error {
5353
return ErrImageRequired
5454
}
5555

56+
if err := validateMounts(container.Mounts); err != nil {
57+
return err
58+
}
59+
5660
// index the networks by name
5761
c.networksAttachments = make(map[string]*api.NetworkAttachment, len(t.Networks))
5862
for _, attachment := range t.Networks {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package container
2+
3+
import (
4+
"fmt"
5+
"path/filepath"
6+
7+
"github.com/docker/swarmkit/api"
8+
)
9+
10+
func validateMounts(mounts []api.Mount) error {
11+
for _, mount := range mounts {
12+
// Target must always be absolute
13+
if !filepath.IsAbs(mount.Target) {
14+
return fmt.Errorf("invalid mount target, must be an absolute path: %s", mount.Target)
15+
}
16+
17+
switch mount.Type {
18+
// The checks on abs paths are required due to the container API confusing
19+
// volume mounts as bind mounts when the source is absolute (and vice-versa)
20+
// See #25253
21+
// TODO: This is probably not neccessary once #22373 is merged
22+
case api.MountTypeBind:
23+
if !filepath.IsAbs(mount.Source) {
24+
return fmt.Errorf("invalid bind mount source, must be an absolute path: %s", mount.Source)
25+
}
26+
case api.MountTypeVolume:
27+
if filepath.IsAbs(mount.Source) {
28+
return fmt.Errorf("invalid volume mount source, must not be an absolute path: %s", mount.Source)
29+
}
30+
case api.MountTypeTmpfs:
31+
if mount.Source != "" {
32+
return fmt.Errorf("invalid tmpfs source, source must be empty")
33+
}
34+
default:
35+
return fmt.Errorf("invalid mount type: %s", mount.Type)
36+
}
37+
}
38+
return nil
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package container
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/docker/docker/daemon"
8+
"github.com/docker/docker/pkg/stringid"
9+
"github.com/docker/swarmkit/api"
10+
)
11+
12+
func newTestControllerWithMount(m api.Mount) (*controller, error) {
13+
return newController(&daemon.Daemon{}, &api.Task{
14+
ID: stringid.GenerateRandomID(),
15+
ServiceID: stringid.GenerateRandomID(),
16+
Spec: api.TaskSpec{
17+
Runtime: &api.TaskSpec_Container{
18+
Container: &api.ContainerSpec{
19+
Image: "image_name",
20+
Labels: map[string]string{
21+
"com.docker.swarm.task.id": "id",
22+
},
23+
Mounts: []api.Mount{m},
24+
},
25+
},
26+
},
27+
})
28+
}
29+
30+
func TestControllerValidateMountBind(t *testing.T) {
31+
// with improper source
32+
if _, err := newTestControllerWithMount(api.Mount{
33+
Type: api.MountTypeBind,
34+
Source: "foo",
35+
Target: testAbsPath,
36+
}); err == nil || !strings.Contains(err.Error(), "invalid bind mount source") {
37+
t.Fatalf("expected error, got: %v", err)
38+
}
39+
40+
// with proper source
41+
if _, err := newTestControllerWithMount(api.Mount{
42+
Type: api.MountTypeBind,
43+
Source: testAbsPath,
44+
Target: testAbsPath,
45+
}); err != nil {
46+
t.Fatalf("expected error, got: %v", err)
47+
}
48+
}
49+
50+
func TestControllerValidateMountVolume(t *testing.T) {
51+
// with improper source
52+
if _, err := newTestControllerWithMount(api.Mount{
53+
Type: api.MountTypeVolume,
54+
Source: testAbsPath,
55+
Target: testAbsPath,
56+
}); err == nil || !strings.Contains(err.Error(), "invalid volume mount source") {
57+
t.Fatalf("expected error, got: %v", err)
58+
}
59+
60+
// with proper source
61+
if _, err := newTestControllerWithMount(api.Mount{
62+
Type: api.MountTypeVolume,
63+
Source: "foo",
64+
Target: testAbsPath,
65+
}); err != nil {
66+
t.Fatalf("expected error, got: %v", err)
67+
}
68+
}
69+
70+
func TestControllerValidateMountTarget(t *testing.T) {
71+
// with improper target
72+
if _, err := newTestControllerWithMount(api.Mount{
73+
Type: api.MountTypeBind,
74+
Source: testAbsPath,
75+
Target: "foo",
76+
}); err == nil || !strings.Contains(err.Error(), "invalid mount target") {
77+
t.Fatalf("expected error, got: %v", err)
78+
}
79+
80+
// with proper target
81+
if _, err := newTestControllerWithMount(api.Mount{
82+
Type: api.MountTypeBind,
83+
Source: testAbsPath,
84+
Target: testAbsPath,
85+
}); err != nil {
86+
t.Fatalf("expected no error, got: %v", err)
87+
}
88+
}
89+
90+
func TestControllerValidateMountTmpfs(t *testing.T) {
91+
// with improper target
92+
if _, err := newTestControllerWithMount(api.Mount{
93+
Type: api.MountTypeTmpfs,
94+
Source: "foo",
95+
Target: testAbsPath,
96+
}); err == nil || !strings.Contains(err.Error(), "invalid tmpfs source") {
97+
t.Fatalf("expected error, got: %v", err)
98+
}
99+
100+
// with proper target
101+
if _, err := newTestControllerWithMount(api.Mount{
102+
Type: api.MountTypeTmpfs,
103+
Target: testAbsPath,
104+
}); err != nil {
105+
t.Fatalf("expected no error, got: %v", err)
106+
}
107+
}
108+
109+
func TestControllerValidateMountInvalidType(t *testing.T) {
110+
// with improper target
111+
if _, err := newTestControllerWithMount(api.Mount{
112+
Type: api.Mount_MountType(9999),
113+
Source: "foo",
114+
Target: testAbsPath,
115+
}); err == nil || !strings.Contains(err.Error(), "invalid mount type") {
116+
t.Fatalf("expected error, got: %v", err)
117+
}
118+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// +build !windows
2+
3+
package container
4+
5+
const (
6+
testAbsPath = "/foo"
7+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package container
2+
3+
const (
4+
testAbsPath = `c:\foo`
5+
)

0 commit comments

Comments
 (0)