2018-12-10 15:40:40 -05:00
|
|
|
package oci // import "github.com/docker/docker/oci"
|
2018-06-15 18:36:10 -04:00
|
|
|
|
|
|
|
import (
|
2018-06-15 19:14:17 -04:00
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
|
2018-06-15 18:36:10 -04:00
|
|
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
|
|
|
)
|
|
|
|
|
2022-07-08 12:27:07 -04:00
|
|
|
// TODO verify if this regex is correct for "a" (all);
|
|
|
|
//
|
|
|
|
// The docs (https://github.com/torvalds/linux/blob/v5.10/Documentation/admin-guide/cgroup-v1/devices.rst) describe:
|
|
|
|
// "'all' means it applies to all types and all major and minor numbers", and shows an example
|
|
|
|
// that *only* passes `a` as value: `echo a > /sys/fs/cgroup/1/devices.allow, which would be
|
|
|
|
// the "implicit" equivalent of "a *:* rwm". Source-code also looks to confirm this, and returns
|
|
|
|
// early for "a" (all); https://github.com/torvalds/linux/blob/v5.10/security/device_cgroup.c#L614-L642
|
|
|
|
var deviceCgroupRuleRegex = regexp.MustCompile("^([acb]) ([0-9]+|\\*):([0-9]+|\\*) ([rwm]{1,3})$") //nolint: gosimple
|
2018-06-15 19:14:17 -04:00
|
|
|
|
2018-12-10 15:40:40 -05:00
|
|
|
// SetCapabilities sets the provided capabilities on the spec
|
2022-01-31 15:08:01 -05:00
|
|
|
// All capabilities are added if privileged is true.
|
2018-12-16 10:11:37 -05:00
|
|
|
func SetCapabilities(s *specs.Spec, caplist []string) error {
|
2018-06-15 18:36:10 -04:00
|
|
|
// setUser has already been executed here
|
2022-01-31 15:08:01 -05:00
|
|
|
if s.Process.User.UID == 0 {
|
|
|
|
s.Process.Capabilities = &specs.LinuxCapabilities{
|
|
|
|
Effective: caplist,
|
|
|
|
Bounding: caplist,
|
|
|
|
Permitted: caplist,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Do not set Effective and Permitted capabilities for non-root users,
|
|
|
|
// to match what execve does.
|
|
|
|
s.Process.Capabilities = &specs.LinuxCapabilities{
|
|
|
|
Bounding: caplist,
|
|
|
|
}
|
2018-06-15 18:36:10 -04:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2018-06-15 19:14:17 -04:00
|
|
|
|
2018-12-10 15:40:40 -05:00
|
|
|
// AppendDevicePermissionsFromCgroupRules takes rules for the devices cgroup to append to the default set
|
|
|
|
func AppendDevicePermissionsFromCgroupRules(devPermissions []specs.LinuxDeviceCgroup, rules []string) ([]specs.LinuxDeviceCgroup, error) {
|
2018-06-15 19:14:17 -04:00
|
|
|
for _, deviceCgroupRule := range rules {
|
|
|
|
ss := deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1)
|
Fix daemon panic when starting container with invalid device cgroup rule
This fixes a panic when an invalid "device cgroup rule" is passed, resulting
in an "index out of range".
This bug was introduced in the original implementation in 1756af6fafabd9197feb56c0324e49dd7d30b11f,
but was not reproducible when using the CLI, because the same commit also added
client-side validation on the flag before making an API request. The following
example, uses an invalid rule (`c *:* rwm` - two spaces before the permissions);
```console
$ docker run --rm --network=host --device-cgroup-rule='c *:* rwm' busybox
invalid argument "c *:* rwm" for "--device-cgroup-rule" flag: invalid device cgroup format 'c *:* rwm'
```
Doing the same, but using the API results in a daemon panic when starting the container;
Create a container with an invalid device cgroup rule:
```console
curl -v \
--unix-socket /var/run/docker.sock \
"http://localhost/v1.41/containers/create?name=foobar" \
-H "Content-Type: application/json" \
-d '{"Image":"busybox:latest", "HostConfig":{"DeviceCgroupRules": ["c *:* rwm"]}}'
```
Start the container:
```console
curl -v \
--unix-socket /var/run/docker.sock \
-X POST \
"http://localhost/v1.41/containers/foobar/start"
```
Observe the daemon logs:
```
2021-01-22 12:53:03.313806 I | http: panic serving @: runtime error: index out of range [0] with length 0
goroutine 571 [running]:
net/http.(*conn).serve.func1(0xc000cb2d20)
/usr/local/go/src/net/http/server.go:1795 +0x13b
panic(0x2f32380, 0xc000aebfc0)
/usr/local/go/src/runtime/panic.go:679 +0x1b6
github.com/docker/docker/oci.AppendDevicePermissionsFromCgroupRules(0xc000175c00, 0x8, 0x8, 0xc0000bd380, 0x1, 0x4, 0x0, 0x0, 0xc0000e69c0, 0x0, ...)
/go/src/github.com/docker/docker/oci/oci.go:34 +0x64f
```
This patch:
- fixes the panic, allowing the daemon to return an error on container start
- adds a unit-test to validate various permutations
- adds a "todo" to verify the regular expression (and handling) of the "a" (all) value
We should also consider performing this validation when _creating_ the container,
so that an error is produced early.
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-01-22 08:10:22 -05:00
|
|
|
if len(ss) == 0 || len(ss[0]) != 5 {
|
2018-06-15 19:14:17 -04:00
|
|
|
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
|
|
|
|
}
|