Skip to content

Commit a1acfcf

Browse files
authored
Merge pull request #4398 from kolyshkin/no-shared-pidns
runc create/run: warn on rootless + shared pidns + no cgroup
2 parents daaed34 + 30f8f51 commit a1acfcf

File tree

8 files changed

+54
-7
lines changed

8 files changed

+54
-7
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/configs/validate/rootless.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func rootlessEUIDMappings(config *configs.Config) error {
3434
return errors.New("rootless container requires user namespaces")
3535
}
3636
// We only require mappings if we are not joining another userns.
37-
if path := config.Namespaces.PathOf(configs.NEWUSER); path == "" {
37+
if config.Namespaces.IsPrivate(configs.NEWUSER) {
3838
if len(config.UIDMappings) == 0 {
3939
return errors.New("rootless containers requires at least one UID mapping")
4040
}

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 {

libcontainer/specconv/spec_linux.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
415415
}
416416
config.Namespaces.Add(t, ns.Path)
417417
}
418-
if config.Namespaces.Contains(configs.NEWNET) && config.Namespaces.PathOf(configs.NEWNET) == "" {
418+
if config.Namespaces.IsPrivate(configs.NEWNET) {
419419
config.Networks = []*configs.Network{
420420
{
421421
Type: "loopback",
@@ -481,7 +481,7 @@ func CreateLibcontainerConfig(opts *CreateOpts) (*configs.Config, error) {
481481
// Only set it if the container will have its own cgroup
482482
// namespace and the cgroupfs will be mounted read/write.
483483
//
484-
hasCgroupNS := config.Namespaces.Contains(configs.NEWCGROUP) && config.Namespaces.PathOf(configs.NEWCGROUP) == ""
484+
hasCgroupNS := config.Namespaces.IsPrivate(configs.NEWCGROUP)
485485
hasRwCgroupfs := false
486486
if hasCgroupNS {
487487
for _, m := range config.Mounts {

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+
}

tests/integration/helpers.bash

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,8 @@ function teardown_bundle() {
727727
[ ! -v ROOT ] && return 0 # nothing to teardown
728728

729729
cd "$INTEGRATION_ROOT" || return
730+
echo "--- teardown ---" >&2
731+
730732
teardown_recvtty
731733
local ct
732734
for ct in $(__runc list -q); do

0 commit comments

Comments
 (0)