Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Support Device CGroup rules update through runc #92

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 56 additions & 22 deletions cgroup/cgroup.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package cgroup

import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"

securejoin "github.com/cyphar/filepath-securejoin"
Expand All @@ -24,32 +26,64 @@ type cgroup struct {
}

func (d cgroup) AddDevicesAllowed(containerID string, permission string) (bool, *dbus.Error) {
// Make sure path is relative to cgroupFSDockerDevices
allowedFile, err := securejoin.SecureJoin(cgroupFSDockerDevices, containerID+string(filepath.Separator)+"devices.allow")
if err != nil {
return false, dbus.MakeFailedError(fmt.Errorf("Security issues with '%s': %s", containerID, err))
}
// Check for CGroups v2
if _, err := os.Stat("/sys/fs/cgroup/cgroup.controllers"); err == nil {
pvizeli marked this conversation as resolved.
Show resolved Hide resolved
permissions := []string{permission}
resources, err := CreateDeviceUpdateResources(permissions)
if err != nil {
error := fmt.Errorf("Error calling runc for '%s': %s", containerID, err)
logging.Error.Printf("%s", error)
return false, dbus.MakeFailedError(error)
}

// Check if file/container exists
_, err = os.Stat(allowedFile)
if os.IsNotExist(err) {
return false, dbus.MakeFailedError(fmt.Errorf("Can't find Container '%s' for adjust CGroup devices.", containerID))
}
fmt.Printf("Container resources update %#v\n", resources)

// Write permission adjustments
file, err := os.Create(allowedFile)
if err != nil {
return false, dbus.MakeFailedError(fmt.Errorf("Can't open CGroup devices '%s': %s", allowedFile, err))
}
defer file.Close()
cmd := exec.Command("runc", "--root", "/var/run/docker/runtime-runc/moby/", "update", "--resources", "-", containerID)
fmt.Printf("Calling command %v", cmd.Args)

_, err = file.WriteString(permission + "\n")
if err != nil {
return false, dbus.MakeFailedError(fmt.Errorf("Can't write CGroup permission '%s': %s", permission, err))
}
// Pass resources as OCI LinuxResources JSON object
stdin, err := cmd.StdinPipe()
enc := json.NewEncoder(stdin)
enc.Encode(resources)
stdin.Close()

logging.Info.Printf("Permission '%s', granted for Container '%s'", containerID, permission)
return true, nil
stdoutStderr, err := cmd.CombinedOutput()
if err != nil {
error := fmt.Errorf("Error calling runc for '%s': %s, output %s", containerID, err, stdoutStderr)
logging.Error.Printf("%s", error)
return false, dbus.MakeFailedError(error)
}

logging.Info.Printf("Permission '%s', granted for Container '%s'", containerID, permission)
return true, nil
} else {
// Make sure path is relative to cgroupFSDockerDevices
allowedFile, err := securejoin.SecureJoin(cgroupFSDockerDevices, containerID+string(filepath.Separator)+"devices.allow")
if err != nil {
return false, dbus.MakeFailedError(fmt.Errorf("Security issues with '%s': %s", containerID, err))
}

// Check if file/container exists
_, err = os.Stat(allowedFile)
if os.IsNotExist(err) {
return false, dbus.MakeFailedError(fmt.Errorf("Can't find Container '%s' for adjust CGroup devices.", containerID))
}

// Write permission adjustments
file, err := os.Create(allowedFile)
if err != nil {
return false, dbus.MakeFailedError(fmt.Errorf("Can't open CGroup devices '%s': %s", allowedFile, err))
}
defer file.Close()

_, err = file.WriteString(permission + "\n")
if err != nil {
return false, dbus.MakeFailedError(fmt.Errorf("Can't write CGroup permission '%s': %s", permission, err))
}

logging.Info.Printf("Permission '%s', granted for Container '%s'", containerID, permission)
return true, nil
}
}

func InitializeDBus(conn *dbus.Conn) {
Expand Down
63 changes: 63 additions & 0 deletions cgroup/oci.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cgroup

import (
"fmt"
"regexp"
"strconv"

"github.com/opencontainers/runtime-spec/specs-go"
)

var deviceCgroupRuleRegex = regexp.MustCompile("^([acb]) ([0-9]+|\\*):([0-9]+|\\*) ([rwm]{1,3})$")

// Lifted from Moby's oci/oci.go
func AppendDevicePermissionsFromCgroupRules(devPermissions []specs.LinuxDeviceCgroup, rules []string) ([]specs.LinuxDeviceCgroup, error) {
for _, deviceCgroupRule := range rules {
ss := deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1)
if len(ss) == 0 || 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
}

func CreateDeviceUpdateResources(rules []string) (*specs.LinuxResources, error) {
resources := specs.LinuxResources{}

devices, err := AppendDevicePermissionsFromCgroupRules(nil, rules)
if err != nil {
return nil, err
}

resources.Devices = devices

return &resources, nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ require (
github.com/getsentry/sentry-go v0.13.0
github.com/godbus/dbus/v5 v5.1.0
github.com/pkg/errors v0.9.1 // indirect
github.com/opencontainers/runtime-spec v1.0.2
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5Vgl
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
Expand Down