]> git.ipfire.org Git - thirdparty/mkosi.git/commitdiff
qemu: Register with systemd-machined in user session
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 22 Dec 2025 13:54:52 +0000 (14:54 +0100)
committerJörg Behrmann <behrmann@physik.fu-berlin.de>
Mon, 22 Dec 2025 16:56:37 +0000 (17:56 +0100)
Now that machine registration works unprivileged
since systemd v259, let's switch to unconditionally
registering machines with the user session
systemd-machined instance.

This breaks compat but the previous implementation
arguably wasn't useful or used, since registration
would only be done when running as root or if the
Register= feature was explicitly enabled. And if
not running as root, you'd have to authenticate
every time when booting the image to register it
which is arguably too annoying that anyone actually
bothered with it.

As vmspawn doesn't yet support registering with the
user machined instance, we stop registering vmspawn
machines for now. https://github.com/systemd/systemd/pull/40185
will add support for user machined regisration to
vmspawn.

For nspawn we stick with system machined registration
for now.

mkosi/__init__.py
mkosi/qemu.py
mkosi/resources/man/mkosi.1.md
mkosi/vmspawn.py

index 097185c33237e9f27e10c05673839166ebb235d3..ec6a878554b5abc8dfb56dcb665b8a35d23790f7 100644 (file)
@@ -111,7 +111,6 @@ from mkosi.qemu import (
     copy_ephemeral,
     finalize_credentials,
     finalize_kernel_command_line_extra,
-    finalize_register,
     join_initrds,
     run_qemu,
     run_ssh,
@@ -4228,7 +4227,7 @@ def run_shell(args: Args, config: Config) -> None:
 
     # Underscores are not allowed in machine names so replace them with hyphens.
     name = config.machine_or_name().replace("_", "-")
-    cmdline += ["--machine", name, "--register", yes_no(finalize_register(config))]
+    cmdline += ["--machine", name, "--register", yes_no(config.register != ConfigFeature.disabled)]
 
     with contextlib.ExitStack() as stack:
         for f in finalize_credentials(config, stack).iterdir():
index cb7e9a827c6095d2a08bda4cf44a8be5020b2591..d1aff439b477dd3c3597d448c8a8af30fe94b5b0 100644 (file)
@@ -962,41 +962,16 @@ def scope_cmd(
     ]  # fmt: skip
 
 
-def machine1_is_available(config: Config) -> bool:
-    if "DBUS_SYSTEM_ADDRESS" not in os.environ and not Path("/run/dbus/system_bus_socket").is_socket():
-        return False
-
-    services = json.loads(
-        run(
-            ["busctl", "list", "--json=pretty"],
-            env=os.environ | config.finalize_environment(),
-            sandbox=config.sandbox(relaxed=True),
-            stdout=subprocess.PIPE,
-        ).stdout.strip()
-    )
-
-    return any(service["name"] == "org.freedesktop.machine1" for service in services)
-
-
 def finalize_register(config: Config) -> bool:
     if config.register == ConfigFeature.disabled:
         return False
 
-    if config.register == ConfigFeature.auto and os.getuid() != 0:
-        return False
-
-    # Unprivileged registration via polkit was added after the varlink interface was added, so if the varlink
-    # interface is not available, we can assume unprivileged registration is not available either.
-    if (
-        not (p := Path("/run/systemd/machine/io.systemd.Machine")).is_socket()
-        or not os.access(p, os.R_OK | os.W_OK)
-    ) and (not machine1_is_available(config) or os.getuid() != 0):
+    if not (INVOKING_USER.runtime_dir() / "systemd/machine/io.systemd.Machine").is_socket():
         if config.register == ConfigFeature.enabled:
             die(
                 "Container registration was requested but systemd-machined is not available",
-                hint="Is the systemd-container package installed and is systemd-machined running?",
+                hint="Is the systemd-container package installed and sufficiently new (v259 or newer)?",
             )
-
         return False
 
     return True
@@ -1006,62 +981,31 @@ def register_machine(config: Config, pid: int, fname: Path, cid: Optional[int])
     if not finalize_register(config):
         return
 
-    if (p := Path("/run/systemd/machine/io.systemd.Machine")).is_socket() and os.access(
-        p, os.R_OK | os.W_OK
-    ):
-        run(
-            [
-                "varlinkctl",
-                "call",
-                p,
-                "io.systemd.Machine.Register",
-                json.dumps(
-                    {
-                        "name": config.machine_or_name().replace("_", "-"),
-                        "service": "mkosi",
-                        "class": "vm",
-                        "leader": pid,
-                        **({"rootDirectory": os.fspath(fname)} if fname.is_dir() else {}),
-                        **({"vSockCid": cid} if cid is not None else {}),
-                        **({"sshAddress": f"vsock/{cid}"} if cid is not None else {}),
-                        **({"sshPrivateKeyPath": f"{config.ssh_key}"} if config.ssh_key else {}),
-                    }
-                ),
-            ],
-            env=os.environ | config.finalize_environment(),
-            sandbox=config.sandbox(relaxed=True),
-            stdin=sys.stdin,
-            # Prevent varlinkctl's empty '{}' response from showing up in the terminal.
-            stdout=subprocess.DEVNULL,
-            # systemd 256 exposes the systemd-machined varlink interface only to the root user, but makes the
-            # varlink socket world readable/writable, which means this will fail when executed as an
-            # unprivileged user, so ignore the error in that case.
-            # TODO: Remove when https://github.com/systemd/systemd/pull/36344 is in a stable release.
-            check=os.getuid() == 0,
-        )
-    else:
-        run(
-            [
-                "busctl",
-                "call",
-                "--quiet",
-                "org.freedesktop.machine1",
-                "/org/freedesktop/machine1",
-                "org.freedesktop.machine1.Manager",
-                "RegisterMachine",
-                "sayssus",
-                config.machine_or_name().replace("_", "-"),
-                "0",
-                "mkosi",
-                "vm",
-                str(pid),
-                fname if fname.is_dir() else "",
-            ],  # fmt: skip
-            env=os.environ | config.finalize_environment(),
-            sandbox=config.sandbox(relaxed=True),
-            stdin=sys.stdin,
-            stdout=sys.stdout,
-        )
+    run(
+        [
+            "varlinkctl",
+            "call",
+            INVOKING_USER.runtime_dir() / "systemd/machine/io.systemd.Machine",
+            "io.systemd.Machine.Register",
+            json.dumps(
+                {
+                    "name": config.machine_or_name().replace("_", "-"),
+                    "service": "mkosi",
+                    "class": "vm",
+                    "leader": pid,
+                    **({"rootDirectory": os.fspath(fname)} if fname.is_dir() else {}),
+                    **({"vSockCid": cid} if cid is not None else {}),
+                    **({"sshAddress": f"vsock/{cid}"} if cid is not None else {}),
+                    **({"sshPrivateKeyPath": f"{config.ssh_key}"} if config.ssh_key else {}),
+                }
+            ),
+        ],
+        env=os.environ | config.finalize_environment(),
+        sandbox=config.sandbox(relaxed=True),
+        stdin=sys.stdin,
+        # Prevent varlinkctl's empty '{}' response from showing up in the terminal.
+        stdout=subprocess.DEVNULL,
+    )
 
 
 def run_qemu(args: Args, config: Config) -> None:
index 001a01a15fa5aa16f0661eea4c33683bfdb1771b..86dccc5080dd1bd2c7f40fba58954b58c12fa19a 100644 (file)
@@ -2029,6 +2029,10 @@ boolean argument: either `1`, `yes`, or `true` to enable, or `0`, `no`,
     systemd-machined. If `auto`, mkosi will register the vm/container
     with systemd-machined if it is available. Defaults to `auto`.
 
+    Note that mkosi will try to register the machine with
+    systemd-machined in the user session. systemd v259 or newer is
+    required to have systemd-machined be available in user sessions.
+
 `ForwardJournal=`, `--forward-journal=`
 :   Specify the path to which journal logs from containers and virtual
     machines should be forwarded. If the path has the `.journal`
index 565b696ed8b9bdf2a852b583c82c10c94220890f..c8e7e55d9fb92faaa0d7151b50a573e6b73aefd6 100644 (file)
@@ -21,7 +21,6 @@ from mkosi.qemu import (
     finalize_firmware,
     finalize_initrd,
     finalize_kernel_command_line_extra,
-    finalize_register,
 )
 from mkosi.run import run
 from mkosi.util import PathString
@@ -56,7 +55,6 @@ def run_vmspawn(args: Args, config: Config) -> None:
         "--vsock", config.vsock.to_tristate(),
         "--tpm", config.tpm.to_tristate(),
         "--secure-boot", yes_no(config.secure_boot),
-        "--register", yes_no(finalize_register(config)),
         "--console", str(config.console),
     ]  # fmt: skip