Skip to content

Commit d1b211e

Browse files
committed
libct/cg/sd: error on untranslatable dev rules in v2
It seems that the code added by commit b810da1 had cgroup v1 in mind, where runc overwrites the rules set by systemd. It is different in v2, because both ebpf programs (systemd's and runc's) has to return "allow" for the device to get access. So, when using cgroup v2 and systemd cgroup driver, access to devices rules for that can't be translated to systemd properties is not possible, and it makes sense to error out (rather than warn) in such case, as the container won't work as intended. Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
1 parent 30f9f80 commit d1b211e

File tree

4 files changed

+39
-13
lines changed

4 files changed

+39
-13
lines changed

libcontainer/cgroups/devices/systemd.go

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717

1818
// systemdProperties takes the configured device rules and generates a
1919
// corresponding set of systemd properties to configure the devices correctly.
20-
func systemdProperties(r *configs.Resources, sdVer int) ([]systemdDbus.Property, error) {
20+
func systemdProperties(r *configs.Resources, sdVer int, cgroupVer int) ([]systemdDbus.Property, error) {
2121
if r.SkipDevices {
2222
return nil, nil
2323
}
@@ -95,12 +95,33 @@ func systemdProperties(r *configs.Resources, sdVer int) ([]systemdDbus.Property,
9595
// The only type of rule we can't handle is wildcard-major rules, and
9696
// so we'll give a warning in that case (note that the fallback code
9797
// will insert any rules systemd couldn't handle). What amazing fun.
98+
//
99+
// The rules generated here are applied by systemd, and then runc applies
100+
// its own rules on top of those. If some rules can not be converted to
101+
// systemd properties, there will be two different sets of rules, and
102+
// the end result depends on cgroup version:
103+
//
104+
// * In cgroup v1 ("device.{allow,deny}" files), the rules set by systemd
105+
// gets overwritten by runc, meaning the correct rules are in effect.
106+
// Therefore, a warning is sufficient here (except for the case when
107+
// systemd is restarted and our rules gets overwritten, but there
108+
// is nothing we can do here).
109+
//
110+
// * In cgroup v2 (ebpf), both systemd and runc ebpf programs are loaded,
111+
// and the device access is granted only if both programs return "allow".
112+
// Therefore, the rules that can not be translated to systemd properties
113+
// won't work, so we must return an error.
98114

99115
if rule.Major == devices.Wildcard {
100116
// "_ *:n _" rules aren't supported by systemd.
101117
if rule.Minor != devices.Wildcard {
102-
logrus.Warnf("systemd doesn't support '*:n' device rules -- temporarily ignoring rule: %v", *rule)
118+
w := fmt.Errorf("systemd does not support wildcard-major device rule: %+v", *rule)
119+
if cgroupVer == 2 {
120+
return nil, w
121+
}
122+
logrus.Warnf("temporarily ignoring rule: %v", w)
103123
continue
124+
104125
}
105126

106127
// "_ *:* _" rules just wildcard everything.
@@ -117,7 +138,11 @@ func systemdProperties(r *configs.Resources, sdVer int) ([]systemdDbus.Property,
117138
}
118139
if group == "" {
119140
// Couldn't find a group.
120-
logrus.Warnf("could not find device group for '%v/%d' in /proc/devices -- temporarily ignoring rule: %v", rule.Type, rule.Major, *rule)
141+
w := fmt.Errorf("systemd older than v240 does not support wildcard-minor rules for devices not listed in /proc/devices: +%v", *rule)
142+
if cgroupVer == 2 {
143+
return nil, err
144+
}
145+
logrus.Warnf("temporarily ignoring rule: %v", w)
121146
continue
122147
}
123148
entry.Path = group
@@ -131,12 +156,13 @@ func systemdProperties(r *configs.Resources, sdVer int) ([]systemdDbus.Property,
131156
}
132157
if sdVer < 240 {
133158
// Old systemd versions use stat(2) on path to find out device major:minor
134-
// numbers and type. If the path doesn't exist, it will not add the rule,
135-
// emitting a warning instead.
136-
// Since all of this logic is best-effort anyway (we manually set these
137-
// rules separately to systemd) we can safely skip entries that don't
138-
// have a corresponding path.
159+
// numbers and type. If the path doesn't exist, it will not add the rule.
139160
if _, err := os.Stat(entry.Path); err != nil {
161+
w := fmt.Errorf("systemd older than v240 does not support device rules for non-existing device: %v", err)
162+
if cgroupVer == 2 {
163+
return nil, err
164+
}
165+
logrus.Warnf("temporarily ignoring rule: %v", w)
140166
continue
141167
}
142168
}

libcontainer/cgroups/systemd/common.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ var (
3333
isRunningSystemdOnce sync.Once
3434
isRunningSystemd bool
3535

36-
GenerateDeviceProps func(r *configs.Resources, sdVer int) ([]systemdDbus.Property, error)
36+
GenerateDeviceProps func(r *configs.Resources, sdVer, cgroupVer int) ([]systemdDbus.Property, error)
3737
)
3838

3939
// NOTE: This function comes from package github.com/coreos/go-systemd/util
@@ -342,13 +342,13 @@ func addCpuset(cm *dbusConnManager, props *[]systemdDbus.Property, cpus, mems st
342342

343343
// generateDeviceProperties takes the configured device rules and generates a
344344
// corresponding set of systemd properties to configure the devices correctly.
345-
func generateDeviceProperties(r *configs.Resources, cm *dbusConnManager) ([]systemdDbus.Property, error) {
345+
func generateDeviceProperties(r *configs.Resources, cm *dbusConnManager, cgroupVersion int) ([]systemdDbus.Property, error) {
346346
if GenerateDeviceProps == nil {
347347
if len(r.Devices) > 0 {
348348
return nil, cgroups.ErrDevicesUnsupported
349349
}
350350
return nil, nil
351351
}
352352

353-
return GenerateDeviceProps(r, systemdVersion(cm))
353+
return GenerateDeviceProps(r, systemdVersion(cm), cgroupVersion)
354354
}

libcontainer/cgroups/systemd/v1.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ var legacySubsystems = []subsystem{
7575
func genV1ResourcesProperties(r *configs.Resources, cm *dbusConnManager) ([]systemdDbus.Property, error) {
7676
var properties []systemdDbus.Property
7777

78-
deviceProperties, err := generateDeviceProperties(r, cm)
78+
deviceProperties, err := generateDeviceProperties(r, cm, 1)
7979
if err != nil {
8080
return nil, err
8181
}

libcontainer/cgroups/systemd/v2.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ func genV2ResourcesProperties(dirPath string, r *configs.Resources, cm *dbusConn
214214
// aren't the end of the world, but it is a bit concerning. However
215215
// it's unclear if systemd removes all eBPF programs attached when
216216
// doing SetUnitProperties...
217-
deviceProperties, err := generateDeviceProperties(r, cm)
217+
deviceProperties, err := generateDeviceProperties(r, cm, 2)
218218
if err != nil {
219219
return nil, err
220220
}

0 commit comments

Comments
 (0)