From 7e522a81ef99a2e7ce0b86bd2c5fd33233e464fe Mon Sep 17 00:00:00 2001 From: nikstur Date: Mon, 17 Jul 2023 17:19:44 +0200 Subject: nixos/tests: refactor gpg-keyring test utility --- nixos/tests/common/gpg-keyring.nix | 23 +++++++++++++++++++++++ nixos/tests/systemd-nspawn.nix | 22 +--------------------- 2 files changed, 24 insertions(+), 21 deletions(-) create mode 100644 nixos/tests/common/gpg-keyring.nix diff --git a/nixos/tests/common/gpg-keyring.nix b/nixos/tests/common/gpg-keyring.nix new file mode 100644 index 0000000000000..50b5846b0965c --- /dev/null +++ b/nixos/tests/common/gpg-keyring.nix @@ -0,0 +1,23 @@ +{ pkgs, ... }: + +pkgs.runCommand "gpg-keyring" { nativeBuildInputs = [ pkgs.gnupg ]; } '' + mkdir -p $out + export GNUPGHOME=$out + cat > foo < $out/pubkey.gpg +'' diff --git a/nixos/tests/systemd-nspawn.nix b/nixos/tests/systemd-nspawn.nix index bc77ee2a4d158..1a4251ef069e8 100644 --- a/nixos/tests/systemd-nspawn.nix +++ b/nixos/tests/systemd-nspawn.nix @@ -1,26 +1,6 @@ import ./make-test-python.nix ({pkgs, lib, ...}: let - gpgKeyring = (pkgs.runCommand "gpg-keyring" { buildInputs = [ pkgs.gnupg ]; } '' - mkdir -p $out - export GNUPGHOME=$out - cat > foo < $out/pubkey.gpg - ''); + gpgKeyring = import ./common/gpg-keyring.nix { inherit pkgs; }; nspawnImages = (pkgs.runCommand "localhost" { buildInputs = [ pkgs.coreutils pkgs.gnupg ]; } '' mkdir -p $out -- cgit 1.4.1 From 5750660f2562b5f095c560a33b80782ac9d1aadc Mon Sep 17 00:00:00 2001 From: nikstur Date: Tue, 18 Jul 2023 11:05:16 +0200 Subject: nixos/tests: use sensible key type for gpg keyring If someone blindly copies this code, at least they have a sensible key type. --- nixos/tests/common/gpg-keyring.nix | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/nixos/tests/common/gpg-keyring.nix b/nixos/tests/common/gpg-keyring.nix index 50b5846b0965c..fb8d07b1183e0 100644 --- a/nixos/tests/common/gpg-keyring.nix +++ b/nixos/tests/common/gpg-keyring.nix @@ -6,10 +6,8 @@ pkgs.runCommand "gpg-keyring" { nativeBuildInputs = [ pkgs.gnupg ]; } '' cat > foo < Date: Mon, 17 Jul 2023 17:20:20 +0200 Subject: nixos/systemd-sysupdate: init --- nixos/doc/manual/release-notes/rl-2311.section.md | 1 + nixos/modules/module-list.nix | 1 + nixos/modules/system/boot/systemd/sysupdate.nix | 142 ++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 nixos/modules/system/boot/systemd/sysupdate.nix diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md index d8faa5e11dbe9..add72886e186c 100644 --- a/nixos/doc/manual/release-notes/rl-2311.section.md +++ b/nixos/doc/manual/release-notes/rl-2311.section.md @@ -34,6 +34,7 @@ - [ebusd](https://ebusd.eu), a daemon for handling communication with eBUS devices connected to a 2-wire bus system (“energy bus” used by numerous heating systems). Available as [services.ebusd](#opt-services.ebusd.enable). +- [systemd-sysupdate](https://www.freedesktop.org/software/systemd/man/systemd-sysupdate.html), atomically updates the host OS, container images, portable service images or other sources. Available as [systemd.sysupdate](opt-systemd.sysupdate). ## Backward Incompatibilities {#sec-release-23.11-incompatibilities} diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index d1de6da182d2f..f7acbb59dc2c1 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1398,6 +1398,7 @@ ./system/boot/systemd/oomd.nix ./system/boot/systemd/repart.nix ./system/boot/systemd/shutdown.nix + ./system/boot/systemd/sysupdate.nix ./system/boot/systemd/tmpfiles.nix ./system/boot/systemd/user.nix ./system/boot/systemd/userdbd.nix diff --git a/nixos/modules/system/boot/systemd/sysupdate.nix b/nixos/modules/system/boot/systemd/sysupdate.nix new file mode 100644 index 0000000000000..2921e97f7560a --- /dev/null +++ b/nixos/modules/system/boot/systemd/sysupdate.nix @@ -0,0 +1,142 @@ +{ config, lib, pkgs, utils, ... }: + +let + cfg = config.systemd.sysupdate; + + format = pkgs.formats.ini { }; + + listOfDefinitions = lib.mapAttrsToList + (name: format.generate "${name}.conf") + (lib.filterAttrs (k: _: !(lib.hasPrefix "_" k)) cfg.transfers); + + definitionsDirectory = pkgs.runCommand "sysupdate.d" { } '' + mkdir -p $out + ${(lib.concatStringsSep "\n" + (map (pkg: "cp ${pkg} $out/${pkg.name}") listOfDefinitions) + )} + ''; +in +{ + options.systemd.sysupdate = { + + enable = lib.mkEnableOption (lib.mdDoc "systemd-sysupdate") // { + description = lib.mdDoc '' + Atomically update the host OS, container images, portable service + images or other sources. + + If enabled, updates are triggered in regular intervals via a + `systemd.timer` unit. + + Please see + + for more details. + ''; + }; + + timerConfig = utils.systemdUtils.unitOptions.timerOptions.options.timerConfig // { + default = { }; + description = lib.mdDoc '' + The timer configuration for performing the update. + + By default, the upstream configuration is used: + + ''; + }; + + reboot = { + enable = lib.mkEnableOption (lib.mdDoc "automatically rebooting after an update") // { + description = lib.mdDoc '' + Whether to automatically reboot after an update. + + If set to `true`, the system will automatically reboot via a + `systemd.timer` unit but only after a new version was installed. + + This uses a unit completely separate from the one performing the + update because it is typically advisable to download updates + regularly while the system is up, but delay reboots until the + appropriate time (i.e. typically at night). + + Set this to `false` if you do not want to reboot after an update. This + is useful when you update a container image or another source where + rebooting is not necessary in order to finalize the update. + ''; + }; + + timerConfig = utils.systemdUtils.unitOptions.timerOptions.options.timerConfig // { + default = { }; + description = lib.mdDoc '' + The timer configuration for rebooting after an update. + + By default, the upstream configuration is used: + + ''; + }; + }; + + transfers = lib.mkOption { + type = with lib.types; attrsOf format.type; + default = { }; + example = { + "10-uki.conf" = { + Transfer = { + ProtectVersion = "%A"; + }; + + Source = { + Type = "url-file"; + Path = "https://download.example.com/"; + MatchPattern = "nixos_@v.efi.xz"; + }; + + Target = { + Type = "regular-file"; + Path = "/EFI/Linux"; + PathRelativeTo = "boot"; + MatchPattern = '' + nixos_@v+@l-@d.efi"; \ + nixos_@v+@l.efi \ + nixos_@v.efi + ''; + Mode = "0444"; + TriesLeft = 3; + TriesDone = 0; + InstancesMax = 2; + }; + }; + }; + description = lib.mdDoc '' + Specify transfers as a set of the names of the transfer files as the + key and the configuration as its value. The configuration can use all + upstream options. See + + for all available options. + ''; + }; + + }; + + config = lib.mkIf cfg.enable { + + systemd.additionalUpstreamSystemUnits = [ + "systemd-sysupdate.service" + "systemd-sysupdate.timer" + "systemd-sysupdate-reboot.service" + "systemd-sysupdate-reboot.timer" + ]; + + systemd.timers = { + "systemd-sysupdate" = { + wantedBy = [ "timers.target" ]; + timerConfig = cfg.timerConfig; + }; + "systemd-sysupdate-reboot" = lib.mkIf cfg.reboot.enable { + wantedBy = [ "timers.target" ]; + timerConfig = cfg.reboot.timerConfig; + }; + }; + + environment.etc."sysupdate.d".source = definitionsDirectory; + }; + + meta.maintainers = with lib.maintainers; [ nikstur ]; +} -- cgit 1.4.1 From e6862fae8fde54eba7e50367fd8a0ff7ebbfc1f3 Mon Sep 17 00:00:00 2001 From: nikstur Date: Mon, 17 Jul 2023 17:20:32 +0200 Subject: nixos/tests/systemd-sysupdate: init --- nixos/tests/all-tests.nix | 1 + nixos/tests/systemd-sysupdate.nix | 66 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 nixos/tests/systemd-sysupdate.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 9ab0bfb21f4a9..c9ce2ebe91f33 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -772,6 +772,7 @@ in { systemd-portabled = handleTest ./systemd-portabled.nix {}; systemd-repart = handleTest ./systemd-repart.nix {}; systemd-shutdown = handleTest ./systemd-shutdown.nix {}; + systemd-sysupdate = runTest ./systemd-sysupdate.nix; systemd-timesyncd = handleTest ./systemd-timesyncd.nix {}; systemd-user-tmpfiles-rules = handleTest ./systemd-user-tmpfiles-rules.nix {}; systemd-misc = handleTest ./systemd-misc.nix {}; diff --git a/nixos/tests/systemd-sysupdate.nix b/nixos/tests/systemd-sysupdate.nix new file mode 100644 index 0000000000000..37811605dbb2b --- /dev/null +++ b/nixos/tests/systemd-sysupdate.nix @@ -0,0 +1,66 @@ +# Tests downloading a signed update aritfact from a server to a target machine. +# This test does not rely on the `systemd.timer` units provided by the +# `systemd-sysupdate` module but triggers the `systemd-sysupdate` service +# manually to make the test more robust. + +{ lib, pkgs, ... }: + +let + gpgKeyring = import ./common/gpg-keyring.nix { inherit pkgs; }; +in +{ + name = "systemd-sysupdate"; + + meta.maintainers = with lib.maintainers; [ nikstur ]; + + nodes = { + server = { pkgs, ... }: { + networking.firewall.enable = false; + services.nginx = { + enable = true; + virtualHosts."server" = { + root = pkgs.runCommand "sysupdate-artifacts" { buildInputs = [ pkgs.gnupg ]; } '' + mkdir -p $out + cd $out + + echo "nixos" > nixos_1.efi + sha256sum nixos_1.efi > SHA256SUMS + + export GNUPGHOME="$(mktemp -d)" + cp -R ${gpgKeyring}/* $GNUPGHOME + + gpg --batch --sign --detach-sign --output SHA256SUMS.gpg SHA256SUMS + ''; + }; + }; + }; + + target = { + systemd.sysupdate = { + enable = true; + transfers = { + "uki" = { + Source = { + Type = "url-file"; + Path = "http://server/"; + MatchPattern = "nixos_@v.efi"; + }; + Target = { + Path = "/boot/EFI/Linux"; + MatchPattern = "nixos_@v.efi"; + }; + }; + }; + }; + + environment.etc."systemd/import-pubring.gpg".source = "${gpgKeyring}/pubkey.gpg"; + }; + }; + + testScript = '' + server.wait_for_unit("nginx.service") + + target.succeed("systemctl start systemd-sysupdate") + assert "nixos" in target.wait_until_succeeds("cat /boot/EFI/Linux/nixos_1.efi", timeout=5) + ''; +} -- cgit 1.4.1