From 438b5bea26d91020dd89afbaed1a170f9a86fdea Mon Sep 17 00:00:00 2001 From: Akihiro Suda Date: Thu, 9 Jul 2026 02:04:27 +0900 Subject: [PATCH] hack: run rootless daemon in a systemd user session when systemd is present Previously, the rootless-systemd CI mode failed with: open /sys/fs/cgroup/user.slice/user-1000.slice/cgroup.controllers: no such file or directory on every container start. The daemon auto-selects the systemd cgroup driver (cgroup v2 + systemd detected), but the test harness launched dockerd-rootless.sh via plain "sudo" with a fabricated XDG_RUNTIME_DIR, so there was no systemd user session at all: no user-${uid}.slice, no user@${uid}.service, and no user D-Bus socket for runc to talk to. withRootless() (daemon/oci_linux.go) then failed reading the controllers file at every container start. Set up the environment the way it is documented for production rootless installations instead: - hack/make/.integration-daemon-start: when systemd is running, enable lingering for unprivilegeduser (which starts user@${uid}.service), use /run/user/${uid} as XDG_RUNTIME_DIR, and export DBUS_SESSION_BUS_ADDRESS so that runc can find the systemd user manager. The variable also propagates to the daemons spawned by the test suite (sudo --preserve-env in internal/testutil/daemon), whose XDG_RUNTIME_DIR points to a non-standard location. - hack/dind-systemd: delegate the cpu/cpuset/io/memory/pids cgroup controllers to unprivileged users, matching https://docs.docker.com/engine/security/rootless/#limiting-resources Fixes the "test (amd64, *, rootless-systemd)" CI failures tracked in issue 44084. Co-Authored-By: Claude Fable 5 Signed-off-by: Akihiro Suda --- hack/dind-systemd | 10 +++++++++ hack/make/.integration-daemon-start | 35 +++++++++++++++++++++++++---- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/hack/dind-systemd b/hack/dind-systemd index 5d7e45eb2d..dc1e3d5fb5 100755 --- a/hack/dind-systemd +++ b/hack/dind-systemd @@ -104,6 +104,16 @@ EOF systemctl enable collect-firewalld-logs.service fi +# Delegate cgroup v2 controllers to unprivileged users, so that the rootless +# daemon can use the systemd cgroup driver and set resource limits. This is +# the setup documented for production rootless installations: +# https://docs.docker.com/engine/security/rootless/#limiting-resources +mkdir -p /etc/systemd/system/user@.service.d +cat > /etc/systemd/system/user@.service.d/delegate.conf << EOF +[Service] +Delegate=cpu cpuset io memory pids +EOF + env > /etc/docker-entrypoint-env cat > /etc/systemd/system/docker-entrypoint.target << EOF diff --git a/hack/make/.integration-daemon-start b/hack/make/.integration-daemon-start index 1cda397a91..88cf5b326f 100644 --- a/hack/make/.integration-daemon-start +++ b/hack/make/.integration-daemon-start @@ -84,14 +84,41 @@ if [ -n "$DOCKER_ROOTLESS" ]; then fi user="unprivilegeduser" uid=$(id -u $user) - # shellcheck disable=SC2174 - mkdir -p -m 700 "/tmp/docker-${uid}" - chown "$user" "/tmp/docker-${uid}" + if [ -d "/run/systemd/system" ]; then + # Set up a systemd user session for the user, with cgroup delegation + # (see hack/dind-systemd), so that the daemon and the tests can use + # the systemd cgroup driver. This mimics the setup documented for + # production ("loginctl enable-linger"), where dockerd-rootless.sh + # runs inside user@${uid}.service. + # https://github.com/moby/moby/issues/44084 + xdg_runtime_dir="/run/user/${uid}" + tries=0 + # Retried in a loop: this may race with systemd-logind/dbus startup, + # as docker-entrypoint.service has no ordering dependency on them. + until loginctl enable-linger "$user" && [ -S "${xdg_runtime_dir}/bus" ]; do + tries=$((tries + 1)) + if [ $tries -gt 30 ]; then + echo >&2 "# failed to set up a systemd user session for $user (no ${xdg_runtime_dir}/bus)" + exit 1 + fi + sleep 1 + done + # Propagated to the daemon started below, and to the test suite, which + # passes it on to the daemons it spawns (sudo --preserve-env in + # internal/testutil/daemon). runc needs it to find the systemd user + # manager when $XDG_RUNTIME_DIR points to a non-standard location. + export DBUS_SESSION_BUS_ADDRESS="unix:path=${xdg_runtime_dir}/bus" + else + xdg_runtime_dir="/tmp/docker-${uid}" + # shellcheck disable=SC2174 + mkdir -p -m 700 "${xdg_runtime_dir}" + chown "$user" "${xdg_runtime_dir}" + fi chmod -R o+w "$DEST" # The rootless daemon won't be able to load modules for tests that need them, so do it here. # There's no modprobe in the dev container, so https://x.com/lucabruno/status/902934379835662336 ip link show br_netfilter || true - dockerd="sudo -u $user -E -E XDG_RUNTIME_DIR=/tmp/docker-${uid} -E HOME=/home/${user} -E PATH=$PATH -- dockerd-rootless.sh" + dockerd="sudo -u $user -E -E XDG_RUNTIME_DIR=${xdg_runtime_dir} -E HOME=/home/${user} -E PATH=$PATH -- dockerd-rootless.sh" fi if [ -z "$DOCKER_TEST_HOST" ]; then