From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from atuin.qyliss.net (localhost [IPv6:::1]) by atuin.qyliss.net (Postfix) with ESMTP id 2644E1ADD9; Wed, 12 Nov 2025 22:18:46 +0000 (UTC) Received: by atuin.qyliss.net (Postfix, from userid 993) id E236A1AC33; Wed, 12 Nov 2025 22:18:17 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on atuin.qyliss.net X-Spam-Level: X-Spam-Status: No, score=-0.1 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,DMARC_PASS,FREEMAIL_FROM,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 Received: from mail-yx1-xb12f.google.com (mail-yx1-xb12f.google.com [IPv6:2607:f8b0:4864:20::b12f]) by atuin.qyliss.net (Postfix) with ESMTPS id B7D7D1AB38 for ; Wed, 12 Nov 2025 22:18:00 +0000 (UTC) Received: by mail-yx1-xb12f.google.com with SMTP id 956f58d0204a3-63fc72db706so126661d50.2 for ; Wed, 12 Nov 2025 14:18:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762985879; x=1763590679; darn=spectrum-os.org; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:from:to:cc:subject:date:message-id :reply-to; bh=oMHfcsxUjPXhheDpPXlNv1XiPUnM7BzDazC/D/pMCko=; b=HT8GfCKGMXOVRvFRt43ulJzuFqqcb4UkHwwIn5K7zZeZa3jMfQ78KysIaTyE4R5apa GcECe1LDzhb5IorxGvsgaOSwqo1JIRtTtqs5S+qk8lJdai4XEj610kA1z7bzwkS3ir30 twNEf6egsP0pFdfl+Y+cr8p97Y/JISn6J1Gq/YpkN+ZUBw/iZfvV9lvejfEsBNkZPjVk Sm/Y1Gx4S7VQA5EwNzusKS8jh8/UUZ8faCdkdkkS0i1cPtMC2rYpE4NucGsAqlfE4axA M/4hwn4RV3VxYGvW/obiTuXAgUkVtBqTYQ0UHugKiHgPhgRduNgNUoVSNEYhE6X8Hnxo v8qw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762985879; x=1763590679; h=cc:to:in-reply-to:references:message-id:content-transfer-encoding :mime-version:subject:date:from:x-gm-gg:x-gm-message-state:from:to :cc:subject:date:message-id:reply-to; bh=oMHfcsxUjPXhheDpPXlNv1XiPUnM7BzDazC/D/pMCko=; b=D7MWkBr9PLjlcAgsagaar4MH4tbSWKGb+D7HmjZJ6KhyglG32yeAP2G4bJOhz+BAHW lbYVRPvttS0MDzRYZP5iWZLyn4ELkiXghYnYmFu5U09Hf9+SxNOMH1zL0hjrqkAiTnlH N8eq1+VNYFevS+oEhe99ymiG1a7vb3KLadwLOD/U94BO0IHx0rkusM6LeG4UD+/czztO 2Xzd7v3ZH6s6dYIUYBsaZCnl/W/fQ/YS4dg9TSi6CIb0kTWgWZcdHi35bhsrXAaoWtLc km0nmdArcMiScORtNpSWSMcERCj2kaf/XTTvBBkVWf2T6kmlor7XH6YB7PpaV3atpvVr SF+A== X-Gm-Message-State: AOJu0YypUOfR9oaeXe1wFHgq8fXPNYe2SuGzznbve91V2mfuiL15pKsE 6Z4sEQifD2SmhnAJOi5YnnrecsZrvYQods1ay+LPUKbaIAEooi69x7KL/Pe22+nN X-Gm-Gg: ASbGncslIHU1PoBjWdW+KQ4W0Qul+uyM7u360RVvLV1sIv9Qo/wrYlx1Zlua6ZtjpOX KLIIaArxMcOfv5xpmQ3zvg6rglTXhnIbVDwUQR7Mie+MG+C/EsmEP7vaAMu7f3sLrLULbuKCCeO rKsp5dhzpMwqw7zKx/BOJjN+HJDqk5wFcJ6lX65zsoxMSLg+jI2fMaX0uGkvh0w9TBmUAx1xHjs dZVAzoMBYqNzBIDS2LcbI7k/UOG9liAA1hkxf1KMJ0goNgekTgBU2zbmwaDeNjByZvRm8sI1ShX wgbZggqfifec/mAPYi1Vh+etaP/6CG8HxZfJ4YazbRWfTbV/KiGZmIXuXxMV5fsXEkKJrzuPR9m 0owsXZcGXhhcbgNvvzt1gI6AoAKws81c9SYjkn+pCl/LsWgZOuSZfBeJBtP2yI3yclzZzzA7oE8 pKBGMPoZsQ7mh/zOqp0e5kDPqdH02buRqI7+uJ7op021M0tn4h4pvpS0ptMkdKOXLrz6o04DopU OfQJCTT0NxlftymJYW5x5XV X-Google-Smtp-Source: AGHT+IHxY5RrNdsNmPYuf0csPa3QgsCOGVUk9ZhZfcvlvqdoPg3r9/Ca5xSMxrVFiEXXiUu1/QKNsA== X-Received: by 2002:a05:690e:23c3:b0:63e:210d:380c with SMTP id 956f58d0204a3-64101b3b981mr3265116d50.57.1762985878831; Wed, 12 Nov 2025 14:17:58 -0800 (PST) Received: from localhost.localdomain (h96-60-249-169.cncrtn.broadband.dynamic.tds.net. [96.60.249.169]) by smtp.gmail.com with UTF8SMTPSA id 956f58d0204a3-6410ea035b1sm72497d50.8.2025.11.12.14.17.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 12 Nov 2025 14:17:58 -0800 (PST) From: Demi Marie Obenour Date: Wed, 12 Nov 2025 17:15:00 -0500 Subject: [PATCH v2 6/8] Support updates via systemd-sysupdate MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20251112-updates-v2-6-88d96bf81b79@gmail.com> References: <20251112-updates-v2-0-88d96bf81b79@gmail.com> In-Reply-To: <20251112-updates-v2-0-88d96bf81b79@gmail.com> To: Spectrum OS Development X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1762985694; l=23173; i=demiobenour@gmail.com; s=20250729; h=from:subject:message-id; bh=Na3BLyYEUOu1MpFq+2t7DW2Oz2Rq//amf8A7ZqvKJWg=; b=L7iVInkkdLojDP9mG8M2q97+MPpBG0Yz6zSd8980y+5I8fEPEm3mIUVaVOuhD1gnQzDe0eCCZ EhBdiLwNrOHD9Wz2naGo4bRdevsbBwKAAijgwz1dAu/05iwi7QEFr0X X-Developer-Key: i=demiobenour@gmail.com; a=ed25519; pk=X57Q4/YQDj9t4SBeKaDwvXYKB6quZJVx/DE2Ly2out0= Message-ID-Hash: ZY6IOB3XLQUL45NS2KWC2NPAKKFZPOKB X-Message-ID-Hash: ZY6IOB3XLQUL45NS2KWC2NPAKKFZPOKB X-MailFrom: demiobenour@gmail.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-devel.spectrum-os.org-0; header-match-devel.spectrum-os.org-1; header-match-devel.spectrum-os.org-2; header-match-devel.spectrum-os.org-3; header-match-devel.spectrum-os.org-4; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Demi Marie Obenour , Alyssa Ross X-Mailman-Version: 3.3.9 Precedence: list List-Id: Patches and low-level development discussion Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Include a new 'update' command to update the system. This works as follows: 1. Take a global, system-wide lock. 2. Create a BTRFS subvolume for the sys.updates VM to write the updates. 3. Bind-mount this subvolume into the VM's shared directory. 4. Start sys.appvm-updates to get the updates. 5. Wait for the VM to shut down. 6. Take a BTRFS snapshot of the subvolume. 7. Call syncfs() to flush all of the data on the subvolume. 8. Inspect the contents of the subvolume. Check that everything is a regular file and that the names are reasonable. Check that SHA256SUMS and SHA256SUMS.gpg are present. 9. Call systemd-sysupdate to run the actual update. sys.appvm-updates uses host-provided information to fetch the update. This allows editing files on the host to change the update URL and signing key. Signed-off-by: Demi Marie Obenour --- host/rootfs/Makefile | 2 + host/rootfs/default.nix | 28 ++++++- host/rootfs/file-list.mk | 4 + host/rootfs/image/etc/fstab | 1 + .../image/etc/sysupdate.d/50-verity.transfer | 20 +++++ host/rootfs/image/etc/sysupdate.d/60-root.transfer | 20 +++++ .../image/etc/sysupdate.d/70-kernel.transfer | 20 +++++ host/rootfs/image/usr/bin/update | 89 ++++++++++++++++++++++ host/rootfs/os-release.in | 13 ++++ host/rootfs/os-release.in.license | 2 + host/rootfs/updatevm-url-env | 3 + host/rootfs/vm-sysupdate.d/50-verity.transfer | 18 +++++ host/rootfs/vm-sysupdate.d/60-root.transfer | 18 +++++ host/rootfs/vm-sysupdate.d/70-kernel.transfer | 18 +++++ lib/config.default.nix | 2 + lib/config.nix | 11 ++- lib/fake-update-signing-key.gpg | 1 + lib/fake-update-signing-key.gpg.license | 2 + release/live/default.nix | 4 +- release/live/shell.nix | 3 +- vm/app/updates.nix | 37 +++++++++ 21 files changed, 309 insertions(+), 7 deletions(-) diff --git a/host/rootfs/Makefile b/host/rootfs/Makefile index 76c8ff1628454d769e09e0bc915d198fece080e0..86f48d4aa196ff35fb0b3e4224201e9a2566626b 100644 --- a/host/rootfs/Makefile +++ b/host/rootfs/Makefile @@ -10,6 +10,7 @@ include file-list.mk dest = build DIRS = \ + boot \ dev \ etc/s6-linux-init/env \ etc/s6-linux-init/run-image/configs \ @@ -56,6 +57,7 @@ BUILD_FILES = build/etc/s6-rc $(dest)/timestamp: ../../scripts/make-erofs.sh $(PACKAGES_FILE) $(FILES) $(BUILD_FILES) build/empty build/fifo file-list.mk $(dest) { \ cat $(PACKAGES_FILE) ;\ + printf '%s\n%s\n' "$$UPDATE_SIGNING_KEY" /etc/systemd/import-pubring.gpg; \ for file in $(FILES) $(LINKS); do printf '%s\n%s\n' $$file "$${file#image/}"; done ;\ for file in $(BUILD_FILES); do printf '%s\n%s\n' $$file $${file#build/}; done ;\ printf 'build/empty\n%s\n' $(DIRS) ;\ diff --git a/host/rootfs/default.nix b/host/rootfs/default.nix index b574b8ddf5858867156507429a55b7f537e3c485..0a7638f8d78cf36592c2721d059bc867b04f233c 100644 --- a/host/rootfs/default.nix +++ b/host/rootfs/default.nix @@ -5,6 +5,7 @@ import ../../lib/call-package.nix ( { callSpectrumPackage, spectrum-build-tools, src , pkgsMusl, pkgsStatic, linux_latest +, config }: pkgsStatic.callPackage ( @@ -13,6 +14,7 @@ pkgsStatic.callPackage ( , busybox, cloud-hypervisor, cryptsetup, dbus, execline, inkscape , iproute2, inotify-tools, jq, mdevd, s6, s6-linux-init, socat , util-linuxMinimal, virtiofsd, xorg, xdg-desktop-portal-spectrum-host +, btrfs-progs }: let @@ -36,6 +38,7 @@ let cloud-hypervisor cryptsetup dbus execline inotify-tools iproute2 jq mdevd s6 s6-linux-init s6-rc socat spectrum-host-tools virtiofsd xdg-desktop-portal-spectrum-host + btrfs-progs (busybox.override { # Use a separate file as it is a bit too big. @@ -79,11 +82,24 @@ let appvm-firefox = callSpectrumPackage ../../vm/app/firefox.nix {}; appvm-foot = callSpectrumPackage ../../vm/app/foot.nix {}; appvm-gnome-text-editor = callSpectrumPackage ../../vm/app/gnome-text-editor.nix {}; + appvm-updates = callSpectrumPackage ../../vm/app/updates.nix {}; }; packagesSysroot = runCommand "packages-sysroot" { depsBuildBuild = [ inkscape ]; nativeBuildInputs = [ xorg.lndir ]; + env = { + VERSION = config.version; + UPDATE_URL = config.update-url; + }; + src = fileset.toSource { + root = ./.; + fileset = fileset.intersection src (fileset.unions [ + ./vm-sysupdate.d + ./os-release.in + ./updatevm-url-env + ]); + }; } '' mkdir -p $out/usr/bin $out/usr/share/dbus-1/services \ $out/usr/share/icons/hicolor/20x20/apps @@ -95,8 +111,7 @@ let done # If systemd-pull is missing systemd-sysupdate will fail with a - # very confusing error message. If systemd-sysupdate doesn't work, - # users will not be able to receive an update that fixes the problem. + # very confusing error message. for i in sysupdate pull; do if ! cat -- "$out/usr/lib/systemd/systemd-$i" > /dev/null; then echo "link to systemd-$i didn't get installed" >&2 @@ -118,6 +133,14 @@ let ln -st $out/usr/share/dbus-1/services \ ${pkgsGui.xdg-desktop-portal-gtk}/share/dbus-1/services/org.freedesktop.impl.portal.desktop.gtk.service + mkdir -p -- "$out/etc/updatevm/sysupdate.d" + substitute "$src/os-release.in" "$out/etc/os-release" --subst-var VERSION + for d in "$src/vm-sysupdate.d"/*.transfer; do + result_file=''${d#"$src/vm-sysupdate.d/"} + substitute "$d" "$out/etc/updatevm/sysupdate.d/$result_file" --subst-var UPDATE_URL + done + substitute "$src/updatevm-url-env" "$out/etc/updatevm/url-env" --subst-var UPDATE_URL + ln -st "$out/usr/bin" ${util-linuxMinimal}/bin/* ${concatStrings (mapAttrsToList (name: path: '' @@ -147,6 +170,7 @@ stdenvNoCC.mkDerivation { printf "%s\n/\n" ${packagesSysroot} >$out sed p ${writeClosure [ packagesSysroot] } >>$out ''; + UPDATE_SIGNING_KEY = config.update-signing-key; }; makeFlags = [ "dest=$(out)" ]; diff --git a/host/rootfs/file-list.mk b/host/rootfs/file-list.mk index 9acaa1d90bed674814775becf89c1c847d0ce3e3..e69dc4fb5ead88ed9ed16848b3c6cba9bbad89a6 100644 --- a/host/rootfs/file-list.mk +++ b/host/rootfs/file-list.mk @@ -42,6 +42,9 @@ FILES = \ image/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/notification-fd \ image/etc/s6-linux-init/run-image/service/xdg-desktop-portal-spectrum-host/template/run \ image/etc/s6-linux-init/scripts/rc.init \ + image/etc/sysupdate.d/50-verity.transfer \ + image/etc/sysupdate.d/60-root.transfer \ + image/etc/sysupdate.d/70-kernel.transfer \ image/etc/udev/rules.d/99-spectrum.rules \ image/etc/xdg/weston/autolaunch \ image/etc/xdg/weston/weston.ini \ @@ -49,6 +52,7 @@ FILES = \ image/usr/bin/create-vm-dependencies \ image/usr/bin/run-appimage \ image/usr/bin/run-vmm \ + image/usr/bin/update \ image/usr/bin/vm-console \ image/usr/bin/vm-import \ image/usr/bin/vm-start \ diff --git a/host/rootfs/image/etc/fstab b/host/rootfs/image/etc/fstab index 6a82ecc85090a37b13603b29f74ca6e554a28c33..78cec99f29dda993ad97048771097121a0e42622 100644 --- a/host/rootfs/image/etc/fstab +++ b/host/rootfs/image/etc/fstab @@ -4,3 +4,4 @@ proc /proc proc defaults 0 0 devpts /dev/pts devpts defaults,gid=4,mode=620 0 0 tmpfs /dev/shm tmpfs defaults 0 0 sysfs /sys sysfs defaults 0 0 +tmpfs /tmp tmpfs defaults,mode=0700 0 0 diff --git a/host/rootfs/image/etc/sysupdate.d/50-verity.transfer b/host/rootfs/image/etc/sysupdate.d/50-verity.transfer new file mode 100644 index 0000000000000000000000000000000000000000..120713218eb37399af41bcff004dc640cd58fec2 --- /dev/null +++ b/host/rootfs/image/etc/sysupdate.d/50-verity.transfer @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour + +# Uses example code from systemd man pages which is under MIT-0 +# (no attribution required). +[Transfer] +ProtectVersion=%A + +[Source] +Type=url-file +Path=file:///run/updater +MatchPattern=Spectrum_@v.verity + +[Target] +Type=partition +Path=auto +MatchPattern=Spectrum_@v.verity +MatchPartitionType=root-verity +PartitionFlags=0 +ReadOnly=1 diff --git a/host/rootfs/image/etc/sysupdate.d/60-root.transfer b/host/rootfs/image/etc/sysupdate.d/60-root.transfer new file mode 100644 index 0000000000000000000000000000000000000000..e71e1ca263401c5f65ac0ed4d90ef7d22987667e --- /dev/null +++ b/host/rootfs/image/etc/sysupdate.d/60-root.transfer @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour + +# Uses example code from systemd man pages which is under MIT-0 +# (no attribution required). +[Transfer] +ProtectVersion=%A + +[Source] +Type=url-file +Path=file:///run/updater +MatchPattern=Spectrum_@v.root + +[Target] +Type=partition +Path=auto +MatchPattern=Spectrum_@v +MatchPartitionType=root +PartitionFlags=0 +ReadOnly=1 diff --git a/host/rootfs/image/etc/sysupdate.d/70-kernel.transfer b/host/rootfs/image/etc/sysupdate.d/70-kernel.transfer new file mode 100644 index 0000000000000000000000000000000000000000..e4190587a6bb127cb7315f38d59e48cf279318a4 --- /dev/null +++ b/host/rootfs/image/etc/sysupdate.d/70-kernel.transfer @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour + +# Uses example code from systemd man pages which is under MIT-0 +# (no attribution required). +[Transfer] +ProtectVersion=%A + +[Source] +Type=url-file +Path=file:///run/updater +MatchPattern=Spectrum_@v.efi + +[Target] +Type=regular-file +Path=/EFI/Linux +PathRelativeTo=boot +MatchPattern=Spectrum_@v.efi +Mode=0644 +InstancesMax=2 diff --git a/host/rootfs/image/usr/bin/update b/host/rootfs/image/usr/bin/update new file mode 100755 index 0000000000000000000000000000000000000000..cbbf8ad8634a7771a0a5f7d6586ee88cdc0672a8 --- /dev/null +++ b/host/rootfs/image/usr/bin/update @@ -0,0 +1,89 @@ +#!/bin/execlineb -WS1 +# SPDX-License-Identifier: EUPL-1.2+ +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour + +# Steps: +# +# 1. Take a global, system-wide lock. +# 2. Create a BTRFS subvolume for the sys.updates VM to write the updates. +# 3. Bind-mount this subvolume into the VM's shared directory. +# 4. Start sys.updates to get the updates. +# 5. Wait for the VM to shut down. +# 6. Take a BTRFS snapshot of the subvolume. +# 7. Call syncfs() to flush all of the data on the subvolume. +# 8. Inspect the contents of the subvolume. +# Check that everything is a regular file and that the names are reasonable. +# Check that SHA256SUMS and SHA256SUMS.gpg are present. +# 9. Call systemd-sysupdate to run the actual update. + +if { mkdir -p -m 0700 /run/updater } +s6-setlock /run/update-lock +foreground { redirfd -w 2 /dev/null rmdir -- $1 } +if { umask 0077 mkdir -p -- $1 } +cd $1 +foreground { + # If this exists already that is okay. + foreground { redirfd -w 2 /dev/null btrfs subvolume create -- shared } + + # Snapshot directory may have files or directories with untrusted names. + # Redirect its output to /dev/null to avoid printing them to the console. + ifelse -n { redirfd -w 2 /dev/null rm -rf -- snapshot } { + foreground { redirfd -w 2 echo "Cannot remove snapshot directory" } + exit 1 + } + + backtick -E update_vm_id_ { + backtick -E id_path { readlink /run/vm/by-name/sys.appvm-updates } + basename -- $id_path + } + + multisubstitute { + define fsdir /run/vm/by-id/${update_vm_id_}/fs + define update_vm_id ${update_vm_id_} + define svcdir /run/service/vmm/instance/${update_vm_id_} + } + + # $fsdir is read-only to the guest, but read-write to the host. + # Directories bind-mounted into it are read-write to the guest. + # See etc/s6-linux-init/run-image/service/vhost-user-fs/template/run + # for details. + + # Set up /etc with what the VM needs. The VM will overlay this + # on its own /etc. + if { rm -rf -- ${fsdir}/etc } + if { umask 022 mkdir -p -- ${fsdir}/updates ${fsdir}/etc/systemd } + if { cp -R -- /etc/updatevm/sysupdate.d /etc/updatevm/url-env ${fsdir}/etc } + if { cp -- /etc/systemd/import-pubring.gpg ${fsdir}/etc/systemd } + + # If the directory is already mounted, unmount it. This prevents a + # confusing error from mount. + foreground { redirfd -w 2 /dev/null umount -- ${fsdir}/updates } + + # Share the update directory with the VM. + if { mount --bind -- shared ${fsdir}/updates } + + # Start the update VM. + if { vm-start $update_vm_id } + + # Wait for the VM to exit. + if { s6-svwait -D ${svcdir} } + + # Remove the bind mount. + if { umount -- ${fsdir}/updates } + + # Ensure that the VM cannot change the directory + # while systemd-sysupdate is using it. + if { btrfs subvolume snapshot -- shared snapshot } + + # Perform the update in a separate mount namespace. + unshare --mount + if { mount --bind -o ro -- snapshot /run/updater } + + # Validate the update directory. + if { updates-dir-check /run/updater } + /usr/lib/systemd/systemd-sysupdate update +} +importas -i sysupdate_exit_status ? +# Clean up. +foreground { btrfs subvolume delete -- snapshot } +exit $sysupdate_exit_status diff --git a/host/rootfs/os-release.in b/host/rootfs/os-release.in new file mode 100644 index 0000000000000000000000000000000000000000..078e8f15ea73555b606e7f23ed34a3e0e3299f0a --- /dev/null +++ b/host/rootfs/os-release.in @@ -0,0 +1,13 @@ +NAME="Spectrum OS" +ID=spectrum +PRETTY_NAME="Spectrum @VERSION@" +VERSION=@VERSION@ +VERSION_ID=@VERSION@ +IMAGE_ID=spectrum-root +IMAGE_VERSION=@VERSION@ +RELEASE_TYPE=development +HOME_URL="https://spectrum-os.org" +BUG_REPORT_URL="mailto:discuss@spectrum-os.org" +ANSI_COLOR="1;34" +VENDOR_NAME=Spectrum +VENDOR_URL="https://spectrum-os.org" diff --git a/host/rootfs/os-release.in.license b/host/rootfs/os-release.in.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/host/rootfs/os-release.in.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/host/rootfs/updatevm-url-env b/host/rootfs/updatevm-url-env new file mode 100644 index 0000000000000000000000000000000000000000..a1a9f6f86509d4c8bab2d5eef3653f732b887ad5 --- /dev/null +++ b/host/rootfs/updatevm-url-env @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour +UPDATE_URL="@UPDATE_URL@" diff --git a/host/rootfs/vm-sysupdate.d/50-verity.transfer b/host/rootfs/vm-sysupdate.d/50-verity.transfer new file mode 100644 index 0000000000000000000000000000000000000000..ae715dcc11a9711d8f3cab3801cd95ecc0fad11b --- /dev/null +++ b/host/rootfs/vm-sysupdate.d/50-verity.transfer @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour + +# Uses example code from systemd man pages which is under MIT-0 +# (no attribution required). +[Transfer] +Verify=yes + +[Source] +Type=url-file +Path=@UPDATE_URL@ +MatchPattern=Spectrum_@v.verity + +[Target] +Type=regular-file +Path=/run/virtiofs/virtiofs0/updates +MatchPattern=Spectrum_@v.verity +Mode=0644 diff --git a/host/rootfs/vm-sysupdate.d/60-root.transfer b/host/rootfs/vm-sysupdate.d/60-root.transfer new file mode 100644 index 0000000000000000000000000000000000000000..b2488dfd21197c72f9c15467e293d335c5b85ee4 --- /dev/null +++ b/host/rootfs/vm-sysupdate.d/60-root.transfer @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour + +# Uses example code from systemd man pages which is under MIT-0 +# (no attribution required). +[Transfer] +Verify=yes + +[Source] +Type=url-file +Path=@UPDATE_URL@ +MatchPattern=Spectrum_@v.root + +[Target] +Type=regular-file +Path=/run/virtiofs/virtiofs0/updates +MatchPattern=Spectrum_@v.root +Mode=0644 diff --git a/host/rootfs/vm-sysupdate.d/70-kernel.transfer b/host/rootfs/vm-sysupdate.d/70-kernel.transfer new file mode 100644 index 0000000000000000000000000000000000000000..cb181239d71c5a6d0a5b3652d5534a23eda64183 --- /dev/null +++ b/host/rootfs/vm-sysupdate.d/70-kernel.transfer @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour + +# Uses example code from systemd man pages which is under MIT-0 +# (no attribution required). +[Transfer] +Verify=yes + +[Source] +Type=url-file +Path=@UPDATE_URL@ +MatchPattern=Spectrum_@v.efi + +[Target] +Type=regular-file +Path=/run/virtiofs/virtiofs0/updates +MatchPattern=Spectrum_@v.efi +Mode=0644 diff --git a/lib/config.default.nix b/lib/config.default.nix index 489c231490a8b66aa01f50053b25646060f7f963..e53b01f1259543b988458a14b3014eb8ca29e90d 100644 --- a/lib/config.default.nix +++ b/lib/config.default.nix @@ -5,4 +5,6 @@ pkgsFun = import ./nixpkgs.default.nix; pkgsArgs = {}; version = "0.0.0"; + update-url = "https://your-spectrum-os-update-server.invalid/download-directory"; + update-signing-key = ./fake-update-signing-key.gpg; } diff --git a/lib/config.nix b/lib/config.nix index 01bcfa2bb2d5c412e212f5a60d9032e89c8a7442..5b6b95013734202b7e2e01d5ffce313080658006 100644 --- a/lib/config.nix +++ b/lib/config.nix @@ -1,5 +1,6 @@ -# SPDX-FileCopyrightText: 2023 Alyssa Ross # SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2024 Alyssa Ross +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour let customConfigPath = builtins.tryEval ; @@ -17,5 +18,11 @@ let callConfig = config: if builtins.typeOf config == "lambda" then config { inherit default; } else config; + finalConfig = default // callConfig config; in - default // callConfig config; + finalConfig // { + update-signing-key = builtins.path { + name = "signing-key"; + path = finalConfig.update-signing-key; + }; + } diff --git a/lib/fake-update-signing-key.gpg b/lib/fake-update-signing-key.gpg new file mode 100644 index 0000000000000000000000000000000000000000..b4c15467614ee15deef02af05f4c6554a1f7a013 --- /dev/null +++ b/lib/fake-update-signing-key.gpg @@ -0,0 +1 @@ +NOT A VALID KEY - UPDATES WILL NOT WORK diff --git a/lib/fake-update-signing-key.gpg.license b/lib/fake-update-signing-key.gpg.license new file mode 100644 index 0000000000000000000000000000000000000000..c4a0586a407fe14c3e0855749a7524ac3871dda4 --- /dev/null +++ b/lib/fake-update-signing-key.gpg.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Demi Marie Obenour diff --git a/release/live/default.nix b/release/live/default.nix index dc649732ffa46a998a4a66360aa8ff7ef6bccae0..581420da9acf855d4b3d9ececc1ef406f742fd75 100644 --- a/release/live/default.nix +++ b/release/live/default.nix @@ -7,7 +7,7 @@ import ../../lib/call-package.nix ( { callSpectrumPackage, spectrum-build-tools, rootfs, src , lib, pkgsStatic, stdenvNoCC , cryptsetup, dosfstools, jq, mtools, util-linux -, systemdUkify, version, efi +, systemdUkify, config, efi }: let @@ -49,7 +49,7 @@ stdenv.mkDerivation { SYSTEMD_BOOT_EFI = "${efi.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi"; EFI_IMAGE = efi; EFINAME = "BOOT${toUpper efiArch}.EFI"; - VERSION = version; + VERSION = config.version; }; buildFlags = [ "dest=$(out)" ]; diff --git a/release/live/shell.nix b/release/live/shell.nix index 05250525defa0e8a10cde45b5e49f878fcec599f..4ca8f53fdbbc11072fe226b9036d69de8a870249 100644 --- a/release/live/shell.nix +++ b/release/live/shell.nix @@ -1,7 +1,7 @@ # SPDX-License-Identifier: MIT # SPDX-FileCopyrightText: 2021-2024 Alyssa Ross -import ../../lib/call-package.nix ({ callSpectrumPackage, stdenv, qemu_kvm, rootfs }: +import ../../lib/call-package.nix ({ callSpectrumPackage, stdenv, qemu_kvm, rootfs, config }: (callSpectrumPackage ./. {}).overrideAttrs ( { nativeBuildInputs ? [], env ? {}, ... }: @@ -11,6 +11,7 @@ import ../../lib/call-package.nix ({ callSpectrumPackage, stdenv, qemu_kvm, root env = env // { ROOT_FS = rootfs; OVMF_CODE = "${qemu_kvm}/share/qemu/edk2-${stdenv.hostPlatform.qemuArch}-code.fd"; + VERSION = config.version; }; } )) (_: {}) diff --git a/vm/app/updates.nix b/vm/app/updates.nix new file mode 100644 index 0000000000000000000000000000000000000000..d2c1e5fcb35b37c7ed8a173f19b97894a36a7f0c --- /dev/null +++ b/vm/app/updates.nix @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2023 Alyssa Ross +# SPDX-FileCopyrightText: 2025 Demi Marie Obenour + +import ../../lib/call-package.nix ( +{ callSpectrumPackage, config, curl, lib, src +, runCommand, systemd, writeScript +}: + +let + update-url = config.update-url; + mountpoint = "/run/virtiofs/virtiofs0"; + sysupdate-path = "${systemd}/lib/systemd/systemd-sysupdate"; + runner = writeScript "update-run-script" + '' + #!/usr/bin/execlineb -P + if { mount -toverlay -olowerdir=${mountpoint}/etc:/etc -- overlay /etc } + envfile ${mountpoint}/etc/url-env + importas -i update_url UPDATE_URL + if { ${sysupdate-path} update } + if { ${curl}/bin/curl -L --proto =http,https + -o ${mountpoint}/updates/SHA256SUMS.gpg ''${update_url}/SHA256SUMS.gpg } + # systemd-sysupdate recently went from needing SHA256SUMS.gpg to SHA256SUMS.sha256.asc. + # I (Demi) have no need if this is intentional or a bug. I also have no idea if this + # behavior will stay unchanged in the future. Therefore, create both files and let + # systemd-sysupdate ignore the one it isn't interested in. + if { ln -f ${mountpoint}/updates/SHA256SUMS.gpg ${mountpoint}/updates/SHA256SUMS.sha256.asc } + ${curl}/bin/curl -L --proto =http,https + -o ${mountpoint}/updates/SHA256SUMS ''${update_url}/SHA256SUMS + ''; +in + +callSpectrumPackage ../make-vm.nix {} { + providers.net = [ "sys.netvm" ]; + type = "nix"; + run = "${runner}"; +}) (_: {}) -- 2.51.2