From: Daan De Meyer Date: Wed, 2 Oct 2024 10:26:23 +0000 (+0200) Subject: Don't store default kernel command line and credentials in Config X-Git-Tag: v25~245^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9eb59a1b3e2235510532b97d6e206d8ccea3a90f;p=thirdparty%2Fmkosi.git Don't store default kernel command line and credentials in Config Let's delay calculation of these until the last moment instead of storing them in the Config object. This makes the summary more succinct and reduces the amount of work we do to parse the configuration. --- diff --git a/mkosi/__init__.py b/mkosi/__init__.py index ad57c12ca..d81d23e4e 100644 --- a/mkosi/__init__.py +++ b/mkosi/__init__.py @@ -85,7 +85,15 @@ from mkosi.manifest import Manifest from mkosi.mounts import finalize_crypto_mounts, finalize_source_mounts, mount_overlay from mkosi.pager import page from mkosi.partition import Partition, finalize_root, finalize_roothash -from mkosi.qemu import KernelType, copy_ephemeral, run_qemu, run_ssh, start_journal_remote +from mkosi.qemu import ( + KernelType, + copy_ephemeral, + finalize_credentials, + finalize_kernel_command_line_extra, + run_qemu, + run_ssh, + start_journal_remote, +) from mkosi.run import ( apivfs_options, chroot_cmd, @@ -3554,7 +3562,7 @@ def run_shell(args: Args, config: Config) -> None: name = config.machine_or_name().replace("_", "-") cmdline += ["--machine", name] - for k, v in config.credentials.items(): + for k, v in finalize_credentials(config).items(): cmdline += [f"--set-credential={k}:{v}"] with contextlib.ExitStack() as stack: @@ -3676,7 +3684,10 @@ def run_shell(args: Args, config: Config) -> None: # When invoked by the kernel, all unknown arguments are passed as environment variables # to pid1. Let's mimic the same behavior when we invoke nspawn as a container. - for arg in itertools.chain(config.kernel_command_line, config.kernel_command_line_extra): + for arg in itertools.chain( + config.kernel_command_line, + finalize_kernel_command_line_extra(config), + ): name, sep, value = arg.partition("=") # If there's a '.' in the argument name, it's not considered an environment diff --git a/mkosi/config.py b/mkosi/config.py index ac6e8a306..d422a150d 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -18,7 +18,6 @@ import os.path import platform import re import shlex -import shutil import string import subprocess import sys @@ -4160,44 +4159,6 @@ def parse_config( return args, tuple([load_config(ns) for ns in images] + [load_config(config)]) -def load_credentials(args: argparse.Namespace) -> dict[str, str]: - creds = { - "firstboot.locale": "C.UTF-8", - **args.credentials, - } - - if "firstboot.timezone" not in creds: - if find_binary("timedatectl"): - tz = run( - ["timedatectl", "show", "-p", "Timezone", "--value"], - stdout=subprocess.PIPE, - check=False, - ).stdout.strip() - else: - tz = "UTC" - - creds["firstboot.timezone"] = tz - - if "ssh.authorized_keys.root" not in creds: - if args.ssh_certificate: - pubkey = run( - ["openssl", "x509", "-in", args.ssh_certificate, "-pubkey", "-noout"], - stdout=subprocess.PIPE, - env=dict(OPENSSL_CONF="/dev/null"), - ).stdout.strip() - sshpubkey = run( - ["ssh-keygen", "-f", "/dev/stdin", "-i", "-m", "PKCS8"], input=pubkey, stdout=subprocess.PIPE - ).stdout.strip() - creds["ssh.authorized_keys.root"] = sshpubkey - elif args.ssh: - die( - "Ssh= is enabled but no SSH certificate was found", - hint="Run 'mkosi genkey' to automatically create one", - ) - - return creds - - def finalize_term() -> str: term = os.getenv("TERM", "unknown") if term == "unknown": @@ -4206,55 +4167,6 @@ def finalize_term() -> str: return term if sys.stderr.isatty() else "dumb" -def load_kernel_command_line_extra(args: argparse.Namespace) -> list[str]: - columns, lines = shutil.get_terminal_size() - term = finalize_term() - - cmdline = [ - "rw", - # Make sure we set up networking in the VM/container. - "systemd.wants=network.target", - # Make sure we don't load vmw_vmci which messes with virtio vsock. - "module_blacklist=vmw_vmci", - f"systemd.tty.term.hvc0={term}", - f"systemd.tty.columns.hvc0={columns}", - f"systemd.tty.rows.hvc0={lines}", - ] - - if not any(s.startswith("ip=") for s in args.kernel_command_line_extra): - cmdline += ["ip=enc0:any", "ip=enp0s1:any", "ip=enp0s2:any", "ip=host0:any", "ip=none"] - - if not any(s.startswith("loglevel=") for s in args.kernel_command_line_extra): - cmdline += ["loglevel=4"] - - if not any(s.startswith("SYSTEMD_SULOGIN_FORCE=") for s in args.kernel_command_line_extra): - cmdline += ["SYSTEMD_SULOGIN_FORCE=1"] - - if not any(s.startswith("systemd.hostname=") for s in args.kernel_command_line_extra) and args.machine: - cmdline += [f"systemd.hostname={args.machine}"] - - if args.qemu_cdrom: - # CD-ROMs are read-only so tell systemd to boot in volatile mode. - cmdline += ["systemd.volatile=yes"] - - if not args.qemu_gui: - cmdline += [ - f"systemd.tty.term.console={term}", - f"systemd.tty.columns.console={columns}", - f"systemd.tty.rows.console={lines}", - "console=hvc0", - f"TERM={term}", - ] - - for s in args.kernel_command_line_extra: - key, sep, value = s.partition("=") - if " " in value: - value = f'"{value}"' - cmdline += [key if not sep else f"{key}={value}"] - - return cmdline - - def load_environment(args: argparse.Namespace) -> dict[str, str]: env = { "SYSTEMD_TMPFILES_FORCE_SUBVOL": "0", @@ -4320,10 +4232,6 @@ def load_config(config: argparse.Namespace) -> Config: if config.sign: config.checksum = True - if not config.image: - config.credentials = load_credentials(config) - config.kernel_command_line_extra = load_kernel_command_line_extra(config) - config.environment = load_environment(config) if config.overlay and not config.base_trees: diff --git a/mkosi/qemu.py b/mkosi/qemu.py index e7975b831..cdb6501c4 100644 --- a/mkosi/qemu.py +++ b/mkosi/qemu.py @@ -34,6 +34,7 @@ from mkosi.config import ( QemuDrive, QemuFirmware, QemuVsockCID, + finalize_term, format_bytes, systemd_tool_version, want_selinux_relabel, @@ -769,6 +770,96 @@ def finalize_state(config: Config, cid: int) -> Iterator[None]: p.unlink(missing_ok=True) +def finalize_kernel_command_line_extra(config: Config) -> list[str]: + columns, lines = shutil.get_terminal_size() + term = finalize_term() + + cmdline = [ + "rw", + # Make sure we set up networking in the VM/container. + "systemd.wants=network.target", + # Make sure we don't load vmw_vmci which messes with virtio vsock. + "module_blacklist=vmw_vmci", + f"systemd.tty.term.hvc0={term}", + f"systemd.tty.columns.hvc0={columns}", + f"systemd.tty.rows.hvc0={lines}", + ] + + if not any(s.startswith("ip=") for s in config.kernel_command_line_extra): + cmdline += ["ip=enc0:any", "ip=enp0s1:any", "ip=enp0s2:any", "ip=host0:any", "ip=none"] + + if not any(s.startswith("loglevel=") for s in config.kernel_command_line_extra): + cmdline += ["loglevel=4"] + + if not any(s.startswith("SYSTEMD_SULOGIN_FORCE=") for s in config.kernel_command_line_extra): + cmdline += ["SYSTEMD_SULOGIN_FORCE=1"] + + if ( + not any(s.startswith("systemd.hostname=") for s in config.kernel_command_line_extra) + and config.machine + ): + cmdline += [f"systemd.hostname={config.machine}"] + + if config.qemu_cdrom: + # CD-ROMs are read-only so tell systemd to boot in volatile mode. + cmdline += ["systemd.volatile=yes"] + + if not config.qemu_gui: + cmdline += [ + f"systemd.tty.term.console={term}", + f"systemd.tty.columns.console={columns}", + f"systemd.tty.rows.console={lines}", + "console=hvc0", + f"TERM={term}", + ] + + for s in config.kernel_command_line_extra: + key, sep, value = s.partition("=") + if " " in value: + value = f'"{value}"' + cmdline += [key if not sep else f"{key}={value}"] + + return cmdline + + +def finalize_credentials(config: Config) -> dict[str, str]: + creds = { + "firstboot.locale": "C.UTF-8", + **config.credentials, + } + + if "firstboot.timezone" not in creds: + if find_binary("timedatectl"): + tz = run( + ["timedatectl", "show", "-p", "Timezone", "--value"], + stdout=subprocess.PIPE, + check=False, + ).stdout.strip() + else: + tz = "UTC" + + creds["firstboot.timezone"] = tz + + if "ssh.authorized_keys.root" not in creds: + if config.ssh_certificate: + pubkey = run( + ["openssl", "x509", "-in", config.ssh_certificate, "-pubkey", "-noout"], + stdout=subprocess.PIPE, + env=dict(OPENSSL_CONF="/dev/null"), + ).stdout.strip() + sshpubkey = run( + ["ssh-keygen", "-f", "/dev/stdin", "-i", "-m", "PKCS8"], input=pubkey, stdout=subprocess.PIPE + ).stdout.strip() + creds["ssh.authorized_keys.root"] = sshpubkey + elif config.ssh: + die( + "Ssh= is enabled but no SSH certificate was found", + hint="Run 'mkosi genkey' to automatically create one", + ) + + return creds + + def scope_cmd( name: str, description: str, @@ -1075,9 +1166,9 @@ def run_qemu(args: Args, config: Config) -> None: KernelType.identify(config, kernel) != KernelType.uki or not config.architecture.supports_smbios(firmware) ): - kcl = config.kernel_command_line + config.kernel_command_line_extra + kcl = config.kernel_command_line + finalize_kernel_command_line_extra(config) else: - kcl = config.kernel_command_line_extra + kcl = finalize_kernel_command_line_extra(config) if kernel: cmdline += ["-kernel", kernel] @@ -1108,7 +1199,7 @@ def run_qemu(args: Args, config: Config) -> None: ] # fmt: skip kcl += ["root=root", "rootfstype=virtiofs"] - credentials = dict(config.credentials) + credentials = finalize_credentials(config) def add_virtiofs_mount( sock: Path, dst: PathString, cmdline: list[PathString], credentials: dict[str, str], *, tag: str diff --git a/mkosi/vmspawn.py b/mkosi/vmspawn.py index 56f132b69..8e3284899 100644 --- a/mkosi/vmspawn.py +++ b/mkosi/vmspawn.py @@ -17,6 +17,8 @@ from mkosi.log import die from mkosi.qemu import ( apply_runtime_size, copy_ephemeral, + finalize_credentials, + finalize_kernel_command_line_extra, finalize_qemu_firmware, ) from mkosi.run import run @@ -67,7 +69,7 @@ def run_vmspawn(args: Args, config: Config) -> None: if config.qemu_gui: cmdline += ["--console=gui"] - cmdline += [f"--set-credential={k}:{v}" for k, v in config.credentials.items()] + cmdline += [f"--set-credential={k}:{v}" for k, v in finalize_credentials(config).items()] with contextlib.ExitStack() as stack: fname = stack.enter_context(copy_ephemeral(config, config.output_dir_or_cwd() / config.output)) @@ -104,7 +106,7 @@ def run_vmspawn(args: Args, config: Config) -> None: if config.forward_journal: cmdline += ["--forward-journal", config.forward_journal] - cmdline += [*args.cmdline, *config.kernel_command_line_extra] + cmdline += [*args.cmdline, *finalize_kernel_command_line_extra(config)] run( cmdline,