Repository: opencontainers/runc
Pull request #2679 (2020-11-10 09:33:02)

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.


crosbymichael: +1, I'm good with the refactoring
thaJeztah: oh! forgot to move it out of draft; wanted to be sure it didn't explode; moving to "ready for review"
thaJeztah: @kolyshkin LGTY?
thaJeztah: @kolyshkin @AkihiroSuda @crosbymichael updated; PTAL

Changed:

- `devices.DevicePermissions` -> `devices.Permissions`
- `devices.DeviceType` -> `devices.Type`
- `devices.DeviceRule` -> `devices.Rule`

thaJeztah: Hm.. some issue with the SUSE repositories?


```
Step 12/23 : RUN echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Unstable/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list     && wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/Debian_Unstable/Release.key -O- | sudo apt-key add -     && apt-get update     && apt-get install -y --no-install-recommends skopeo     && rm -rf /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list     && apt-get clean     && rm -rf /var/cache/apt /var/lib/apt/lists/*;
 ---> Running in 7b59184181e1
Warning: apt-key output should not be parsed (stdout is not a terminal)
https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Unstable/Release.key:
2020-11-19 11:08:33 ERROR 404: Not Found.
gpg: no valid OpenPGP data found.
The command '/bin/sh -c echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Unstable/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list     && wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/Debian_Unstable/Release.key -O- | sudo apt-key add -     && apt-get update     && apt-get install -y --no-install-recommends skopeo     && rm -rf /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list     && apt-get clean     && rm -rf /var/cache/apt /var/lib/apt/lists/*;' returned a non-zero code: 2
Makefile:62: recipe for target 'runcimage' failed
make: *** [runcimage] Error 2
The command "make clean ci cross" exited with 2.
cache.2
store build cache
```
thaJeztah: let me try if close/open kicks CI here
thaJeztah: Failing again 😞 
```
The command '/bin/sh -c echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/Debian_Unstable/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list     && wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/Debian_Unstable/Release.key -O- | sudo apt-key add -     && apt-get update     && apt-get install -y --no-install-recommends skopeo     && rm -rf /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list     && apt-get clean     && rm -rf /var/cache/apt /var/lib/apt/lists/*;' returned a non-zero code: 2
Makefile:62: recipe for target 'runcimage' failed
make: *** [runcimage] Error 2
The command "make clean ci cross" exited with 2.
cache.2
store build cache
```

@cyphar any ideas? is this an issue with the opensuse package repos?
thaJeztah: Rebased, as CI should now be fixed by https://github.com/opencontainers/runc/pull/2686

@AkihiroSuda @kolyshkin @crosbymichael ptal

Patch:
diff --git a/libcontainer/cgroups/devices/devices_emulator.go b/libcontainer/cgroups/devices/devices_emulator.go
index 6afedbc6..3572a5ea 100644
--- a/libcontainer/cgroups/devices/devices_emulator.go
+++ b/libcontainer/cgroups/devices/devices_emulator.go
@@ -27,29 +27,29 @@ import (
 	"sort"
 	"strconv"
 
-	"github.com/opencontainers/runc/libcontainer/configs"
+	"github.com/opencontainers/runc/libcontainer/devices"
 
 	"github.com/pkg/errors"
 )
 
-// deviceMeta is a DeviceRule without the Allow or Permissions fields, and no
+// deviceMeta is a Rule without the Allow or Permissions fields, and no
 // wildcard-type support. It's effectively the "match" portion of a metadata
 // rule, for the purposes of our emulation.
 type deviceMeta struct {
-	node  configs.DeviceType
+	node  devices.Type
 	major int64
 	minor int64
 }
 
-// deviceRule is effectively the tuple (deviceMeta, DevicePermissions).
+// deviceRule is effectively the tuple (deviceMeta, Permissions).
 type deviceRule struct {
 	meta  deviceMeta
-	perms configs.DevicePermissions
+	perms devices.Permissions
 }
 
 // deviceRules is a mapping of device metadata rules to the associated
 // permissions in the ruleset.
