libcontainer: isolate libcontainer/devices

Move the Device-related types to libcontainer/devices, so that
the package can be used in isolation. Aliases have been created
in libcontainer/configs for backward compatibility.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2020-11-10 10:30:51 +01:00
parent 2b92c25130
commit 677baf22d2
20 changed files with 426 additions and 405 deletions
@@ -27,7 +27,7 @@ import (
"sort" "sort"
"strconv" "strconv"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/devices"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@@ -36,7 +36,7 @@ import (
// wildcard-type support. It's effectively the "match" portion of a metadata // wildcard-type support. It's effectively the "match" portion of a metadata
// rule, for the purposes of our emulation. // rule, for the purposes of our emulation.
type deviceMeta struct { type deviceMeta struct {
node configs.DeviceType node devices.DeviceType
major int64 major int64
minor int64 minor int64
} }
@@ -44,12 +44,12 @@ type deviceMeta struct {
// deviceRule is effectively the tuple (deviceMeta, DevicePermissions). // deviceRule is effectively the tuple (deviceMeta, DevicePermissions).
type deviceRule struct { type deviceRule struct {
meta deviceMeta meta deviceMeta
perms configs.DevicePermissions perms devices.DevicePermissions
} }
// deviceRules is a mapping of device metadata rules to the associated // deviceRules is a mapping of device metadata rules to the associated
// permissions in the ruleset. // permissions in the ruleset.
type deviceRules map[deviceMeta]configs.DevicePermissions type deviceRules map[deviceMeta]devices.DevicePermissions
func (r deviceRules) orderedEntries() []deviceRule { func (r deviceRules) orderedEntries() []deviceRule {
var rules []deviceRule var rules []deviceRule
@@ -103,9 +103,9 @@ func parseLine(line string) (*deviceRule, error) {
// TODO: Double-check that the entire file is "a *:* rwm". // TODO: Double-check that the entire file is "a *:* rwm".
return nil, nil return nil, nil
case "b": case "b":
rule.meta.node = configs.BlockDevice rule.meta.node = devices.BlockDevice
case "c": case "c":
rule.meta.node = configs.CharDevice rule.meta.node = devices.CharDevice
default: default:
// Should never happen! // Should never happen!
return nil, errors.Errorf("unknown device type %q", node) return nil, errors.Errorf("unknown device type %q", node)
@@ -113,7 +113,7 @@ func parseLine(line string) (*deviceRule, error) {
// Parse the major number. // Parse the major number.
if major == "*" { if major == "*" {
rule.meta.major = configs.Wildcard rule.meta.major = devices.Wildcard
} else { } else {
val, err := strconv.ParseUint(major, 10, 32) val, err := strconv.ParseUint(major, 10, 32)
if err != nil { if err != nil {
@@ -124,7 +124,7 @@ func parseLine(line string) (*deviceRule, error) {
// Parse the minor number. // Parse the minor number.
if minor == "*" { if minor == "*" {
rule.meta.minor = configs.Wildcard rule.meta.minor = devices.Wildcard
} else { } else {
val, err := strconv.ParseUint(minor, 10, 32) val, err := strconv.ParseUint(minor, 10, 32)
if err != nil { if err != nil {
@@ -134,7 +134,7 @@ func parseLine(line string) (*deviceRule, error) {
} }
// Parse the access permissions. // Parse the access permissions.
rule.perms = configs.DevicePermissions(perms) rule.perms = devices.DevicePermissions(perms)
if !rule.perms.IsValid() || rule.perms.IsEmpty() { if !rule.perms.IsValid() || rule.perms.IsEmpty() {
// Should never happen! // Should never happen!
return nil, errors.Errorf("parse access mode: contained unknown modes or is empty: %q", perms) return nil, errors.Errorf("parse access mode: contained unknown modes or is empty: %q", perms)
@@ -144,7 +144,7 @@ func parseLine(line string) (*deviceRule, error) {
func (e *Emulator) addRule(rule deviceRule) error { func (e *Emulator) addRule(rule deviceRule) error {
if e.rules == nil { if e.rules == nil {
e.rules = make(map[deviceMeta]configs.DevicePermissions) e.rules = make(map[deviceMeta]devices.DevicePermissions)
} }
// Merge with any pre-existing permissions. // Merge with any pre-existing permissions.
@@ -169,9 +169,9 @@ func (e *Emulator) rmRule(rule deviceRule) error {
// to mention it'd be really slow (the kernel side is implemented as a // to mention it'd be really slow (the kernel side is implemented as a
// linked-list of exceptions). // linked-list of exceptions).
for _, partialMeta := range []deviceMeta{ for _, partialMeta := range []deviceMeta{
{node: rule.meta.node, major: configs.Wildcard, minor: rule.meta.minor}, {node: rule.meta.node, major: devices.Wildcard, minor: rule.meta.minor},
{node: rule.meta.node, major: rule.meta.major, minor: configs.Wildcard}, {node: rule.meta.node, major: rule.meta.major, minor: devices.Wildcard},
{node: rule.meta.node, major: configs.Wildcard, minor: configs.Wildcard}, {node: rule.meta.node, major: devices.Wildcard, minor: devices.Wildcard},
} { } {
// This wildcard rule is equivalent to the requested rule, so skip it. // This wildcard rule is equivalent to the requested rule, so skip it.
if rule.meta == partialMeta { if rule.meta == partialMeta {
@@ -202,7 +202,7 @@ func (e *Emulator) rmRule(rule deviceRule) error {
func (e *Emulator) allow(rule *deviceRule) error { func (e *Emulator) allow(rule *deviceRule) error {
// This cgroup is configured as a black-list. Reset the entire emulator, // This cgroup is configured as a black-list. Reset the entire emulator,
// and put is into black-list mode. // and put is into black-list mode.
if rule == nil || rule.meta.node == configs.WildcardDevice { if rule == nil || rule.meta.node == devices.WildcardDevice {
*e = Emulator{ *e = Emulator{
defaultAllow: true, defaultAllow: true,
rules: nil, rules: nil,
@@ -222,7 +222,7 @@ func (e *Emulator) allow(rule *deviceRule) error {
func (e *Emulator) deny(rule *deviceRule) error { func (e *Emulator) deny(rule *deviceRule) error {
// This cgroup is configured as a white-list. Reset the entire emulator, // This cgroup is configured as a white-list. Reset the entire emulator,
// and put is into white-list mode. // and put is into white-list mode.
if rule == nil || rule.meta.node == configs.WildcardDevice { if rule == nil || rule.meta.node == devices.WildcardDevice {
*e = Emulator{ *e = Emulator{
defaultAllow: false, defaultAllow: false,
rules: nil, rules: nil,
@@ -239,7 +239,7 @@ func (e *Emulator) deny(rule *deviceRule) error {
return err return err
} }
func (e *Emulator) Apply(rule configs.DeviceRule) error { func (e *Emulator) Apply(rule devices.DeviceRule) error {
if !rule.Type.CanCgroup() { if !rule.Type.CanCgroup() {
return errors.Errorf("cannot add rule [%#v] with non-cgroup type %q", rule, rule.Type) return errors.Errorf("cannot add rule [%#v] with non-cgroup type %q", rule, rule.Type)
} }
@@ -252,7 +252,7 @@ func (e *Emulator) Apply(rule configs.DeviceRule) error {
}, },
perms: rule.Permissions, perms: rule.Permissions,
} }
if innerRule.meta.node == configs.WildcardDevice { if innerRule.meta.node == devices.WildcardDevice {
innerRule = nil innerRule = nil
} }
@@ -307,8 +307,8 @@ func EmulatorFromList(list io.Reader) (*Emulator, error) {
// This function is the sole reason for all of Emulator -- to allow us // This function is the sole reason for all of Emulator -- to allow us
// to figure out how to update a containers' cgroups without causing spurrious // to figure out how to update a containers' cgroups without causing spurrious
// device errors (if possible). // device errors (if possible).
func (source *Emulator) Transition(target *Emulator) ([]*configs.DeviceRule, error) { func (source *Emulator) Transition(target *Emulator) ([]*devices.DeviceRule, error) {
var transitionRules []*configs.DeviceRule var transitionRules []*devices.DeviceRule
oldRules := source.rules oldRules := source.rules
// If the default policy doesn't match, we need to include a "disruptive" // If the default policy doesn't match, we need to include a "disruptive"
@@ -319,11 +319,11 @@ func (source *Emulator) Transition(target *Emulator) ([]*configs.DeviceRule, err
// deny rules are in place in a black-list cgroup. Thus if the source is a // deny rules are in place in a black-list cgroup. Thus if the source is a
// black-list we also have to include a disruptive rule. // black-list we also have to include a disruptive rule.
if source.IsBlacklist() || source.defaultAllow != target.defaultAllow { if source.IsBlacklist() || source.defaultAllow != target.defaultAllow {
transitionRules = append(transitionRules, &configs.DeviceRule{ transitionRules = append(transitionRules, &devices.DeviceRule{
Type: 'a', Type: 'a',
Major: -1, Major: -1,
Minor: -1, Minor: -1,
Permissions: configs.DevicePermissions("rwm"), Permissions: devices.DevicePermissions("rwm"),
Allow: target.defaultAllow, Allow: target.defaultAllow,
}) })
// The old rules are only relevant if we aren't starting out with a // The old rules are only relevant if we aren't starting out with a
@@ -342,7 +342,7 @@ func (source *Emulator) Transition(target *Emulator) ([]*configs.DeviceRule, err
newPerms := target.rules[meta] newPerms := target.rules[meta]
droppedPerms := oldPerms.Difference(newPerms) droppedPerms := oldPerms.Difference(newPerms)
if !droppedPerms.IsEmpty() { if !droppedPerms.IsEmpty() {
transitionRules = append(transitionRules, &configs.DeviceRule{ transitionRules = append(transitionRules, &devices.DeviceRule{
Type: meta.node, Type: meta.node,
Major: meta.major, Major: meta.major,
Minor: meta.minor, Minor: meta.minor,
@@ -360,7 +360,7 @@ func (source *Emulator) Transition(target *Emulator) ([]*configs.DeviceRule, err
oldPerms := oldRules[meta] oldPerms := oldRules[meta]
gainedPerms := newPerms.Difference(oldPerms) gainedPerms := newPerms.Difference(oldPerms)
if !gainedPerms.IsEmpty() { if !gainedPerms.IsEmpty() {
transitionRules = append(transitionRules, &configs.DeviceRule{ transitionRules = append(transitionRules, &devices.DeviceRule{
Type: meta.node, Type: meta.node,
Major: meta.major, Major: meta.major,
Minor: meta.minor, Minor: meta.minor,
File diff suppressed because it is too large Load Diff
@@ -11,7 +11,7 @@ import (
"strconv" "strconv"
"github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/asm"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/devices"
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@@ -22,7 +22,7 @@ const (
) )
// DeviceFilter returns eBPF device filter program and its license string // DeviceFilter returns eBPF device filter program and its license string
func DeviceFilter(devices []*configs.DeviceRule) (asm.Instructions, string, error) { func DeviceFilter(devices []*devices.DeviceRule) (asm.Instructions, string, error) {
p := &program{} p := &program{}
p.init() p.init()
for i := len(devices) - 1; i >= 0; i-- { for i := len(devices) - 1; i >= 0; i-- {
@@ -68,7 +68,7 @@ func (p *program) init() {
} }
// appendDevice needs to be called from the last element of OCI linux.resources.devices to the head element. // appendDevice needs to be called from the last element of OCI linux.resources.devices to the head element.
func (p *program) appendDevice(dev *configs.DeviceRule) error { func (p *program) appendDevice(dev *devices.DeviceRule) error {
if p.blockID < 0 { if p.blockID < 0 {
return errors.New("the program is finalized") return errors.New("the program is finalized")
} }
@@ -4,7 +4,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/devices"
"github.com/opencontainers/runc/libcontainer/specconv" "github.com/opencontainers/runc/libcontainer/specconv"
) )
@@ -20,7 +20,7 @@ func hash(s, comm string) string {
return strings.Join(res, "\n") return strings.Join(res, "\n")
} }
func testDeviceFilter(t testing.TB, devices []*configs.DeviceRule, expectedStr string) { func testDeviceFilter(t testing.TB, devices []*devices.DeviceRule, expectedStr string) {
insts, _, err := DeviceFilter(devices) insts, _, err := DeviceFilter(devices)
if err != nil { if err != nil {
t.Fatalf("%s: %v (devices: %+v)", t.Name(), err, devices) t.Fatalf("%s: %v (devices: %+v)", t.Name(), err, devices)
@@ -137,7 +137,7 @@ block-11:
62: Mov32Imm dst: r0 imm: 0 62: Mov32Imm dst: r0 imm: 0
63: Exit 63: Exit
` `
var devices []*configs.DeviceRule var devices []*devices.DeviceRule
for _, device := range specconv.AllowedDevices { for _, device := range specconv.AllowedDevices {
devices = append(devices, &device.DeviceRule) devices = append(devices, &device.DeviceRule)
} }
@@ -145,7 +145,7 @@ block-11:
} }
func TestDeviceFilter_Privileged(t *testing.T) { func TestDeviceFilter_Privileged(t *testing.T) {
devices := []*configs.DeviceRule{ devices := []*devices.DeviceRule{
{ {
Type: 'a', Type: 'a',
Major: -1, Major: -1,
@@ -172,7 +172,7 @@ block-0:
} }
func TestDeviceFilter_PrivilegedExceptSingleDevice(t *testing.T) { func TestDeviceFilter_PrivilegedExceptSingleDevice(t *testing.T) {
devices := []*configs.DeviceRule{ devices := []*devices.DeviceRule{
{ {
Type: 'a', Type: 'a',
Major: -1, Major: -1,
@@ -212,7 +212,7 @@ block-1:
} }
func TestDeviceFilter_Weird(t *testing.T) { func TestDeviceFilter_Weird(t *testing.T) {
devices := []*configs.DeviceRule{ devices := []*devices.DeviceRule{
{ {
Type: 'b', Type: 'b',
Major: 8, Major: 8,
+6 -5
View File
@@ -8,9 +8,10 @@ import (
"reflect" "reflect"
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/devices" cgroupdevices "github.com/opencontainers/runc/libcontainer/cgroups/devices"
"github.com/opencontainers/runc/libcontainer/cgroups/fscommon" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runc/libcontainer/system"
) )
@@ -34,17 +35,17 @@ func (s *DevicesGroup) Apply(path string, d *cgroupData) error {
return join(path, d.pid) return join(path, d.pid)
} }
func loadEmulator(path string) (*devices.Emulator, error) { func loadEmulator(path string) (*cgroupdevices.Emulator, error) {
list, err := fscommon.ReadFile(path, "devices.list") list, err := fscommon.ReadFile(path, "devices.list")
if err != nil { if err != nil {
return nil, err return nil, err
} }
return devices.EmulatorFromList(bytes.NewBufferString(list)) return cgroupdevices.EmulatorFromList(bytes.NewBufferString(list))
} }
func buildEmulator(rules []*configs.DeviceRule) (*devices.Emulator, error) { func buildEmulator(rules []*devices.DeviceRule) (*cgroupdevices.Emulator, error) {
// This defaults to a white-list -- which is what we want! // This defaults to a white-list -- which is what we want!
emu := &devices.Emulator{} emu := &cgroupdevices.Emulator{}
for _, rule := range rules { for _, rule := range rules {
if err := emu.Apply(*rule); err != nil { if err := emu.Apply(*rule); err != nil {
return nil, err return nil, err
+6 -6
View File
@@ -6,7 +6,7 @@ import (
"testing" "testing"
"github.com/opencontainers/runc/libcontainer/cgroups/fscommon" "github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/devices"
) )
func TestDevicesSetAllow(t *testing.T) { func TestDevicesSetAllow(t *testing.T) {
@@ -19,18 +19,18 @@ func TestDevicesSetAllow(t *testing.T) {
"devices.list": "a *:* rwm", "devices.list": "a *:* rwm",
}) })
helper.CgroupData.config.Resources.Devices = []*configs.DeviceRule{ helper.CgroupData.config.Resources.Devices = []*devices.DeviceRule{
{ {
Type: configs.CharDevice, Type: devices.CharDevice,
Major: 1, Major: 1,
Minor: 5, Minor: 5,
Permissions: configs.DevicePermissions("rwm"), Permissions: devices.DevicePermissions("rwm"),
Allow: true, Allow: true,
}, },
} }
devices := &DevicesGroup{testingSkipFinalCheck: true} d := &DevicesGroup{testingSkipFinalCheck: true}
if err := devices.Set(helper.CgroupPath, helper.CgroupData.config); err != nil { if err := d.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
t.Fatal(err) t.Fatal(err)
} }
+2 -1
View File
@@ -6,11 +6,12 @@ import (
"github.com/opencontainers/runc/libcontainer/cgroups/ebpf" "github.com/opencontainers/runc/libcontainer/cgroups/ebpf"
"github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter" "github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
func isRWM(perms configs.DevicePermissions) bool { func isRWM(perms devices.DevicePermissions) bool {
var r, w, m bool var r, w, m bool
for _, perm := range perms { for _, perm := range perms {
switch perm { switch perm {
+18 -17
View File
@@ -13,8 +13,9 @@ import (
systemdDbus "github.com/coreos/go-systemd/v22/dbus" systemdDbus "github.com/coreos/go-systemd/v22/dbus"
dbus "github.com/godbus/dbus/v5" dbus "github.com/godbus/dbus/v5"
"github.com/opencontainers/runc/libcontainer/cgroups/devices" cgroupdevices "github.com/opencontainers/runc/libcontainer/cgroups/devices"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@@ -88,11 +89,11 @@ func ExpandSlice(slice string) (string, error) {
return path, nil return path, nil
} }
func groupPrefix(ruleType configs.DeviceType) (string, error) { func groupPrefix(ruleType devices.DeviceType) (string, error) {
switch ruleType { switch ruleType {
case configs.BlockDevice: case devices.BlockDevice:
return "block-", nil return "block-", nil
case configs.CharDevice: case devices.CharDevice:
return "char-", nil return "char-", nil
default: default:
return "", errors.Errorf("device type %v has no group prefix", ruleType) return "", errors.Errorf("device type %v has no group prefix", ruleType)
@@ -103,7 +104,7 @@ func groupPrefix(ruleType configs.DeviceType) (string, error) {
// /proc/devices) with the type prefixed as required for DeviceAllow, for a // /proc/devices) with the type prefixed as required for DeviceAllow, for a
// given (type, major) combination. If more than one device group exists, an // given (type, major) combination. If more than one device group exists, an
// arbitrary one is chosen. // arbitrary one is chosen.
func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, error) { func findDeviceGroup(ruleType devices.DeviceType, ruleMajor int64) (string, error) {
fh, err := os.Open("/proc/devices") fh, err := os.Open("/proc/devices")
if err != nil { if err != nil {
return "", err return "", err
@@ -116,7 +117,7 @@ func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, erro
} }
scanner := bufio.NewScanner(fh) scanner := bufio.NewScanner(fh)
var currentType configs.DeviceType var currentType devices.DeviceType
for scanner.Scan() { for scanner.Scan() {
// We need to strip spaces because the first number is column-aligned. // We need to strip spaces because the first number is column-aligned.
line := strings.TrimSpace(scanner.Text()) line := strings.TrimSpace(scanner.Text())
@@ -124,10 +125,10 @@ func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, erro
// Handle the "header" lines. // Handle the "header" lines.
switch line { switch line {
case "Block devices:": case "Block devices:":
currentType = configs.BlockDevice currentType = devices.BlockDevice
continue continue
case "Character devices:": case "Character devices:":
currentType = configs.CharDevice currentType = devices.CharDevice
continue continue
case "": case "":
continue continue
@@ -163,7 +164,7 @@ func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, erro
// generateDeviceProperties takes the configured device rules and generates a // generateDeviceProperties takes the configured device rules and generates a
// corresponding set of systemd properties to configure the devices correctly. // corresponding set of systemd properties to configure the devices correctly.
func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Property, error) { func generateDeviceProperties(rules []*devices.DeviceRule) ([]systemdDbus.Property, error) {
// DeviceAllow is the type "a(ss)" which means we need a temporary struct // DeviceAllow is the type "a(ss)" which means we need a temporary struct
// to represent it in Go. // to represent it in Go.
type deviceAllowEntry struct { type deviceAllowEntry struct {
@@ -179,7 +180,7 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
} }
// Figure out the set of rules. // Figure out the set of rules.
configEmu := &devices.Emulator{} configEmu := &cgroupdevices.Emulator{}
for _, rule := range rules { for _, rule := range rules {
if err := configEmu.Apply(*rule); err != nil { if err := configEmu.Apply(*rule); err != nil {
return nil, errors.Wrap(err, "apply rule for systemd") return nil, errors.Wrap(err, "apply rule for systemd")
@@ -206,7 +207,7 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
// Now generate the set of rules we actually need to apply. Unlike the // Now generate the set of rules we actually need to apply. Unlike the
// normal devices cgroup, in "strict" mode systemd defaults to a deny-all // normal devices cgroup, in "strict" mode systemd defaults to a deny-all
// whitelist which is the default for devices.Emulator. // whitelist which is the default for devices.Emulator.
baseEmu := &devices.Emulator{} baseEmu := &cgroupdevices.Emulator{}
finalRules, err := baseEmu.Transition(configEmu) finalRules, err := baseEmu.Transition(configEmu)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "get simplified rules for systemd") return nil, errors.Wrap(err, "get simplified rules for systemd")
@@ -218,7 +219,7 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
return nil, errors.Errorf("[internal error] cannot add deny rule to systemd DeviceAllow list: %v", *rule) return nil, errors.Errorf("[internal error] cannot add deny rule to systemd DeviceAllow list: %v", *rule)
} }
switch rule.Type { switch rule.Type {
case configs.BlockDevice, configs.CharDevice: case devices.BlockDevice, devices.CharDevice:
default: default:
// Should never happen. // Should never happen.
return nil, errors.Errorf("invalid device type for DeviceAllow: %v", rule.Type) return nil, errors.Errorf("invalid device type for DeviceAllow: %v", rule.Type)
@@ -250,9 +251,9 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
// so we'll give a warning in that case (note that the fallback code // so we'll give a warning in that case (note that the fallback code
// will insert any rules systemd couldn't handle). What amazing fun. // will insert any rules systemd couldn't handle). What amazing fun.
if rule.Major == configs.Wildcard { if rule.Major == devices.Wildcard {
// "_ *:n _" rules aren't supported by systemd. // "_ *:n _" rules aren't supported by systemd.
if rule.Minor != configs.Wildcard { if rule.Minor != devices.Wildcard {
logrus.Warnf("systemd doesn't support '*:n' device rules -- temporarily ignoring rule: %v", *rule) logrus.Warnf("systemd doesn't support '*:n' device rules -- temporarily ignoring rule: %v", *rule)
continue continue
} }
@@ -263,7 +264,7 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
return nil, err return nil, err
} }
entry.Path = prefix + "*" entry.Path = prefix + "*"
} else if rule.Minor == configs.Wildcard { } else if rule.Minor == devices.Wildcard {
// "_ n:* _" rules require a device group from /proc/devices. // "_ n:* _" rules require a device group from /proc/devices.
group, err := findDeviceGroup(rule.Type, rule.Major) group, err := findDeviceGroup(rule.Type, rule.Major)
if err != nil { if err != nil {
@@ -278,9 +279,9 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
} else { } else {
// "_ n:m _" rules are just a path in /dev/{block,char}/. // "_ n:m _" rules are just a path in /dev/{block,char}/.
switch rule.Type { switch rule.Type {
case configs.BlockDevice: case devices.BlockDevice:
entry.Path = fmt.Sprintf("/dev/block/%d:%d", rule.Major, rule.Minor) entry.Path = fmt.Sprintf("/dev/block/%d:%d", rule.Major, rule.Minor)
case configs.CharDevice: case devices.CharDevice:
entry.Path = fmt.Sprintf("/dev/char/%d:%d", rule.Major, rule.Minor) entry.Path = fmt.Sprintf("/dev/char/%d:%d", rule.Major, rule.Minor)
} }
} }
+2 -1
View File
@@ -2,6 +2,7 @@ package configs
import ( import (
systemdDbus "github.com/coreos/go-systemd/v22/dbus" systemdDbus "github.com/coreos/go-systemd/v22/dbus"
"github.com/opencontainers/runc/libcontainer/devices"
) )
type FreezerState string type FreezerState string
@@ -42,7 +43,7 @@ type Cgroup struct {
type Resources struct { type Resources struct {
// Devices is the set of access rules for devices in the container. // Devices is the set of access rules for devices in the container.
Devices []*DeviceRule `json:"devices"` Devices []*devices.DeviceRule `json:"devices"`
// Memory limit (in bytes) // Memory limit (in bytes)
Memory int64 `json:"memory"` Memory int64 `json:"memory"`
+2 -1
View File
@@ -7,6 +7,7 @@ import (
"os/exec" "os/exec"
"time" "time"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@@ -107,7 +108,7 @@ type Config struct {
Mounts []*Mount `json:"mounts"` Mounts []*Mount `json:"mounts"`
// The device nodes that should be automatically created within the container upon container start. Note, make sure that the node is marked as allowed in the cgroup as well! // The device nodes that should be automatically created within the container upon container start. Note, make sure that the node is marked as allowed in the cgroup as well!
Devices []*Device `json:"devices"` Devices []*devices.Device `json:"devices"`
MountLabel string `json:"mount_label"` MountLabel string `json:"mount_label"`
+17
View File
@@ -0,0 +1,17 @@
package configs
import "github.com/opencontainers/runc/libcontainer/devices"
type (
// Deprecated: use libcontainer/devices.Device
Device = devices.Device
// Deprecated: use libcontainer/devices.DeviceRule
DeviceRule = devices.DeviceRule
// Deprecated: use libcontainer/devices.DeviceType
DeviceType = devices.DeviceType
// Deprecated: use libcontainer/devices.DevicePermissions
DevicePermissions = devices.DevicePermissions
)
@@ -1,4 +1,4 @@
package configs package devices
import ( import (
"fmt" "fmt"
@@ -1,6 +1,6 @@
// +build !windows // +build !windows
package configs package devices
import ( import (
"errors" "errors"
@@ -1,4 +1,4 @@
package configs package devices
func (d *DeviceRule) Mkdev() (uint64, error) { func (d *DeviceRule) Mkdev() (uint64, error) {
return 0, nil return 0, nil
+12 -13
View File
@@ -6,7 +6,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/opencontainers/runc/libcontainer/configs"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@@ -23,7 +22,7 @@ var (
// Given the path to a device and its cgroup_permissions(which cannot be easily queried) look up the // Given the path to a device and its cgroup_permissions(which cannot be easily queried) look up the
// information about a linux device and return that information as a Device struct. // information about a linux device and return that information as a Device struct.
func DeviceFromPath(path, permissions string) (*configs.Device, error) { func DeviceFromPath(path, permissions string) (*Device, error) {
var stat unix.Stat_t var stat unix.Stat_t
err := unixLstat(path, &stat) err := unixLstat(path, &stat)
if err != nil { if err != nil {
@@ -31,7 +30,7 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) {
} }
var ( var (
devType configs.DeviceType devType DeviceType
mode = stat.Mode mode = stat.Mode
devNumber = uint64(stat.Rdev) devNumber = uint64(stat.Rdev)
major = unix.Major(devNumber) major = unix.Major(devNumber)
@@ -39,20 +38,20 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) {
) )
switch mode & unix.S_IFMT { switch mode & unix.S_IFMT {
case unix.S_IFBLK: case unix.S_IFBLK:
devType = configs.BlockDevice devType = BlockDevice
case unix.S_IFCHR: case unix.S_IFCHR:
devType = configs.CharDevice devType = CharDevice
case unix.S_IFIFO: case unix.S_IFIFO:
devType = configs.FifoDevice devType = FifoDevice
default: default:
return nil, ErrNotADevice return nil, ErrNotADevice
} }
return &configs.Device{ return &Device{
DeviceRule: configs.DeviceRule{ DeviceRule: DeviceRule{
Type: devType, Type: devType,
Major: int64(major), Major: int64(major),
Minor: int64(minor), Minor: int64(minor),
Permissions: configs.DevicePermissions(permissions), Permissions: DevicePermissions(permissions),
}, },
Path: path, Path: path,
FileMode: os.FileMode(mode), FileMode: os.FileMode(mode),
@@ -62,18 +61,18 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) {
} }
// HostDevices returns all devices that can be found under /dev directory. // HostDevices returns all devices that can be found under /dev directory.
func HostDevices() ([]*configs.Device, error) { func HostDevices() ([]*Device, error) {
return GetDevices("/dev") return GetDevices("/dev")
} }
// GetDevices recursively traverses a directory specified by path // GetDevices recursively traverses a directory specified by path
// and returns all devices found there. // and returns all devices found there.
func GetDevices(path string) ([]*configs.Device, error) { func GetDevices(path string) ([]*Device, error) {
files, err := ioutilReadDir(path) files, err := ioutilReadDir(path)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var out []*configs.Device var out []*Device
for _, f := range files { for _, f := range files {
switch { switch {
case f.IsDir(): case f.IsDir():
@@ -104,7 +103,7 @@ func GetDevices(path string) ([]*configs.Device, error) {
} }
return nil, err return nil, err
} }
if device.Type == configs.FifoDevice { if device.Type == FifoDevice {
continue continue
} }
out = append(out, device) out = append(out, device)
+3 -4
View File
@@ -6,7 +6,6 @@ import (
"os" "os"
"testing" "testing"
"github.com/opencontainers/runc/libcontainer/configs"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@@ -86,11 +85,11 @@ func TestHostDevicesAllValid(t *testing.T) {
// Devices should only have file modes that correspond to their type. // Devices should only have file modes that correspond to their type.
var expectedType os.FileMode var expectedType os.FileMode
switch device.Type { switch device.Type {
case configs.BlockDevice: case BlockDevice:
expectedType = unix.S_IFBLK expectedType = unix.S_IFBLK
case configs.CharDevice: case CharDevice:
expectedType = unix.S_IFCHR expectedType = unix.S_IFCHR
case configs.FifoDevice: case FifoDevice:
t.Logf("fifo devices shouldn't show up from HostDevices") t.Logf("fifo devices shouldn't show up from HostDevices")
fallthrough fallthrough
default: default:
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"strconv" "strconv"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/opencontainers/runc/libcontainer/specconv" "github.com/opencontainers/runc/libcontainer/specconv"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@@ -30,7 +30,7 @@ type tParam struct {
// it uses a network strategy of just setting a loopback interface // it uses a network strategy of just setting a loopback interface
// and the default setup for devices // and the default setup for devices
func newTemplateConfig(p *tParam) *configs.Config { func newTemplateConfig(p *tParam) *configs.Config {
var allowedDevices []*configs.DeviceRule var allowedDevices []*devices.DeviceRule
for _, device := range specconv.AllowedDevices { for _, device := range specconv.AllowedDevices {
allowedDevices = append(allowedDevices, &device.DeviceRule) allowedDevices = append(allowedDevices, &device.DeviceRule)
} }
+7 -7
View File
@@ -18,12 +18,12 @@ import (
"github.com/mrunalp/fileutils" "github.com/mrunalp/fileutils"
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/opencontainers/runc/libcontainer/system" "github.com/opencontainers/runc/libcontainer/system"
"github.com/opencontainers/runc/libcontainer/utils" "github.com/opencontainers/runc/libcontainer/utils"
libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux/label" "github.com/opencontainers/selinux/go-selinux/label"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@@ -614,7 +614,7 @@ func createDevices(config *configs.Config) error {
return nil return nil
} }
func bindMountDeviceNode(dest string, node *configs.Device) error { func bindMountDeviceNode(dest string, node *devices.Device) error {
f, err := os.Create(dest) f, err := os.Create(dest)
if err != nil && !os.IsExist(err) { if err != nil && !os.IsExist(err) {
return err return err
@@ -626,7 +626,7 @@ func bindMountDeviceNode(dest string, node *configs.Device) error {
} }
// Creates the device node in the rootfs of the container. // Creates the device node in the rootfs of the container.
func createDeviceNode(rootfs string, node *configs.Device, bind bool) error { func createDeviceNode(rootfs string, node *devices.Device, bind bool) error {
if node.Path == "" { if node.Path == "" {
// The node only exists for cgroup reasons, ignore it here. // The node only exists for cgroup reasons, ignore it here.
return nil return nil
@@ -649,14 +649,14 @@ func createDeviceNode(rootfs string, node *configs.Device, bind bool) error {
return nil return nil
} }
func mknodDevice(dest string, node *configs.Device) error { func mknodDevice(dest string, node *devices.Device) error {
fileMode := node.FileMode fileMode := node.FileMode
switch node.Type { switch node.Type {
case configs.BlockDevice: case devices.BlockDevice:
fileMode |= unix.S_IFBLK fileMode |= unix.S_IFBLK
case configs.CharDevice: case devices.CharDevice:
fileMode |= unix.S_IFCHR fileMode |= unix.S_IFCHR
case configs.FifoDevice: case devices.FifoDevice:
fileMode |= unix.S_IFIFO fileMode |= unix.S_IFIFO
default: default:
return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path) return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path)
+44 -43
View File
@@ -17,6 +17,7 @@ import (
dbus "github.com/godbus/dbus/v5" dbus "github.com/godbus/dbus/v5"
"github.com/opencontainers/runc/libcontainer/cgroups" "github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/opencontainers/runc/libcontainer/seccomp" "github.com/opencontainers/runc/libcontainer/seccomp"
libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils" libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
@@ -61,22 +62,22 @@ var mountPropagationMapping = map[string]int{
// //
// ... unfortunately I'm too scared to change this now because who knows how // ... unfortunately I'm too scared to change this now because who knows how
// many people depend on this (incorrect and arguably insecure) behaviour. // many people depend on this (incorrect and arguably insecure) behaviour.
var AllowedDevices = []*configs.Device{ var AllowedDevices = []*devices.Device{
// allow mknod for any device // allow mknod for any device
{ {
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.CharDevice, Type: devices.CharDevice,
Major: configs.Wildcard, Major: devices.Wildcard,
Minor: configs.Wildcard, Minor: devices.Wildcard,
Permissions: "m", Permissions: "m",
Allow: true, Allow: true,
}, },
}, },
{ {
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.BlockDevice, Type: devices.BlockDevice,
Major: configs.Wildcard, Major: devices.Wildcard,
Minor: configs.Wildcard, Minor: devices.Wildcard,
Permissions: "m", Permissions: "m",
Allow: true, Allow: true,
}, },
@@ -86,8 +87,8 @@ var AllowedDevices = []*configs.Device{
FileMode: 0666, FileMode: 0666,
Uid: 0, Uid: 0,
Gid: 0, Gid: 0,
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.CharDevice, Type: devices.CharDevice,
Major: 1, Major: 1,
Minor: 3, Minor: 3,
Permissions: "rwm", Permissions: "rwm",
@@ -99,8 +100,8 @@ var AllowedDevices = []*configs.Device{
FileMode: 0666, FileMode: 0666,
Uid: 0, Uid: 0,
Gid: 0, Gid: 0,
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.CharDevice, Type: devices.CharDevice,
Major: 1, Major: 1,
Minor: 8, Minor: 8,
Permissions: "rwm", Permissions: "rwm",
@@ -112,8 +113,8 @@ var AllowedDevices = []*configs.Device{
FileMode: 0666, FileMode: 0666,
Uid: 0, Uid: 0,
Gid: 0, Gid: 0,
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.CharDevice, Type: devices.CharDevice,
Major: 1, Major: 1,
Minor: 7, Minor: 7,
Permissions: "rwm", Permissions: "rwm",
@@ -125,8 +126,8 @@ var AllowedDevices = []*configs.Device{
FileMode: 0666, FileMode: 0666,
Uid: 0, Uid: 0,
Gid: 0, Gid: 0,
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.CharDevice, Type: devices.CharDevice,
Major: 5, Major: 5,
Minor: 0, Minor: 0,
Permissions: "rwm", Permissions: "rwm",
@@ -138,8 +139,8 @@ var AllowedDevices = []*configs.Device{
FileMode: 0666, FileMode: 0666,
Uid: 0, Uid: 0,
Gid: 0, Gid: 0,
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.CharDevice, Type: devices.CharDevice,
Major: 1, Major: 1,
Minor: 5, Minor: 5,
Permissions: "rwm", Permissions: "rwm",
@@ -151,8 +152,8 @@ var AllowedDevices = []*configs.Device{
FileMode: 0666, FileMode: 0666,
Uid: 0, Uid: 0,
Gid: 0, Gid: 0,
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.CharDevice, Type: devices.CharDevice,
Major: 1, Major: 1,
Minor: 9, Minor: 9,
Permissions: "rwm", Permissions: "rwm",
@@ -161,17 +162,17 @@ var AllowedDevices = []*configs.Device{
}, },
// /dev/pts/ - pts namespaces are "coming soon" // /dev/pts/ - pts namespaces are "coming soon"
{ {
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.CharDevice, Type: devices.CharDevice,
Major: 136, Major: 136,
Minor: configs.Wildcard, Minor: devices.Wildcard,
Permissions: "rwm", Permissions: "rwm",
Allow: true, Allow: true,
}, },
}, },
{ {
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.CharDevice, Type: devices.CharDevice,
Major: 5, Major: 5,
Minor: 2, Minor: 2,
Permissions: "rwm", Permissions: "rwm",
@@ -180,8 +181,8 @@ var AllowedDevices = []*configs.Device{
}, },
// tuntap // tuntap
{ {
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.CharDevice, Type: devices.CharDevice,
Major: 10, Major: 10,
Minor: 200, Minor: 200,
Permissions: "rwm", Permissions: "rwm",
@@ -413,7 +414,7 @@ func initSystemdProps(spec *specs.Spec) ([]systemdDbus.Property, error) {
return sp, nil return sp, nil
} }
func CreateCgroupConfig(opts *CreateOpts, defaultDevs []*configs.Device) (*configs.Cgroup, error) { func CreateCgroupConfig(opts *CreateOpts, defaultDevs []*devices.Device) (*configs.Cgroup, error) {
var ( var (
myCgroupPath string myCgroupPath string
@@ -491,11 +492,11 @@ func CreateCgroupConfig(opts *CreateOpts, defaultDevs []*configs.Device) (*confi
if err != nil { if err != nil {
return nil, err return nil, err
} }
c.Resources.Devices = append(c.Resources.Devices, &configs.DeviceRule{ c.Resources.Devices = append(c.Resources.Devices, &devices.DeviceRule{
Type: dt, Type: dt,
Major: major, Major: major,
Minor: minor, Minor: minor,
Permissions: configs.DevicePermissions(d.Access), Permissions: devices.DevicePermissions(d.Access),
Allow: d.Allow, Allow: d.Allow,
}) })
} }
@@ -634,36 +635,36 @@ func CreateCgroupConfig(opts *CreateOpts, defaultDevs []*configs.Device) (*confi
return c, nil return c, nil
} }
func stringToCgroupDeviceRune(s string) (configs.DeviceType, error) { func stringToCgroupDeviceRune(s string) (devices.DeviceType, error) {
switch s { switch s {
case "a": case "a":
return configs.WildcardDevice, nil return devices.WildcardDevice, nil
case "b": case "b":
return configs.BlockDevice, nil return devices.BlockDevice, nil
case "c": case "c":
return configs.CharDevice, nil return devices.CharDevice, nil
default: default:
return 0, fmt.Errorf("invalid cgroup device type %q", s) return 0, fmt.Errorf("invalid cgroup device type %q", s)
} }
} }
func stringToDeviceRune(s string) (configs.DeviceType, error) { func stringToDeviceRune(s string) (devices.DeviceType, error) {
switch s { switch s {
case "p": case "p":
return configs.FifoDevice, nil return devices.FifoDevice, nil
case "u", "c": case "u", "c":
return configs.CharDevice, nil return devices.CharDevice, nil
case "b": case "b":
return configs.BlockDevice, nil return devices.BlockDevice, nil
default: default:
return 0, fmt.Errorf("invalid device type %q", s) return 0, fmt.Errorf("invalid device type %q", s)
} }
} }
func createDevices(spec *specs.Spec, config *configs.Config) ([]*configs.Device, error) { func createDevices(spec *specs.Spec, config *configs.Config) ([]*devices.Device, error) {
// If a spec device is redundant with a default device, remove that default // If a spec device is redundant with a default device, remove that default
// device (the spec one takes priority). // device (the spec one takes priority).
dedupedAllowDevs := []*configs.Device{} dedupedAllowDevs := []*devices.Device{}
next: next:
for _, ad := range AllowedDevices { for _, ad := range AllowedDevices {
@@ -699,8 +700,8 @@ next:
if d.FileMode != nil { if d.FileMode != nil {
filemode = *d.FileMode filemode = *d.FileMode
} }
device := &configs.Device{ device := &devices.Device{
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: dt, Type: dt,
Major: d.Major, Major: d.Major,
Minor: d.Minor, Minor: d.Minor,
+5 -5
View File
@@ -7,12 +7,12 @@ import (
"strings" "strings"
"testing" "testing"
"golang.org/x/sys/unix"
dbus "github.com/godbus/dbus/v5" dbus "github.com/godbus/dbus/v5"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/configs/validate" "github.com/opencontainers/runc/libcontainer/configs/validate"
"github.com/opencontainers/runc/libcontainer/devices"
"github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-spec/specs-go"
"golang.org/x/sys/unix"
) )
func TestCreateCommandHookTimeout(t *testing.T) { func TestCreateCommandHookTimeout(t *testing.T) {
@@ -722,13 +722,13 @@ func TestCreateDevices(t *testing.T) {
// Verify that createDevices() deduplicated the /dev/tty entry in the config // Verify that createDevices() deduplicated the /dev/tty entry in the config
for _, configDev := range conf.Devices { for _, configDev := range conf.Devices {
if configDev.Path == "/dev/tty" { if configDev.Path == "/dev/tty" {
wantDev := &configs.Device{ wantDev := &devices.Device{
Path: "/dev/tty", Path: "/dev/tty",
FileMode: 0666, FileMode: 0666,
Uid: 1000, Uid: 1000,
Gid: 1000, Gid: 1000,
DeviceRule: configs.DeviceRule{ DeviceRule: devices.DeviceRule{
Type: configs.CharDevice, Type: devices.CharDevice,
Major: 5, Major: 5,
Minor: 0, Minor: 0,
}, },