From 36557a47012e95b3058600fe3430b2c74800245b Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Mon, 9 May 2022 13:50:14 -0400 Subject: [PATCH 01/15] RFC 0125: Use an intermediary representation (bootspec) for bootloader backends Bootspec is a set of memoized facts about a system's closure. These facts are used as the primary input for bootloader backends like systemd-boot and grub, for creating files in `/boot/loader/entries/` and `grub.cfg`. In this proposal we create a stable, comprehensive, and machine-parsable definition of a NixOS Generation as an intermediate representation (IR) between the NixOS system definition and the bootloader management tools. --- rfcs/0125-bootspec.md | 212 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 rfcs/0125-bootspec.md diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md new file mode 100644 index 000000000..390af7c30 --- /dev/null +++ b/rfcs/0125-bootspec.md @@ -0,0 +1,212 @@ +--- +feature: bootspec +start-date: 2022-05-09 +author: Graham Christensen +co-authors: Cole Helbling +shepherd-team: (names, to be nominated and accepted by RFC steering committee) +shepherd-leader: (name to be appointed by RFC steering committee) +related-issues: (will contain links to implementation PRs) +--- + +# Summary +[summary]: #summary + +Bootspec is a set of memoized facts about a system's closure. +These facts are used as the primary input for bootloader backends like systemd-boot and grub, for creating files in `/boot/loader/entries/` and `grub.cfg`. + +In this proposal, we create a stable, comprehensive, and machine-parsable definition of a NixOS Generation as an intermediate representation (IR) between the NixOS system definition and the bootloader management tools. + +# Motivation +[motivation]: #motivation + +NixOS's bootloader backends don't support a uniform collection of features and design decisions, partially due to the complexity of implementing the features. +Using a statically parsable bootspec definition reduces the work involved in implementing bootloader support. + +If we survey the current set of bootloader and feature matrix, we see a bit of a smattering: + +| Bootloader | Generation limits | Initrd Secrets | Multiple Profiles | Specialisation | Custom Labels | +| --------------------------- | ----------------- | -------------- | ----------------- | -------------- | ------------- | +| systemd-boot | YES | YES | YES | YES | YES | +| grub | YES | YES | YES | YES | YES | +| generic-extlinux-compatible | YES | | | | YES | +| raspberrypi | YES | | | | | +| init-script | | | | YES | | +| generations-dir | | | | | | + +One reason the matrix is not filled out is the technical difficulty of implementing these various features. +The current API for detecting most of this information is by globbing directories looking for specific files. + +By forcing implementations to spelunk the filesystem, we create a complicated problem in multiple ways: + +- Introducing or improving bootloader features is complicated and requires significant effort to implement it for the major bootloaders. +- The relatively complicated filesystem-based behavior involves reimplementing similar logic across Perl, Python, and Bash. +- Our current tools can only support limited workflows, and are difficult to extend. + +## Supporting Externalized Bootloader Backends +[externalized-bootloaders]: #externalized-bootloaders + +Our NixOS-provided tooling is sufficient for most use cases; however, there are more complicated cases that are not adequately covered by our tools and would not be appropriate to include in NixOS directly. +Let's use SecureBoot as an example. + +A naive, single-user implementation of SecureBoot may have signing keys on the local filesystem, and every `nixos-rebuild boot` call signs the relevant files. +This is a valid method of implementation, but is insufficient for larger deployments. + +An enterprise deployment of SecureBoot may have a centralized signing service with careful auditing about what is signed. +It is more likely that the signed bootloader files come prebuilt and presigned from upstream and an unsigned file means a violation of policy. +This is also a valid implementation, but is too heavy for a single user. + +There are infinitely many policy choices and implementations possible, and a good solution here is to allow deployers of NixOS to implement their own bootloader management tools. +By creating a well defined specification of generations and the boot-relevant data we enable this external development. + +## What about systemd's Bootloader Specification? +[systemd-bootloader-specification]: #systemd-bootloader-specification + +Systemd's bootloader specification is a good format for a different problem. +A single NixOS generation can contain multiple bootable systems and options, with additional features unique to NixOS built on top. +Most Linux distributions don't deal with many unique and ever-changing bootables. +This proposal is specifically to deal with the collection of bootables and improve our ability to interface with. + +# Goals +[goals]: #goals + +- Enable a more uniform bootloader feature support across our packaged bootloaders. + Concretely, making it relatively straightforward to convert most of the empty spaces in the feature matrix to YES's. +- Enable users of NixOS to implement custom bootloader tools and policy without needing to dive through the system profiles, and without patching Nixpkgs / NixOS. +- Define a stable specification of a generation's boot data which internal and external users can rely on. + Changes to the specification should go through an RFC. + +### Non-Goals +[non-goals]: #non-goals + +- Rewriting the existing bootloader backends to actually fill out the feature matrix. + The goal of this RFC is to make the feature development *easier*, not actually do it. +- Supporting SecureBoot. + The authors of this RFC have done work in this regard, but this RFC is not about SecureBoot. + +# Proposed Solution +[proposed-solution]: #proposed-solution + +- Each NixOS generation will have a bootspec (a JSON document) at `$out/boot.v1.json` containing all of the boot properties for that generation. + NixOS's bootloader backends will read these files as inputs to the bootloader installation phase. + - The bootloader installation phase is relatively unchanged from the way it is now. + The bootloader backend will have an executable that is run against a collection of generations, and the backend is any of the currently supported backends plus an "external" backend which the user can define. +- The bootloader backends will avoid reading data from the other files and directories when possible, preferring the information in the bootspec. +- A bootspec synthesizing tool will be used to synthesize a bootspec for generations which don't already have one. + This tool will be shared across all of the bootloader backends, helping produce more uniform behavior. +- Existing bootloader backends will be updated to read properties from the bootspec, removing most if not all of their filesystem-spelunking code. + +### Bootspec Format v1 +[format-v1]: #format-v1 + + +Using the following JSON: + +```json5 +{ + // Version of the specification used in the document + "schemaVersion": 1, + + // Path to the stage-2 init, executed by the initrd + "init": "/nix/store/xxx-nixos-system-xxx/init", + + // Path to the initrd + "initrd": "/nix/store/xxx-initrd-linux/initrd", + + // Optional path to a tool to dynamically add secrets to an initrd + "initrdSecrets": "/nix/store/xxx-append-secrets/bin/append-initrd-secrets", + + // Path to the kernel image + "kernel": "/nix/store/xxx-linux/bzImage", + + // Kernel commandline options + "kernelParams": [ + "amd_iommu=on", + "amd_iommu=pt", + "iommu=pt", + "kvm.ignore_msrs=1", + "kvm.report_ignored_msrs=0", + "udev.log_priority=3", + "systemd.unified_cgroup_hierarchy=1", + "loglevel=4" + ], + + // The label of the system. It should contain the operating system, kernel version, + // and other user-relevant information to identify the system. This corresponds + // loosely to `config.system.nixos.label`. + "label": "NixOS 21.11.20210810.dirty (Linux 5.15.30)", + + // Top level path of the closure, in case some spelunking is required + "toplevel": "/nix/store/xxx-nixos-system-xxx", + + "specialisation": { + // corresponds to in specialisation..configuration. + // Note: a specialisation's bootspec document should not contain any specialisations. + "": { + // bootspec is the path to a bootspec document + "bootspec": "/path/to/a/bootspec.v1.json", + } + } +} +``` + +### Risks +[risks]: #risks + +- Some of the bootloader backends are quite complicated, and in many cases have inadequate tests. + We could accidentally break corner cases. +- The bootloader backends are inherently a weak point for NixOS, as it is our last option for rolling back. + We cannot roll back a broken bootloader. + This and the previous point are risks, but also help demonstrate the value of reducing the amount of code and complexity in the generator. + +### Milestones +[milestones]: #milestones + +- Create and package the backwards compatibility synthesizer as a standalone tool. + A version of it already exists, but it is not standalone. +- Generate the bootspec files as part of building the system closure. +- Update the bootloader backends to use bootspec as its primary source of installation data. +- Implement a NixOS module which supports external bootloader tooling. + +# FAQ +[faq]: #faq + +**Does this involve patching any of the bootloaders?** + +No: this work is all about creating a cleaner interface for the tools we maintain which generate the files and configuration for our supported bootloaders. + +**Why can't we solve this with the module system?** + +Boot-menu generation is by definition cross-generation. +Bootloader backends, by definition reconfigure the boot partition and records, and are supposed to apply retroactively to old configurations. +This regeneration happens way beyond the lifecycle of the module system. + +**How is this easier than `if (-f "$out/append-initrd-secrets")`?** + +The main problem with that approach is there are a lot of files that you need to look for and examine in specific ways. +We end up duplicating a lot of work across bootloader backends to "learn" about the system. +By creating a source of truth for this information the bootloader backends can focus more on the work that is unique to their problem. + +Doing this work now may also make it easier to refactor a NixOS system's `$out` , reducing the number of symlinks and "noise" if we wanted to do that. + +**Does this interrupt the work to make stage-1 based on systemd?** + +No. +Stage-1 won't use this document at all. + +**What about memtest and other boot options?** + +That should be left to configuration passed to the bootloader backend. +There could be any number of "extra" bootables like this, and importantly they have nothing to do with a specific NixOS system closure. +Concretely, a user who enables memtest probably wants the most recent memtest, and not one memtest from every generation. + +# Open Questions +[open-questions]: #open-questions + +- Should there be a general-purpose "meta" or "extensions" field which allows arbitrary extension of the data? What should this look like? + +# Future Work +[future]: #future-work + +- Completing the migration from filesystem-spelunking into using the bootspec data. +- Implementing a NixOS module for supporting externalized bootloader backends. +- Implementing a base level of SecureBoot support. From 2157195b9991d6d5282bb505f5ff21969fa2d82a Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Wed, 18 May 2022 15:36:07 -0400 Subject: [PATCH 02/15] bootspec: relocate to $out/bootspec/boot.v1.json --- rfcs/0125-bootspec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index 390af7c30..1fc03c146 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -86,7 +86,7 @@ This proposal is specifically to deal with the collection of bootables and impro # Proposed Solution [proposed-solution]: #proposed-solution -- Each NixOS generation will have a bootspec (a JSON document) at `$out/boot.v1.json` containing all of the boot properties for that generation. +- Each NixOS generation will have a bootspec (a JSON document) at `$out/bootspec/boot.v1.json` containing all of the boot properties for that generation. NixOS's bootloader backends will read these files as inputs to the bootloader installation phase. - The bootloader installation phase is relatively unchanged from the way it is now. The bootloader backend will have an executable that is run against a collection of generations, and the backend is any of the currently supported backends plus an "external" backend which the user can define. From cd81aa8e66bc9b27c1d8911cec609fdc299bf64d Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Wed, 18 May 2022 15:37:25 -0400 Subject: [PATCH 03/15] Clarify that this document describes bootspec v1 --- rfcs/0125-bootspec.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index 1fc03c146..8dabc790f 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -16,6 +16,8 @@ These facts are used as the primary input for bootloader backends like systemd-b In this proposal, we create a stable, comprehensive, and machine-parsable definition of a NixOS Generation as an intermediate representation (IR) between the NixOS system definition and the bootloader management tools. +This document describes Bootspec v1. + # Motivation [motivation]: #motivation @@ -88,6 +90,7 @@ This proposal is specifically to deal with the collection of bootables and impro - Each NixOS generation will have a bootspec (a JSON document) at `$out/bootspec/boot.v1.json` containing all of the boot properties for that generation. NixOS's bootloader backends will read these files as inputs to the bootloader installation phase. + A bootspec document named `boot.v1.json` must have the `schemaVersion` set to `1`. Any other value makes the document invalid. - The bootloader installation phase is relatively unchanged from the way it is now. The bootloader backend will have an executable that is run against a collection of generations, and the backend is any of the currently supported backends plus an "external" backend which the user can define. - The bootloader backends will avoid reading data from the other files and directories when possible, preferring the information in the bootspec. From 1921a9f4db9961fb186a30121addd6ed2c313f48 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Wed, 18 May 2022 15:37:48 -0400 Subject: [PATCH 04/15] Note that the filename must match the contained schemaVersion. --- rfcs/0125-bootspec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index 8dabc790f..b20a5ba11 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -90,7 +90,7 @@ This proposal is specifically to deal with the collection of bootables and impro - Each NixOS generation will have a bootspec (a JSON document) at `$out/bootspec/boot.v1.json` containing all of the boot properties for that generation. NixOS's bootloader backends will read these files as inputs to the bootloader installation phase. - A bootspec document named `boot.v1.json` must have the `schemaVersion` set to `1`. Any other value makes the document invalid. + A bootspec document named `boot.v1.json` must have the `schemaVersion` set to `1`. Any other value makes the document invalid. Generally, the file must be named `boot.v${schemaVersion}.json`. - The bootloader installation phase is relatively unchanged from the way it is now. The bootloader backend will have an executable that is run against a collection of generations, and the backend is any of the currently supported backends plus an "external" backend which the user can define. - The bootloader backends will avoid reading data from the other files and directories when possible, preferring the information in the bootspec. From bcc171dccf8e51de202531dde5f62a8d0ec502f3 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Wed, 18 May 2022 15:39:27 -0400 Subject: [PATCH 05/15] Replace the specialisation's pointer to a bootspec with the full bootspec document --- rfcs/0125-bootspec.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index b20a5ba11..d5044e4c2 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -145,8 +145,8 @@ Using the following JSON: // corresponds to in specialisation..configuration. // Note: a specialisation's bootspec document should not contain any specialisations. "": { - // bootspec is the path to a bootspec document - "bootspec": "/path/to/a/bootspec.v1.json", + // A full Bootspec v1 document. + // Note that it is not valid for a specialisation to have further specialisations. } } } From aa2253b72099b7b85f146e7eeea85aa72ef925aa Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Wed, 18 May 2022 15:40:07 -0400 Subject: [PATCH 06/15] Clarify the 'to interface with' bit --- rfcs/0125-bootspec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index d5044e4c2..31affa84c 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -66,7 +66,7 @@ By creating a well defined specification of generations and the boot-relevant da Systemd's bootloader specification is a good format for a different problem. A single NixOS generation can contain multiple bootable systems and options, with additional features unique to NixOS built on top. Most Linux distributions don't deal with many unique and ever-changing bootables. -This proposal is specifically to deal with the collection of bootables and improve our ability to interface with. +This proposal is specifically to deal with the collection of bootables and improve our ability to treat the collection consistently. # Goals [goals]: #goals From 33eff40507e6091ca1626a2fdfb9e40a7c1797fc Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Wed, 18 May 2022 15:40:30 -0400 Subject: [PATCH 07/15] Link to the bootspec implementation PR --- rfcs/0125-bootspec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index 31affa84c..1e9a6532b 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -5,7 +5,7 @@ author: Graham Christensen co-authors: Cole Helbling shepherd-team: (names, to be nominated and accepted by RFC steering committee) shepherd-leader: (name to be appointed by RFC steering committee) -related-issues: (will contain links to implementation PRs) +related-issues: https://github.com/NixOS/nixpkgs/pull/172237 --- # Summary From 739b6a191b56ced7fe9a7b931d4ae21647e633fd Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Wed, 18 May 2022 15:42:22 -0400 Subject: [PATCH 08/15] Note it is likely for bootloader backends to change their results slightly by integrating bootspec --- rfcs/0125-bootspec.md | 1 + 1 file changed, 1 insertion(+) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index 1e9a6532b..08a854e89 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -84,6 +84,7 @@ This proposal is specifically to deal with the collection of bootables and impro The goal of this RFC is to make the feature development *easier*, not actually do it. - Supporting SecureBoot. The authors of this RFC have done work in this regard, but this RFC is not about SecureBoot. +- Integrating Bootspec into an existing bootloader backend is not expected to perfectly produce the exact same result. For example, it is likely that the text describing the entrties in the menu may differ. # Proposed Solution [proposed-solution]: #proposed-solution From 2f239c97826125b2b62adfeb7b5b32e77a8419d4 Mon Sep 17 00:00:00 2001 From: Cole Helbling Date: Tue, 31 May 2022 08:50:39 -0700 Subject: [PATCH 09/15] Relocate to $out/boot.json, top-level key of specification version --- rfcs/0125-bootspec.md | 88 ++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index 08a854e89..3c3e72819 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -89,9 +89,10 @@ This proposal is specifically to deal with the collection of bootables and impro # Proposed Solution [proposed-solution]: #proposed-solution -- Each NixOS generation will have a bootspec (a JSON document) at `$out/bootspec/boot.v1.json` containing all of the boot properties for that generation. +- Each NixOS generation will have a bootspec (a JSON document) at `$out/boot.json` containing all of the boot properties for that generation. NixOS's bootloader backends will read these files as inputs to the bootloader installation phase. - A bootspec document named `boot.v1.json` must have the `schemaVersion` set to `1`. Any other value makes the document invalid. Generally, the file must be named `boot.v${schemaVersion}.json`. + A bootspec document named `boot.json` must have a `"v1"` key that contains the body of the specification. + Generally, the key must follow the format of `v${schemaVersion}`. - The bootloader installation phase is relatively unchanged from the way it is now. The bootloader backend will have an executable that is run against a collection of generations, and the backend is any of the currently supported backends plus an "external" backend which the user can define. - The bootloader backends will avoid reading data from the other files and directories when possible, preferring the information in the bootspec. @@ -107,47 +108,48 @@ Using the following JSON: ```json5 { - // Version of the specification used in the document - "schemaVersion": 1, - - // Path to the stage-2 init, executed by the initrd - "init": "/nix/store/xxx-nixos-system-xxx/init", - - // Path to the initrd - "initrd": "/nix/store/xxx-initrd-linux/initrd", - - // Optional path to a tool to dynamically add secrets to an initrd - "initrdSecrets": "/nix/store/xxx-append-secrets/bin/append-initrd-secrets", - - // Path to the kernel image - "kernel": "/nix/store/xxx-linux/bzImage", - - // Kernel commandline options - "kernelParams": [ - "amd_iommu=on", - "amd_iommu=pt", - "iommu=pt", - "kvm.ignore_msrs=1", - "kvm.report_ignored_msrs=0", - "udev.log_priority=3", - "systemd.unified_cgroup_hierarchy=1", - "loglevel=4" - ], - - // The label of the system. It should contain the operating system, kernel version, - // and other user-relevant information to identify the system. This corresponds - // loosely to `config.system.nixos.label`. - "label": "NixOS 21.11.20210810.dirty (Linux 5.15.30)", - - // Top level path of the closure, in case some spelunking is required - "toplevel": "/nix/store/xxx-nixos-system-xxx", - - "specialisation": { - // corresponds to in specialisation..configuration. - // Note: a specialisation's bootspec document should not contain any specialisations. - "": { - // A full Bootspec v1 document. - // Note that it is not valid for a specialisation to have further specialisations. + // Toplevel key describing the version of the specification used in the document + "v1": { + + // Path to the stage-2 init, executed by the initrd + "init": "/nix/store/xxx-nixos-system-xxx/init", + + // Path to the initrd + "initrd": "/nix/store/xxx-initrd-linux/initrd", + + // Optional path to a tool to dynamically add secrets to an initrd + "initrdSecrets": "/nix/store/xxx-append-secrets/bin/append-initrd-secrets", + + // Path to the kernel image + "kernel": "/nix/store/xxx-linux/bzImage", + + // Kernel commandline options + "kernelParams": [ + "amd_iommu=on", + "amd_iommu=pt", + "iommu=pt", + "kvm.ignore_msrs=1", + "kvm.report_ignored_msrs=0", + "udev.log_priority=3", + "systemd.unified_cgroup_hierarchy=1", + "loglevel=4" + ], + + // The label of the system. It should contain the operating system, kernel version, + // and other user-relevant information to identify the system. This corresponds + // loosely to `config.system.nixos.label`. + "label": "NixOS 21.11.20210810.dirty (Linux 5.15.30)", + + // Top level path of the closure, in case some spelunking is required + "toplevel": "/nix/store/xxx-nixos-system-xxx", + + "specialisation": { + // corresponds to in specialisation..configuration. + // Note that it is not valid for a specialisation to be a different version than the main document. + "": { + // A full Bootspec v1 document, _without_ the `"v1"` key. + // Note that it is not valid for a specialisation to have further specialisations. + } } } } From 5ee0cc57d946ca62beb8f38f9738ee548be89dc4 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Wed, 1 Jun 2022 01:44:21 +0200 Subject: [PATCH 10/15] Fix nits from code review Co-authored-by: Cole Helbling Co-authored-by: Linus Heckemann --- rfcs/0125-bootspec.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index 3c3e72819..f59eaab94 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -84,7 +84,7 @@ This proposal is specifically to deal with the collection of bootables and impro The goal of this RFC is to make the feature development *easier*, not actually do it. - Supporting SecureBoot. The authors of this RFC have done work in this regard, but this RFC is not about SecureBoot. -- Integrating Bootspec into an existing bootloader backend is not expected to perfectly produce the exact same result. For example, it is likely that the text describing the entrties in the menu may differ. +- Integrating Bootspec into an existing bootloader backend is not expected to perfectly produce the exact same result. For example, it is likely that the text describing the entries in the menu may differ. # Proposed Solution [proposed-solution]: #proposed-solution @@ -170,7 +170,7 @@ Using the following JSON: - Create and package the backwards compatibility synthesizer as a standalone tool. A version of it already exists, but it is not standalone. - Generate the bootspec files as part of building the system closure. -- Update the bootloader backends to use bootspec as its primary source of installation data. +- Update the bootloader backends to use bootspec as their primary source of installation data. - Implement a NixOS module which supports external bootloader tooling. # FAQ From a12f7725a1310202c112d44ef9aa29ff71173907 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Thu, 18 Aug 2022 10:21:40 -0400 Subject: [PATCH 11/15] 0125-bootspec: add the shepherd team and leader Co-authored-by: Kevin Cox --- rfcs/0125-bootspec.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index f59eaab94..05d45ce89 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -3,8 +3,8 @@ feature: bootspec start-date: 2022-05-09 author: Graham Christensen co-authors: Cole Helbling -shepherd-team: (names, to be nominated and accepted by RFC steering committee) -shepherd-leader: (name to be appointed by RFC steering committee) +shepherd-team: @lheckemann, @06kellyjac, @GovanifY, @RaitoBezarius +shepherd-leader: @lheckemann related-issues: https://github.com/NixOS/nixpkgs/pull/172237 --- From 70976d0c9bfc54fd027296ef1b283388f38cc91d Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Wed, 21 Dec 2022 21:30:26 +0100 Subject: [PATCH 12/15] 0125-bootspec: initrd is optional, add system, extensions, clarify the meaning of optional --- rfcs/0125-bootspec.md | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index 05d45ce89..d280d2518 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -110,20 +110,22 @@ Using the following JSON: { // Toplevel key describing the version of the specification used in the document "v1": { + // (Required) System type the bootspec is intended for (e.g. `x86_64-linux`, `aarch64-linux`) + "system": "x86_64-linux", - // Path to the stage-2 init, executed by the initrd + // (Required) Path to the stage-2 init, executed by the initrd "init": "/nix/store/xxx-nixos-system-xxx/init", - // Path to the initrd + // (Optional) Path to the initrd "initrd": "/nix/store/xxx-initrd-linux/initrd", - // Optional path to a tool to dynamically add secrets to an initrd + // (Optional) path to a tool to dynamically add secrets to an initrd "initrdSecrets": "/nix/store/xxx-append-secrets/bin/append-initrd-secrets", - // Path to the kernel image + // (Required) Path to the kernel image "kernel": "/nix/store/xxx-linux/bzImage", - // Kernel commandline options + // (Required) Kernel commandline options "kernelParams": [ "amd_iommu=on", "amd_iommu=pt", @@ -135,14 +137,15 @@ Using the following JSON: "loglevel=4" ], - // The label of the system. It should contain the operating system, kernel version, + // (Required) The label of the system. It should contain the operating system, kernel version, // and other user-relevant information to identify the system. This corresponds // loosely to `config.system.nixos.label`. "label": "NixOS 21.11.20210810.dirty (Linux 5.15.30)", - // Top level path of the closure, in case some spelunking is required + // (Required) Top level path of the closure, in case some spelunking is required "toplevel": "/nix/store/xxx-nixos-system-xxx", + // (Optional) Mapping of specialisation names to their bootspec document "specialisation": { // corresponds to in specialisation..configuration. // Note that it is not valid for a specialisation to be a different version than the main document. @@ -150,11 +153,23 @@ Using the following JSON: // A full Bootspec v1 document, _without_ the `"v1"` key. // Note that it is not valid for a specialisation to have further specialisations. } + }, + + // (Optional) User-specified metadata + "extensions": { + // corresponds to a user-provided namespace + // to reduce the chances of collision when using + // multiple extensions at once + "": { + // Any kind of representable JSON is valid here + } } } } ``` +An *optional* field mean: either missing or present, **not null**. + ### Risks [risks]: #risks @@ -208,8 +223,6 @@ Concretely, a user who enables memtest probably wants the most recent memtest, a # Open Questions [open-questions]: #open-questions -- Should there be a general-purpose "meta" or "extensions" field which allows arbitrary extension of the data? What should this look like? - # Future Work [future]: #future-work From d7fb344a8d748e50afadaacdea6a8ab1ff342b55 Mon Sep 17 00:00:00 2001 From: Ryan Lahfa Date: Wed, 21 Dec 2022 23:17:24 +0100 Subject: [PATCH 13/15] PR: address comments (1) Co-authored-by: Cole Helbling --- rfcs/0125-bootspec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index d280d2518..71dccc3d5 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -119,7 +119,7 @@ Using the following JSON: // (Optional) Path to the initrd "initrd": "/nix/store/xxx-initrd-linux/initrd", - // (Optional) path to a tool to dynamically add secrets to an initrd + // (Optional) Path to a tool to dynamically add secrets to an initrd "initrdSecrets": "/nix/store/xxx-append-secrets/bin/append-initrd-secrets", // (Required) Path to the kernel image From d5fd9e0729c5c577bdb925ef156cdad091a3abc4 Mon Sep 17 00:00:00 2001 From: Ryan Lahfa Date: Wed, 21 Dec 2022 23:17:33 +0100 Subject: [PATCH 14/15] PR: address comments (2) Co-authored-by: Cole Helbling --- rfcs/0125-bootspec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index 71dccc3d5..3d4ec64fe 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -168,7 +168,7 @@ Using the following JSON: } ``` -An *optional* field mean: either missing or present, **not null**. +An *optional* field means: a field that is either missing or present, but **never `null`**. ### Risks [risks]: #risks From 427942c34112be410b9d79876b3e5ace2e7c53d5 Mon Sep 17 00:00:00 2001 From: Linus Heckemann Date: Sat, 18 Mar 2023 16:57:13 +0100 Subject: [PATCH 15/15] Update RFC text with changes agreed on with RaitoBezarius --- rfcs/0125-bootspec.md | 54 ++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/rfcs/0125-bootspec.md b/rfcs/0125-bootspec.md index 3d4ec64fe..43ffd8246 100644 --- a/rfcs/0125-bootspec.md +++ b/rfcs/0125-bootspec.md @@ -85,20 +85,22 @@ This proposal is specifically to deal with the collection of bootables and impro - Supporting SecureBoot. The authors of this RFC have done work in this regard, but this RFC is not about SecureBoot. - Integrating Bootspec into an existing bootloader backend is not expected to perfectly produce the exact same result. For example, it is likely that the text describing the entries in the menu may differ. +- Specifying how to discover generations. This is desirable, but should not be tied to bootspec directly since bootspec may be useful with diverse discovery mechanisms. # Proposed Solution [proposed-solution]: #proposed-solution - Each NixOS generation will have a bootspec (a JSON document) at `$out/boot.json` containing all of the boot properties for that generation. NixOS's bootloader backends will read these files as inputs to the bootloader installation phase. - A bootspec document named `boot.json` must have a `"v1"` key that contains the body of the specification. - Generally, the key must follow the format of `v${schemaVersion}`. + A v1 bootspec document must have a key `"org.nixos.bootspec.v1"` that describes a bootable NixOS configuration. - The bootloader installation phase is relatively unchanged from the way it is now. The bootloader backend will have an executable that is run against a collection of generations, and the backend is any of the currently supported backends plus an "external" backend which the user can define. - The bootloader backends will avoid reading data from the other files and directories when possible, preferring the information in the bootspec. - A bootspec synthesizing tool will be used to synthesize a bootspec for generations which don't already have one. This tool will be shared across all of the bootloader backends, helping produce more uniform behavior. - Existing bootloader backends will be updated to read properties from the bootspec, removing most if not all of their filesystem-spelunking code. +- The file paths referred to in the bootspec document should be references to Nix store paths, so that the presence of the bootspec document in the store (assuming no breakage of the Nix store invariants through modifications outside Nix or bugs in Nix) guarantees the presence of the files required for boot. + Bootspec documents should not be copied out of the store to avoid breaking this invariant. ### Bootspec Format v1 [format-v1]: #format-v1 @@ -109,17 +111,21 @@ Using the following JSON: ```json5 { // Toplevel key describing the version of the specification used in the document - "v1": { + "org.nixos.bootspec.v1": { // (Required) System type the bootspec is intended for (e.g. `x86_64-linux`, `aarch64-linux`) "system": "x86_64-linux", - // (Required) Path to the stage-2 init, executed by the initrd + // (Required) Path to the stage-2 init, executed by the initrd (if present) "init": "/nix/store/xxx-nixos-system-xxx/init", // (Optional) Path to the initrd "initrd": "/nix/store/xxx-initrd-linux/initrd", - // (Optional) Path to a tool to dynamically add secrets to an initrd + // (Optional) Path to a tool to dynamically add secrets to an initrd. + // Consumers of a bootspec document should copy the file referenced by the "initrd" key to a writable location, ensure that the file is writable, invoke this tool with the path to the initrd as its only argument, and use the initrd as modified by the tool for booting. + // This may be used to add files from outside the Nix store to the initrd. + // This tool is expected to run on the system whose boot specification is being set up, and may thus fail if used on a system where the expected stateful files are not in place or whose CPU does not support the instruction set of the system to be booted. + // If this field is present and the tool fails, no boot configuration should be generated for the system. "initrdSecrets": "/nix/store/xxx-append-secrets/bin/append-initrd-secrets", // (Required) Path to the kernel image @@ -144,27 +150,26 @@ Using the following JSON: // (Required) Top level path of the closure, in case some spelunking is required "toplevel": "/nix/store/xxx-nixos-system-xxx", - - // (Optional) Mapping of specialisation names to their bootspec document - "specialisation": { - // corresponds to in specialisation..configuration. - // Note that it is not valid for a specialisation to be a different version than the main document. - "": { - // A full Bootspec v1 document, _without_ the `"v1"` key. - // Note that it is not valid for a specialisation to have further specialisations. - } - }, - - // (Optional) User-specified metadata - "extensions": { - // corresponds to a user-provided namespace - // to reduce the chances of collision when using - // multiple extensions at once - "": { - // Any kind of representable JSON is valid here + }, + // The top-level object may contain arbitrary further keys ("extensions"), whose semantics may be defined by third parties. + // The use of reverse-domain-name namespacing is recommended in order to avoid name collisions. + + // (Optional) Specialisations are an extension to the specification which allows bundling multiple variants of a NixOS configuration with a single parent. + // These are shaped like the top level; to be precise: + // - Each entry in the toplevel "org.nixos.specialisation.v1" object represents a specialisation. + // - In order for the top-level document to be a valid v1 bootspec, each specialisation must have a valid "org.nixos.bootspec.v1" key whose value conforms to the same schema as the toplevel "org.nixos.bootspec.v1" object. + // - The behaviour of nested specialisations (i.e. entries in "org.nixos.specialisation.v1" which themselves contain the "org.nixos.specialisation.v1" key) is not defined. + // - In particular, there is no expectation that such nested specialisations will be handled by consumers of bootspec documents. + // - Each specialisation document may contain arbitrary further keys (extensions), like the top-level document. + // - The semantics of these should be the same as when these keys are used at the top level, but only apply for the given specialisation. + "org.nixos.specialisation.v1": { + // Each key in this object corresponds to a specialisation as defined by the `specialisation.` NixOS option. + "": { + "org.nixos.bootspec.v1": { + // See above } } - } + }, } ``` @@ -229,3 +234,4 @@ Concretely, a user who enables memtest probably wants the most recent memtest, a - Completing the migration from filesystem-spelunking into using the bootspec data. - Implementing a NixOS module for supporting externalized bootloader backends. - Implementing a base level of SecureBoot support. +- Addressing the known weaknesses of this specification (no support for multiple initrds, janky initrd secrets mechanism, lack of device tree support) in a later version of the spec.