export_oci-caps
authorArnaud Rebillout <arnaud.rebillout@collabora.com>
Tue, 8 Oct 2019 03:37:25 +0000 (04:37 +0100)
committerDmitry Smirnov <onlyjob@debian.org>
Tue, 8 Oct 2019 03:37:25 +0000 (04:37 +0100)
Gbp-Pq: Name export_oci-caps.patch

engine/oci/caps/utils.go [new file with mode: 0644]
engine/oci/defaults.go
engine/oci/oci.go [new file with mode: 0644]

diff --git a/engine/oci/caps/utils.go b/engine/oci/caps/utils.go
new file mode 100644 (file)
index 0000000..ffd3f6f
--- /dev/null
@@ -0,0 +1,169 @@
+package caps // import "github.com/docker/docker/oci/caps"
+
+import (
+       "fmt"
+       "strings"
+
+       "github.com/docker/docker/errdefs"
+       "github.com/syndtr/gocapability/capability"
+)
+
+var capabilityList Capabilities
+
+func init() {
+       last := capability.CAP_LAST_CAP
+       // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
+       if last == capability.Cap(63) {
+               last = capability.CAP_BLOCK_SUSPEND
+       }
+       for _, cap := range capability.List() {
+               if cap > last {
+                       continue
+               }
+               capabilityList = append(capabilityList,
+                       &CapabilityMapping{
+                               Key:   "CAP_" + strings.ToUpper(cap.String()),
+                               Value: cap,
+                       },
+               )
+       }
+}
+
+type (
+       // CapabilityMapping maps linux capability name to its value of capability.Cap type
+       // Capabilities is one of the security systems in Linux Security Module (LSM)
+       // framework provided by the kernel.
+       // For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html
+       CapabilityMapping struct {
+               Key   string         `json:"key,omitempty"`
+               Value capability.Cap `json:"value,omitempty"`
+       }
+       // Capabilities contains all CapabilityMapping
+       Capabilities []*CapabilityMapping
+)
+
+// String returns <key> of CapabilityMapping
+func (c *CapabilityMapping) String() string {
+       return c.Key
+}
+
+// GetCapability returns CapabilityMapping which contains specific key
+func GetCapability(key string) *CapabilityMapping {
+       for _, capp := range capabilityList {
+               if capp.Key == key {
+                       cpy := *capp
+                       return &cpy
+               }
+       }
+       return nil
+}
+
+// GetAllCapabilities returns all of the capabilities
+func GetAllCapabilities() []string {
+       output := make([]string, len(capabilityList))
+       for i, capability := range capabilityList {
+               output[i] = capability.String()
+       }
+       return output
+}
+
+// inSlice tests whether a string is contained in a slice of strings or not.
+func inSlice(slice []string, s string) bool {
+       for _, ss := range slice {
+               if s == ss {
+                       return true
+               }
+       }
+       return false
+}
+
+const allCapabilities = "ALL"
+
+// NormalizeLegacyCapabilities normalizes, and validates CapAdd/CapDrop capabilities
+// by upper-casing them, and adding a CAP_ prefix (if not yet present).
+//
+// This function also accepts the "ALL" magic-value, that's used by CapAdd/CapDrop.
+func NormalizeLegacyCapabilities(caps []string) ([]string, error) {
+       var normalized []string
+
+       valids := GetAllCapabilities()
+       for _, c := range caps {
+               c = strings.ToUpper(c)
+               if c == allCapabilities {
+                       normalized = append(normalized, c)
+                       continue
+               }
+               if !strings.HasPrefix(c, "CAP_") {
+                       c = "CAP_" + c
+               }
+               if !inSlice(valids, c) {
+                       return nil, errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c))
+               }
+               normalized = append(normalized, c)
+       }
+       return normalized, nil
+}
+
+// ValidateCapabilities validates if caps only contains valid capabilities
+func ValidateCapabilities(caps []string) error {
+       valids := GetAllCapabilities()
+       for _, c := range caps {
+               if !inSlice(valids, c) {
+                       return errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c))
+               }
+       }
+       return nil
+}
+
+// TweakCapabilities tweaks capabilities by adding, dropping, or overriding
+// capabilities in the basics capabilities list.
+func TweakCapabilities(basics, adds, drops, capabilities []string, privileged bool) ([]string, error) {
+       switch {
+       case privileged:
+               // Privileged containers get all capabilities
+               return GetAllCapabilities(), nil
+       case capabilities != nil:
+               // Use custom set of capabilities
+               if err := ValidateCapabilities(capabilities); err != nil {
+                       return nil, err
+               }
+               return capabilities, nil
+       case len(adds) == 0 && len(drops) == 0:
+               // Nothing to tweak; we're done
+               return basics, nil
+       }
+
+       capDrop, err := NormalizeLegacyCapabilities(drops)
+       if err != nil {
+               return nil, err
+       }
+       capAdd, err := NormalizeLegacyCapabilities(adds)
+       if err != nil {
+               return nil, err
+       }
+
+       var caps []string
+
+       switch {
+       case inSlice(capAdd, allCapabilities):
+               // Add all capabilities except ones on capDrop
+               for _, c := range GetAllCapabilities() {
+                       if !inSlice(capDrop, c) {
+                               caps = append(caps, c)
+                       }
+               }
+       case inSlice(capDrop, allCapabilities):
+               // "Drop" all capabilities; use what's in capAdd instead
+               caps = capAdd
+       default:
+               // First drop some capabilities
+               for _, c := range basics {
+                       if !inSlice(capDrop, c) {
+                               caps = append(caps, c)
+                       }
+               }
+               // Then add the list of capabilities from capAdd
+               caps = append(caps, capAdd...)
+       }
+       return caps, nil
+}
index 8ba92407a39d3143ccee560a175a76f19c080994..35fbcd1d8707b1c95ee08de8219459aed6b7936f 100644 (file)
@@ -11,7 +11,8 @@ func iPtr(i int64) *int64        { return &i }
 func u32Ptr(i int64) *uint32     { u := uint32(i); return &u }
 func fmPtr(i int64) *os.FileMode { fm := os.FileMode(i); return &fm }
 
