Browse code

Move caps and device spec utils to `oci` pkg

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

Michael Crosby authored on 2018/12/11 05:40:40
Showing 7 changed files
1 1
deleted file mode 100644
... ...
@@ -1,139 +0,0 @@
1
-package caps // import "github.com/docker/docker/daemon/caps"
2
-
3
-import (
4
-	"fmt"
5
-	"strings"
6
-
7
-	"github.com/syndtr/gocapability/capability"
8
-)
9
-
10
-var capabilityList Capabilities
11
-
12
-func init() {
13
-	last := capability.CAP_LAST_CAP
14
-	// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
15
-	if last == capability.Cap(63) {
16
-		last = capability.CAP_BLOCK_SUSPEND
17
-	}
18
-	for _, cap := range capability.List() {
19
-		if cap > last {
20
-			continue
21
-		}
22
-		capabilityList = append(capabilityList,
23
-			&CapabilityMapping{
24
-				Key:   "CAP_" + strings.ToUpper(cap.String()),
25
-				Value: cap,
26
-			},
27
-		)
28
-	}
29
-}
30
-
31
-type (
32
-	// CapabilityMapping maps linux capability name to its value of capability.Cap type
33
-	// Capabilities is one of the security systems in Linux Security Module (LSM)
34
-	// framework provided by the kernel.
35
-	// For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html
36
-	CapabilityMapping struct {
37
-		Key   string         `json:"key,omitempty"`
38
-		Value capability.Cap `json:"value,omitempty"`
39
-	}
40
-	// Capabilities contains all CapabilityMapping
41
-	Capabilities []*CapabilityMapping
42
-)
43
-
44
-// String returns <key> of CapabilityMapping
45
-func (c *CapabilityMapping) String() string {
46
-	return c.Key
47
-}
48
-
49
-// GetCapability returns CapabilityMapping which contains specific key
50
-func GetCapability(key string) *CapabilityMapping {
51
-	for _, capp := range capabilityList {
52
-		if capp.Key == key {
53
-			cpy := *capp
54
-			return &cpy
55
-		}
56
-	}
57
-	return nil
58
-}
59
-
60
-// GetAllCapabilities returns all of the capabilities
61
-func GetAllCapabilities() []string {
62
-	output := make([]string, len(capabilityList))
63
-	for i, capability := range capabilityList {
64
-		output[i] = capability.String()
65
-	}
66
-	return output
67
-}
68
-
69
-// inSlice tests whether a string is contained in a slice of strings or not.
70
-// Comparison is case insensitive
71
-func inSlice(slice []string, s string) bool {
72
-	for _, ss := range slice {
73
-		if strings.ToLower(s) == strings.ToLower(ss) {
74
-			return true
75
-		}
76
-	}
77
-	return false
78
-}
79
-
80
-// TweakCapabilities can tweak capabilities by adding or dropping capabilities
81
-// based on the basics capabilities.
82
-func TweakCapabilities(basics, adds, drops []string) ([]string, error) {
83
-	var (
84
-		newCaps []string
85
-		allCaps = GetAllCapabilities()
86
-	)
87
-
88
-	// FIXME(tonistiigi): docker format is without CAP_ prefix, oci is with prefix
89
-	// Currently they are mixed in here. We should do conversion in one place.
90
-
91
-	// look for invalid cap in the drop list
92
-	for _, cap := range drops {
93
-		if strings.ToLower(cap) == "all" {
94
-			continue
95
-		}
96
-
97
-		if !inSlice(allCaps, "CAP_"+cap) {
98
-			return nil, fmt.Errorf("Unknown capability drop: %q", cap)
99
-		}
100
-	}
101
-
102
-	// handle --cap-add=all
103
-	if inSlice(adds, "all") {
104
-		basics = allCaps
105
-	}
106
-
107
-	if !inSlice(drops, "all") {
108
-		for _, cap := range basics {
109
-			// skip `all` already handled above
110
-			if strings.ToLower(cap) == "all" {
111
-				continue
112
-			}
113
-
114
-			// if we don't drop `all`, add back all the non-dropped caps
115
-			if !inSlice(drops, cap[4:]) {
116
-				newCaps = append(newCaps, strings.ToUpper(cap))
117
-			}
118
-		}
119
-	}
120
-
121
-	for _, cap := range adds {
122
-		// skip `all` already handled above
123
-		if strings.ToLower(cap) == "all" {
124
-			continue
125
-		}
126
-
127
-		cap = "CAP_" + cap
128
-
129
-		if !inSlice(allCaps, cap) {
130
-			return nil, fmt.Errorf("Unknown capability to add: %q", cap)
131
-		}
132
-
133
-		// add cap if not already in the list
134
-		if !inSlice(newCaps, cap) {
135
-			newCaps = append(newCaps, strings.ToUpper(cap))
136
-		}
137
-	}
138
-	return newCaps, nil
139
-}
... ...
@@ -2,8 +2,8 @@ package daemon // import "github.com/docker/docker/daemon"
2 2
 
