Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)

nixos/bootspec: adopt the merged RFC-0125

This removes the feature preview warning, enable by default bootspec,
adds a validation flag to prevent Go to go into build-time closure.

This will break all downstream users of bootspec as those changes are
not backward-compatible.

authored by Raito Bezarius and committed by helbling.dev bc502d0a b76b960e

+49 -39
+2
nixos/doc/manual/release-notes/rl-2305.section.md
··· 28 29 - `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). 30 31 ## New Services {#sec-release-23.05-new-services} 32 33 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
··· 28 29 - `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). 30 31 + - `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. 32 + 33 ## New Services {#sec-release-23.05-new-services} 34 35 <!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+20 -7
nixos/modules/system/activation/bootspec.cue
··· 1 - #V1: { 2 system: string 3 init: string 4 initrd?: string ··· 7 kernelParams: [...string] 8 label: string 9 toplevel: string 10 - specialisation?: { 11 - [=~"^"]: #V1 12 - } 13 - extensions?: {...} 14 } 15 16 - Document: { 17 - v1: #V1 18 }
··· 1 + import "struct" 2 + 3 + #BootspecV1: { 4 system: string 5 init: string 6 initrd?: string ··· 9 kernelParams: [...string] 10 label: string 11 toplevel: string 12 + } 13 + 14 + // A restricted document does not allow any official specialisation 15 + // information in it to avoid "recursive specialisations". 16 + #RestrictedDocument: struct.MinFields(1) & { 17 + "org.nixos.bootspec.v1": #BootspecV1 18 + [=~"^"]: #BootspecExtension 19 } 20 21 + // Specialisations are a hashmap of strings 22 + #BootspecSpecialisationV1: [string]: #RestrictedDocument 23 + 24 + // Bootspec extensions are defined by the extension author. 25 + #BootspecExtension: {...} 26 + 27 + // A "full" document allows official specialisation information 28 + // in the top-level with a reserved namespaced key. 29 + Document: #RestrictedDocument & { 30 + "org.nixos.specialisation.v1"?: #BootspecSpecialisationV1 31 }
+18 -26
nixos/modules/system/activation/bootspec.nix
··· 16 filename = "boot.json"; 17 json = 18 pkgs.writeText filename 19 - (builtins.toJSON 20 { 21 - v1 = { 22 system = config.boot.kernelPackages.stdenv.hostPlatform.system; 23 kernel = "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}"; 24 kernelParams = config.boot.kernelParams; 25 label = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"; 26 - 27 - inherit (cfg) extensions; 28 } // lib.optionalAttrs config.boot.initrd.enable { 29 initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}"; 30 initrdSecrets = "${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets"; 31 }; 32 - }); 33 34 generator = 35 let ··· 42 toplevelInjector = lib.escapeShellArgs [ 43 "${pkgs.jq}/bin/jq" 44 '' 45 - .v1.toplevel = $toplevel | 46 - .v1.init = $init 47 '' 48 "--sort-keys" 49 "--arg" "toplevel" "${placeholder "out"}" ··· 62 lib.escapeShellArgs [ 63 "${pkgs.jq}/bin/jq" 64 "--sort-keys" 65 - ".v1.specialisation = ($ARGS.named | map_values(. | first | .v1))" 66 ] + " ${lib.concatStringsSep " " specialisationLoader}"; 67 in 68 - '' 69 - mkdir -p $out/bootspec 70 - 71 - ${toplevelInjector} | ${specialisationInjector} > $out/${filename} 72 - ''; 73 74 validator = pkgs.writeCueValidator ./bootspec.cue { 75 document = "Document"; # Universal validator for any version as long the schema is correctly set. ··· 79 in 80 { 81 options.boot.bootspec = { 82 - enable = lib.mkEnableOption (lib.mdDoc "Enable generation of RFC-0125 bootspec in $system/bootspec, e.g. /run/current-system/bootspec"); 83 84 extensions = lib.mkOption { 85 - type = lib.types.attrsOf lib.types.attrs; # <namespace>: { ...namespace-specific fields } 86 default = { }; 87 description = lib.mdDoc '' 88 User-defined data that extends the bootspec document. ··· 111 internal = true; 112 default = schemas.v1.filename; 113 }; 114 - }; 115 - 116 - config = lib.mkIf (cfg.enable) { 117 - warnings = [ 118 - ''RFC-0125 is not merged yet, this is a feature preview of bootspec. 119 - The schema is not definitive and features are not guaranteed to be stable until RFC-0125 is merged. 120 - See: 121 - - https://github.com/NixOS/nixpkgs/pull/172237 to track merge status in nixpkgs. 122 - - https://github.com/NixOS/rfcs/pull/125 to track RFC status. 123 - '' 124 - ]; 125 }; 126 }
··· 16 filename = "boot.json"; 17 json = 18 pkgs.writeText filename 19 + (builtins.toJSON 20 + # Merge extensions first to not let them shadow NixOS bootspec data. 21 + (cfg.extensions // 22 { 23 + "org.nixos.bootspec.v1" = { 24 system = config.boot.kernelPackages.stdenv.hostPlatform.system; 25 kernel = "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}"; 26 kernelParams = config.boot.kernelParams; 27 label = "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"; 28 } // lib.optionalAttrs config.boot.initrd.enable { 29 initrd = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}"; 30 initrdSecrets = "${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets"; 31 }; 32 + })); 33 34 generator = 35 let ··· 42 toplevelInjector = lib.escapeShellArgs [ 43 "${pkgs.jq}/bin/jq" 44 '' 45 + ."org.nixos.bootspec.v1".toplevel = $toplevel | 46 + ."org.nixos.bootspec.v1".init = $init 47 '' 48 "--sort-keys" 49 "--arg" "toplevel" "${placeholder "out"}" ··· 62 lib.escapeShellArgs [ 63 "${pkgs.jq}/bin/jq" 64 "--sort-keys" 65 + ''."org.nixos.specialisation.v1" = ($ARGS.named | map_values(. | first))'' 66 ] + " ${lib.concatStringsSep " " specialisationLoader}"; 67 in 68 + "${toplevelInjector} | ${specialisationInjector} > $out/${filename}"; 69 70 validator = pkgs.writeCueValidator ./bootspec.cue { 71 document = "Document"; # Universal validator for any version as long the schema is correctly set. ··· 75 in 76 { 77 options.boot.bootspec = { 78 + enable = lib.mkEnableOption (lib.mdDoc "the generation of RFC-0125 bootspec in $system/boot.json, e.g. /run/current-system/boot.json") 79 + // { default = true; internal = true; }; 80 + enableValidation = lib.mkEnableOption (lib.mdDoc ''the validation of bootspec documents for each build. 81 + This will introduce Go in the build-time closure as we are relying on [Cuelang](https://cuelang.org/) for schema validation. 82 + Enable this option if you want to ascertain that your documents are correct. 83 + '' 84 + ); 85 86 extensions = lib.mkOption { 87 + # NOTE(RaitoBezarius): this is not enough to validate: extensions."osRelease" = drv; those are picked up by cue validation. 88 + type = lib.types.attrsOf lib.types.anything; # <namespace>: { ...namespace-specific fields } 89 default = { }; 90 description = lib.mdDoc '' 91 User-defined data that extends the bootspec document. ··· 114 internal = true; 115 default = schemas.v1.filename; 116 }; 117 }; 118 }
+2 -1
nixos/modules/system/activation/top-level.nix
··· 82 83 ${optionalString (!config.boot.isContainer && config.boot.bootspec.enable) '' 84 ${config.boot.bootspec.writer} 85 - ${config.boot.bootspec.validator} "$out/${config.boot.bootspec.filename}" 86 ''} 87 88 ${config.system.extraSystemBuilderCmds}
··· 82 83 ${optionalString (!config.boot.isContainer && config.boot.bootspec.enable) '' 84 ${config.boot.bootspec.writer} 85 + ${optionalString config.boot.bootspec.enableValidation 86 + ''${config.boot.bootspec.validator} "$out/${config.boot.bootspec.filename}"''} 87 ''} 88 89 ${config.system.extraSystemBuilderCmds}
+7 -5
nixos/tests/bootspec.nix
··· 110 111 machine.succeed("test -e /run/current-system/boot.json") 112 113 - bootspec = json.loads(machine.succeed("jq -r '.v1' /run/current-system/boot.json")) 114 115 assert all(key in bootspec for key in ('initrd', 'initrdSecrets')), "Bootspec should contain initrd or initrdSecrets field when initrd is enabled" 116 ''; ··· 136 machine.succeed("test -e /run/current-system/boot.json") 137 machine.succeed("test -e /run/current-system/specialisation/something/boot.json") 138 139 - sp_in_parent = json.loads(machine.succeed("jq -r '.v1.specialisation.something' /run/current-system/boot.json")) 140 sp_in_fs = json.loads(machine.succeed("cat /run/current-system/specialisation/something/boot.json")) 141 142 - assert sp_in_parent == sp_in_fs['v1'], "Bootspecs of the same specialisation are different!" 143 ''; 144 }; 145 ··· 152 imports = [ standard ]; 153 environment.systemPackages = [ pkgs.jq ]; 154 boot.bootspec.extensions = { 155 - osRelease = config.environment.etc."os-release".source; 156 }; 157 }; 158 ··· 161 machine.wait_for_unit("multi-user.target") 162 163 current_os_release = machine.succeed("cat /etc/os-release") 164 - bootspec_os_release = machine.succeed("cat $(jq -r '.v1.extensions.osRelease' /run/current-system/boot.json)") 165 166 assert current_os_release == bootspec_os_release, "Filename referenced by extension has unexpected contents" 167 '';
··· 110 111 machine.succeed("test -e /run/current-system/boot.json") 112 113 + bootspec = json.loads(machine.succeed("jq -r '.\"org.nixos.bootspec.v1\"' /run/current-system/boot.json")) 114 115 assert all(key in bootspec for key in ('initrd', 'initrdSecrets')), "Bootspec should contain initrd or initrdSecrets field when initrd is enabled" 116 ''; ··· 136 machine.succeed("test -e /run/current-system/boot.json") 137 machine.succeed("test -e /run/current-system/specialisation/something/boot.json") 138 139 + sp_in_parent = json.loads(machine.succeed("jq -r '.\"org.nixos.specialisation.v1\".something' /run/current-system/boot.json")) 140 sp_in_fs = json.loads(machine.succeed("cat /run/current-system/specialisation/something/boot.json")) 141 142 + assert sp_in_parent['org.nixos.bootspec.v1'] == sp_in_fs['org.nixos.bootspec.v1'], "Bootspecs of the same specialisation are different!" 143 ''; 144 }; 145 ··· 152 imports = [ standard ]; 153 environment.systemPackages = [ pkgs.jq ]; 154 boot.bootspec.extensions = { 155 + "org.nix-tests.product" = { 156 + osRelease = config.environment.etc."os-release".source; 157 + }; 158 }; 159 }; 160 ··· 163 machine.wait_for_unit("multi-user.target") 164 165 current_os_release = machine.succeed("cat /etc/os-release") 166 + bootspec_os_release = machine.succeed("cat $(jq -r '.\"org.nix-tests.product\".osRelease' /run/current-system/boot.json)") 167 168 assert current_os_release == bootspec_os_release, "Filename referenced by extension has unexpected contents" 169 '';