Compare commits

..

1 commit
main ... foo

Author SHA1 Message Date
Jörg Thalheim
fa0c4d1523
also disable man page to save a few mb 2024-05-22 18:46:04 +02:00
25 changed files with 204 additions and 547 deletions

View file

@ -11,7 +11,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
tag: tag:
- nixos-24.05 - nixos-23.11
- nixos-unstable - nixos-unstable
os: os:
- nscloud-ubuntu-22.04-arm64-4x16 - nscloud-ubuntu-22.04-arm64-4x16
@ -19,7 +19,7 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30 - uses: cachix/install-nix-action@v26
with: with:
nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixpkgs-unstable.tar.gz nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixpkgs-unstable.tar.gz
- name: Build image - name: Build image

View file

@ -11,9 +11,9 @@ jobs:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v30 uses: cachix/install-nix-action@v26
- name: Update flake.lock - name: Update flake.lock
uses: DeterminateSystems/update-flake-lock@v24 uses: DeterminateSystems/update-flake-lock@v21
with: with:
pr-labels: | pr-labels: |
merge-queue merge-queue

View file

@ -1,7 +1,7 @@
queue_rules: queue_rules:
- name: default - name: default
merge_conditions: merge_conditions:
- check-success=buildbot/nix-build - check-success=buildbot/nix-eval
defaults: defaults:
actions: actions:
queue: queue:

View file

@ -11,10 +11,10 @@ Kexec is a mechanism in Linux to load a new kernel from a running Linux to
replace the current kernel. This is useful for booting the Nixos installer from replace the current kernel. This is useful for booting the Nixos installer from
existing Linux distributions, such as server provider that do not offer a NixOS existing Linux distributions, such as server provider that do not offer a NixOS
option. After running kexec, the NixOS installer exists only in memory. At the option. After running kexec, the NixOS installer exists only in memory. At the
time of writing, this requires secure boot off in BIOS settings and at least 1GB time of writing, this requires at least 1GB of physical RAM (swap does not
of physical RAM (swap does not count) in the system. If not enough RAM is available, count) in the system. If not enough RAM is available, the initrd cannot be
the initrd cannot be loaded. Because the NixOS runs only in RAM, users can reformat loaded. Because the NixOS runs only in RAM, users can reformat all the system's
all the system's discs to prepare for a new NixOS installation. discs to prepare for a new NixOS installation.
It can be booted as follows by running these commands as root: It can be booted as follows by running these commands as root:

View file