3 3
 import (
4 4
 	"github.com/docker/docker/container"
5
-	"github.com/docker/docker/daemon/caps"
6 5
 	"github.com/docker/docker/daemon/exec"
6
+	"github.com/docker/docker/oci/caps"
7 7
 	"github.com/opencontainers/runc/libcontainer/apparmor"
8 8
 	"github.com/opencontainers/runtime-spec/specs-go"
9 9
 )
10 10
deleted file mode 100644
... ...
@@ -1,78 +0,0 @@
1
-package daemon // import "github.com/docker/docker/daemon"
2
-
3
-import (
4
-	"fmt"
5
-	"regexp"
6
-	"strconv"
7
-
8
-	"github.com/docker/docker/container"
9
-	"github.com/docker/docker/daemon/caps"
10
-	specs "github.com/opencontainers/runtime-spec/specs-go"
11
-)
12
-
13
-// nolint: gosimple
14
-var (
15
-	deviceCgroupRuleRegex = regexp.MustCompile("^([acb]) ([0-9]+|\\*):([0-9]+|\\*) ([rwm]{1,3})$")
16
-)
17
-
18
-func setCapabilities(s *specs.Spec, c *container.Container) error {
19
-	var caplist []string
20
-	var err error
21
-	if c.HostConfig.Privileged {
22
-		caplist = caps.GetAllCapabilities()
23
-	} else {
24
-		caplist, err = caps.TweakCapabilities(s.Process.Capabilities.Bounding, c.HostConfig.CapAdd, c.HostConfig.CapDrop)
25
-		if err != nil {
26
-			return err
27
-		}
28
-	}
29
-	s.Process.Capabilities.Effective = caplist
30
-	s.Process.Capabilities.Bounding = caplist
31
-	s.Process.Capabilities.Permitted = caplist
32
-	s.Process.Capabilities.Inheritable = caplist
33
-	// setUser has already been executed here
34
-	// if non root drop capabilities in the way execve does
35
-	if s.Process.User.UID != 0 {
36
-		s.Process.Capabilities.Effective = []string{}
37
-		s.Process.Capabilities.Permitted = []string{}
38
-	}
39
-	return nil
40
-}
41
-
42
-func appendDevicePermissionsFromCgroupRules(devPermissions []specs.LinuxDeviceCgroup, rules []string) ([]specs.LinuxDeviceCgroup, error) {
43
-	for _, deviceCgroupRule := range rules {
44
-		ss := deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1)
45
-		if len(ss[0]) != 5 {
46
-			return nil, fmt.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule)
47
-		}
48
-		matches := ss[0]
49
-
50
-		dPermissions := specs.LinuxDeviceCgroup{
51
-			Allow:  true,
52
-			Type:   matches[1],
53
-			Access: matches[4],
54
-		}
55
-		if matches[2] == "*" {
56
-			major := int64(-1)
57
-			dPermissions.Major = &major
58
-		} else {
59
-			major, err := strconv.ParseInt(matches[2], 10, 64)
60
-			if err != nil {
61
-				return nil, fmt.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule)
62
-			}
63
-			dPermissions.Major = &major
64
-		}
65
-		if matches[3] == "*" {
66
-			minor := int64(-1)
67
-			dPermissions.Minor = &minor
68
-		} else {
69
-			minor, err := strconv.ParseInt(matches[3], 10, 64)
70
-			if err != nil {
71
-				return nil, fmt.Errorf("invalid minor value in device cgroup rule format: '%s'", deviceCgroupRule)
72
-			}
73
-			dPermissions.Minor = &minor
74
-		}
75
-		devPermissions = append(devPermissions, dPermissions)
76
-	}
77
-	return devPermissions, nil
78
-}
... ...
@@ -113,7 +113,7 @@ func setDevices(s *specs.Spec, c *container.Container) error {
113 113
 		}
114 114
 
115 115
 		var err error
116
-		devPermissions, err = appendDevicePermissionsFromCgroupRules(devPermissions, c.HostConfig.DeviceCgroupRules)
116
+		devPermissions, err = oci.AppendDevicePermissionsFromCgroupRules(devPermissions, c.HostConfig.DeviceCgroupRules)
117 117
 		if err != nil {
118 118
 			return err
119 119
 		}