-type deviceRules map[deviceMeta]configs.DevicePermissions
+type deviceRules map[deviceMeta]devices.Permissions
 
 func (r deviceRules) orderedEntries() []deviceRule {
 	var rules []deviceRule
@@ -103,9 +103,9 @@ func parseLine(line string) (*deviceRule, error) {
 		// TODO: Double-check that the entire file is "a *:* rwm".
 		return nil, nil
 	case "b":
-		rule.meta.node = configs.BlockDevice
+		rule.meta.node = devices.BlockDevice
 	case "c":
-		rule.meta.node = configs.CharDevice
+		rule.meta.node = devices.CharDevice
 	default:
 		// Should never happen!
 		return nil, errors.Errorf("unknown device type %q", node)
@@ -113,7 +113,7 @@ func parseLine(line string) (*deviceRule, error) {
 
 	// Parse the major number.
 	if major == "*" {
-		rule.meta.major = configs.Wildcard
+		rule.meta.major = devices.Wildcard
 	} else {
 		val, err := strconv.ParseUint(major, 10, 32)
 		if err != nil {
@@ -124,7 +124,7 @@ func parseLine(line string) (*deviceRule, error) {
 
 	// Parse the minor number.
 	if minor == "*" {
-		rule.meta.minor = configs.Wildcard
+		rule.meta.minor = devices.Wildcard
 	} else {
 		val, err := strconv.ParseUint(minor, 10, 32)
 		if err != nil {
@@ -134,7 +134,7 @@ func parseLine(line string) (*deviceRule, error) {
 	}
 
 	// Parse the access permissions.
-	rule.perms = configs.DevicePermissions(perms)
+	rule.perms = devices.Permissions(perms)
 	if !rule.perms.IsValid() || rule.perms.IsEmpty() {
 		// Should never happen!
 		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 {
 	if e.rules == nil {
-		e.rules = make(map[deviceMeta]configs.DevicePermissions)
+		e.rules = make(map[deviceMeta]devices.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
 	// linked-list of exceptions).
 	for _, partialMeta := range []deviceMeta{
-		{node: rule.meta.node, major: configs.Wildcard, minor: rule.meta.minor},
-		{node: rule.meta.node, major: rule.meta.major, minor: configs.Wildcard},
-		{node: rule.meta.node, major: configs.Wildcard, minor: configs.Wildcard},
+		{node: rule.meta.node, major: devices.Wildcard, minor: rule.meta.minor},
+		{node: rule.meta.node, major: rule.meta.major, minor: devices.Wildcard},
+		{node: rule.meta.node, major: devices.Wildcard, minor: devices.Wildcard},
 	} {
 		// This wildcard rule is equivalent to the requested rule, so skip it.
 		if rule.meta == partialMeta {
@@ -202,7 +202,7 @@ func (e *Emulator) rmRule(rule deviceRule) error {
 func (e *Emulator) allow(rule *deviceRule) error {
 	// This cgroup is configured as a black-list. Reset the entire emulator,
 	// 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{
 			defaultAllow: true,
 			rules:        nil,
@@ -222,7 +222,7 @@ func (e *Emulator) allow(rule *deviceRule) error {
 func (e *Emulator) deny(rule *deviceRule) error {
 	// This cgroup is configured as a white-list. Reset the entire emulator,
 	// 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{
 			defaultAllow: false,
 			rules:        nil,
@@ -239,7 +239,7 @@ func (e *Emulator) deny(rule *deviceRule) error {
 	return err
 }
 
-func (e *Emulator) Apply(rule configs.DeviceRule) error {
+func (e *Emulator) Apply(rule devices.Rule) error {
 	if !rule.Type.CanCgroup() {
 		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,
 	}
-	if innerRule.meta.node == configs.WildcardDevice {
+	if innerRule.meta.node == devices.WildcardDevice {
 		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
 // to figure out how to update a containers' cgroups without causing spurrious
 // device errors (if possible).
-func (source *Emulator) Transition(target *Emulator) ([]*configs.DeviceRule, error) {
-	var transitionRules []*configs.DeviceRule
+func (source *Emulator) Transition(target *Emulator) ([]*devices.Rule, error) {
+	var transitionRules []*devices.Rule
 	oldRules := source.rules
 
 	// 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
 	// black-list we also have to include a disruptive rule.
 	if source.IsBlacklist() || source.defaultAllow != target.defaultAllow {
-		transitionRules = append(transitionRules, &configs.DeviceRule{
+		transitionRules = append(transitionRules, &devices.Rule{
 			Type:        'a',
 			Major:       -1,
 			Minor:       -1,
-			Permissions: configs.DevicePermissions("rwm"),
+			Permissions: devices.Permissions("rwm"),
 			Allow:       target.defaultAllow,
 		})
 		// 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]
 		droppedPerms := oldPerms.Difference(newPerms)
 		if !droppedPerms.IsEmpty() {
-			transitionRules = append(transitionRules, &configs.DeviceRule{
+			transitionRules = append(transitionRules, &devices.Rule{
 				Type:        meta.node,
 				Major:       meta.major,
 				Minor:       meta.minor,
@@ -360,7 +360,7 @@ func (source *Emulator) Transition(target *Emulator) ([]*configs.DeviceRule, err
 		oldPerms := oldRules[meta]
 		gainedPerms := newPerms.Difference(oldPerms)
 		if !gainedPerms.IsEmpty() {
-			transitionRules = append(transitionRules, &configs.DeviceRule{
+			transitionRules = append(transitionRules, &devices.Rule{
 				Type:        meta.node,
 				Major:       meta.major,
 				Minor:       meta.minor,
diff --git a/libcontainer/cgroups/ebpf/devicefilter/devicefilter.go b/libcontainer/cgroups/ebpf/devicefilter/devicefilter.go
index c1681a50..a173fd4a 100644
--- a/libcontainer/cgroups/ebpf/devicefilter/devicefilter.go
+++ b/libcontainer/cgroups/ebpf/devicefilter/devicefilter.go
@@ -11,7 +11,7 @@ import (
 	"strconv"
 
 	"github.com/cilium/ebpf/asm"
-	"github.com/opencontainers/runc/libcontainer/configs"
+	"github.com/opencontainers/runc/libcontainer/devices"
 	"github.com/pkg/errors"
 	"golang.org/x/sys/unix"
 )
@@ -22,7 +22,7 @@ const (
 )
 
 // DeviceFilter returns eBPF device filter program and its license string
-func DeviceFilter(devices []*configs.DeviceRule) (asm.Instructions, string, error) {
+func DeviceFilter(devices []*devices.Rule) (asm.Instructions, string, error) {
 	p := &program{}
 	p.init()
 	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.
-func (p *program) appendDevice(dev *configs.DeviceRule) error {
+func (p *program) appendDevice(dev *devices.Rule) error {
 	if p.blockID < 0 {
 		return errors.New("the program is finalized")
 	}
@@ -88,7 +88,7 @@ func (p *program) appendDevice(dev *configs.DeviceRule) error {
 		hasType = false
 	default:
 		// if not specified in OCI json, typ is set to DeviceTypeAll
-		return errors.Errorf("invalid DeviceType %q", string(dev.Type))
+		return errors.Errorf("invalid Type %q", string(dev.Type))
 	}
 	if dev.Major > math.MaxUint32 {
 		return errors.Errorf("invalid major %d", dev.Major)
diff --git a/libcontainer/cgroups/fs/devices.go b/libcontainer/cgroups/fs/devices.go
index fd8f00d5..9a37e68e 100644
--- a/libcontainer/cgroups/fs/devices.go
+++ b/libcontainer/cgroups/fs/devices.go
@@ -8,9 +8,10 @@ import (
 	"reflect"
 
 	"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/configs"
+	"github.com/opencontainers/runc/libcontainer/devices"
 	"github.com/opencontainers/runc/libcontainer/system"
 )
 
@@ -34,17 +35,17 @@ func (s *DevicesGroup) Apply(path string, d *cgroupData) error {
 	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")
 	if err != nil {
 		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.Rule) (*cgroupdevices.Emulator, error) {
 	// This defaults to a white-list -- which is what we want!
-	emu := &devices.Emulator{}
+	emu := &cgroupdevices.Emulator{}
 	for _, rule := range rules {
 		if err := emu.Apply(*rule); err != nil {
 			return nil, err
diff --git a/libcontainer/cgroups/fs2/devices.go b/libcontainer/cgroups/fs2/devices.go
index e06617f1..4c793a1c 100644
--- a/libcontainer/cgroups/fs2/devices.go
+++ b/libcontainer/cgroups/fs2/devices.go
@@ -6,11 +6,12 @@ import (
 	"github.com/opencontainers/runc/libcontainer/cgroups/ebpf"
 	"github.com/opencontainers/runc/libcontainer/cgroups/ebpf/devicefilter"
 	"github.com/opencontainers/runc/libcontainer/configs"
+	"github.com/opencontainers/runc/libcontainer/devices"
 	"github.com/pkg/errors"
 	"golang.org/x/sys/unix"
 )
 
-func isRWM(perms configs.DevicePermissions) bool {
+func isRWM(perms devices.Permissions) bool {
 	var r, w, m bool
 	for _, perm := range perms {
 		switch perm {
diff --git a/libcontainer/cgroups/systemd/common.go b/libcontainer/cgroups/systemd/common.go
index 22072856..9a30e4e1 100644
--- a/libcontainer/cgroups/systemd/common.go
+++ b/libcontainer/cgroups/systemd/common.go
@@ -13,8 +13,9 @@ import (
 
 	systemdDbus "github.com/coreos/go-systemd/v22/dbus"
 	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/devices"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 )
@@ -88,11 +89,11 @@ func ExpandSlice(slice string) (string, error) {
 	return path, nil
 }
 
-func groupPrefix(ruleType configs.DeviceType) (string, error) {
+func groupPrefix(ruleType devices.Type) (string, error) {
 	switch ruleType {
-	case configs.BlockDevice:
+	case devices.BlockDevice:
 		return "block-", nil
-	case configs.CharDevice:
+	case devices.CharDevice:
 		return "char-", nil
 	default:
 		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
 // given (type, major) combination. If more than one device group exists, an
 // arbitrary one is chosen.
-func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, error) {
+func findDeviceGroup(ruleType devices.Type, ruleMajor int64) (string, error) {
 	fh, err := os.Open("/proc/devices")
 	if err != nil {
 		return "", err
@@ -116,7 +117,7 @@ func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, erro
 	}
 
 	scanner := bufio.NewScanner(fh)
-	var currentType configs.DeviceType
+	var currentType devices.Type
 	for scanner.Scan() {
 		// We need to strip spaces because the first number is column-aligned.
 		line := strings.TrimSpace(scanner.Text())
@@ -124,10 +125,10 @@ func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, erro
 		// Handle the "header" lines.
 		switch line {
 		case "Block devices:":
-			currentType = configs.BlockDevice
+			currentType = devices.BlockDevice
 			continue
 		case "Character devices:":
-			currentType = configs.CharDevice
+			currentType = devices.CharDevice
 			continue
 		case "":
 			continue
@@ -163,7 +164,7 @@ func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, erro
 
 // generateDeviceProperties takes the configured device rules and generates a
 // corresponding set of systemd properties to configure the devices correctly.
-func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Property, error) {
+func generateDeviceProperties(rules []*devices.Rule) ([]systemdDbus.Property, error) {
 	// DeviceAllow is the type "a(ss)" which means we need a temporary struct
 	// to represent it in Go.
 	type deviceAllowEntry struct {
@@ -179,7 +180,7 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
 	}
 
 	// Figure out the set of rules.
-	configEmu := &devices.Emulator{}
+	configEmu := &cgroupdevices.Emulator{}
 	for _, rule := range rules {
 		if err := configEmu.Apply(*rule); err != nil {
 			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
 	// normal devices cgroup, in "strict" mode systemd defaults to a deny-all
 	// whitelist which is the default for devices.Emulator.
-	baseEmu := &devices.Emulator{}
+	baseEmu := &cgroupdevices.Emulator{}
 	finalRules, err := baseEmu.Transition(configEmu)
 	if err != nil {
 		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)
 		}
 		switch rule.Type {
-		case configs.BlockDevice, configs.CharDevice:
+		case devices.BlockDevice, devices.CharDevice:
 		default:
 			// Should never happen.
 			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
 		// 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.
-			if rule.Minor != configs.Wildcard {
+			if rule.Minor != devices.Wildcard {
 				logrus.Warnf("systemd doesn't support '*:n' device rules -- temporarily ignoring rule: %v", *rule)
 				continue
 			}
@@ -263,7 +264,7 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
 				return nil, err
 			}
 			entry.Path = prefix + "*"
-		} else if rule.Minor == configs.Wildcard {
+		} else if rule.Minor == devices.Wildcard {
 			// "_ n:* _" rules require a device group from /proc/devices.
 			group, err := findDeviceGroup(rule.Type, rule.Major)
 			if err != nil {
@@ -278,9 +279,9 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
 		} else {
 			// "_ n:m _" rules are just a path in /dev/{block,char}/.
 			switch rule.Type {
-			case configs.BlockDevice:
+			case devices.BlockDevice:
 				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)
 			}
 		}
diff --git a/libcontainer/configs/cgroup_linux.go b/libcontainer/configs/cgroup_linux.go
index dcc29c61..aada5d62 100644
--- a/libcontainer/configs/cgroup_linux.go
+++ b/libcontainer/configs/cgroup_linux.go
@@ -2,6 +2,7 @@ package configs
 
 import (
 	systemdDbus "github.com/coreos/go-systemd/v22/dbus"
+	"github.com/opencontainers/runc/libcontainer/devices"
 )
 
 type FreezerState string
@@ -42,7 +43,7 @@ type Cgroup struct {
 
 type Resources struct {
 	// Devices is the set of access rules for devices in the container.
-	Devices []*DeviceRule `json:"devices"`
+	Devices []*devices.Rule `json:"devices"`
 
 	// Memory limit (in bytes)
 	Memory int64 `json:"memory"`
diff --git a/libcontainer/configs/config.go b/libcontainer/configs/config.go
index 55ca65f2..e1cd1626 100644
--- a/libcontainer/configs/config.go
+++ b/libcontainer/configs/config.go
@@ -7,6 +7,7 @@ import (
 	"os/exec"
 	"time"
 
+	"github.com/opencontainers/runc/libcontainer/devices"
 	"github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
@@ -107,7 +108,7 @@ type Config struct {
 	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!
-	Devices []*Device `json:"devices"`
+	Devices []*devices.Device `json:"devices"`
 
 	MountLabel string `json:"mount_label"`
 
diff --git a/libcontainer/configs/device_windows.go b/libcontainer/configs/device_windows.go
deleted file mode 100644
index 72928939..00000000
--- a/libcontainer/configs/device_windows.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package configs
-
-func (d *DeviceRule) Mkdev() (uint64, error) {
-	return 0, nil
-}
diff --git a/libcontainer/configs/devices.go b/libcontainer/configs/devices.go
new file mode 100644
index 00000000..b9e3664c
--- /dev/null
+++ b/libcontainer/configs/devices.go
@@ -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.Rule
+	DeviceRule = devices.Rule
+
+	// Deprecated: use libcontainer/devices.Type
+	DeviceType = devices.Type
+
+	// Deprecated: use libcontainer/devices.Permissions
+	DevicePermissions = devices.Permissions
+)
diff --git a/libcontainer/configs/device.go b/libcontainer/devices/device.go
similarity index 63%
rename from libcontainer/configs/device.go
rename to libcontainer/devices/device.go
index 632bf6ac..3eb73cc7 100644
--- a/libcontainer/configs/device.go
+++ b/libcontainer/devices/device.go
@@ -1,4 +1,4 @@
-package configs
+package devices
 
 import (
 	"fmt"
@@ -11,7 +11,7 @@ const (
 )
 
 type Device struct {
-	DeviceRule
+	Rule
 
 	// Path to the device.
 	Path string `json:"path"`
@@ -26,10 +26,10 @@ type Device struct {
 	Gid uint32 `json:"gid"`
 }
 
-// DevicePermissions is a cgroupv1-style string to represent device access. It
+// Permissions is a cgroupv1-style string to represent device access. It
 // has to be a string for backward compatibility reasons, hence why it has
 // methods to do set operations.
-type DevicePermissions string
+type Permissions string
 
 const (
 	deviceRead uint = (1 << iota)
@@ -37,7 +37,7 @@ const (
 	deviceMknod
 )
 
-func (p DevicePermissions) toSet() uint {
+func (p Permissions) toSet() uint {
 	var set uint
 	for _, perm := range p {
 		switch perm {
@@ -52,7 +52,7 @@ func (p DevicePermissions) toSet() uint {
 	return set
 }
 
-func fromSet(set uint) DevicePermissions {
+func fromSet(set uint) Permissions {
 	var perm string
 	if set&deviceRead == deviceRead {
 		perm += "r"
@@ -63,53 +63,53 @@ func fromSet(set uint) DevicePermissions {
 	if set&deviceMknod == deviceMknod {
 		perm += "m"
 	}
-	return DevicePermissions(perm)
+	return Permissions(perm)
 }
 
-// Union returns the union of the two sets of DevicePermissions.
-func (p DevicePermissions) Union(o DevicePermissions) DevicePermissions {
+// Union returns the union of the two sets of Permissions.
+func (p Permissions) Union(o Permissions) Permissions {
 	lhs := p.toSet()
 	rhs := o.toSet()
 	return fromSet(lhs | rhs)
 }
 
-// Difference returns the set difference of the two sets of DevicePermissions.
+// Difference returns the set difference of the two sets of Permissions.
 // In set notation, A.Difference(B) gives you A\B.
-func (p DevicePermissions) Difference(o DevicePermissions) DevicePermissions {
+func (p Permissions) Difference(o Permissions) Permissions {
 	lhs := p.toSet()
 	rhs := o.toSet()
 	return fromSet(lhs &^ rhs)
 }
 
-// Intersection computes the intersection of the two sets of DevicePermissions.
-func (p DevicePermissions) Intersection(o DevicePermissions) DevicePermissions {
+// Intersection computes the intersection of the two sets of Permissions.
+func (p Permissions) Intersection(o Permissions) Permissions {
 	lhs := p.toSet()
 	rhs := o.toSet()
 	return fromSet(lhs & rhs)
 }
 
-// IsEmpty returns whether the set of permissions in a DevicePermissions is
+// IsEmpty returns whether the set of permissions in a Permissions is
 // empty.
-func (p DevicePermissions) IsEmpty() bool {
-	return p == DevicePermissions("")
+func (p Permissions) IsEmpty() bool {
+	return p == Permissions("")
 }
 
 // IsValid returns whether the set of permissions is a subset of valid
 // permissions (namely, {r,w,m}).
-func (p DevicePermissions) IsValid() bool {
+func (p Permissions) IsValid() bool {
 	return p == fromSet(p.toSet())
 }
 
-type DeviceType rune
+type Type rune
 
 const (
-	WildcardDevice DeviceType = 'a'
-	BlockDevice    DeviceType = 'b'
-	CharDevice     DeviceType = 'c' // or 'u'
-	FifoDevice     DeviceType = 'p'
+	WildcardDevice Type = 'a'
+	BlockDevice    Type = 'b'
+	CharDevice     Type = 'c' // or 'u'
+	FifoDevice     Type = 'p'
 )
 
-func (t DeviceType) IsValid() bool {
+func (t Type) IsValid() bool {
 	switch t {
 	case WildcardDevice, BlockDevice, CharDevice, FifoDevice:
 		return true
@@ -118,7 +118,7 @@ func (t DeviceType) IsValid() bool {
 	}
 }
 
-func (t DeviceType) CanMknod() bool {
+func (t Type) CanMknod() bool {
 	switch t {
 	case BlockDevice, CharDevice, FifoDevice:
 		return true
@@ -127,7 +127,7 @@ func (t DeviceType) CanMknod() bool {
 	}
 }
 
-func (t DeviceType) CanCgroup() bool {
+func (t Type) CanCgroup() bool {
 	switch t {
 	case WildcardDevice, BlockDevice, CharDevice:
 		return true
@@ -136,10 +136,10 @@ func (t DeviceType) CanCgroup() bool {
 	}
 }
 
-type DeviceRule struct {
+type Rule struct {
 	// Type of device ('c' for char, 'b' for block). If set to 'a', this rule
 	// acts as a wildcard and all fields other than Allow are ignored.
-	Type DeviceType `json:"type"`
+	Type Type `json:"type"`
 
 	// Major is the device's major number.
 	Major int64 `json:"major"`
@@ -149,13 +149,13 @@ type DeviceRule struct {
 
 	// Permissions is the set of permissions that this rule applies to (in the
 	// cgroupv1 format -- any combination of "rwm").
-	Permissions DevicePermissions `json:"permissions"`
+	Permissions Permissions `json:"permissions"`
 
 	// Allow specifies whether this rule is allowed.
 	Allow bool `json:"allow"`
 }
 
-func (d *DeviceRule) CgroupString() string {
+func (d *Rule) CgroupString() string {
 	var (
 		major = strconv.FormatInt(d.Major, 10)
 		minor = strconv.FormatInt(d.Minor, 10)
diff --git a/libcontainer/configs/device_unix.go b/libcontainer/devices/device_unix.go
similarity index 79%
rename from libcontainer/configs/device_unix.go
rename to libcontainer/devices/device_unix.go
index 650c4684..a400341e 100644
--- a/libcontainer/configs/device_unix.go
+++ b/libcontainer/devices/device_unix.go
@@ -1,6 +1,6 @@
 // +build !windows
 
-package configs
+package devices
 
 import (
 	"errors"
@@ -8,7 +8,7 @@ import (
 	"golang.org/x/sys/unix"
 )
 
-func (d *DeviceRule) Mkdev() (uint64, error) {
+func (d *Rule) Mkdev() (uint64, error) {
 	if d.Major == Wildcard || d.Minor == Wildcard {
 		return 0, errors.New("cannot mkdev() device with wildcards")
 	}
diff --git a/libcontainer/devices/device_windows.go b/libcontainer/devices/device_windows.go
new file mode 100644
index 00000000..8511bf00
--- /dev/null
+++ b/libcontainer/devices/device_windows.go
@@ -0,0 +1,5 @@
+package devices
+
+func (d *Rule) Mkdev() (uint64, error) {
+	return 0, nil
+}
diff --git a/libcontainer/devices/devices.go b/libcontainer/devices/devices.go
index 79f89c2d..5011f373 100644
--- a/libcontainer/devices/devices.go
+++ b/libcontainer/devices/devices.go
@@ -6,7 +6,6 @@ import (
 	"os"
 	"path/filepath"
 
-	"github.com/opencontainers/runc/libcontainer/configs"
 	"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
 // 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
 	err := unixLstat(path, &stat)
 	if err != nil {
@@ -31,7 +30,7 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) {
 	}
 
 	var (
-		devType   configs.DeviceType
+		devType   Type
 		mode      = stat.Mode
 		devNumber = uint64(stat.Rdev)
 		major     = unix.Major(devNumber)
@@ -39,20 +38,20 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) {
 	)
 	switch mode & unix.S_IFMT {
 	case unix.S_IFBLK:
-		devType = configs.BlockDevice
+		devType = BlockDevice
 	case unix.S_IFCHR:
-		devType = configs.CharDevice
+		devType = CharDevice
 	case unix.S_IFIFO:
-		devType = configs.FifoDevice
+		devType = FifoDevice
 	default:
 		return nil, ErrNotADevice
 	}
-	return &configs.Device{
-		DeviceRule: configs.DeviceRule{
+	return &Device{
+		Rule: Rule{
 			Type:        devType,
 			Major:       int64(major),
 			Minor:       int64(minor),
-			Permissions: configs.DevicePermissions(permissions),
+			Permissions: Permissions(permissions),
 		},
 		Path:     path,
 		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.
-func HostDevices() ([]*configs.Device, error) {
+func HostDevices() ([]*Device, error) {
 	return GetDevices("/dev")
 }
 
 // GetDevices recursively traverses a directory specified by path
 // and returns all devices found there.
-func GetDevices(path string) ([]*configs.Device, error) {
+func GetDevices(path string) ([]*Device, error) {
 	files, err := ioutilReadDir(path)
 	if err != nil {
 		return nil, err
 	}
-	var out []*configs.Device
+	var out []*Device
 	for _, f := range files {
 		switch {
 		case f.IsDir():
@@ -104,7 +103,7 @@ func GetDevices(path string) ([]*configs.Device, error) {
 			}
 			return nil, err
 		}
-		if device.Type == configs.FifoDevice {
+		if device.Type == FifoDevice {
 			continue
 		}
 		out = append(out, device)
diff --git a/libcontainer/rootfs_linux.go b/libcontainer/rootfs_linux.go
index 7e03afc9..ad856bff 100644
--- a/libcontainer/rootfs_linux.go
+++ b/libcontainer/rootfs_linux.go
@@ -18,12 +18,12 @@ import (
 	"github.com/mrunalp/fileutils"
 	"github.com/opencontainers/runc/libcontainer/cgroups"
 	"github.com/opencontainers/runc/libcontainer/configs"
+	"github.com/opencontainers/runc/libcontainer/devices"
 	"github.com/opencontainers/runc/libcontainer/system"
 	"github.com/opencontainers/runc/libcontainer/utils"
 	libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
 	"github.com/opencontainers/runtime-spec/specs-go"
 	"github.com/opencontainers/selinux/go-selinux/label"
-
 	"golang.org/x/sys/unix"
 )
 
@@ -614,7 +614,7 @@ func createDevices(config *configs.Config) error {
 	return nil
 }
 
-func bindMountDeviceNode(dest string, node *configs.Device) error {
+func bindMountDeviceNode(dest string, node *devices.Device) error {
 	f, err := os.Create(dest)
 	if err != nil && !os.IsExist(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.
-func createDeviceNode(rootfs string, node *configs.Device, bind bool) error {
+func createDeviceNode(rootfs string, node *devices.Device, bind bool) error {
 	if node.Path == "" {
 		// The node only exists for cgroup reasons, ignore it here.
 		return nil
@@ -649,14 +649,14 @@ func createDeviceNode(rootfs string, node *configs.Device, bind bool) error {
 	return nil
 }
 
-func mknodDevice(dest string, node *configs.Device) error {
+func mknodDevice(dest string, node *devices.Device) error {
 	fileMode := node.FileMode
 	switch node.Type {
-	case configs.BlockDevice:
+	case devices.BlockDevice:
 		fileMode |= unix.S_IFBLK
-	case configs.CharDevice:
+	case devices.CharDevice:
 		fileMode |= unix.S_IFCHR
-	case configs.FifoDevice:
+	case devices.FifoDevice:
 		fileMode |= unix.S_IFIFO
 	default:
 		return fmt.Errorf("%c is not a valid device type for device %s", node.Type, node.Path)
diff --git a/libcontainer/specconv/spec_linux.go b/libcontainer/specconv/spec_linux.go
index 97c8fb8b..07161ae0 100644
--- a/libcontainer/specconv/spec_linux.go
+++ b/libcontainer/specconv/spec_linux.go
@@ -17,6 +17,7 @@ import (
 	dbus "github.com/godbus/dbus/v5"
 	"github.com/opencontainers/runc/libcontainer/cgroups"
 	"github.com/opencontainers/runc/libcontainer/configs"
+	"github.com/opencontainers/runc/libcontainer/devices"
 	"github.com/opencontainers/runc/libcontainer/seccomp"
 	libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
 	"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
 //    many people depend on this (incorrect and arguably insecure) behaviour.
-var AllowedDevices = []*configs.Device{
+var AllowedDevices = []*devices.Device{
 	// allow mknod for any device
 	{
-		DeviceRule: configs.DeviceRule{
-			Type:        configs.CharDevice,
-			Major:       configs.Wildcard,
-			Minor:       configs.Wildcard,
+		Rule: devices.Rule{
+			Type:        devices.CharDevice,
+			Major:       devices.Wildcard,
+			Minor:       devices.Wildcard,
 			Permissions: "m",
 			Allow:       true,
 		},
 	},
 	{
-		DeviceRule: configs.DeviceRule{
-			Type:        configs.BlockDevice,
-			Major:       configs.Wildcard,
-			Minor:       configs.Wildcard,
+		Rule: devices.Rule{
+			Type:        devices.BlockDevice,
+			Major:       devices.Wildcard,
+			Minor:       devices.Wildcard,
 			Permissions: "m",
 			Allow:       true,
 		},
@@ -86,8 +87,8 @@ var AllowedDevices = []*configs.Device{
 		FileMode: 0666,
 		Uid:      0,
 		Gid:      0,
-		DeviceRule: configs.DeviceRule{
-			Type:        configs.CharDevice,
+		Rule: devices.Rule{
+			Type:        devices.CharDevice,
 			Major:       1,
 			Minor:       3,
 			Permissions: "rwm",
@@ -99,8 +100,8 @@ var AllowedDevices = []*configs.Device{
 		FileMode: 0666,
 		Uid:      0,
 		Gid:      0,
-		DeviceRule: configs.DeviceRule{
-			Type:        configs.CharDevice,
+		Rule: devices.Rule{
+			Type:        devices.CharDevice,
 			Major:       1,
 			Minor:       8,
 			Permissions: "rwm",
@@ -112,8 +113,8 @@ var AllowedDevices = []*configs.Device{
 		FileMode: 0666,
 		Uid:      0,
 		Gid:      0,
-		DeviceRule: configs.DeviceRule{
-			Type:        configs.CharDevice,
+		Rule: devices.Rule{
+			Type:        devices.CharDevice,
 			Major:       1,
 			Minor:       7,
 			Permissions: "rwm",
@@ -125,8 +126,8 @@ var AllowedDevices = []*configs.Device{
 		FileMode: 0666,
 		Uid:      0,
 		Gid:      0,
-		DeviceRule: configs.DeviceRule{
-			Type:        configs.CharDevice,
+		Rule: devices.Rule{
+			Type:        devices.CharDevice,
 			Major:       5,
 			Minor:       0,
 			Permissions: "rwm",
@@ -138,8 +139,8 @@ var AllowedDevices = []*configs.Device{
 		FileMode: 0666,
 		Uid:      0,
 		Gid:      0,
-		DeviceRule: configs.DeviceRule{
-			Type:        configs.CharDevice,
+		Rule: devices.Rule{
+			Type:        devices.CharDevice,
 			Major:       1,
 			Minor:       5,
 			Permissions: "rwm",
@@ -151,8 +152,8 @@ var AllowedDevices = []*configs.Device{
 		FileMode: 0666,
 		Uid:      0,
 		Gid:      0,
-		DeviceRule: configs.DeviceRule{
-			Type:        configs.CharDevice,
+		Rule: devices.Rule{
+			Type:        devices.CharDevice,
 			Major:       1,
 			Minor:       9,
 			Permissions: "rwm",
@@ -161,17 +162,17 @@ var AllowedDevices = []*configs.Device{
 	},
 	// /dev/pts/ - pts namespaces are "coming soon"
 	{
-		DeviceRule: configs.DeviceRule{
-			Type:        configs.CharDevice,
+		Rule: devices.Rule{
+			Type:        devices.CharDevice,
 			Major:       136,
-			Minor:       configs.Wildcard,
+			Minor:       devices.Wildcard,
 			Permissions: "rwm",
 			Allow:       true,
 		},
 	},
 	{
-		DeviceRule: configs.DeviceRule{
-			Type:        configs.CharDevice,
+		Rule: devices.Rule{
+			Type:        devices.CharDevice,
 			Major:       5,
 			Minor:       2,
 			Permissions: "rwm",
@@ -180,8 +181,8 @@ var AllowedDevices = []*configs.Device{
 	},
 	// tuntap
 	{
-		DeviceRule: configs.DeviceRule{
-			Type:        configs.CharDevice,
+		Rule: devices.Rule{
+			Type:        devices.CharDevice,
 			Major:       10,
 			Minor:       200,
 			Permissions: "rwm",
@@ -413,7 +414,7 @@ func initSystemdProps(spec *specs.Spec) ([]systemdDbus.Property, error) {
 	return sp, nil
 }
 
-func CreateCgroupConfig(opts *CreateOpts, defaultDevs []*configs.Device) (*configs.Cgroup, error) {
+func CreateCgroupConfig(opts *CreateOpts, defaultDevs []*devices.Device) (*configs.Cgroup, error) {
 	var (
 		myCgroupPath string
 
@@ -491,11 +492,11 @@ func CreateCgroupConfig(opts *CreateOpts, defaultDevs []*configs.Device) (*confi
 				if err != nil {
 					return nil, err
 				}
-				c.Resources.Devices = append(c.Resources.Devices, &configs.DeviceRule{
+				c.Resources.Devices = append(c.Resources.Devices, &devices.Rule{
 					Type:        dt,
 					Major:       major,
 					Minor:       minor,
-					Permissions: configs.DevicePermissions(d.Access),
+					Permissions: devices.Permissions(d.Access),
 					Allow:       d.Allow,
 				})
 			}
@@ -629,41 +630,41 @@ func CreateCgroupConfig(opts *CreateOpts, defaultDevs []*configs.Device) (*confi
 
 	// Append the default allowed devices to the end of the list.
 	for _, device := range defaultDevs {
-		c.Resources.Devices = append(c.Resources.Devices, &device.DeviceRule)
+		c.Resources.Devices = append(c.Resources.Devices, &device.Rule)
 	}
 	return c, nil
 }
 
-func stringToCgroupDeviceRune(s string) (configs.DeviceType, error) {
+func stringToCgroupDeviceRune(s string) (devices.Type, error) {
 	switch s {
 	case "a":
-		return configs.WildcardDevice, nil
+		return devices.WildcardDevice, nil
 	case "b":
-		return configs.BlockDevice, nil
+		return devices.BlockDevice, nil
 	case "c":
-		return configs.CharDevice, nil
+		return devices.CharDevice, nil
 	default:
 		return 0, fmt.Errorf("invalid cgroup device type %q", s)
 	}
 }
 
-func stringToDeviceRune(s string) (configs.DeviceType, error) {
+func stringToDeviceRune(s string) (devices.Type, error) {
 	switch s {
 	case "p":
-		return configs.FifoDevice, nil
+		return devices.FifoDevice, nil
 	case "u", "c":
-		return configs.CharDevice, nil
+		return devices.CharDevice, nil
 	case "b":
-		return configs.BlockDevice, nil
+		return devices.BlockDevice, nil
 	default:
 		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
 	// device (the spec one takes priority).
-	dedupedAllowDevs := []*configs.Device{}
+	dedupedAllowDevs := []*devices.Device{}
 
 next:
 	for _, ad := range AllowedDevices {
@@ -699,8 +700,8 @@ next:
 			if d.FileMode != nil {
 				filemode = *d.FileMode
 			}
-			device := &configs.Device{
-				DeviceRule: configs.DeviceRule{
+			device := &devices.Device{
+				Rule: devices.Rule{
 					Type:  dt,
 					Major: d.Major,
 					Minor: d.Minor,


Test patch:
diff --git a/libcontainer/cgroups/devices/devices_emulator_test.go b/libcontainer/cgroups/devices/devices_emulator_test.go
index 75e751c3..5dbcbdee 100644
--- a/libcontainer/cgroups/devices/devices_emulator_test.go
+++ b/libcontainer/cgroups/devices/devices_emulator_test.go
@@ -23,7 +23,7 @@ import (
 	"reflect"
 	"testing"
 
-	"github.com/opencontainers/runc/libcontainer/configs"
+	"github.com/opencontainers/runc/libcontainer/devices"
 )
 
 func TestDeviceEmulatorLoad(t *testing.T) {
@@ -45,10 +45,10 @@ func TestDeviceEmulatorLoad(t *testing.T) {
 				defaultAllow: false,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 4,
 						minor: 2,
-					}: configs.DevicePermissions("rw"),
+					}: devices.Permissions("rw"),
 				},
 			},
 		},
@@ -59,10 +59,10 @@ func TestDeviceEmulatorLoad(t *testing.T) {
 				defaultAllow: false,
 				rules: deviceRules{
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 0,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("m"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("m"),
 				},
 			},
 		},
@@ -74,16 +74,16 @@ c 1:1 r`,
 				defaultAllow: false,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
-						major: configs.Wildcard,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("rwm"),
+						node:  devices.CharDevice,
+						major: devices.Wildcard,
+						minor: devices.Wildcard,
+					}: devices.Permissions("rwm"),
 					// To match the kernel, we allow redundant rules.
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 1,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 		},
@@ -104,60 +104,60 @@ c 10:200 rwm`,
 				defaultAllow: false,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
-						major: configs.Wildcard,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("m"),
+						node:  devices.CharDevice,
+						major: devices.Wildcard,
+						minor: devices.Wildcard,
+					}: devices.Permissions("m"),
 					{
-						node:  configs.BlockDevice,
-						major: configs.Wildcard,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("m"),
+						node:  devices.BlockDevice,
+						major: devices.Wildcard,
+						minor: devices.Wildcard,
+					}: devices.Permissions("m"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 3,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 5,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 7,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 8,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 9,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 5,
 						minor: 0,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 5,
 						minor: 2,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 136,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("rwm"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 10,
 						minor: 200,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 				},
 			},
 		},
@@ -220,32 +220,32 @@ c 10:200 rwm`,
 func testDeviceEmulatorApply(t *testing.T, baseDefaultAllow bool) {
 	tests := []struct {
 		name           string
-		rule           configs.DeviceRule
+		rule           devices.Rule
 		base, expected *Emulator
 	}{
 		// Switch between default modes.
 		{
 			name: "SwitchToOtherMode",
-			rule: configs.DeviceRule{
-				Type:        configs.WildcardDevice,
-				Major:       configs.Wildcard,
-				Minor:       configs.Wildcard,
-				Permissions: configs.DevicePermissions("rwm"),
+			rule: devices.Rule{
+				Type:        devices.WildcardDevice,
+				Major:       devices.Wildcard,
+				Minor:       devices.Wildcard,
+				Permissions: devices.Permissions("rwm"),
 				Allow:       !baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
-						major: configs.Wildcard,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("rwm"),
+						node:  devices.CharDevice,
+						major: devices.Wildcard,
+						minor: devices.Wildcard,
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 1,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 			expected: &Emulator{
@@ -255,11 +255,11 @@ func testDeviceEmulatorApply(t *testing.T, baseDefaultAllow bool) {
 		},
 		{
 			name: "SwitchToSameModeNoop",
-			rule: configs.DeviceRule{
-				Type:        configs.WildcardDevice,
-				Major:       configs.Wildcard,
-				Minor:       configs.Wildcard,
-				Permissions: configs.DevicePermissions("rwm"),
+			rule: devices.Rule{
+				Type:        devices.WildcardDevice,
+				Major:       devices.Wildcard,
+				Minor:       devices.Wildcard,
+				Permissions: devices.Permissions("rwm"),
 				Allow:       baseDefaultAllow,
 			},
 			base: &Emulator{
@@ -273,26 +273,26 @@ func testDeviceEmulatorApply(t *testing.T, baseDefaultAllow bool) {
 		},
 		{
 			name: "SwitchToSameMode",
-			rule: configs.DeviceRule{
-				Type:        configs.WildcardDevice,
-				Major:       configs.Wildcard,
-				Minor:       configs.Wildcard,
-				Permissions: configs.DevicePermissions("rwm"),
+			rule: devices.Rule{
+				Type:        devices.WildcardDevice,
+				Major:       devices.Wildcard,
+				Minor:       devices.Wildcard,
+				Permissions: devices.Permissions("rwm"),
 				Allow:       baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
-						major: configs.Wildcard,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("rwm"),
+						node:  devices.CharDevice,
+						major: devices.Wildcard,
+						minor: devices.Wildcard,
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 1,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 			expected: &Emulator{
@@ -303,373 +303,373 @@ func testDeviceEmulatorApply(t *testing.T, baseDefaultAllow bool) {
 		// Rule addition logic.
 		{
 			name: "RuleAdditionBasic",
-			rule: configs.DeviceRule{
-				Type:        configs.CharDevice,
+			rule: devices.Rule{
+				Type:        devices.CharDevice,
 				Major:       42,
 				Minor:       1337,
-				Permissions: configs.DevicePermissions("rm"),
+				Permissions: devices.Permissions("rm"),
 				Allow:       !baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 2,
 						minor: 1,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 1,
 						minor: 5,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 			expected: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 2,
 						minor: 1,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 1,
 						minor: 5,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("rm"),
+					}: devices.Permissions("rm"),
 				},
 			},
 		},
 		{
 			name: "RuleAdditionBasicDuplicate",
-			rule: configs.DeviceRule{
-				Type:        configs.CharDevice,
+			rule: devices.Rule{
+				Type:        devices.CharDevice,
 				Major:       42,
 				Minor:       1337,
-				Permissions: configs.DevicePermissions("rm"),
+				Permissions: devices.Permissions("rm"),
 				Allow:       !baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("rwm"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("rwm"),
 				},
 			},
 			expected: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("rwm"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("rwm"),
 					// To match the kernel, we allow redundant rules.
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("rm"),
+					}: devices.Permissions("rm"),
 				},
 			},
 		},
 		{
 			name: "RuleAdditionBasicDuplicateNoop",
-			rule: configs.DeviceRule{
-				Type:        configs.CharDevice,
+			rule: devices.Rule{
+				Type:        devices.CharDevice,
 				Major:       42,
 				Minor:       1337,
-				Permissions: configs.DevicePermissions("rm"),
+				Permissions: devices.Permissions("rm"),
 				Allow:       !baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("rm"),
+					}: devices.Permissions("rm"),
 				},
 			},
 			expected: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("rm"),
+					}: devices.Permissions("rm"),
 				},
 			},
 		},
 		{
 			name: "RuleAdditionMerge",
-			rule: configs.DeviceRule{
-				Type:        configs.BlockDevice,
+			rule: devices.Rule{
+				Type:        devices.BlockDevice,
 				Major:       5,
 				Minor:       12,
-				Permissions: configs.DevicePermissions("rm"),
+				Permissions: devices.Permissions("rm"),
 				Allow:       !baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 2,
 						minor: 1,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 5,
 						minor: 12,
-					}: configs.DevicePermissions("rw"),
+					}: devices.Permissions("rw"),
 				},
 			},
 			expected: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 2,
 						minor: 1,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 5,
 						minor: 12,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 				},
 			},
 		},
 		{
 			name: "RuleAdditionMergeWildcard",
-			rule: configs.DeviceRule{
-				Type:        configs.BlockDevice,
+			rule: devices.Rule{
+				Type:        devices.BlockDevice,
 				Major:       5,
-				Minor:       configs.Wildcard,
-				Permissions: configs.DevicePermissions("rm"),
+				Minor:       devices.Wildcard,
+				Permissions: devices.Permissions("rm"),
 				Allow:       !baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 2,
 						minor: 1,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 5,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("rw"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("rw"),
 				},
 			},
 			expected: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 2,
 						minor: 1,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 5,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("rwm"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("rwm"),
 				},
 			},
 		},
 		{
 			name: "RuleAdditionMergeNoop",
-			rule: configs.DeviceRule{
-				Type:        configs.BlockDevice,
+			rule: devices.Rule{
+				Type:        devices.BlockDevice,
 				Major:       5,
 				Minor:       12,
-				Permissions: configs.DevicePermissions("r"),
+				Permissions: devices.Permissions("r"),
 				Allow:       !baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 2,
 						minor: 1,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 5,
 						minor: 12,
-					}: configs.DevicePermissions("rw"),
+					}: devices.Permissions("rw"),
 				},
 			},
 			expected: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 2,
 						minor: 1,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 5,
 						minor: 12,
-					}: configs.DevicePermissions("rw"),
+					}: devices.Permissions("rw"),
 				},
 			},
 		},
 		// Rule removal logic.
 		{
 			name: "RuleRemovalBasic",
-			rule: configs.DeviceRule{
-				Type:        configs.CharDevice,
+			rule: devices.Rule{
+				Type:        devices.CharDevice,
 				Major:       42,
 				Minor:       1337,
-				Permissions: configs.DevicePermissions("rm"),
+				Permissions: devices.Permissions("rm"),
 				Allow:       baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("rm"),
+					}: devices.Permissions("rm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 1,
 						minor: 5,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 			expected: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 1,
 						minor: 5,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 		},
 		{
 			name: "RuleRemovalNonexistent",
-			rule: configs.DeviceRule{
-				Type:        configs.CharDevice,
+			rule: devices.Rule{
+				Type:        devices.CharDevice,
 				Major:       4,
 				Minor:       1,
-				Permissions: configs.DevicePermissions("rw"),
+				Permissions: devices.Permissions("rw"),
 				Allow:       baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 1,
 						minor: 5,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 			expected: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 1,
 						minor: 5,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 		},
 		{
 			name: "RuleRemovalFull",
-			rule: configs.DeviceRule{
-				Type:        configs.CharDevice,
+			rule: devices.Rule{
+				Type:        devices.CharDevice,
 				Major:       42,
 				Minor:       1337,
-				Permissions: configs.DevicePermissions("rw"),
+				Permissions: devices.Permissions("rw"),
 				Allow:       baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("w"),
+					}: devices.Permissions("w"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 1,
 						minor: 5,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 			expected: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 1,
 						minor: 5,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 		},
 		{
 			name: "RuleRemovalPartial",
-			rule: configs.DeviceRule{
-				Type:        configs.CharDevice,
+			rule: devices.Rule{
+				Type:        devices.CharDevice,
 				Major:       42,
 				Minor:       1337,
-				Permissions: configs.DevicePermissions("r"),
+				Permissions: devices.Permissions("r"),
 				Allow:       baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("rm"),
+					}: devices.Permissions("rm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 1,
 						minor: 5,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 			expected: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("m"),
+					}: devices.Permissions("m"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 1,
 						minor: 5,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 		},
@@ -677,62 +677,62 @@ func testDeviceEmulatorApply(t *testing.T, baseDefaultAllow bool) {
 		// out" holes in a wildcard rule.
 		{
 			name: "RuleRemovalWildcardPunchoutImpossible",
-			rule: configs.DeviceRule{
-				Type:        configs.CharDevice,
+			rule: devices.Rule{
+				Type:        devices.CharDevice,
 				Major:       42,
 				Minor:       1337,
-				Permissions: configs.DevicePermissions("r"),
+				Permissions: devices.Permissions("r"),
 				Allow:       baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("rm"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("rm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 			expected: nil,
 		},
 		{
 			name: "RuleRemovalWildcardPunchoutPossible",
-			rule: configs.DeviceRule{
-				Type:        configs.CharDevice,
+			rule: devices.Rule{
+				Type:        devices.CharDevice,
 				Major:       42,
 				Minor:       1337,
-				Permissions: configs.DevicePermissions("r"),
+				Permissions: devices.Permissions("r"),
 				Allow:       baseDefaultAllow,
 			},
 			base: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("wm"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("wm"),
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 			expected: &Emulator{
 				defaultAllow: baseDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("wm"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("wm"),
 				},
 			},
 		},
@@ -767,7 +767,7 @@ func testDeviceEmulatorTransition(t *testing.T, sourceDefaultAllow bool) {
 	tests := []struct {
 		name           string
 		source, target *Emulator
-		expected       []*configs.DeviceRule
+		expected       []*devices.Rule
 	}{
 		// No-op changes.
 		{
@@ -776,20 +776,20 @@ func testDeviceEmulatorTransition(t *testing.T, sourceDefaultAllow bool) {
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("wm"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("wm"),
 				},
 			},
 			target: &Emulator{
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 42,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("wm"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("wm"),
 				},
 			},
 			// Identical white-lists produce no extra rules.
@@ -802,37 +802,37 @@ func testDeviceEmulatorTransition(t *testing.T, sourceDefaultAllow bool) {
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 				},
 			},
 			target: &Emulator{
 				defaultAllow: !sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 42,
-						minor: configs.Wildcard,
-					}: configs.DevicePermissions("wm"),
+						minor: devices.Wildcard,
+					}: devices.Permissions("wm"),
 				},
 			},
-			expected: []*configs.DeviceRule{
+			expected: []*devices.Rule{
 				// Clear-all rule.
 				{
-					Type:        configs.WildcardDevice,
-					Major:       configs.Wildcard,
-					Minor:       configs.Wildcard,
-					Permissions: configs.DevicePermissions("rwm"),
+					Type:        devices.WildcardDevice,
+					Major:       devices.Wildcard,
+					Minor:       devices.Wildcard,
+					Permissions: devices.Permissions("rwm"),
 					Allow:       !sourceDefaultAllow,
 				},
 				// The actual rule-set.
 				{
-					Type:        configs.BlockDevice,
+					Type:        devices.BlockDevice,
 					Major:       42,
-					Minor:       configs.Wildcard,
-					Permissions: configs.DevicePermissions("wm"),
+					Minor:       devices.Wildcard,
+					Permissions: devices.Permissions("wm"),
 					Allow:       sourceDefaultAllow,
 				},
 			},
@@ -844,33 +844,33 @@ func testDeviceEmulatorTransition(t *testing.T, sourceDefaultAllow bool) {
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 				},
 			},
 			target: &Emulator{
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 				},
 			},
-			expected: []*configs.DeviceRule{
+			expected: []*devices.Rule{
 				{
-					Type:        configs.BlockDevice,
+					Type:        devices.BlockDevice,
 					Major:       42,
 					Minor:       1337,
-					Permissions: configs.DevicePermissions("rwm"),
+					Permissions: devices.Permissions("rwm"),
 					Allow:       !sourceDefaultAllow,
 				},
 			},
@@ -881,33 +881,33 @@ func testDeviceEmulatorTransition(t *testing.T, sourceDefaultAllow bool) {
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 42,
 						minor: 1337,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 				},
 			},
 			target: &Emulator{
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 				},
 			},
-			expected: []*configs.DeviceRule{
+			expected: []*devices.Rule{
 				{
-					Type:        configs.BlockDevice,
+					Type:        devices.BlockDevice,
 					Major:       42,
 					Minor:       1337,
-					Permissions: configs.DevicePermissions("rwm"),
+					Permissions: devices.Permissions("rwm"),
 					Allow:       sourceDefaultAllow,
 				},
 			},
@@ -918,33 +918,33 @@ func testDeviceEmulatorTransition(t *testing.T, sourceDefaultAllow bool) {
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 					{
-						node:  configs.BlockDevice,
+						node:  devices.BlockDevice,
 						major: 3,
 						minor: 9,
-					}: configs.DevicePermissions("rw"),
+					}: devices.Permissions("rw"),
 				},
 			},
 			target: &Emulator{
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 				},
 			},
-			expected: []*configs.DeviceRule{
+			expected: []*devices.Rule{
 				{
-					Type:        configs.BlockDevice,
+					Type:        devices.BlockDevice,
 					Major:       3,
 					Minor:       9,
-					Permissions: configs.DevicePermissions("rw"),
+					Permissions: devices.Permissions("rw"),
 					Allow:       sourceDefaultAllow,
 				},
 			},
@@ -956,28 +956,28 @@ func testDeviceEmulatorTransition(t *testing.T, sourceDefaultAllow bool) {
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("r"),
+					}: devices.Permissions("r"),
 				},
 			},
 			target: &Emulator{
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("rwm"),
+					}: devices.Permissions("rwm"),
 				},
 			},
-			expected: []*configs.DeviceRule{
+			expected: []*devices.Rule{
 				{
-					Type:        configs.CharDevice,
+					Type:        devices.CharDevice,
 					Major:       1,
 					Minor:       2,
-					Permissions: configs.DevicePermissions("wm"),
+					Permissions: devices.Permissions("wm"),
 					Allow:       !sourceDefaultAllow,
 				},
 			},
@@ -988,28 +988,28 @@ func testDeviceEmulatorTransition(t *testing.T, sourceDefaultAllow bool) {
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("rw"),
+					}: devices.Permissions("rw"),
 				},
 			},
 			target: &Emulator{
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("w"),
+					}: devices.Permissions("w"),
 				},
 			},
-			expected: []*configs.DeviceRule{
+			expected: []*devices.Rule{
 				{
-					Type:        configs.CharDevice,
+					Type:        devices.CharDevice,
 					Major:       1,
 					Minor:       2,
-					Permissions: configs.DevicePermissions("r"),
+					Permissions: devices.Permissions("r"),
 					Allow:       sourceDefaultAllow,
 				},
 			},
@@ -1020,35 +1020,35 @@ func testDeviceEmulatorTransition(t *testing.T, sourceDefaultAllow bool) {
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("rw"),
+					}: devices.Permissions("rw"),
 				},
 			},
 			target: &Emulator{
 				defaultAllow: sourceDefaultAllow,
 				rules: deviceRules{
 					{
-						node:  configs.CharDevice,
+						node:  devices.CharDevice,
 						major: 1,
 						minor: 2,
-					}: configs.DevicePermissions("rm"),
+					}: devices.Permissions("rm"),
 				},
 			},
-			expected: []*configs.DeviceRule{
+			expected: []*devices.Rule{
 				{
-					Type:        configs.CharDevice,
+					Type:        devices.CharDevice,
 					Major:       1,
 					Minor:       2,
-					Permissions: configs.DevicePermissions("w"),
+					Permissions: devices.Permissions("w"),
 					Allow:       sourceDefaultAllow,
 				},
 				{
-					Type:        configs.CharDevice,
+					Type:        devices.CharDevice,
 					Major:       1,
 					Minor:       2,
-					Permissions: configs.DevicePermissions("m"),
+					Permissions: devices.Permissions("m"),
 					Allow:       !sourceDefaultAllow,
 				},
 			},
@@ -1063,15 +1063,15 @@ func testDeviceEmulatorTransition(t *testing.T, sourceDefaultAllow bool) {
 			// white-list mode in mind), and then make a full copy of the
 			// target rules.
 			if sourceDefaultAllow && test.source.defaultAllow == test.target.defaultAllow {
-				test.expected = []*configs.DeviceRule{{
-					Type:        configs.WildcardDevice,
-					Major:       configs.Wildcard,
-					Minor:       configs.Wildcard,
-					Permissions: configs.DevicePermissions("rwm"),
+				test.expected = []*devices.Rule{{
+					Type:        devices.WildcardDevice,
+					Major:       devices.Wildcard,
+					Minor:       devices.Wildcard,
+					Permissions: devices.Permissions("rwm"),
 					Allow:       test.target.defaultAllow,
 				}}
 				for _, rule := range test.target.rules.orderedEntries() {
-					test.expected = append(test.expected, &configs.DeviceRule{
+					test.expected = append(test.expected, &devices.Rule{
 						Type:        rule.meta.node,
 						Major:       rule.meta.major,
 						Minor:       rule.meta.minor,
diff --git a/libcontainer/cgroups/ebpf/devicefilter/devicefilter_test.go b/libcontainer/cgroups/ebpf/devicefilter/devicefilter_test.go
index d81e90f8..1ed7a071 100644
--- a/libcontainer/cgroups/ebpf/devicefilter/devicefilter_test.go
+++ b/libcontainer/cgroups/ebpf/devicefilter/devicefilter_test.go
@@ -4,7 +4,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/opencontainers/runc/libcontainer/configs"
+	"github.com/opencontainers/runc/libcontainer/devices"
 	"github.com/opencontainers/runc/libcontainer/specconv"
 )
 
@@ -20,7 +20,7 @@ func hash(s, comm string) string {
 	return strings.Join(res, "\n")
 }
 
-func testDeviceFilter(t testing.TB, devices []*configs.DeviceRule, expectedStr string) {
+func testDeviceFilter(t testing.TB, devices []*devices.Rule, expectedStr string) {
 	insts, _, err := DeviceFilter(devices)
 	if err != nil {
 		t.Fatalf("%s: %v (devices: %+v)", t.Name(), err, devices)
@@ -137,15 +137,15 @@ block-11:
         62: Mov32Imm dst: r0 imm: 0
         63: Exit
 `
-	var devices []*configs.DeviceRule
+	var devices []*devices.Rule
 	for _, device := range specconv.AllowedDevices {
-		devices = append(devices, &device.DeviceRule)
+		devices = append(devices, &device.Rule)
 	}
 	testDeviceFilter(t, devices, expected)
 }
 
 func TestDeviceFilter_Privileged(t *testing.T) {
-	devices := []*configs.DeviceRule{
+	devices := []*devices.Rule{
 		{
 			Type:        'a',
 			Major:       -1,
@@ -172,7 +172,7 @@ block-0:
 }
 
 func TestDeviceFilter_PrivilegedExceptSingleDevice(t *testing.T) {
-	devices := []*configs.DeviceRule{
+	devices := []*devices.Rule{
 		{
 			Type:        'a',
 			Major:       -1,
@@ -212,7 +212,7 @@ block-1:
 }
 
 func TestDeviceFilter_Weird(t *testing.T) {
-	devices := []*configs.DeviceRule{
+	devices := []*devices.Rule{
 		{
 			Type:        'b',
 			Major:       8,
diff --git a/libcontainer/cgroups/fs/devices_test.go b/libcontainer/cgroups/fs/devices_test.go
index da7ccb01..752fadd9 100644
--- a/libcontainer/cgroups/fs/devices_test.go
+++ b/libcontainer/cgroups/fs/devices_test.go
@@ -6,7 +6,7 @@ import (
 	"testing"
 
 	"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
-	"github.com/opencontainers/runc/libcontainer/configs"
+	"github.com/opencontainers/runc/libcontainer/devices"
 )
 
 func TestDevicesSetAllow(t *testing.T) {
@@ -19,18 +19,18 @@ func TestDevicesSetAllow(t *testing.T) {
 		"devices.list":  "a *:* rwm",
 	})
 
-	helper.CgroupData.config.Resources.Devices = []*configs.DeviceRule{
+	helper.CgroupData.config.Resources.Devices = []*devices.Rule{
 		{
-			Type:        configs.CharDevice,
+			Type:        devices.CharDevice,
 			Major:       1,
 			Minor:       5,
-			Permissions: configs.DevicePermissions("rwm"),
+			Permissions: devices.Permissions("rwm"),
 			Allow:       true,
 		},
 	}
 
-	devices := &DevicesGroup{testingSkipFinalCheck: true}
-	if err := devices.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
+	d := &DevicesGroup{testingSkipFinalCheck: true}
+	if err := d.Set(helper.CgroupPath, helper.CgroupData.config); err != nil {
 		t.Fatal(err)
 	}
 
diff --git a/libcontainer/devices/devices_test.go b/libcontainer/devices/devices_test.go
index 887b6ff0..02ebb4b6 100644
--- a/libcontainer/devices/devices_test.go
+++ b/libcontainer/devices/devices_test.go
@@ -6,7 +6,6 @@ import (
 	"os"
 	"testing"
 
-	"github.com/opencontainers/runc/libcontainer/configs"
 	"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.
 		var expectedType os.FileMode
 		switch device.Type {
-		case configs.BlockDevice:
+		case BlockDevice:
 			expectedType = unix.S_IFBLK
-		case configs.CharDevice:
+		case CharDevice:
 			expectedType = unix.S_IFCHR
-		case configs.FifoDevice:
+		case FifoDevice:
 			t.Logf("fifo devices shouldn't show up from HostDevices")
 			fallthrough
 		default:
diff --git a/libcontainer/integration/template_test.go b/libcontainer/integration/template_test.go
index 51a6b536..b8f7d0a4 100644
--- a/libcontainer/integration/template_test.go
+++ b/libcontainer/integration/template_test.go
@@ -5,8 +5,8 @@ import (
 	"strconv"
 
 	"github.com/opencontainers/runc/libcontainer/configs"
+	"github.com/opencontainers/runc/libcontainer/devices"
 	"github.com/opencontainers/runc/libcontainer/specconv"
-
 	"golang.org/x/sys/unix"
 )
 
@@ -30,9 +30,9 @@ type tParam struct {
 // it uses a network strategy of just setting a loopback interface
 // and the default setup for devices
 func newTemplateConfig(p *tParam) *configs.Config {
-	var allowedDevices []*configs.DeviceRule
+	var allowedDevices []*devices.Rule
 	for _, device := range specconv.AllowedDevices {
-		allowedDevices = append(allowedDevices, &device.DeviceRule)
+		allowedDevices = append(allowedDevices, &device.Rule)
 	}
 	config := &configs.Config{
 		Rootfs: p.rootfs,
diff --git a/libcontainer/specconv/spec_linux_test.go b/libcontainer/specconv/spec_linux_test.go
index 52954399..dc880f32 100644
--- a/libcontainer/specconv/spec_linux_test.go
+++ b/libcontainer/specconv/spec_linux_test.go
@@ -7,12 +7,12 @@ import (
 	"strings"
 	"testing"
 
-	"golang.org/x/sys/unix"
-
 	dbus "github.com/godbus/dbus/v5"
 	"github.com/opencontainers/runc/libcontainer/configs"
 	"github.com/opencontainers/runc/libcontainer/configs/validate"
+	"github.com/opencontainers/runc/libcontainer/devices"
 	"github.com/opencontainers/runtime-spec/specs-go"
+	"golang.org/x/sys/unix"
 )
 
 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
 	for _, configDev := range conf.Devices {
 		if configDev.Path == "/dev/tty" {
-			wantDev := &configs.Device{
+			wantDev := &devices.Device{
 				Path:     "/dev/tty",
 				FileMode: 0666,
 				Uid:      1000,
 				Gid:      1000,
-				DeviceRule: configs.DeviceRule{
-					Type:  configs.CharDevice,
+				Rule: devices.Rule{
+					Type:  devices.CharDevice,
 					Major: 5,
 					Minor: 0,
 				},