diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md index 601109ccee5e4..0d880b75563a7 100644 --- a/nixos/doc/manual/release-notes/rl-2305.section.md +++ b/nixos/doc/manual/release-notes/rl-2305.section.md @@ -28,6 +28,8 @@ In addition to numerous new and upgraded packages, this release has the followin - `libxcrypt`, the library providing the `crypt(3)` password hashing function, is now built without support for algorithms not flagged [`strong`](https://github.com/besser82/libxcrypt/blob/v4.4.33/lib/hashes.conf#L48). This affects the availability of password hashing algorithms used for system login (`login(1)`, `passwd(1)`), but also Apache2 Basic-Auth, Samba, OpenLDAP, Dovecot, and [many other packages](https://github.com/search?q=repo%3ANixOS%2Fnixpkgs%20libxcrypt&type=code). +- `boot.bootspec.enable` (internal option) is now enabled by default because [RFC-0125](https://github.com/NixOS/rfcs/pull/125) was merged. This means you will have a bootspec document called `boot.json` generated for each system and specialisation in the top-level. This is useful to enable advanced boot usecases in NixOS such as SecureBoot. + ## New Services {#sec-release-23.05-new-services} diff --git a/nixos/modules/system/activation/bootspec.cue b/nixos/modules/system/activation/bootspec.cue index 9f857a1b1cd80..1f7b4afa87ac4 100644 --- a/nixos/modules/system/activation/bootspec.cue +++ b/nixos/modules/system/activation/bootspec.cue @@ -1,4 +1,6 @@ -#V1: { +import "struct" + +#BootspecV1: { system: string init: string initrd?: string @@ -7,12 +9,23 @@ kernelParams: [...string] label: string toplevel: string - specialisation?: { - [=~"^"]: #V1 - } - extensions?: {...} } -Document: { - v1: #V1 +// A restricted document does not allow any official specialisation +// information in it to avoid "recursive specialisations". +#RestrictedDocument: struct.MinFields(1) & { + "org.nixos.bootspec.v1": #BootspecV1 + [=~"^"]: #BootspecExtension +} + +// Specialisations are a hashmap of strings +#BootspecSpecialisationV1: [string]: #RestrictedDocument + +// Bootspec extensions are defined by the extension author. +#BootspecExtension: {...} + +// A "full" document allows official specialisation information +// in the top-level with a reserved namespaced key. +Document: #RestrictedDocument & { + "org.nixos.specialisation.v1"?: #BootspecSpecialisationV1 } diff --git a/nixos/modules/system/activation/bootspec.nix b/nixos/modules/system/activation/bootspec.nix index 1055ed1dda902..b35a4a405ac5d 100644 --- a/nixos/modules/system/activation/bootspec.nix +++ b/nixos/modules/system/activation/bootspec.nix @@ -16,20 +16,20 @@ let filename = "boot.json"; json = pkgs.writeText filename - (builtins.toJSON + (builtins.toJSON + # Merge extensions first to not let them shadow NixOS bootspec data. + (cfg.extensions // { - v1 = { + "org.nixos.bootspec.v1" = { system = config.boot.kernelPackages.stdenv.hostPlatform.system; kernel = "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}"; kernelParams = config.boot.kernelParams; label = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"; - - inherit (cfg) extensions; } // lib.optionalAttrs config.boot.initrd.enable { initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}"; initrdSecrets = "${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets"; }; - }); + })); generator = let @@ -42,8 +42,8 @@ let toplevelInjector = lib.escapeShellArgs [ "${pkgs.jq}/bin/jq" '' - .v1.toplevel = $toplevel | - .v1.init = $init + ."org.nixos.bootspec.v1".toplevel = $toplevel | + ."org.nixos.bootspec.v1".init = $init '' "--sort-keys" "--arg" "toplevel" "${placeholder "out"}" @@ -62,14 +62,10 @@ let lib.escapeShellArgs [ "${pkgs.jq}/bin/jq" "--sort-keys" - ".v1.specialisation = ($ARGS.named | map_values(. | first | .v1))" + ''."org.nixos.specialisation.v1" = ($ARGS.named | map_values(. | first))'' ] + " ${lib.concatStringsSep " " specialisationLoader}"; in - '' - mkdir -p $out/bootspec - - ${toplevelInjector} | ${specialisationInjector} > $out/${filename} - ''; + "${toplevelInjector} | ${specialisationInjector} > $out/${filename}"; validator = pkgs.writeCueValidator ./bootspec.cue { document = "Document"; # Universal validator for any version as long the schema is correctly set. @@ -79,9 +75,16 @@ let in { options.boot.bootspec = { - enable = lib.mkEnableOption (lib.mdDoc "Enable generation of RFC-0125 bootspec in $system/bootspec, e.g. /run/current-system/bootspec"); + enable = lib.mkEnableOption "the generation of RFC-0125 bootspec in $system/boot.json, e.g. /run/current-system/boot.json" + // { default = true; internal = true; }; + enableValidation = lib.mkEnableOption (''the validation of bootspec documents for each build. + This will introduce Go in the build-time closure as we are relying on [Cuelang](https://cuelang.org/) for schema validation. + Enable this option if you want to ascertain that your documents are correct. + '' + ); extensions = lib.mkOption { + # FIXME(RaitoBezarius): this is not enough to validate: extensions."osRelease" = drv; those are picked up by cue validation. type = lib.types.attrsOf lib.types.attrs; # : { ...namespace-specific fields } default = { }; description = lib.mdDoc '' @@ -112,15 +115,4 @@ in default = schemas.v1.filename; }; }; - - config = lib.mkIf (cfg.enable) { - warnings = [ - ''RFC-0125 is not merged yet, this is a feature preview of bootspec. - The schema is not definitive and features are not guaranteed to be stable until RFC-0125 is merged. - See: - - https://github.com/NixOS/nixpkgs/pull/172237 to track merge status in nixpkgs. - - https://github.com/NixOS/rfcs/pull/125 to track RFC status. - '' - ]; - }; } diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 74ab7070fd0fd..f2e7413547828 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -82,7 +82,8 @@ let ${optionalString (!config.boot.isContainer && config.boot.bootspec.enable) '' ${config.boot.bootspec.writer} - ${config.boot.bootspec.validator} "$out/${config.boot.bootspec.filename}" + ${optionalString config.boot.bootspec.enableValidation + ''${config.boot.bootspec.validator} "$out/${config.boot.bootspec.filename}"''} ''} ${config.system.extraSystemBuilderCmds} diff --git a/nixos/tests/bootspec.nix b/nixos/tests/bootspec.nix index f388a15ffa2d7..45215321930d9 100644 --- a/nixos/tests/bootspec.nix +++ b/nixos/tests/bootspec.nix @@ -110,7 +110,7 @@ in machine.succeed("test -e /run/current-system/boot.json") - bootspec = json.loads(machine.succeed("jq -r '.v1' /run/current-system/boot.json")) + bootspec = json.loads(machine.succeed("jq -r '.\"org.nixos.bootspec.v1\"' /run/current-system/boot.json")) assert all(key in bootspec for key in ('initrd', 'initrdSecrets')), "Bootspec should contain initrd or initrdSecrets field when initrd is enabled" ''; @@ -136,10 +136,10 @@ in machine.succeed("test -e /run/current-system/boot.json") machine.succeed("test -e /run/current-system/specialisation/something/boot.json") - sp_in_parent = json.loads(machine.succeed("jq -r '.v1.specialisation.something' /run/current-system/boot.json")) + sp_in_parent = json.loads(machine.succeed("jq -r '.\"org.nixos.specialisation.v1\".something' /run/current-system/boot.json")) sp_in_fs = json.loads(machine.succeed("cat /run/current-system/specialisation/something/boot.json")) - assert sp_in_parent == sp_in_fs['v1'], "Bootspecs of the same specialisation are different!" + assert sp_in_parent == sp_in_fs['org.nixos.bootspec.v1'], "Bootspecs of the same specialisation are different!" ''; }; @@ -152,7 +152,9 @@ in imports = [ standard ]; environment.systemPackages = [ pkgs.jq ]; boot.bootspec.extensions = { - osRelease = config.environment.etc."os-release".source; + "org.nix-tests.product" = { + osRelease = config.environment.etc."os-release".source; + }; }; }; @@ -161,7 +163,7 @@ in machine.wait_for_unit("multi-user.target") current_os_release = machine.succeed("cat /etc/os-release") - bootspec_os_release = machine.succeed("cat $(jq -r '.v1.extensions.osRelease' /run/current-system/boot.json)") + bootspec_os_release = machine.succeed("cat $(jq -r '.\"org.nix-tests.product\".osRelease' /run/current-system/boot.json)") assert current_os_release == bootspec_os_release, "Filename referenced by extension has unexpected contents" '';