... ...
@@ -762,7 +762,7 @@ func (daemon *Daemon) createSpec(c *container.Container) (retSpec *specs.Spec, e
762 762
 	if err := setNamespaces(daemon, &s, c); err != nil {
763 763
 		return nil, fmt.Errorf("linux spec namespaces: %v", err)
764 764
 	}
765
-	if err := setCapabilities(&s, c); err != nil {
765
+	if err := oci.SetCapabilities(&s, c.HostConfig.CapAdd, c.HostConfig.CapDrop, c.HostConfig.Privileged); err != nil {
766 766
 		return nil, fmt.Errorf("linux spec capabilities: %v", err)
767 767
 	}
768 768
 	if err := setSeccomp(daemon, &s, c); err != nil {
... ...
@@ -368,10 +368,10 @@ func (daemon *Daemon) createSpecLinuxFields(c *container.Container, s *specs.Spe
368 368
 	}
369 369
 	s.Root.Path = "rootfs"
370 370
 	s.Root.Readonly = c.HostConfig.ReadonlyRootfs
371
-	if err := setCapabilities(s, c); err != nil {
371
+	if err := oci.SetCapabilities(s, c.HostConfig.CapAdd, c.HostConfig.CapDrop, c.HostConfig.Privileged); err != nil {
372 372
 		return fmt.Errorf("linux spec capabilities: %v", err)
373 373
 	}
374
-	devPermissions, err := appendDevicePermissionsFromCgroupRules(nil, c.HostConfig.DeviceCgroupRules)
374
+	devPermissions, err := oci.AppendDevicePermissionsFromCgroupRules(nil, c.HostConfig.DeviceCgroupRules)
375 375
 	if err != nil {
376 376
 		return fmt.Errorf("linux runtime spec devices: %v", err)
377 377
 	}
378 378
new file mode 100644
... ...
@@ -0,0 +1,139 @@
0
+package caps // import "github.com/docker/docker/oci/caps"
1
+
2
+import (
3
+	"fmt"
4
+	"strings"
5
+
6
+	"github.com/syndtr/gocapability/capability"
7
+)
8
+
9
+var capabilityList Capabilities
10
+
11
+func init() {
12
+	last := capability.CAP_LAST_CAP
13
+	// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
14
+	if last == capability.Cap(63) {
15
+		last = capability.CAP_BLOCK_SUSPEND
16
+	}
17
+	for _, cap := range capability.List() {
18
+		if cap > last {
19
+			continue
20
+		}
21
+		capabilityList = append(capabilityList,
22
+			&CapabilityMapping{
23
+				Key:   "CAP_" + strings.ToUpper(cap.String()),
24
+				Value: cap,
25
+			},
26
+		)
27
+	}
28
+}
29
+
30
+type (
31
+	// CapabilityMapping maps linux capability name to its value of capability.Cap type
32
+	// Capabilities is one of the security systems in Linux Security Module (LSM)
33
+	// framework provided by the kernel.
34
+	// For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html
35
+	CapabilityMapping struct {
36
+		Key   string         `json:"key,omitempty"`
37
+		Value capability.Cap `json:"value,omitempty"`
38
+	}
39
+	// Capabilities contains all CapabilityMapping
40
+	Capabilities []*CapabilityMapping
41
+)
42
+
43
+// String returns <key> of CapabilityMapping
44
+func (c *CapabilityMapping) String() string {
45
+	return c.Key
46
+}
47
+
48
+// GetCapability returns CapabilityMapping which contains specific key
49
+func GetCapability(key string) *CapabilityMapping {
50
+	for _, capp := range capabilityList {
51
+		if capp.Key == key {
52
+			cpy := *capp
53
+			return &cpy
54
+		}
55
+	}
56
+	return nil
57
+}
58
+
59
+// GetAllCapabilities returns all of the capabilities
60
+func GetAllCapabilities() []string {
61
+	output := make([]string, len(capabilityList))
62
+	for i, capability := range capabilityList {
63
+		output[i] = capability.String()
64
+	}
65
+	return output
66
+}
67
+
68
+// inSlice tests whether a string is contained in a slice of strings or not.
69
+// Comparison is case insensitive
70
+func inSlice(slice []string, s string) bool {
71
+	for _, ss := range slice {
72
+		if strings.ToLower(s) == strings.ToLower(ss) {
73
+			return true
74
+		}
75
+	}
76
+	return false
77
+}
78
+
79
+// TweakCapabilities can tweak capabilities by adding or dropping capabilities
80
+// based on the basics capabilities.
81
+func TweakCapabilities(basics, adds, drops []string) ([]string, error) {
82
+	var (
83
+		newCaps []string
84
+		allCaps = GetAllCapabilities()
85
+	)
86
+
87
+	// FIXME(tonistiigi): docker format is without CAP_ prefix, oci is with prefix
88
+	// Currently they are mixed in here. We should do conversion in one place.
89
+
90
+	// look for invalid cap in the drop list
91
+	for _, cap := range drops {
92
+		if strings.ToLower(cap) == "all" {
93
+			continue
94
+		}
95
+
96
+		if !inSlice(allCaps, "CAP_"+cap) {
97
+			return nil, fmt.Errorf("Unknown capability drop: %q", cap)
98
+		}
99
+	}
100
+
101
+	// handle --cap-add=all
102
+	if inSlice(adds, "all") {
103
+		basics = allCaps
104
+	}
105
+
106
+	if !inSlice(drops, "all") {
107
+		for _, cap := range basics {
108
+			// skip `all` already handled above
109
+			if strings.ToLower(cap) == "all" {
110
+				continue
111
+			}
112
+
113
+			// if we don't drop `all`, add back all the non-dropped caps
114
+			if !inSlice(drops, cap[4:]) {
115
+				newCaps = append(newCaps, strings.ToUpper(cap))
116
+			}
117
+		}
118
+	}
119
+
120
+	for _, cap := range adds {
121
+		// skip `all` already handled above
122
+		if strings.ToLower(cap) == "all" {
123
+			continue
124
+		}
125
+
126
+		cap = "CAP_" + cap
127
+
128
+		if !inSlice(allCaps, cap) {
129
+			return nil, fmt.Errorf("Unknown capability to add: %q", cap)
130
+		}
131
+
132
+		// add cap if not already in the list
133
+		if !inSlice(newCaps, cap) {
134
+			newCaps = append(newCaps, strings.ToUpper(cap))
135
+		}
136
+	}
137
+	return newCaps, nil
138
+}
0 139
new file mode 100644
... ...
@@ -0,0 +1,80 @@
0
+package oci // import "github.com/docker/docker/oci"
1
+
2
+import (
3
+	"fmt"
4
+	"regexp"
5
+	"strconv"
6
+
7
+	"github.com/docker/docker/oci/caps"
8
+	specs "github.com/opencontainers/runtime-spec/specs-go"
9
+)
10
+
11
+// nolint: gosimple
12
+var deviceCgroupRuleRegex = regexp.MustCompile("^([acb]) ([0-9]+|\\*):([0-9]+|\\*) ([rwm]{1,3})$")
13
+
14
+// SetCapabilities sets the provided capabilities on the spec
15
+// All capabilities are added if privileged is true
16
+func SetCapabilities(s *specs.Spec, add, drop []string, privileged bool) error {
17
+	var (
18
+		caplist []string
19
+		err     error
20
+	)
21
+	if privileged {
22
+		caplist = caps.GetAllCapabilities()
23
+	} else {
24
+		caplist, err = caps.TweakCapabilities(s.Process.Capabilities.Bounding, add, drop)
25
+		if err != nil {
26
+			return err
27
+		}
28
+	}
29
+	s.Process.Capabilities.Effective = caplist
30
+	s.Process.Capabilities.Bounding = caplist
31
+	s.Process.Capabilities.Permitted = caplist
32
+	s.Process.Capabilities.Inheritable = caplist
33
+	// setUser has already been executed here
34
+	// if non root drop capabilities in the way execve does
35
+	if s.Process.User.UID != 0 {
36
+		s.Process.Capabilities.Effective = []string{}
37
+		s.Process.Capabilities.Permitted = []string{}
38
+	}
39
+	return nil
40
+}
41
+
42
+// AppendDevicePermissionsFromCgroupRules takes rules for the devices cgroup to append to the default set
43
+func AppendDevicePermissionsFromCgroupRules(devPermissions []specs.LinuxDeviceCgroup, rules []string) ([]specs.LinuxDeviceCgroup, error) {
44
+	for _, deviceCgroupRule := range rules {
45
+		ss := deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1)
46
+		if len(ss[0]) != 5 {
47
+			return nil, fmt.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule)
48
+		}
49
+		matches := ss[0]
50
+
51
+		dPermissions := specs.LinuxDeviceCgroup{
52
+			Allow:  true,
53
+			Type:   matches[1],
54
+			Access: matches[4],
55
+		}
56
+		if matches[2] == "*" {
57
+			major := int64(-1)
58
+			dPermissions.Major = &major
59
+		} else {
60
+			major, err := strconv.ParseInt(matches[2], 10, 64)
61
+			if err != nil {
62
+				return nil, fmt.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule)
63
+			}
64
+			dPermissions.Major = &major
65
+		}
66
+		if matches[3] == "*" {
67
+			minor := int64(-1)
68
+			dPermissions.Minor = &minor
69
+		} else {
70
+			minor, err := strconv.ParseInt(matches[3], 10, 64)
71
+			if err != nil {
72
+				return nil, fmt.Errorf("invalid minor value in device cgroup rule format: '%s'", deviceCgroupRule)
73
+			}
74
+			dPermissions.Minor = &minor
75
+		}
76
+		devPermissions = append(devPermissions, dPermissions)
77
+	}
78
+	return devPermissions, nil
79
+}