Revert "nixos/grub: generate BLS entries"

Changed files
+49 -206
nixos
doc
manual
release-notes
modules
system
boot
tests
-4
nixos/doc/manual/release-notes/rl-2505.section.md
···
- `services.gitea` now supports CAPTCHA usage through the `services.gitea.captcha` variable.
-
- The GRUB bootloader (`boot.loader.grub`) now generates [boot loader entries](https://uapi-group.org/specifications/specs/boot_loader_specification/).
-
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.
-
As a consequence, `systemctl kexec` now works automatically: specifying the kernel and its arguments with `kexec --load` is no longer required.
-
- `bind.cacheNetworks` now only controls access for recursive queries, where it previously controlled access for all queries.
- [`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
···
then realGrub.override { efiSupport = cfg.efiSupport; }
else null;
-
bootPath = if cfg.mirroredBoots != [ ]
-
then (builtins.head cfg.mirroredBoots).path
-
else "/boot";
-
f = x: optionalString (x != null) ("" + x);
grubConfig = args:
···
system.boot.loader.id = "grub";
environment.systemPackages = mkIf (grub != null) [ grub ];
-
-
# Link /boot under /run/boot-loder-entries to make
-
# systemd happy even on non-EFI system
-
systemd.mounts = lib.optional (!cfg.efiSupport) {
-
what = bootPath;
-
where = "/run/boot-loader-entries";
-
type = "none";
-
options = "bind";
-
requiredBy = [ "local-fs.target" ];
-
};
boot.loader.grub.extraPrepareConfig =
concatStrings (mapAttrsToList (n: v: ''
+2 -75
nixos/modules/system/boot/loader/grub/install-grub.pl
···
print STDERR "updating GRUB 2 menu...\n";
-
# Make GRUB directory
-
make_path("$bootPath/grub", { mode => 0700 });
-
-
# Make BLS entries directory, see addBLSEntry
-
make_path("$bootPath/loader/entries", { mode => 0700 });
-
writeFile("$bootPath/loader/entries.srel", "type1");
-
-
# and a temporary one for new entries
-
make_path("$bootPath/loader/entries.tmp", { mode => 0700 });
+
make_path("$bootPath/grub", { mode => 0700 });
# Discover whether the bootPath is on the same filesystem as / and
# /nix/store. If not, then all kernels and initrds must be copied to
···
}
sub addEntry {
-
# Creates a Grub menu entry for a given system
my ($name, $path, $options, $current) = @_;
return unless -e "$path/kernel" && -e "$path/initrd";
···
$conf .= "}\n\n";
}
-
sub addBLSEntry {
-
# Creates a Boot Loader Specification[1] entry for a given system.
-
# The information contained in the entry mirrors a boot entry in GRUB menu.
-
#
-
# [1]: https://uapi-group.org/specifications/specs/boot_loader_specification
-
my ($prof, $spec, $gen, $link) = @_;
-
-
# collect data from system
-
my %bootspec = %{decode_json(readFile("$link/boot.json"))->{"org.nixos.bootspec.v1"}};
-
my $date = strftime("%F", localtime(lstat($link)->mtime));
-
my $kernel = $bootspec{kernel} =~ s@$storePath/@@r =~ s@/@-@r;
-
my $initrd = $bootspec{initrd} =~ s@$storePath/@@r =~ s@/@-@r;
-
my $kernelParams = readFile("$link/kernel-params");
-
my $machineId = readFile("/etc/machine-id");
-
-
if ($grubEfi eq "" && !$copyKernels) {
-
# workaround for https://github.com/systemd/systemd/issues/35729
-
make_path("$bootPath/kernels", { mode => 0755 });
-
symlink($bootspec{kernel}, "$bootPath/kernels/$kernel");
-
symlink($bootspec{initrd}, "$bootPath/kernels/$initrd");
-
$copied{"$bootPath/kernels/$kernel"} = 1;
-
$copied{"$bootPath/kernels/$initrd"} = 1;
-
}
-
-
# fill in the entry
-
my $extras = join(' ', $prof = $prof ne "system" ? " [$prof] " : "",
-
$spec = $spec ne "" ? " ($spec) " : "");
-
my $entry = <<~END;
-
title @distroName@$extras
-
sort-key nixos
-
version Generation $gen $bootspec{label}, built on $date
-
linux kernels/$kernel
-
initrd kernels/$initrd
-
options init=$bootspec{init} $kernelParams
-
END
-
$entry .= "machine-id $machineId" if defined $machineId;
-
-
# entry file basename
-
my $name = join("-", grep { length $_ > 0 }
-
"nixos", $prof ne "system" ? $prof : "",
-
"generation", $gen,
-
$spec ? "specialisation-$spec" : "");
-
-
# write entry to the temp directory
-
writeFile("$bootPath/loader/entries.tmp/$name.conf", $entry);
-
-
# mark the default entry
-
if (readlink($link) eq $defaultConfig) {
-
writeFile("$bootPath/loader/loader.conf", "default $name.conf");
-
}
-
}
-
sub addGeneration {
my ($name, $nameSuffix, $path, $options, $current) = @_;
···
warn "skipping corrupt system profile entry ‘$link’\n";
next;
}
-
my $gen = nrFromGen($link);
my $date = strftime("%F", localtime(lstat($link)->mtime));
my $version =
-e "$link/nixos-version"
? readFile("$link/nixos-version")
: basename((glob(dirname(Cwd::abs_path("$link/kernel")) . "/lib/modules/*"))[0]);
-
addGeneration("@distroName@ - Configuration " . $gen, " ($date - $version)", $link, $subEntryOptions, 0);
-
-
addBLSEntry(basename($profile), "", $gen, $link);
-
foreach my $spec (glob "$link/specialisation/*") {
-
addBLSEntry(basename($profile), $spec, $gen, $spec);
-
}
+
addGeneration("@distroName@ - Configuration " . nrFromGen($link), " ($date - $version)", $link, $subEntryOptions, 0);
}
$conf .= "}\n";
···
next unless $name =~ /^\w+$/;
addProfile $profile, "@distroName@ - Profile '$name'";
}
-
-
# Atomically replace the BLS entries directory
-
my $entriesDir = "$bootPath/loader/entries";
-
rename $entriesDir, "$entriesDir.bak" or die "cannot rename $entriesDir to $entriesDir.bak: $!\n";
-
rename "$entriesDir.tmp", $entriesDir or die "cannot rename $entriesDir.tmp to $entriesDir: $!\n";
-
rmtree "$entriesDir.bak" or die "cannot remove $entriesDir.bak: $!\n";
# extraPrepareConfig could refer to @bootPath@, which we have to substitute
$extraPrepareConfig =~ s/\@bootPath\@/$bootPath/g;
+1 -1
nixos/tests/all-tests.nix
···
greetd-no-shadow = handleTest ./greetd-no-shadow.nix {};
grocy = handleTest ./grocy.nix {};
grow-partition = runTest ./grow-partition.nix;
-
grub = import ./grub.nix { inherit pkgs runTest; };
+
grub = handleTest ./grub.nix {};
guacamole-server = handleTest ./guacamole-server.nix {};
guix = handleTest ./guix {};
gvisor = handleTest ./gvisor.nix {};
+46 -109
nixos/tests/grub.nix
···
-
{ pkgs, runTest }:
-
-
{
-
# Basic GRUB setup with BIOS and a password
-
basic = runTest {
-
name = "grub-basic";
-
meta.maintainers = with pkgs.lib.maintainers; [ rnhmjoj ];
-
-
nodes.machine = { ... }: {
-
virtualisation.useBootLoader = true;
-
boot.loader.timeout = null;
-
boot.loader.grub = {
-
enable = true;
-
users.alice.password = "supersecret";
-
# OCR is not accurate enough
-
extraConfig = "serial; terminal_output serial";
-
};
-
};
-
-
testScript = ''
-
def grub_login_as(user, password):
-
"""
-
Enters user and password to log into GRUB
-
"""
-
machine.wait_for_console_text("Enter username:")
-
machine.send_chars(user + "\n")
-
machine.wait_for_console_text("Enter password:")
-
machine.send_chars(password + "\n")
-
-
-
def grub_select_all_configurations():
-
"""
-
Selects "All configurations" from the GRUB menu
-
to trigger a login request.
-
"""
-
machine.send_monitor_command("sendkey down")
-
machine.send_monitor_command("sendkey ret")
-
-
-
machine.start()
+
import ./make-test-python.nix ({ lib, ... }: {
+
name = "grub";
-
# wait for grub screen
-
machine.wait_for_console_text("GNU GRUB")
+
meta = with lib.maintainers; {
+
maintainers = [ rnhmjoj ];
+
};
-
grub_select_all_configurations()
-
with subtest("Invalid credentials are rejected"):
-
grub_login_as("wronguser", "wrongsecret")
-
machine.wait_for_console_text("error: access denied.")
+
nodes.machine = { ... }: {
+
virtualisation.useBootLoader = true;
-
grub_select_all_configurations()
-
with subtest("Valid credentials are accepted"):
-
grub_login_as("alice", "supersecret")
-
machine.send_chars("\n") # press enter to boot
-
machine.wait_for_console_text("Linux version")
+
boot.loader.timeout = null;
+
boot.loader.grub = {
+
enable = true;
+
users.alice.password = "supersecret";
-
with subtest("Machine boots correctly"):
-
machine.wait_for_unit("multi-user.target")
-
'';
+
# OCR is not accurate enough
+
extraConfig = "serial; terminal_output serial";
};
+
};
-
# Test boot loader entries on EFI
-
bls-efi = runTest {
-
name = "grub-bls-efi";
-
meta.maintainers = with pkgs.lib.maintainers; [ rnhmjoj ];
+
testScript = ''
+
def grub_login_as(user, password):
+
"""
+
Enters user and password to log into GRUB
+
"""
+
machine.wait_for_console_text("Enter username:")
+
machine.send_chars(user + "\n")
+
machine.wait_for_console_text("Enter password:")
+
machine.send_chars(password + "\n")
-
nodes.machine = { pkgs, ... }: {
-
virtualisation.useBootLoader = true;
-
virtualisation.useEFIBoot = true;
-
boot.loader.efi.canTouchEfiVariables = true;
-
boot.loader.grub.enable = true;
-
boot.loader.grub.efiSupport = true;
-
};
-
testScript = ''
-
with subtest("Machine boots correctly"):
-
machine.wait_for_unit("multi-user.target")
+
def grub_select_all_configurations():
+
"""
+
Selects "All configurations" from the GRUB menu
+
to trigger a login request.
+
"""
+
machine.send_monitor_command("sendkey down")
+
machine.send_monitor_command("sendkey ret")
-
with subtest("Boot entries are installed"):
-
entries = machine.succeed("bootctl list")
-
print(entries)
-
error = "NixOS boot entry not found in bootctl list."
-
assert "version: Generation 1" in entries, error
-
with subtest("systemctl kexec can detect the kernel"):
-
machine.succeed("systemctl kexec --dry-run")
+
machine.start()
-
with subtest("systemctl kexec really works"):
-
machine.execute("systemctl kexec", check_return=False)
-
machine.connected = False
-
machine.connect()
-
machine.wait_for_unit("multi-user.target")
-
'';
-
};
+
# wait for grub screen
+
machine.wait_for_console_text("GNU GRUB")
-
# Test boot loader entries on BIOS
-
bls-bios = runTest {
-
name = "grub-bls-bios";
-
meta.maintainers = with pkgs.lib.maintainers; [ rnhmjoj ];
-
-
nodes.machine = { pkgs, ... }: {
-
virtualisation.useBootLoader = true;
-
boot.loader.grub.enable = true;
-
};
-
-
testScript = ''
-
with subtest("Machine boots correctly"):
-
machine.wait_for_unit("multi-user.target")
-
-
with subtest("Boot entries are installed"):
-
machine.succeed("test -f /boot/loader/entries/nixos-generation-1.conf")
-
-
with subtest("systemctl kexec can detect the kernel"):
-
machine.succeed("systemctl kexec --dry-run")
+
grub_select_all_configurations()
+
with subtest("Invalid credentials are rejected"):
+
grub_login_as("wronguser", "wrongsecret")
+
machine.wait_for_console_text("error: access denied.")
-
with subtest("systemctl kexec really works"):
-
machine.execute("systemctl kexec", check_return=False)
-
machine.connected = False
-
machine.connect()
-
machine.wait_for_unit("multi-user.target")
-
'';
-
};
+
grub_select_all_configurations()
+
with subtest("Valid credentials are accepted"):
+
grub_login_as("alice", "supersecret")
+
machine.send_chars("\n") # press enter to boot
+
machine.wait_for_console_text("Linux version")
-
}
+
with subtest("Machine boots correctly"):
+
machine.wait_for_unit("multi-user.target")
+
'';
+
})
-3
nixos/tests/nixos-rebuild-install-bootloader.nix
···
# at this point we've tested regression #262724, but haven't tested the bootloader itself
# TODO: figure out how to how to tell the test driver to start the bootloader instead of
# booting into the kernel directly.
-
-
with subtest("New boot entry has been added"):
-
machine.succeed("test -f /boot/loader/entries/nixos-generation-2.conf")
'';
})