Revert "nixos/grub: generate BLS entries"

authored by Samuel Dionne-Riel and committed by GitHub 6ec6eae5 8113e200

+49 -206
-4
nixos/doc/manual/release-notes/rl-2505.section.md
··· 519 520 - `services.gitea` now supports CAPTCHA usage through the `services.gitea.captcha` variable. 521 522 - - The GRUB bootloader (`boot.loader.grub`) now generates [boot loader entries](https://uapi-group.org/specifications/specs/boot_loader_specification/). 523 - These files are used by userspace tools (for example, `bootctl`) to inspect the bootloader status, getting the default boot entry, the path of the kernel binary, etc. 524 - As a consequence, `systemctl kexec` now works automatically: specifying the kernel and its arguments with `kexec --load` is no longer required. 525 - 526 - `bind.cacheNetworks` now only controls access for recursive queries, where it previously controlled access for all queries. 527 528 - [`services.mongodb.enableAuth`](#opt-services.mongodb.enableAuth) now uses the newer [mongosh](https://github.com/mongodb-js/mongosh) shell instead of the legacy shell to configure the initial superuser. You can configure the mongosh package to use through the [`services.mongodb.mongoshPackage`](#opt-services.mongodb.mongoshPackage) option.
··· 519 520 - `services.gitea` now supports CAPTCHA usage through the `services.gitea.captcha` variable. 521 522 - `bind.cacheNetworks` now only controls access for recursive queries, where it previously controlled access for all queries. 523 524 - [`services.mongodb.enableAuth`](#opt-services.mongodb.enableAuth) now uses the newer [mongosh](https://github.com/mongodb-js/mongosh) shell instead of the legacy shell to configure the initial superuser. You can configure the mongosh package to use through the [`services.mongodb.mongoshPackage`](#opt-services.mongodb.mongoshPackage) option.
-14
nixos/modules/system/boot/loader/grub/grub.nix
··· 50 then realGrub.override { efiSupport = cfg.efiSupport; } 51 else null; 52 53 - bootPath = if cfg.mirroredBoots != [ ] 54 - then (builtins.head cfg.mirroredBoots).path 55 - else "/boot"; 56 - 57 f = x: optionalString (x != null) ("" + x); 58 59 grubConfig = args: ··· 759 system.boot.loader.id = "grub"; 760 761 environment.systemPackages = mkIf (grub != null) [ grub ]; 762 - 763 - # Link /boot under /run/boot-loder-entries to make 764 - # systemd happy even on non-EFI system 765 - systemd.mounts = lib.optional (!cfg.efiSupport) { 766 - what = bootPath; 767 - where = "/run/boot-loader-entries"; 768 - type = "none"; 769 - options = "bind"; 770 - requiredBy = [ "local-fs.target" ]; 771 - }; 772 773 boot.loader.grub.extraPrepareConfig = 774 concatStrings (mapAttrsToList (n: v: ''
··· 50 then realGrub.override { efiSupport = cfg.efiSupport; } 51 else null; 52 53 f = x: optionalString (x != null) ("" + x); 54 55 grubConfig = args: ··· 755 system.boot.loader.id = "grub"; 756 757 environment.systemPackages = mkIf (grub != null) [ grub ]; 758 759 boot.loader.grub.extraPrepareConfig = 760 concatStrings (mapAttrsToList (n: v: ''
+2 -75
nixos/modules/system/boot/loader/grub/install-grub.pl
··· 99 100 print STDERR "updating GRUB 2 menu...\n"; 101 102 - # Make GRUB directory 103 - make_path("$bootPath/grub", { mode => 0700 }); 104 - 105 - # Make BLS entries directory, see addBLSEntry 106 - make_path("$bootPath/loader/entries", { mode => 0700 }); 107 - writeFile("$bootPath/loader/entries.srel", "type1"); 108 - 109 - # and a temporary one for new entries 110 - make_path("$bootPath/loader/entries.tmp", { mode => 0700 }); 111 112 # Discover whether the bootPath is on the same filesystem as / and 113 # /nix/store. If not, then all kernels and initrds must be copied to ··· 468 } 469 470 sub addEntry { 471 - # Creates a Grub menu entry for a given system 472 my ($name, $path, $options, $current) = @_; 473 return unless -e "$path/kernel" && -e "$path/initrd"; 474 ··· 530 $conf .= "}\n\n"; 531 } 532 533 - sub addBLSEntry { 534 - # Creates a Boot Loader Specification[1] entry for a given system. 535 - # The information contained in the entry mirrors a boot entry in GRUB menu. 536 - # 537 - # [1]: https://uapi-group.org/specifications/specs/boot_loader_specification 538 - my ($prof, $spec, $gen, $link) = @_; 539 - 540 - # collect data from system 541 - my %bootspec = %{decode_json(readFile("$link/boot.json"))->{"org.nixos.bootspec.v1"}}; 542 - my $date = strftime("%F", localtime(lstat($link)->mtime)); 543 - my $kernel = $bootspec{kernel} =~ s@$storePath/@@r =~ s@/@-@r; 544 - my $initrd = $bootspec{initrd} =~ s@$storePath/@@r =~ s@/@-@r; 545 - my $kernelParams = readFile("$link/kernel-params"); 546 - my $machineId = readFile("/etc/machine-id"); 547 - 548 - if ($grubEfi eq "" && !$copyKernels) { 549 - # workaround for https://github.com/systemd/systemd/issues/35729 550 - make_path("$bootPath/kernels", { mode => 0755 }); 551 - symlink($bootspec{kernel}, "$bootPath/kernels/$kernel"); 552 - symlink($bootspec{initrd}, "$bootPath/kernels/$initrd"); 553 - $copied{"$bootPath/kernels/$kernel"} = 1; 554 - $copied{"$bootPath/kernels/$initrd"} = 1; 555 - } 556 - 557 - # fill in the entry 558 - my $extras = join(' ', $prof = $prof ne "system" ? " [$prof] " : "", 559 - $spec = $spec ne "" ? " ($spec) " : ""); 560 - my $entry = <<~END; 561 - title @distroName@$extras 562 - sort-key nixos 563 - version Generation $gen $bootspec{label}, built on $date 564 - linux kernels/$kernel 565 - initrd kernels/$initrd 566 - options init=$bootspec{init} $kernelParams 567 - END 568 - $entry .= "machine-id $machineId" if defined $machineId; 569 - 570 - # entry file basename 571 - my $name = join("-", grep { length $_ > 0 } 572 - "nixos", $prof ne "system" ? $prof : "", 573 - "generation", $gen, 574 - $spec ? "specialisation-$spec" : ""); 575 - 576 - # write entry to the temp directory 577 - writeFile("$bootPath/loader/entries.tmp/$name.conf", $entry); 578 - 579 - # mark the default entry 580 - if (readlink($link) eq $defaultConfig) { 581 - writeFile("$bootPath/loader/loader.conf", "default $name.conf"); 582 - } 583 - } 584 - 585 sub addGeneration { 586 my ($name, $nameSuffix, $path, $options, $current) = @_; 587 ··· 653 warn "skipping corrupt system profile entry ‘$link’\n"; 654 next; 655 } 656 - my $gen = nrFromGen($link); 657 my $date = strftime("%F", localtime(lstat($link)->mtime)); 658 my $version = 659 -e "$link/nixos-version" 660 ? readFile("$link/nixos-version") 661 : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]); 662 - addGeneration("@distroName@ - Configuration " . $gen, " ($date - $version)", $link, $subEntryOptions, 0); 663 - 664 - addBLSEntry(basename($profile), "", $gen, $link); 665 - foreach my $spec (glob "$link/specialisation/*") { 666 - addBLSEntry(basename($profile), $spec, $gen, $spec); 667 - } 668 } 669 670 $conf .= "}\n"; ··· 677 next unless $name =~ /^\w+$/; 678 addProfile $profile, "@distroName@ - Profile '$name'"; 679 } 680 - 681 - # Atomically replace the BLS entries directory 682 - my $entriesDir = "$bootPath/loader/entries"; 683 - rename $entriesDir, "$entriesDir.bak" or die "cannot rename $entriesDir to $entriesDir.bak: $!\n"; 684 - rename "$entriesDir.tmp", $entriesDir or die "cannot rename $entriesDir.tmp to $entriesDir: $!\n"; 685 - rmtree "$entriesDir.bak" or die "cannot remove $entriesDir.bak: $!\n"; 686 687 # extraPrepareConfig could refer to @bootPath@, which we have to substitute 688 $extraPrepareConfig =~ s/\@bootPath\@/$bootPath/g;
··· 99 100 print STDERR "updating GRUB 2 menu...\n"; 101 102 + make_path("$bootPath/grub", { mode => 0700 }); 103 104 # Discover whether the bootPath is on the same filesystem as / and 105 # /nix/store. If not, then all kernels and initrds must be copied to ··· 460 } 461 462 sub addEntry { 463 my ($name, $path, $options, $current) = @_; 464 return unless -e "$path/kernel" && -e "$path/initrd"; 465 ··· 521 $conf .= "}\n\n"; 522 } 523 524 sub addGeneration { 525 my ($name, $nameSuffix, $path, $options, $current) = @_; 526 ··· 592 warn "skipping corrupt system profile entry ‘$link’\n"; 593 next; 594 } 595 my $date = strftime("%F", localtime(lstat($link)->mtime)); 596 my $version = 597 -e "$link/nixos-version" 598 ? readFile("$link/nixos-version") 599 : basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]); 600 + addGeneration("@distroName@ - Configuration " . nrFromGen($link), " ($date - $version)", $link, $subEntryOptions, 0); 601 } 602 603 $conf .= "}\n"; ··· 610 next unless $name =~ /^\w+$/; 611 addProfile $profile, "@distroName@ - Profile '$name'"; 612 } 613 614 # extraPrepareConfig could refer to @bootPath@, which we have to substitute 615 $extraPrepareConfig =~ s/\@bootPath\@/$bootPath/g;
+1 -1
nixos/tests/all-tests.nix
··· 463 greetd-no-shadow = handleTest ./greetd-no-shadow.nix {}; 464 grocy = handleTest ./grocy.nix {}; 465 grow-partition = runTest ./grow-partition.nix; 466 - grub = import ./grub.nix { inherit pkgs runTest; }; 467 guacamole-server = handleTest ./guacamole-server.nix {}; 468 guix = handleTest ./guix {}; 469 gvisor = handleTest ./gvisor.nix {};
··· 463 greetd-no-shadow = handleTest ./greetd-no-shadow.nix {}; 464 grocy = handleTest ./grocy.nix {}; 465 grow-partition = runTest ./grow-partition.nix; 466 + grub = handleTest ./grub.nix {}; 467 guacamole-server = handleTest ./guacamole-server.nix {}; 468 guix = handleTest ./guix {}; 469 gvisor = handleTest ./gvisor.nix {};
+46 -109
nixos/tests/grub.nix
··· 1 - { pkgs, runTest }: 2 - 3 - { 4 - # Basic GRUB setup with BIOS and a password 5 - basic = runTest { 6 - name = "grub-basic"; 7 - meta.maintainers = with pkgs.lib.maintainers; [ rnhmjoj ]; 8 - 9 - nodes.machine = { ... }: { 10 - virtualisation.useBootLoader = true; 11 - boot.loader.timeout = null; 12 - boot.loader.grub = { 13 - enable = true; 14 - users.alice.password = "supersecret"; 15 - # OCR is not accurate enough 16 - extraConfig = "serial; terminal_output serial"; 17 - }; 18 - }; 19 - 20 - testScript = '' 21 - def grub_login_as(user, password): 22 - """ 23 - Enters user and password to log into GRUB 24 - """ 25 - machine.wait_for_console_text("Enter username:") 26 - machine.send_chars(user + "\n") 27 - machine.wait_for_console_text("Enter password:") 28 - machine.send_chars(password + "\n") 29 - 30 - 31 - def grub_select_all_configurations(): 32 - """ 33 - Selects "All configurations" from the GRUB menu 34 - to trigger a login request. 35 - """ 36 - machine.send_monitor_command("sendkey down") 37 - machine.send_monitor_command("sendkey ret") 38 - 39 - 40 - machine.start() 41 42 - # wait for grub screen 43 - machine.wait_for_console_text("GNU GRUB") 44 45 - grub_select_all_configurations() 46 - with subtest("Invalid credentials are rejected"): 47 - grub_login_as("wronguser", "wrongsecret") 48 - machine.wait_for_console_text("error: access denied.") 49 50 - grub_select_all_configurations() 51 - with subtest("Valid credentials are accepted"): 52 - grub_login_as("alice", "supersecret") 53 - machine.send_chars("\n") # press enter to boot 54 - machine.wait_for_console_text("Linux version") 55 56 - with subtest("Machine boots correctly"): 57 - machine.wait_for_unit("multi-user.target") 58 - ''; 59 }; 60 61 - # Test boot loader entries on EFI 62 - bls-efi = runTest { 63 - name = "grub-bls-efi"; 64 - meta.maintainers = with pkgs.lib.maintainers; [ rnhmjoj ]; 65 66 - nodes.machine = { pkgs, ... }: { 67 - virtualisation.useBootLoader = true; 68 - virtualisation.useEFIBoot = true; 69 - boot.loader.efi.canTouchEfiVariables = true; 70 - boot.loader.grub.enable = true; 71 - boot.loader.grub.efiSupport = true; 72 - }; 73 74 - testScript = '' 75 - with subtest("Machine boots correctly"): 76 - machine.wait_for_unit("multi-user.target") 77 78 - with subtest("Boot entries are installed"): 79 - entries = machine.succeed("bootctl list") 80 - print(entries) 81 - error = "NixOS boot entry not found in bootctl list." 82 - assert "version: Generation 1" in entries, error 83 84 - with subtest("systemctl kexec can detect the kernel"): 85 - machine.succeed("systemctl kexec --dry-run") 86 87 - with subtest("systemctl kexec really works"): 88 - machine.execute("systemctl kexec", check_return=False) 89 - machine.connected = False 90 - machine.connect() 91 - machine.wait_for_unit("multi-user.target") 92 - ''; 93 - }; 94 95 - # Test boot loader entries on BIOS 96 - bls-bios = runTest { 97 - name = "grub-bls-bios"; 98 - meta.maintainers = with pkgs.lib.maintainers; [ rnhmjoj ]; 99 - 100 - nodes.machine = { pkgs, ... }: { 101 - virtualisation.useBootLoader = true; 102 - boot.loader.grub.enable = true; 103 - }; 104 - 105 - testScript = '' 106 - with subtest("Machine boots correctly"): 107 - machine.wait_for_unit("multi-user.target") 108 - 109 - with subtest("Boot entries are installed"): 110 - machine.succeed("test -f /boot/loader/entries/nixos-generation-1.conf") 111 - 112 - with subtest("systemctl kexec can detect the kernel"): 113 - machine.succeed("systemctl kexec --dry-run") 114 115 - with subtest("systemctl kexec really works"): 116 - machine.execute("systemctl kexec", check_return=False) 117 - machine.connected = False 118 - machine.connect() 119 - machine.wait_for_unit("multi-user.target") 120 - ''; 121 - }; 122 123 - }
··· 1 + import ./make-test-python.nix ({ lib, ... }: { 2 + name = "grub"; 3 4 + meta = with lib.maintainers; { 5 + maintainers = [ rnhmjoj ]; 6 + }; 7 8 + nodes.machine = { ... }: { 9 + virtualisation.useBootLoader = true; 10 11 + boot.loader.timeout = null; 12 + boot.loader.grub = { 13 + enable = true; 14 + users.alice.password = "supersecret"; 15 16 + # OCR is not accurate enough 17 + extraConfig = "serial; terminal_output serial"; 18 }; 19 + }; 20 21 + testScript = '' 22 + def grub_login_as(user, password): 23 + """ 24 + Enters user and password to log into GRUB 25 + """ 26 + machine.wait_for_console_text("Enter username:") 27 + machine.send_chars(user + "\n") 28 + machine.wait_for_console_text("Enter password:") 29 + machine.send_chars(password + "\n") 30 31 32 + def grub_select_all_configurations(): 33 + """ 34 + Selects "All configurations" from the GRUB menu 35 + to trigger a login request. 36 + """ 37 + machine.send_monitor_command("sendkey down") 38 + machine.send_monitor_command("sendkey ret") 39 40 41 + machine.start() 42 43 + # wait for grub screen 44 + machine.wait_for_console_text("GNU GRUB") 45 46 + grub_select_all_configurations() 47 + with subtest("Invalid credentials are rejected"): 48 + grub_login_as("wronguser", "wrongsecret") 49 + machine.wait_for_console_text("error: access denied.") 50 51 + grub_select_all_configurations() 52 + with subtest("Valid credentials are accepted"): 53 + grub_login_as("alice", "supersecret") 54 + machine.send_chars("\n") # press enter to boot 55 + machine.wait_for_console_text("Linux version") 56 57 + with subtest("Machine boots correctly"): 58 + machine.wait_for_unit("multi-user.target") 59 + ''; 60 + })
-3
nixos/tests/nixos-rebuild-install-bootloader.nix
··· 71 # at this point we've tested regression #262724, but haven't tested the bootloader itself 72 # TODO: figure out how to how to tell the test driver to start the bootloader instead of 73 # booting into the kernel directly. 74 - 75 - with subtest("New boot entry has been added"): 76 - machine.succeed("test -f /boot/loader/entries/nixos-generation-2.conf") 77 ''; 78 })
··· 71 # at this point we've tested regression #262724, but haven't tested the bootloader itself 72 # TODO: figure out how to how to tell the test driver to start the bootloader instead of 73 # booting into the kernel directly. 74 ''; 75 })