-func defaultCapabilities() []string {
+// DefaultCapabilities returns a Linux kernel default capabilities
+func DefaultCapabilities() []string {
        return []string{
                "CAP_CHOWN",
                "CAP_DAC_OVERRIDE",
@@ -59,10 +60,10 @@ func DefaultLinuxSpec() specs.Spec {
                Version: specs.Version,
                Process: &specs.Process{
                        Capabilities: &specs.LinuxCapabilities{
-                               Bounding:    defaultCapabilities(),
-                               Permitted:   defaultCapabilities(),
-                               Inheritable: defaultCapabilities(),
-                               Effective:   defaultCapabilities(),
+                               Bounding:    DefaultCapabilities(),
+                               Permitted:   DefaultCapabilities(),
+                               Inheritable: DefaultCapabilities(),
+                               Effective:   DefaultCapabilities(),
                        },
                },
                Root: &specs.Root{},
diff --git a/engine/oci/oci.go b/engine/oci/oci.go
new file mode 100644 (file)
index 0000000..6c84ba3
--- /dev/null
@@ -0,0 +1,67 @@
+package oci // import "github.com/docker/docker/oci"
+
+import (
+       "fmt"
+       "regexp"
+       "strconv"
+
+       specs "github.com/opencontainers/runtime-spec/specs-go"
+)
+
+// nolint: gosimple
+var deviceCgroupRuleRegex = regexp.MustCompile("^([acb]) ([0-9]+|\\*):([0-9]+|\\*) ([rwm]{1,3})$")
+
+// SetCapabilities sets the provided capabilities on the spec
+// All capabilities are added if privileged is true
+func SetCapabilities(s *specs.Spec, caplist []string) error {
+       s.Process.Capabilities.Effective = caplist
+       s.Process.Capabilities.Bounding = caplist
+       s.Process.Capabilities.Permitted = caplist
+       s.Process.Capabilities.Inheritable = caplist
+       // setUser has already been executed here
+       // if non root drop capabilities in the way execve does
+       if s.Process.User.UID != 0 {
+               s.Process.Capabilities.Effective = []string{}
+               s.Process.Capabilities.Permitted = []string{}
+       }
+       return nil
+}
+
+// AppendDevicePermissionsFromCgroupRules takes rules for the devices cgroup to append to the default set
+func AppendDevicePermissionsFromCgroupRules(devPermissions []specs.LinuxDeviceCgroup, rules []string) ([]specs.LinuxDeviceCgroup, error) {
+       for _, deviceCgroupRule := range rules {
+               ss := deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1)
+               if len(ss[0]) != 5 {
+                       return nil, fmt.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule)
+               }
+               matches := ss[0]
+
+               dPermissions := specs.LinuxDeviceCgroup{
+                       Allow:  true,
+                       Type:   matches[1],
+                       Access: matches[4],
+               }
+               if matches[2] == "*" {
+                       major := int64(-1)
+                       dPermissions.Major = &major
+               } else {
+                       major, err := strconv.ParseInt(matches[2], 10, 64)
+                       if err != nil {
+                               return nil, fmt.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule)
+                       }
+                       dPermissions.Major = &major
+               }
+               if matches[3] == "*" {
+                       minor := int64(-1)
+                       dPermissions.Minor = &minor
+               } else {
+                       minor, err := strconv.ParseInt(matches[3], 10, 64)
+                       if err != nil {
+                               return nil, fmt.Errorf("invalid minor value in device cgroup rule format: '%s'", deviceCgroupRule)
+                       }
+                       dPermissions.Minor = &minor
+               }
+               devPermissions = append(devPermissions, dPermissions)
+       }
+       return devPermissions, nil
+}