summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common.go4
-rw-r--r--cmd/podman/shared/create.go1
-rw-r--r--cmd/podman/shared/intermediate.go1
-rw-r--r--completions/bash/podman1
-rw-r--r--docs/source/markdown/podman-create.1.md7
-rw-r--r--pkg/spec/config_linux.go37
-rw-r--r--pkg/spec/config_unsupported.go4
-rw-r--r--pkg/spec/createconfig.go1
-rw-r--r--pkg/spec/parse.go18
-rw-r--r--pkg/spec/spec.go6
-rw-r--r--test/e2e/run_test.go12
11 files changed, 92 insertions, 0 deletions
diff --git a/cmd/podman/common.go b/cmd/podman/common.go
index 7610edbc0..6fa2b3c71 100644
--- a/cmd/podman/common.go
+++ b/cmd/podman/common.go
@@ -257,6 +257,10 @@ func getCreateFlags(c *cliconfig.PodmanCommand) {
"Add a host device to the container (default [])",
)
createFlags.StringSlice(
+ "device-cgroup-rule", []string{},
+ "Add a rule to the cgroup allowed devices list",
+ )
+ createFlags.StringSlice(
"device-read-bps", []string{},
"Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)",
)
diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go
index 3062b0ca3..4c901b2a8 100644
--- a/cmd/podman/shared/create.go
+++ b/cmd/podman/shared/create.go
@@ -761,6 +761,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod.
CPURtPeriod: c.Uint64("cpu-rt-period"),
CPURtRuntime: c.Int64("cpu-rt-runtime"),
CPUs: c.Float64("cpus"),
+ DeviceCgroupRules: c.StringSlice("device-cgroup-rule"),
DeviceReadBps: c.StringSlice("device-read-bps"),
DeviceReadIOps: c.StringSlice("device-read-iops"),
DeviceWriteBps: c.StringSlice("device-write-bps"),
diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go
index cfb3f612c..ee212234f 100644
--- a/cmd/podman/shared/intermediate.go
+++ b/cmd/podman/shared/intermediate.go
@@ -386,6 +386,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes
m["detach"] = newCRBool(c, "detach")
m["detach-keys"] = newCRString(c, "detach-keys")
m["device"] = newCRStringSlice(c, "device")
+ m["device-cgroup-rule"] = newCRStringSlice(c, "device-cgroup-rule")
m["device-read-bps"] = newCRStringSlice(c, "device-read-bps")
m["device-read-iops"] = newCRStringSlice(c, "device-read-iops")
m["device-write-bps"] = newCRStringSlice(c, "device-write-bps")
diff --git a/completions/bash/podman b/completions/bash/podman
index 7c14cf67c..e5d2e2b29 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -1870,6 +1870,7 @@ _podman_container_run() {
--cpuset-mems
--cpu-shares -c
--device
+ --device-cgroup-rule
--device-read-bps
--device-read-iops
--device-write-bps
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 977382e61..ca38be6a1 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -209,6 +209,13 @@ Note: if the user only has access rights via a group then accessing the device
from inside a rootless container will fail. The `crun` runtime offers a
workaround for this by adding the option `--annotation run.oci.keep_original_groups=1`.
+**--device-cgroup-rule**="type major:minor mode"
+
+Add a rule to the cgroup allowed devices list. The rule is expected to be in the format specified in the Linux kernel documentation (Documentation/cgroup-v1/devices.txt):
+ - type: a (all), c (char), or b (block);
+ - major and minor: either a number, or * for all;
+ - mode: a composition of r (read), w (write), and m (mknod(2)).
+
**--device-read-bps**=*path*
Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)
diff --git a/pkg/spec/config_linux.go b/pkg/spec/config_linux.go
index 32d8cb4de..5f39b6d0d 100644
--- a/pkg/spec/config_linux.go
+++ b/pkg/spec/config_linux.go
@@ -7,6 +7,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strconv"
"strings"
"github.com/containers/libpod/pkg/rootless"
@@ -90,6 +91,42 @@ func devicesFromPath(g *generate.Generator, devicePath string) error {
return addDevice(g, strings.Join(append([]string{resolvedDevicePath}, devs[1:]...), ":"))
}
+func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error {
+ for _, deviceCgroupRule := range deviceCgroupRules {
+ if err := validateDeviceCgroupRule(deviceCgroupRule); err != nil {
+ return err
+ }
+ ss := parseDeviceCgroupRule(deviceCgroupRule)
+ if len(ss[0]) != 5 {
+ return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule)
+ }
+ matches := ss[0]
+ var major, minor *int64
+ if matches[2] == "*" {
+ majorDev := int64(-1)
+ major = &majorDev
+ } else {
+ majorDev, err := strconv.ParseInt(matches[2], 10, 64)
+ if err != nil {
+ return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule)
+ }
+ major = &majorDev
+ }
+ if matches[3] == "*" {
+ minorDev := int64(-1)
+ minor = &minorDev
+ } else {
+ minorDev, err := strconv.ParseInt(matches[2], 10, 64)
+ if err != nil {
+ return errors.Errorf("invalid major value in device cgroup rule format: '%s'", deviceCgroupRule)
+ }
+ minor = &minorDev
+ }
+ g.AddLinuxResourcesDevice(true, matches[1], major, minor, matches[4])
+ }
+ return nil
+}
+
func addDevice(g *generate.Generator, device string) error {
src, dst, permissions, err := ParseDevice(device)
if err != nil {
diff --git a/pkg/spec/config_unsupported.go b/pkg/spec/config_unsupported.go
index a2c7f4416..be3e7046d 100644
--- a/pkg/spec/config_unsupported.go
+++ b/pkg/spec/config_unsupported.go
@@ -30,3 +30,7 @@ func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrott
func devicesFromPath(g *generate.Generator, devicePath string) error {
return errors.New("function not implemented")
}
+
+func deviceCgroupRules(g *generate.Generator, deviceCgroupRules []string) error {
+ return errors.New("function not implemented")
+}
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index fb222083b..cf09299bc 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -38,6 +38,7 @@ type CreateResourceConfig struct {
CPUs float64 // cpus
CPUsetCPUs string
CPUsetMems string // cpuset-mems
+ DeviceCgroupRules []string //device-cgroup-rule
DeviceReadBps []string // device-read-bps
DeviceReadIOps []string // device-read-iops
DeviceWriteBps []string // device-write-bps
diff --git a/pkg/spec/parse.go b/pkg/spec/parse.go
index 6fa0b0636..a5dfccdb9 100644
--- a/pkg/spec/parse.go
+++ b/pkg/spec/parse.go
@@ -2,12 +2,17 @@ package createconfig
import (
"fmt"
+ "regexp"
"strconv"
"strings"
"github.com/docker/go-units"
+ "github.com/pkg/errors"
)
+// deviceCgroupRulegex defines the valid format of device-cgroup-rule
+var deviceCgroupRuleRegex = regexp.MustCompile(`^([acb]) ([0-9]+|\*):([0-9]+|\*) ([rwm]{1,3})$`)
+
// Pod signifies a kernel namespace is being shared
// by a container with the pod it is associated with
const Pod = "pod"
@@ -205,3 +210,16 @@ func IsValidDeviceMode(mode string) bool {
}
return true
}
+
+// validateDeviceCgroupRule validates the format of deviceCgroupRule
+func validateDeviceCgroupRule(deviceCgroupRule string) error {
+ if !deviceCgroupRuleRegex.MatchString(deviceCgroupRule) {
+ return errors.Errorf("invalid device cgroup rule format: '%s'", deviceCgroupRule)
+ }
+ return nil
+}
+
+// parseDeviceCgroupRule matches and parses the deviceCgroupRule into slice
+func parseDeviceCgroupRule(deviceCgroupRule string) [][]string {
+ return deviceCgroupRuleRegex.FindAllStringSubmatch(deviceCgroupRule, -1)
+}
diff --git a/pkg/spec/spec.go b/pkg/spec/spec.go
index cae055bb0..b2a152a2d 100644
--- a/pkg/spec/spec.go
+++ b/pkg/spec/spec.go
@@ -232,6 +232,12 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
return nil, err
}
}
+ if len(config.Resources.DeviceCgroupRules) != 0 {
+ if err := deviceCgroupRules(&g, config.Resources.DeviceCgroupRules); err != nil {
+ return nil, err
+ }
+ addedResources = true
+ }
}
// SECURITY OPTS
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 72547ea00..3eb93b84a 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -999,4 +999,16 @@ USER mail`
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Not(Equal(0)))
})
+
+ It("podman run --device-cgroup-rule", func() {
+ SkipIfRemote()
+ SkipIfRootless()
+ deviceCgroupRule := "c 42:* rwm"
+ session := podmanTest.Podman([]string{"run", "--name", "test", "-d", "--device-cgroup-rule", deviceCgroupRule, ALPINE, "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ session = podmanTest.Podman([]string{"exec", "test", "mknod", "newDev", "c", "42", "1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
})