@ -5,8 +5,8 @@ set -xeuo pipefail
shopt -s lastpipe shopt -s lastpipe
build_netboot_image() { build_netboot_image() {
declare -r tag=$1 channel=$2 arch=$3 tmp=$4 declare -r tag=$1 arch=$2 tmp=$3
img=$(nix build --print-out-paths --option accept-flake-config true -L ".#packages.${arch}.netboot-nixos-${channel//./}") img=$(nix build --print-out-paths --option accept-flake-config true -L ".#packages.${arch}.netboot-${tag//./}")
kernel=$(echo "$img"/*Image) kernel=$(echo "$img"/*Image)
kernelName=$(basename "$kernel") kernelName=$(basename "$kernel")
ln -s "$kernel" "$tmp/$kernelName-$arch" ln -s "$kernel" "$tmp/$kernelName-$arch"
@ -22,14 +22,14 @@ build_netboot_image() {
} }
build_kexec_installer() { build_kexec_installer() {
declare -r channel=$1 arch=$2 tmp=$3 variant=$4 declare -r tag=$1 arch=$2 tmp=$3 variant=$4
out=$(nix build --print-out-paths --option accept-flake-config true -L ".#packages.${arch}.kexec-installer-nixos-${channel}${variant}") out=$(nix build --print-out-paths --option accept-flake-config true -L ".#packages.${arch}.kexec-installer-${tag//./}${variant}")
echo "$out/nixos-kexec-installer${variant}-$arch.tar.gz" echo "$out/nixos-kexec-installer${variant}-$arch.tar.gz"
} }
build_image_installer() { build_image_installer() {
declare -r channel=$1 arch=$2 tmp=$3 declare -r tag=$1 arch=$2 tmp=$3
out=$(nix build --print-out-paths --option accept-flake-config true -L ".#packages.${arch}.image-installer-nixos-${channel//./}") out=$(nix build --print-out-paths --option accept-flake-config true -L ".#packages.${arch}.image-installer-${tag//./}")
echo "$out/iso/nixos-installer-${arch}.iso" echo "$out/iso/nixos-installer-${arch}.iso"
} }
@ -38,11 +38,10 @@ main() {
tmp="$(mktemp -d)" tmp="$(mktemp -d)"
trap 'rm -rf -- "$tmp"' EXIT trap 'rm -rf -- "$tmp"' EXIT
( (
channel=$(if [[ "$tag" == nixos-unstable ]]; then echo "unstable"; else echo "stable"; fi) build_kexec_installer "$tag" "$arch" "$tmp" ""
build_kexec_installer "$channel" "$arch" "$tmp" "" build_kexec_installer "$tag" "$arch" "$tmp" "-noninteractive"
build_kexec_installer "$channel" "$arch" "$tmp" "-noninteractive" build_netboot_image "$tag" "$arch" "$tmp"
build_netboot_image "$tag" "$channel" "$arch" "$tmp" build_image_installer "$tag" "$arch" "$tmp"
build_image_installer "$channel" "$arch" "$tmp"
) | readarray -t assets ) | readarray -t assets
for asset in "${assets[@]}"; do for asset in "${assets[@]}"; do
pushd "$(dirname "$asset")" pushd "$(dirname "$asset")"

View file

@ -1,28 +1,28 @@
{ {
"nodes": { "nodes": {
"nixos-stable": { "nixos-2311": {
"locked": { "locked": {
"lastModified": 1729181673, "lastModified": 1715818734,
"narHash": "sha256-LDiPhQ3l+fBjRATNtnuDZsBS7hqoBtPkKBkhpoBHv3I=", "narHash": "sha256-WvAJWCwPj/6quKcsgsvQYyZRxV8ho/yUzj0HZQ34DVU=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "4eb33fe664af7b41a4c446f87d20c9a0a6321fa3", "rev": "95742536dc6debb5a8b8b78b27001c38f369f1e7",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-24.05", "ref": "release-23.11",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixos-unstable": { "nixos-unstable": {
"locked": { "locked": {
"lastModified": 1729450260, "lastModified": 1715777523,
"narHash": "sha256-3GNZr0V4b19RZ5mlyiY/4F8N2pzitvjDU6aHMWjAqLI=", "narHash": "sha256-S6g1OWbKXswOMoTssq3aOm4OhxhlKoIwEAXWmU57vts=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e3f55158e7587c5a5fdb0e86eb7ca4f455f0928f", "rev": "c029b7f004009923bbfc90bbc31263cd4b08759f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -34,7 +34,7 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"nixos-stable": "nixos-stable", "nixos-2311": "nixos-2311",
"nixos-unstable": "nixos-unstable" "nixos-unstable": "nixos-unstable"
} }
} }

View file

@ -2,51 +2,48 @@
description = "NixOS images"; description = "NixOS images";
inputs.nixos-unstable.url = "github:NixOS/nixpkgs/nixos-unstable-small"; inputs.nixos-unstable.url = "github:NixOS/nixpkgs/nixos-unstable-small";
inputs.nixos-stable.url = "github:NixOS/nixpkgs/nixos-24.05"; inputs.nixos-2311.url = "github:NixOS/nixpkgs/release-23.11";
nixConfig.extra-substituters = [ "https://nix-community.cachix.org" ]; nixConfig.extra-substituters = [ "https://numtide.cachix.org" ];
nixConfig.extra-trusted-public-keys = [ "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" ]; nixConfig.extra-trusted-public-keys = [ "numtide.cachix.org-1:2ps1kLBUWjxIneOy1Ik6cQjb41X0iXVXeHigGmycPPE=" ];
outputs = { self, nixos-unstable, nixos-stable }: outputs = { self, nixos-unstable, nixos-2311 }:
let let
supportedSystems = [ "riscv64-linux" ]; supportedSystems = [ "aarch64-linux" "x86_64-linux" ];
forAllSystems = nixos-unstable.lib.genAttrs supportedSystems; forAllSystems = nixos-unstable.lib.genAttrs supportedSystems;
in in
{ {
hydraJobs = { inherit (self) checks; };
packages = forAllSystems (system: packages = forAllSystems (system:
let let
netboot = nixpkgs: (import (nixpkgs + "/nixos/release.nix") { }).netboot.${system}; netboot = nixpkgs: (import (nixpkgs + "/nixos/release.nix") { }).netboot.${system};
kexec-installer = nixpkgs: module: (nixpkgs.legacyPackages.${system}.nixos [ module self.nixosModules.kexec-installer ]).config.system.build.kexecTarball; kexec-installer = nixpkgs: modules: (nixpkgs.legacyPackages.${system}.nixos (modules ++ [ self.nixosModules.kexec-installer ])).config.system.build.kexecTarball;
netboot-installer = nixpkgs: (nixpkgs.legacyPackages.${system}.nixos [ self.nixosModules.netboot-installer ]).config.system.build.netboot; netboot-installer = nixpkgs: (nixpkgs.legacyPackages.${system}.nixos [ self.nixosModules.netboot-installer ]).config.system.build.netboot;
image-installer = nixpkgs: (nixpkgs.legacyPackages.${system}.nixos [ self.nixosModules.image-installer ]).config.system.build.isoImage; image-installer = nixpkgs: (nixpkgs.legacyPackages.${system}.nixos [ self.nixosModules.image-installer ]).config.system.build.isoImage;
in in
{ {
netboot-nixos-unstable = netboot nixos-unstable; netboot-nixos-unstable = netboot nixos-unstable;
netboot-nixos-stable = netboot nixos-stable; netboot-nixos-2311 = netboot nixos-2311;
kexec-installer-nixos-unstable = kexec-installer nixos-unstable {}; kexec-installer-nixos-unstable = kexec-installer nixos-unstable [ ];
kexec-installer-nixos-stable = kexec-installer nixos-stable {}; kexec-installer-nixos-2311 = kexec-installer nixos-2311 [ ];
image-installer-nixos-unstable = image-installer nixos-unstable; image-installer-nixos-unstable = image-installer nixos-unstable;
image-installer-nixos-stable = image-installer nixos-stable; image-installer-nixos-2311 = image-installer nixos-2311;
kexec-installer-nixos-unstable-noninteractive = kexec-installer nixos-unstable { kexec-installer-nixos-unstable-noninteractive = kexec-installer nixos-unstable [
_file = __curPos.file; {
system.kexec-installer.name = "nixos-kexec-installer-noninteractive"; system.kexec-installer.name = "nixos-kexec-installer-noninteractive";
imports = [ }
self.nixosModules.noninteractive self.nixosModules.noninteractive
]; ];
}; kexec-installer-nixos-2311-noninteractive = kexec-installer nixos-2311 [
kexec-installer-nixos-stable-noninteractive = kexec-installer nixos-stable { {
_file = __curPos.file;
system.kexec-installer.name = "nixos-kexec-installer-noninteractive"; system.kexec-installer.name = "nixos-kexec-installer-noninteractive";
imports = [ }
self.nixosModules.noninteractive self.nixosModules.noninteractive
]; ];
};
netboot-installer-nixos-unstable = netboot-installer nixos-unstable; netboot-installer-nixos-unstable = netboot-installer nixos-unstable;
netboot-installer-nixos-stable = netboot-installer nixos-stable; netboot-installer-nixos-2311 = netboot-installer nixos-2311;
}); });
nixosModules = { nixosModules = {
kexec-installer = ./nix/kexec-installer/module.nix; kexec-installer = ./nix/kexec-installer/module.nix;
@ -61,25 +58,24 @@
packages = forAllSystems (system: nixos-unstable.lib.mapAttrs' (n: nixos-unstable.lib.nameValuePair "package-${n}") self.packages.${system}); packages = forAllSystems (system: nixos-unstable.lib.mapAttrs' (n: nixos-unstable.lib.nameValuePair "package-${n}") self.packages.${system});
checks = checks =
let let
pkgsUnstable = nixos-unstable.legacyPackages.riscv64-linux; pkgs = nixos-unstable.legacyPackages.x86_64-linux;
pkgsStable = nixos-stable.legacyPackages.riscv64-linux;
bootTests = pkgs: channel: suffix: pkgs.lib.mapAttrs' (name: pkgs.lib.nameValuePair "${name}${suffix}") (pkgs.callPackages ./nix/image-installer/tests.nix {
nixpkgs = channel;
nixosModules = self.nixosModules;
});
in in
{ {
kexec-installer-unstable = pkgsUnstable.callPackage ./nix/kexec-installer/test.nix { kexec-installer-unstable = pkgs.callPackage ./nix/kexec-installer/test.nix {
kexecTarball = self.packages.riscv64-linux.kexec-installer-nixos-unstable-noninteractive; kexecTarball = self.packages.x86_64-linux.kexec-installer-nixos-unstable-noninteractive;
};
shellcheck = pkgs.runCommand "shellcheck"
{
nativeBuildInputs = [ pkgs.shellcheck ];
} ''
shellcheck ${(pkgs.nixos [self.nixosModules.kexec-installer]).config.system.build.kexecRun}
touch $out
'';
kexec-installer-2311 = nixos-2311.legacyPackages.x86_64-linux.callPackage ./nix/kexec-installer/test.nix {
kexecTarball = self.packages.x86_64-linux.kexec-installer-nixos-2311-noninteractive;
}; };
kexec-installer-stable = nixos-stable.legacyPackages.riscv64-linux.callPackage ./nix/kexec-installer/test.nix {
kexecTarball = self.packages.riscv64-linux.kexec-installer-nixos-stable-noninteractive;
}; };
} // (bootTests pkgsUnstable nixos-unstable "-nixos-unstable")
// (bootTests pkgsStable nixos-stable "-nixos-stable");
in in
nixos-unstable.lib.recursiveUpdate packages { riscv64-linux = checks; }; nixos-unstable.lib.recursiveUpdate packages { x86_64-linux = checks; };
}; };
} }

5
garnix.yaml Normal file
View file

@ -0,0 +1,5 @@
builds:
include:
- '*.x86_64-linux.*'
- '*.aarch64-linux.*'
- nixosConfigurations.*

View file

@ -40,9 +40,8 @@ in
imports = [ imports = [
(modulesPath + "/installer/cd-dvd/installation-cd-base.nix") (modulesPath + "/installer/cd-dvd/installation-cd-base.nix")
../installer.nix ../installer.nix
../noveau-workaround.nix
./hidden-ssh-announcement.nix
./wifi.nix ./wifi.nix
./hidden-ssh-announcement.nix
]; ];
systemd.tmpfiles.rules = [ "d /var/shared 0777 root root - -" ]; systemd.tmpfiles.rules = [ "d /var/shared 0777 root root - -" ];
services.openssh.settings.PermitRootLogin = "yes"; services.openssh.settings.PermitRootLogin = "yes";

View file

@ -1,105 +0,0 @@
{
pkgs,
lib,
nixpkgs,
nixos,
nixosModules,
}:
let
testConfig = (
nixos [
(
{ modulesPath, ... }:
{
imports = [
nixosModules.image-installer
"${modulesPath}/testing/test-instrumentation.nix"
];
}
)
]
);
iso = testConfig.config.system.build.isoImage;
mkStartCommand =
{
memory ? 2048,
cdrom ? null,
usb ? null,
uefi ? false,
extraFlags ? [ ],
}:
let
qemu-common = import (nixpkgs + "/nixos/lib/qemu-common.nix") { inherit lib pkgs; };
qemu = qemu-common.qemuBinary pkgs.qemu_test;
flags =
[
"-m"
(toString memory)
"-netdev"
"user,id=net0"
"-device"
"virtio-net-pci,netdev=net0"
]
++ lib.optionals (cdrom != null) [
"-cdrom"
cdrom
]
++ lib.optionals (usb != null) [
"-device"
"usb-ehci"
"-drive"
"id=usbdisk,file=${usb},if=none,readonly"
"-device"
"usb-storage,drive=usbdisk"
]
++ lib.optionals uefi [
"-drive"
"if=pflash,format=raw,unit=0,readonly=on,file=${pkgs.OVMF.firmware}"
"-drive"
"if=pflash,format=raw,unit=1,readonly=on,file=${pkgs.OVMF.variables}"
]
++ extraFlags;
flagsStr = lib.concatStringsSep " " flags;
in
"${qemu} ${flagsStr}";
makeBootTest =
name: config:
let
startCommand = mkStartCommand config;
in
pkgs.testers.runNixOSTest {
name = "boot-${name}";
nodes = { };
testScript = ''
machine = create_machine("${startCommand}")
machine.start()
machine.wait_for_unit("multi-user.target")
machine.succeed("nix store verify --no-trust -r --option experimental-features nix-command /run/current-system")
machine.shutdown()
'';
};
in
{
uefi-cdrom = makeBootTest "uefi-cdrom" {
uefi = true;
cdrom = "${iso}/iso/nixos-installer-${pkgs.hostPlatform.system}.iso";
};
uefi-usb = makeBootTest "uefi-usb" {
uefi = true;
usb = "${iso}/iso/nixos-installer-${pkgs.hostPlatform.system}.iso";
};
bios-cdrom = makeBootTest "bios-cdrom" {
cdrom = "${iso}/iso/nixos-installer-${pkgs.hostPlatform.system}.iso";
};
bios-usb = makeBootTest "bios-usb" {
usb = "${iso}/iso/nixos-installer-${pkgs.hostPlatform.system}.iso";
};
}

View file

@ -2,42 +2,44 @@
config, config,
lib, lib,
pkgs, pkgs,
modulesPath,
... ...
}: }:
let
# can be dropped after 23.11 is removed
hasPerlless = builtins.pathExists "${modulesPath}/profiles/perlless.nix";
in
{ {
imports = [
# ./latest-zfs-kernel.nix
./nix-settings.nix
];
# more descriptive hostname than just "nixos" # more descriptive hostname than just "nixos"
networking.hostName = lib.mkDefault "nixos-installer"; networking.hostName = lib.mkDefault "nixos-installer";
# We are stateless, so just default to latest. # We are stateless, so just default to latest.
system.stateVersion = config.system.nixos.version; system.stateVersion = config.system.nixos.version;
# Enable bcachefs support
boot.supportedFilesystems.bcachefs = lib.mkDefault true;
# use latest kernel we can support to get more hardware support # use latest kernel we can support to get more hardware support
# boot.zfs.package = pkgs.zfsUnstable; boot.kernelPackages =
lib.mkDefault
(pkgs.zfs.override { removeLinuxDRM = pkgs.hostPlatform.isAarch64; }).latestCompatibleLinuxPackages;
boot.zfs.removeLinuxDRM = lib.mkDefault pkgs.hostPlatform.isAarch64;
documentation.enable = false; documentation.enable = false;
documentation.man.man-db.enable = false; documentation.man.man-db.enable = false;
# make it easier to debug boot failures
boot.initrd.systemd.emergencyAccess = true;
environment.systemPackages = [ environment.systemPackages = [
pkgs.nixos-install-tools
# for zapping of disko # for zapping of disko
pkgs.jq pkgs.jq
# for copying extra files of nixos-anywhere # for copying extra files of nixos-anywhere
pkgs.rsync pkgs.rsync
# alternative to nixos-generate-config ];
# TODO: use nixpkgs again after next nixos release
(pkgs.callPackage ./nixos-facter.nix {})
pkgs.disko imports = [
./nix-settings.nix
# reduce closure size by removing perl
] ++ lib.optionals hasPerlless [
"${modulesPath}/profiles/perlless.nix"
# We relax the perl check in perlless.nix as not all images are actually perlless
# and we also want to allow users to install perl if they need it.
{ system.forbiddenDependenciesRegexes = lib.mkForce []; }
]; ];
# Don't add nixpkgs to the image to save space, for our intended use case we don't need it # Don't add nixpkgs to the image to save space, for our intended use case we don't need it

25
nix/kexec-installer/kexec-run.sh Executable file → Normal file
View file

@ -1,19 +1,6 @@
#!/bin/sh #!/bin/sh
set -ex set -ex
kexec_extra_flags=""
while [ $# -gt 0 ]; do
case "$1" in
--kexec-extra-flags)
kexec_extra_flags="$2"
shift
;;
esac
shift
done
# provided by nix # provided by nix
init="@init@" init="@init@"
kernelParams="@kernelParams@" kernelParams="@kernelParams@"
@ -78,12 +65,10 @@ if printf "%s\n" "6.1" "$(uname -r)" | sort -c -V 2>&1; then
kexecSyscallFlags="--kexec-syscall-auto" kexecSyscallFlags="--kexec-syscall-auto"
fi fi
if ! sh -c "'$SCRIPT_DIR/kexec' --load '$SCRIPT_DIR/bzImage' \ if ! "$SCRIPT_DIR/kexec" --load "$SCRIPT_DIR/bzImage" \
$kexecSyscallFlags \ "$kexecSyscallFlags" \
$kexec_extra_flags \ --initrd="$SCRIPT_DIR/initrd" --no-checks \
--initrd='$SCRIPT_DIR/initrd' --no-checks \ --command-line "init=$init $kernelParams"; then
--command-line 'init=$init $kernelParams'"
then
echo "kexec failed, dumping dmesg" echo "kexec failed, dumping dmesg"
dmesg | tail -n 100 dmesg | tail -n 100
exit 1 exit 1
@ -99,4 +84,4 @@ else
fi fi
# We will kexec in background so we can cleanly finish the script before the hosts go down. # We will kexec in background so we can cleanly finish the script before the hosts go down.
# This makes integration with tools like terraform easier. # This makes integration with tools like terraform easier.
nohup sh -c "sleep 6 && '$SCRIPT_DIR/kexec' -e ${kexec_extra_flags}" & nohup sh -c "sleep 6 && '$SCRIPT_DIR/kexec' -e" &

View file

@ -1,26 +0,0 @@
#!/usr/bin/env -S nix shell --inputs-from .# nixos-unstable#bash nixos-unstable#iproute2 nixos-unstable#findutils nixos-unstable#coreutils nixos-unstable#python3 nixos-unstable#jq --command bash
set -eu
SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
# This script can be used to see what network configuration would be restored by the restore_routes.py script for the current system.
tmp=$(mktemp -d)
trap "rm -rf $tmp" EXIT
ip --json address >"$tmp/addrs.json"
ip -6 --json route >"$tmp/routes-v6.json"
ip -4 --json route >"$tmp/routes-v4.json"
python3 "$SCRIPT_DIR/restore_routes.py" "$tmp/addrs.json" "$tmp/routes-v4.json" "$tmp/routes-v6.json" "$tmp"
ls -la "$tmp"
find "$tmp" -type f -name "*.json" -print0 | while IFS= read -r -d '' file; do
echo -e "\033[0;31m$(basename "$file")\033[0m"
jq . "$file"
echo ""
done
find "$tmp" -type f -name "*.network" -print0 | while IFS= read -r -d '' file; do
echo -e "\033[0;31m$(basename "$file")\033[0m"
cat "$file"
echo ""
done

View file

@ -1,14 +1,7 @@
{ config, lib, modulesPath, pkgs, ... }: { config, lib, modulesPath, pkgs, ... }:
let let
writePython3 = pkgs.writers.makePythonWriter restore-network = pkgs.writers.writePython3 "restore-network" { flakeIgnore = [ "E501" ]; }
pkgs.python3Minimal pkgs.python3Packages pkgs.buildPackages.python3Packages; ./restore_routes.py;
# writePython3Bin takes the same arguments as writePython3 but outputs a directory (like writeScriptBin)
writePython3Bin = name: writePython3 "/bin/${name}";
restore-network = writePython3Bin "restore-network" {
flakeIgnore = [ "E501" ];
} ./restore_routes.py;
# does not link with iptables enabled # does not link with iptables enabled
iprouteStatic = pkgs.pkgsStatic.iproute2.override { iptables = null; }; iprouteStatic = pkgs.pkgsStatic.iproute2.override { iptables = null; };
@ -21,7 +14,6 @@ in
../serial.nix ../serial.nix
../restore-remote-access.nix ../restore-remote-access.nix
]; ];
options = { options = {
system.kexec-installer.name = lib.mkOption { system.kexec-installer.name = lib.mkOption {
type = lib.types.str; type = lib.types.str;
@ -33,7 +25,6 @@ in
}; };
config = { config = {
boot.initrd.compressor = "xz";
# This is a variant of the upstream kexecScript that also allows embedding # This is a variant of the upstream kexecScript that also allows embedding
# a ssh key. # a ssh key.
system.build.kexecRun = pkgs.runCommand "kexec-run" { } '' system.build.kexecRun = pkgs.runCommand "kexec-run" { } ''
@ -73,7 +64,7 @@ in
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
ExecStart = [ ExecStart = [
"${restore-network}/bin/restore-network /root/network/addrs.json /root/network/routes-v4.json /root/network/routes-v6.json /etc/systemd/network" "${restore-network} /root/network/addrs.json /root/network/routes-v4.json /root/network/routes-v6.json /etc/systemd/network"
]; ];
}; };

View file

@ -1,80 +1,34 @@
import json import json
import sys import sys
from pathlib import Path from pathlib import Path
from typing import Any, Iterator from typing import Any
from dataclasses import dataclass
@dataclass def filter_interfaces(network: list[dict[str, Any]]) -> list[dict[str, Any]]:
class Address: output = []
address: str
family: str
prefixlen: int
preferred_life_time: int = 0
valid_life_time: int = 0
@dataclass
class Interface:
name: str
ifname: str | None
mac_address: str
dynamic_addresses: list[Address]
static_addresses: list[Address]
static_routes: list[dict[str, Any]]
def filter_interfaces(network: list[dict[str, Any]]) -> list[Interface]:
interfaces = []
for net in network: for net in network:
if net.get("link_type") == "loopback": if net.get("link_type") == "loopback":
continue continue
if not (mac_address := net.get("address")): if not net.get("address"):
# We need a mac address to match devices reliable # We need a mac address to match devices reliable
continue continue
static_addresses = [] addr_info = []
dynamic_addresses = [] has_dynamic_address = False
for info in net.get("addr_info", []): for addr in net.get("addr_info", []):
# no link-local ipv4/ipv6 # no link-local ipv4/ipv6
if info.get("scope") == "link": if addr.get("scope") == "link":
continue continue
if (preferred_life_time := info.get("preferred_life_time")) is None: # do not explicitly configure addresses from dhcp or router advertisement
if addr.get("dynamic", False):
has_dynamic_address = True
continue continue
if (valid_life_time := info.get("valid_life_time")) is None:
continue
if (prefixlen := info.get("prefixlen")) is None:
continue
if (family := info.get("family")) not in ["inet", "inet6"]:
continue
if (local := info.get("local")) is None:
continue
if (dynamic := info.get("dynamic", False)) is None:
continue
address = Address(
address=local,
family=family,
prefixlen=prefixlen,
preferred_life_time=preferred_life_time,
valid_life_time=valid_life_time,
)
if dynamic:
dynamic_addresses.append(address)
else: else:
static_addresses.append(address) addr_info.append(addr)
interfaces.append( if addr_info != [] or has_dynamic_address:
Interface( net["addr_info"] = addr_info
name=net.get("ifname", mac_address.replace(":", "-")), output.append(net)
ifname=net.get("ifname"),
mac_address=mac_address,
dynamic_addresses=dynamic_addresses,
static_addresses=static_addresses,
static_routes=[],
)
)
return interfaces return output
def filter_routes(routes: list[dict[str, Any]]) -> list[dict[str, Any]]: def filter_routes(routes: list[dict[str, Any]]) -> list[dict[str, Any]]:
@ -88,54 +42,38 @@ def filter_routes(routes: list[dict[str, Any]]) -> list[dict[str, Any]]:
return filtered return filtered
def find_most_recent_v4_lease(addresses: list[Address]) -> Address | None:
most_recent_address = None
most_recent_lifetime = -1
for addr in addresses:
if addr.family == "inet6":
continue
lifetime = max(addr.preferred_life_time, addr.valid_life_time)
if lifetime > most_recent_lifetime:
most_recent_lifetime = lifetime
most_recent_address = addr
return most_recent_address
def generate_routes(
interface: Interface, routes: list[dict[str, Any]]
) -> Iterator[str]:
for route in routes:
if interface.ifname is None or route.get("dev") != interface.ifname:
continue
# we may ignore on-link default routes here, but I don't see how
# they would be useful for internet connectivity anyway
yield "[Route]"
if route.get("dst") != "default":
# can be skipped for default routes
yield f"Destination = {route['dst']}"
gateway = route.get("gateway")
# route v4 via v6
route_via = route.get("via")
if route_via and route_via.get("family") == "inet6":
gateway = route_via.get("host")
if route.get("dst") == "default":
yield "Destination = 0.0.0.0/0"
if gateway:
yield f"Gateway = {gateway}"
def generate_networkd_units( def generate_networkd_units(
interfaces: list[Interface], routes: list[dict[str, Any]], directory: Path interfaces: list[dict[str, Any]], routes: list[dict[str, Any]], directory: Path
) -> None: ) -> None:
directory.mkdir(exist_ok=True) directory.mkdir(exist_ok=True)
for interface in interfaces: for interface in interfaces:
name = f"00-{interface['ifname']}.network"
addresses = [
f"Address = {addr['local']}/{addr['prefixlen']}"
for addr in interface.get("addr_info", [])
]
route_sections = []
for route in routes:
if route.get("dev", "nodev") != interface.get("ifname", "noif"):
continue
route_section = "[Route]\n"
if route.get("dst") != "default":
# can be skipped for default routes
route_section += f"Destination = {route['dst']}\n"
gateway = route.get("gateway")
if gateway:
route_section += f"Gateway = {gateway}\n"
# we may ignore on-link default routes here, but I don't see how
# they would be useful for internet connectivity anyway
route_sections.append(route_section)
# FIXME in some networks we might not want to trust dhcp or router advertisements # FIXME in some networks we might not want to trust dhcp or router advertisements
unit_sections = [ unit = f"""
f"""
[Match] [Match]
MACAddress = {interface.mac_address} MACAddress = {interface["address"]}
[Network] [Network]
# both ipv4 and ipv6 # both ipv4 and ipv6
@ -145,24 +83,12 @@ LLDP = yes
# ipv6 router advertisements # ipv6 router advertisements
IPv6AcceptRA = yes IPv6AcceptRA = yes
# allows us to ping "nixos.local" # allows us to ping "nixos.local"
MulticastDNS = yes""" MulticastDNS = yes
]
unit_sections.extend(
f"Address = {addr.address}/{addr.prefixlen}"
for addr in interface.static_addresses
)
unit_sections.extend(generate_routes(interface, routes))
most_recent_v4_lease = find_most_recent_v4_lease(interface.dynamic_addresses)
if most_recent_v4_lease:
unit_sections.append("[DHCPv4]")
unit_sections.append(f"RequestAddress = {most_recent_v4_lease.address}")
# trailing newline at the end """
unit_sections.append("") unit += "\n".join(addresses)
unit += "\n" + "\n".join(route_sections)
(directory / f"00-{interface.name}.network").write_text( (directory / name).write_text(unit)
"\n".join(unit_sections)
)
def main() -> None: def main() -> None:

View file

@ -1,24 +1,32 @@
{ pkgs { pkgs
, lib , lib
, kexecTarball , kexecTarball
, nixos-facter ? null
}: }:
pkgs.testers.runNixOSTest { let
makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix");
makeTest' = args: makeTest args {
inherit pkgs;
inherit (pkgs) system;
};
in
makeTest' {
name = "kexec-installer"; name = "kexec-installer";
meta = with pkgs.lib.maintainers; { meta = with pkgs.lib.maintainers; {
maintainers = [ mic92 ]; maintainers = [ mic92 ];
}; };
nodes = { nodes = {
node1 = { modulesPath, pkgs, ... }: { node1 = { modulesPath, ... }: {
virtualisation.vlans = [ ]; virtualisation.vlans = [ ];
environment.noXlibs = false; # avoid recompilation
imports = [ imports = [
(modulesPath + "/profiles/minimal.nix") (modulesPath + "/profiles/minimal.nix")
]; ];
system.extraDependencies = [ kexecTarball ]; system.extraDependencies = [ kexecTarball ];
virtualisation.memorySize = 1 * 1024; # TODO: remove the conditional after 23.11 is end-of-life
virtualisation.memorySize = 1 * 1024 + (if (lib.versionOlder lib.version "24.05pre") then 256 else 0);
virtualisation.diskSize = 4 * 1024; virtualisation.diskSize = 4 * 1024;
virtualisation.forwardPorts = [{ virtualisation.forwardPorts = [{
host.port = 2222; host.port = 2222;
@ -47,17 +55,10 @@ pkgs.testers.runNixOSTest {
"192.168.42.1/24" "192.168.42.1/24"
"42::1/64" "42::1/64"
]; ];
routes = if pkgs.lib.versionAtLeast lib.version "24.11" then [ routes = [
{ Destination = "192.168.43.0/24"; }
{ Destination = "192.168.44.0/24"; Gateway = "192.168.43.1"; }
{ Destination = "192.168.45.0/24"; Gateway = "43::1"; }
{ Destination = "43::0/64"; }
{ Destination = "44::1/64"; Gateway = "43::1"; }
] else [
# Some static routes that we want to see in the kexeced image # Some static routes that we want to see in the kexeced image
{ routeConfig = { Destination = "192.168.43.0/24"; }; } { routeConfig = { Destination = "192.168.43.0/24"; }; }
{ routeConfig = { Destination = "192.168.44.0/24"; Gateway = "192.168.43.1"; }; } { routeConfig = { Destination = "192.168.44.0/24"; Gateway = "192.168.43.1"; }; }
{ routeConfig = { Destination = "192.168.45.0/24"; Gateway = "43::1"; }; }
{ routeConfig = { Destination = "43::0/64"; }; } { routeConfig = { Destination = "43::0/64"; }; }
{ routeConfig = { Destination = "44::1/64"; Gateway = "43::1"; }; } { routeConfig = { Destination = "44::1/64"; Gateway = "43::1"; }; }
]; ];
@ -65,14 +66,10 @@ pkgs.testers.runNixOSTest {
}; };
}; };
}; };
} // lib.optionalAttrs (lib.versionOlder lib.version "24.11pre") {
# avoid second overlay
environment.noXlibs = false;
}; };
}; };
testScript = /*python*/ '' testScript = ''
import json
import time import time
import subprocess import subprocess
import socket import socket
@ -167,11 +164,6 @@ pkgs.testers.runNixOSTest {
host = ssh(["hostname"], stdout=subprocess.PIPE).stdout.strip() host = ssh(["hostname"], stdout=subprocess.PIPE).stdout.strip()
assert host == "nixos-installer", f"hostname is {host}, not nixos-installer" assert host == "nixos-installer", f"hostname is {host}, not nixos-installer"
has_nixos_facter=${if nixos-facter != null then "True" else "False"}
if has_nixos_facter == True:
data = json.loads(ssh(["nixos-facter"], stdout=subprocess.PIPE).stdout)
assert data["virtualisation"] == "kvm", f"virtualisation is {data['virtualisation']}, not kvm"
host_ed25519_after = ssh(["cat", "/etc/ssh/ssh_host_ed25519_key.pub"], stdout=subprocess.PIPE).stdout.strip() host_ed25519_after = ssh(["cat", "/etc/ssh/ssh_host_ed25519_key.pub"], stdout=subprocess.PIPE).stdout.strip()
assert host_ed25519_before == host_ed25519_after, f"'{host_ed25519_before}' != '{host_ed25519_after}'" assert host_ed25519_before == host_ed25519_after, f"'{host_ed25519_before}' != '{host_ed25519_after}'"
@ -194,10 +186,6 @@ pkgs.testers.runNixOSTest {
print(out) print(out)
assert "192.168.44.2 via 192.168.43.1" in out, f"route to `192.168.44.2 via 192.168.43.1` not found: {out}" assert "192.168.44.2 via 192.168.43.1" in out, f"route to `192.168.44.2 via 192.168.43.1` not found: {out}"
out = ssh(["ip", "route", "get", "192.168.45.2"], stdout=subprocess.PIPE).stdout
print(out)
assert "192.168.45.2 via inet6 43::1" in out, f"route to `192.168.45.2 via inet6 43::1` not found: {out}"
out = ssh(["ip", "route", "get", "43::2"], stdout=subprocess.PIPE).stdout out = ssh(["ip", "route", "get", "43::2"], stdout=subprocess.PIPE).stdout
print(out) print(out)
assert "43::2 from :: dev" in out, f"route `43::2 from dev` not found: {out}" assert "43::2 from :: dev" in out, f"route `43::2 from dev` not found: {out}"

View file

@ -1,26 +0,0 @@
{
lib,
pkgs,
config,
...
}:
let
isUnstable = config.boot.zfs.package == pkgs.zfsUnstable;
zfsCompatibleKernelPackages = lib.filterAttrs (
name: kernelPackages:
(builtins.match "linux_[0-9]+_[0-9]+" name) != null
&& (builtins.tryEval kernelPackages).success
&& (
(!isUnstable && !kernelPackages.zfs.meta.broken)
|| (isUnstable && !kernelPackages.zfs_unstable.meta.broken)
)
) pkgs.linuxKernel.packages;
latestKernelPackage = lib.last (
lib.sort (a: b: (lib.versionOlder a.kernel.version b.kernel.version)) (builtins.attrValues zfsCompatibleKernelPackages)
);
in
{
# Note this might jump back and worth as kernel get added or removed.
boot.kernelPackages = latestKernelPackage;
}

View file

@ -5,7 +5,11 @@
nix.settings.connect-timeout = 5; nix.settings.connect-timeout = 5;
# Enable flakes # Enable flakes
nix.settings.experimental-features = [ "nix-command" "flakes" ]; nix.settings.experimental-features = [
"nix-command"
"flakes"
"repl-flake"
];
# The default at 10 is rarely enough. # The default at 10 is rarely enough.
nix.settings.log-lines = lib.mkDefault 25; nix.settings.log-lines = lib.mkDefault 25;

View file

@ -1,71 +0,0 @@
{
lib,
buildGoModule,
fetchFromGitHub,
hwinfo,
libusb1,
gcc,
pkg-config,
util-linux,
pciutils,
stdenv,
}:
let
# We are waiting on some changes to be merged upstream: https://github.com/openSUSE/hwinfo/pulls
hwinfoOverride = hwinfo.overrideAttrs {
src = fetchFromGitHub {
owner = "numtide";
repo = "hwinfo";
rev = "a559f34934098d54096ed2078e750a8245ae4044";
hash = "sha256-3abkWPr98qXXQ17r1Z43gh2M5hl/DHjW2hfeWl+GSAs=";
};
};
in
buildGoModule rec {
pname = "nixos-facter";
version = "0.1.1";
src = fetchFromGitHub {
owner = "numtide";
repo = "nixos-facter";
rev = "v${version}";
hash = "sha256-vlPmvCrgX64dcf//BPtQszBt7dkq35JpgQg+/LW0AqM=";
};
vendorHash = "sha256-5leiTNp3FJmgFd0SKhu18hxYZ2G9SuQPhZJjki2SDVs=";
CGO_ENABLED = 1;
buildInputs = [
libusb1
hwinfoOverride
];
nativeBuildInputs = [
gcc
pkg-config
];
runtimeInputs = [
libusb1
util-linux
pciutils
];
ldflags = [
"-s"
"-w"
"-X git.numtide.com/numtide/nixos-facter/build.Name=nixos-facter"
"-X git.numtide.com/numtide/nixos-facter/build.Version=v${version}"
"-X github.com/numtide/nixos-facter/pkg/build.System=${stdenv.hostPlatform.system}"
];
meta = {
description = "Declarative hardware configuration for NixOS";
homepage = "https://github.com/numtide/nixos-facter";
license = lib.licenses.gpl3Plus;
maintainers = [ lib.maintainers.brianmcgee ];
mainProgram = "nixos-facter";
platforms = lib.platforms.linux;
};
}

View file

@ -1,7 +1,8 @@
{ lib, ... }: { {
# HACK: Drop this, once we have 24.11 everywhere # HACK: Drop this once, we have https://github.com/NixOS/nixpkgs/pull/312863 merged
nixpkgs.overlays = lib.optionals (lib.versionOlder lib.version "24.11pre") [
# Both syslinux and grub also reference perl # Both syslinux and grub also reference perl
nixpkgs.overlays = [
(final: prev: { (final: prev: {
# we don't need grub: save ~ 60MB # we don't need grub: save ~ 60MB
grub2 = prev.coreutils; grub2 = prev.coreutils;

View file

@ -1,7 +1,7 @@
# This module optimizes for non-interactive deployments by remove some store paths # This module optimizes for non-interactive deployments by remove some store paths
# which are primarily useful for interactive installations. # which are primarily useful for interactive installations.
{ lib, pkgs, modulesPath, ... }: { lib, pkgs, ... }:
{ {
disabledModules = [ disabledModules = [
# This module adds values to multiple lists (systemPackages, supportedFilesystems) # This module adds values to multiple lists (systemPackages, supportedFilesystems)
@ -10,14 +10,8 @@
]; ];
imports = [ imports = [
# ./zfs-minimal.nix ./zfs-minimal.nix
./no-bootloaders.nix ./no-bootloaders.nix
./python-minimal.nix
./noveau-workaround.nix
# reduce closure size by removing perl
"${modulesPath}/profiles/perlless.nix"
# FIXME: we still are left with nixos-generate-config due to nixos-install-tools
{ system.forbiddenDependenciesRegexes = lib.mkForce []; }
]; ];
# among others, this prevents carrying a stdenv with gcc in the image # among others, this prevents carrying a stdenv with gcc in the image
@ -30,29 +24,10 @@
programs.nano.enable = false; programs.nano.enable = false;
# prevents strace # prevents strace
environment.defaultPackages = lib.mkForce [ environment.defaultPackages = lib.mkForce [ pkgs.rsync pkgs.parted pkgs.gptfdisk ];
pkgs.rsync
pkgs.parted
pkgs.gptfdisk
pkgs.e2fsprogs
];
# normal users are not allowed with sys-users
# see https://github.com/NixOS/nixpkgs/pull/328926
users.users.nixos = {
isSystemUser = true;
isNormalUser = lib.mkForce false;
shell = "/run/current-system/sw/bin/bash";
group = "nixos";
};
users.groups.nixos = {};
# we prefer root as this is also what we use in nixos-anywhere
services.getty.autologinUser = lib.mkForce "root";
# we are missing this from base.nix # we are missing this from base.nix
boot.supportedFilesystems = [ boot.supportedFilesystems = [
"ext4"
"btrfs" "btrfs"
# probably not needed but does not seem to increase closure size # probably not needed but does not seem to increase closure size
"cifs" "cifs"

View file

@ -1,4 +0,0 @@
{
# fixes blank screen on boot for some cards
boot.kernelParams = [ "nouveau.modeset=0" ];
}

View file

@ -1,10 +0,0 @@
{
nixpkgs.overlays = [
(final: prev: {
bcachefs-tools = prev.bcachefs-tools.override { python3 = final.python3Minimal; };
cifs-utils = prev.cifs-utils.override { python3 = final.python3Minimal; };
nfs-utils = prev.nfs-utils.override { python3 = final.python3Minimal; };
talloc = prev.talloc.override { python3 = final.python3Minimal; };
})
];
}

View file

@ -1,6 +1,10 @@
{ lib, ... }:
let
is2405 = lib.versionAtLeast lib.version "24.05pre";
in
{ {
# We have a bug in 23.11 in combination with netboot. # We have a bug in 23.11 in combination with netboot.
boot.initrd.systemd.enable = true; boot.initrd.systemd.enable = is2405;
boot.initrd.systemd.services.restore-state-from-initrd = { boot.initrd.systemd.services.restore-state-from-initrd = {
unitConfig = { unitConfig = {
DefaultDependencies = false; DefaultDependencies = false;
@ -26,4 +30,17 @@
fi fi
''; '';
}; };
boot.initrd.postMountCommands = lib.mkIf (!is2405) ''
mkdir -m 700 -p /mnt-root/root/.ssh
mkdir -m 755 -p /mnt-root/etc/ssh
mkdir -m 755 -p /mnt-root/root/network
if [[ -f ssh/authorized_keys ]]; then
install -m 400 ssh/authorized_keys /mnt-root/root/.ssh
fi
install -m 400 ssh/ssh_host_* /mnt-root/etc/ssh
cp *.json /mnt-root/root/network/
if [[ -f machine-id ]]; then
cp machine-id /mnt-root/etc/machine-id
fi
'';
} }

View file

@ -1,11 +1,9 @@
{ config, lib, pkgs, ... }: { config, lib, pkgs, ... }:
# incorperate a space-optimized version of zfs # incorperate a space-optimized version of zfs
let let
zfs = pkgs.zfsUnstable.override { zfs = pkgs.zfs.override {
# this overrides saves 10MB # this overrides saves 10MB
samba = pkgs.coreutils; samba = pkgs.coreutils;
python3 = pkgs.python3Minimal;
}; };
in in
{ {
@ -15,7 +13,20 @@ in
environment.defaultPackages = lib.mkForce [ zfs ]; # this merges with outer noninteractive module. environment.defaultPackages = lib.mkForce [ zfs ]; # this merges with outer noninteractive module.
boot.kernelModules = [ "zfs" ]; boot.kernelModules = [ "zfs" ];
boot.extraModulePackages = [ config.boot.kernelPackages.zfs_unstable ]; boot.extraModulePackages = [
(config.boot.kernelPackages.zfs.override {
inherit (config.boot.zfs) removeLinuxDRM;
})
];
boot.kernelPatches = lib.optional (config.boot.zfs.removeLinuxDRM && pkgs.stdenv.hostPlatform.system == "aarch64-linux") {
name = "export-neon-symbols-as-gpl";
patch = pkgs.fetchpatch {
url = "https://github.com/torvalds/linux/commit/aaeca98456431a8d9382ecf48ac4843e252c07b3.patch";
hash = "sha256-L2g4G1tlWPIi/QRckMuHDcdWBcKpObSWSRTvbHRIwIk=";
revert = true;
};
};
networking.hostId = lib.mkDefault "8425e349"; networking.hostId = lib.mkDefault "8425e349";
} }