Skip to content

Commit f8f91b9

Browse files
authored
Merge pull request #92 from adamamyl/fix/docker-rootless-machinectl
fix(docker): use machinectl shell for rootless setup, not sudo -u
2 parents 57ded07 + 9d6b217 commit f8f91b9

3 files changed

Lines changed: 38 additions & 23 deletions

File tree

lib/constants.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,10 @@
8787
# Packages required for Docker (Based on Docker install script pre-reqs: ca-certificates curl)
8888
DOCKER_DEPS: List[str] = ["curl", "gnupg", "lsb-release", "ca-certificates"]
8989

90-
# newuidmap/newgidmap, required for rootless mode's user namespace UID/GID mapping
91-
ROOTLESS_DOCKER_DEPS: List[str] = ["uidmap"]
90+
# newuidmap/newgidmap for rootless mode's UID/GID mapping; systemd-container
91+
# provides machinectl, needed to get a real per-user session (see
92+
# module_docker._machinectl_shell for why plain sudo -u doesn't work).
93+
ROOTLESS_DOCKER_DEPS: List[str] = ["uidmap", "systemd-container"]
9294

9395
# Full modern Docker suite (Matching successful installation log)
9496
DOCKER_PKGS: List[str] = [

lib/installer_utils/module_docker.py

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from ..logger import log
1010
from ..constants import DOCKER_DEPS, DOCKER_PKGS, ROOTLESS_DOCKER_DEPS
1111
from .apt_tools import apt_install, ensure_apt_repo
12-
from .user_mgmt import add_user_to_group
12+
from .user_mgmt import add_user_to_group, require_user
1313

1414
def _get_os_release() -> Dict[str, str]:
1515
"""
@@ -188,12 +188,6 @@ def install_docker_and_add_users(
188188
log.success("Docker installation complete.")
189189

190190

191-
def _user_exists(user: str) -> bool:
192-
return subprocess.run(
193-
['id', user], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
194-
).returncode == 0
195-
196-
197191
def _get_uid(user: str) -> int:
198192
return int(subprocess.run(
199193
['id', '-u', user], capture_output=True, text=True, check=True
@@ -240,17 +234,39 @@ def _ensure_subid_range(exec_obj: Executor, path: str, user: str) -> None:
240234
exec_obj.run(f"echo '{user}:{next_start}:65536' | tee -a {path} > /dev/null", force_sudo=True)
241235

242236

237+
def _machinectl_shell(exec_obj: Executor, user: str, uid: int, inner_cmd: str) -> None:
238+
"""
239+
Runs inner_cmd as user via 'machinectl shell', with XDG_RUNTIME_DIR and
240+
DBUS_SESSION_BUS_ADDRESS set.
241+
242+
'sudo -u user' does NOT create a real login session, so the user's
243+
systemd --user instance / D-Bus bus either isn't reachable or isn't the
244+
persistent one lingering keeps alive — dockerd-rootless-setuptool.sh and
245+
systemctl --user then silently fall back to a non-systemd path ("systemd
246+
not detected") or fail with "Unit docker.service does not exist".
247+
machinectl shell attaches to the real per-user session that
248+
loginctl enable-linger keeps running.
249+
"""
250+
script = (
251+
f"export XDG_RUNTIME_DIR=/run/user/{uid}; "
252+
'export DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/bus"; '
253+
f"{inner_cmd}"
254+
)
255+
exec_obj.run(f"machinectl shell {user}@ /bin/bash -c '{script}'", force_sudo=True, check=True)
256+
257+
243258
def _setup_rootless_docker(exec_obj: Executor, user: str) -> None:
244259
"""
245260
Configures rootless Docker for *user*: uidmap prerequisites, subuid/subgid
246261
ranges, lingering (so their systemd --user instance survives without an
247-
active login), then runs dockerd-rootless-setuptool.sh as that user.
262+
active login), then runs dockerd-rootless-setuptool.sh as that user via
263+
'machinectl shell' (see _machinectl_shell for why not plain sudo -u).
248264
249265
--force is passed to the setuptool because the system-wide dockerd is
250266
intentionally left running for other services; rootless and rootful
251267
Docker run side by side using separate sockets.
252268
"""
253-
if not _user_exists(user):
269+
if not require_user(exec_obj, user, prompt_before_create=True):
254270
log.warning(f"User '{user}' does not exist; skipping rootless Docker setup.")
255271
return
256272

@@ -272,21 +288,13 @@ def _setup_rootless_docker(exec_obj: Executor, user: str) -> None:
272288
else:
273289
log.warning(f"{runtime_dir} did not appear after enabling linger; proceeding anyway.")
274290

275-
env_prefix = f"XDG_RUNTIME_DIR={runtime_dir} PATH=/usr/bin:$PATH"
276-
277291
log.info(f"Running dockerd-rootless-setuptool.sh for '{user}'...")
278-
exec_obj.run(
279-
f"{env_prefix} dockerd-rootless-setuptool.sh install --force",
280-
user=user,
281-
check=True,
292+
_machinectl_shell(
293+
exec_obj, user, uid, "PATH=/usr/bin:$PATH dockerd-rootless-setuptool.sh install --force"
282294
)
283295

284296
log.info(f"Enabling and starting the rootless docker.service for '{user}'...")
285-
exec_obj.run(
286-
f"XDG_RUNTIME_DIR={runtime_dir} systemctl --user enable --now docker.service",
287-
user=user,
288-
check=True,
289-
)
297+
_machinectl_shell(exec_obj, user, uid, "systemctl --user enable --now docker.service")
290298

291299
_add_rootless_env_to_shell_rc(exec_obj, user, uid)
292300
_verify_rootless_docker(exec_obj, user, runtime_dir)

setup_machine.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ def parse_args() -> Tuple[argparse.Namespace, List[str]]:
8888
group_modules.add_argument("--docker", action="store_true", dest="do_docker",
8989
help="Install Docker. Target user gets rootless Docker by "
9090
"default; see --docker-rootful to override.")
91+
group_modules.add_argument(
92+
"--docker-user", default=DEFAULT_VM_USER, dest="docker_user",
93+
help=f"User to configure Docker for; created (with confirmation) if missing "
94+
f"(default: {DEFAULT_VM_USER})."
95+
)
9196
group_modules.add_argument(
9297
"--docker-rootful", action="store_true", dest="do_docker_rootful",
9398
help="Use traditional rootful Docker (add user to the 'docker' group) "
@@ -386,7 +391,7 @@ def main() -> None:
386391
if tasks["docker"]:
387392
log_module_start("DOCKER", EXEC)
388393
module_docker.install_docker_and_add_users(
389-
EXEC, DEFAULT_VM_USER, rootless=not args.do_docker_rootful
394+
EXEC, args.docker_user, rootless=not args.do_docker_rootful
390395
)
391396

392397
if tasks["wolfcraig"]:

0 commit comments

Comments
 (0)