Skip to content

Commit 30f8f51

Browse files
committed
runc create/run: warn on rootless + shared pidns + no cgroup
Shared pid namespace means `runc kill` (or `runc delete -f`) have to kill all container processes, not just init. To do so, it needs a cgroup to read the PIDs from. If there is no cgroup, processes will be leaked, and so such configuration is bad and should not be allowed. To keep backward compatibility, though, let's merely warn about this for now. Alas, the only way to know if cgroup access is available is by returning an error from Manager.Apply. Amend fs cgroup managers to do so (systemd doesn't need it, since v1 can't work with rootless, and cgroup v2 does not have a special rootless case). Signed-off-by: Kir Kolyshkin <[email protected]>
1 parent 21c6116 commit 30f8f51

File tree

5 files changed

+49
-4
lines changed

5 files changed

+49
-4
lines changed

libcontainer/cgroups/cgroups.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ var (
1111
// is not configured to set device rules.
1212
ErrDevicesUnsupported = errors.New("cgroup manager is not configured to set device rules")
1313

14+
// ErrRootless is returned by [Manager.Apply] when there is an error
15+
// creating cgroup directory, and cgroup.Rootless is set. In general,
16+
// this error is to be ignored.
17+
ErrRootless = errors.New("cgroup manager can not access cgroup (rootless container)")
18+
1419
// DevicesSetV1 and DevicesSetV2 are functions to set devices for
1520
// cgroup v1 and v2, respectively. Unless
1621
// [github.com/opencontainers/runc/libcontainer/cgroups/devices]

libcontainer/cgroups/fs/fs.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func isIgnorableError(rootless bool, err error) bool {
105105
return false
106106
}
107107

108-
func (m *Manager) Apply(pid int) (err error) {
108+
func (m *Manager) Apply(pid int) (retErr error) {
109109
m.mu.Lock()
110110
defer m.mu.Unlock()
111111

@@ -129,14 +129,15 @@ func (m *Manager) Apply(pid int) (err error) {
129129
// later by Set, which fails with a friendly error (see
130130
// if path == "" in Set).
131131
if isIgnorableError(c.Rootless, err) && c.Path == "" {
132+
retErr = cgroups.ErrRootless
132133
delete(m.paths, name)
133134
continue
134135
}
135136
return err
136137
}
137138

138139
}
139-
return nil
140+
return retErr
140141
}
141142

142143
func (m *Manager) Destroy() error {

libcontainer/cgroups/fs2/fs2.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func (m *Manager) Apply(pid int) error {
7171
if m.config.Rootless {
7272
if m.config.Path == "" {
7373
if blNeed, nErr := needAnyControllers(m.config.Resources); nErr == nil && !blNeed {
74-
return nil
74+
return cgroups.ErrRootless
7575
}
7676
return fmt.Errorf("rootless needs no limits + no cgrouppath when no permission is granted for cgroups: %w", err)
7777
}

libcontainer/process_linux.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,18 @@ func (p *initProcess) start() (retErr error) {
580580
// cgroup. We don't need to worry about not doing this and not being root
581581
// because we'd be using the rootless cgroup manager in that case.
582582
if err := p.manager.Apply(p.pid()); err != nil {
583-
return fmt.Errorf("unable to apply cgroup configuration: %w", err)
583+
if errors.Is(err, cgroups.ErrRootless) {
584+
// ErrRootless is to be ignored except when
585+
// the container doesn't have private pidns.
586+
if !p.config.Config.Namespaces.IsPrivate(configs.NEWPID) {
587+
// TODO: make this an error in runc 1.3.
588+
logrus.Warn("Creating a rootless container with no cgroup and no private pid namespace. " +
589+
"Such configuration is strongly discouraged (as it is impossible to properly kill all container's processes) " +
590+
"and will result in an error in a future runc version.")
591+
}
592+
} else {
593+
return fmt.Errorf("unable to apply cgroup configuration: %w", err)
594+
}
584595
}
585596
if p.intelRdtManager != nil {
586597
if err := p.intelRdtManager.Apply(p.pid()); err != nil {

tests/integration/create.bats

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,31 @@ function teardown() {
8181

8282
testcontainer test_busybox running
8383
}
84+
85+
# https://github.com/opencontainers/runc/issues/4394#issuecomment-2334926257
86+
@test "runc create [shared pidns + rootless]" {
87+
# Remove pidns so it's shared with the host.
88+
update_config ' .linux.namespaces -= [{"type": "pid"}]'
89+
if [ $EUID -ne 0 ]; then
90+
if rootless_cgroup; then
91+
# Rootless containers have empty cgroup path by default.
92+
set_cgroups_path
93+
fi
94+
# Can't mount real /proc when rootless + no pidns,
95+
# so change it to a bind-mounted one from the host.
96+
update_config ' .mounts |= map((select(.type == "proc")
97+
| .type = "none"
98+
| .source = "/proc"
99+
| .options = ["rbind", "nosuid", "nodev", "noexec"]
100+
) // .)'
101+
fi
102+
103+
exp="Such configuration is strongly discouraged"
104+
runc create --console-socket "$CONSOLE_SOCKET" test
105+
[ "$status" -eq 0 ]
106+
if [ $EUID -ne 0 ] && ! rootless_cgroup; then
107+
[[ "$output" = *"$exp"* ]]
108+
else
109+
[[ "$output" != *"$exp"* ]]
110+
fi
111+
}

0 commit comments

Comments
 (0)