Compare commits
78 commits
Author | SHA1 | Date | |
---|---|---|---|
1d4c4b7f6f | |||
|
a5fbf34b25 | ||
|
3103f26e06 | ||
|
2ae5d8c1d9 | ||
|
74da633144 | ||
|
dccb43ecc4 | ||
|
9892aba986 | ||
|
a20142faf9 | ||
|
d489f41dfa | ||
|
88409f19c7 | ||
|
f63f58eecb | ||
|
c6d733b5cb | ||
|
ab9c837ddc | ||
|
13ea1c6669 | ||
|
acd37edb64 | ||
|
3e7978bab1 | ||
|
eb2ad6029e | ||
|
74909c85db | ||
|
65d9487002 | ||
|
13b54e8e22 | ||
|
f4b3c682cf | ||
|
d8293de5be | ||
|
debda57e91 | ||
|
881fad4fe0 | ||
|
8650b1df8f | ||
|
1420644027 | ||
|
776ee2484d | ||
|
4de5aac8e9 | ||
|
4e105478ce | ||
|
830790e801 | ||
|
8660853a74 | ||
|
6ccd248715 | ||
|
1d01357c59 | ||
|
9e7834cc3c | ||
|
dd5f75eec3 | ||
|
28d4a57b3a | ||
|
8e077ae20b | ||
|
41eb99f3cc | ||
|
0bd625f4eb | ||
|
770a010bb7 | ||
|
fcebbac471 | ||
|
e1478b5b78 | ||
|
912b7dd7fb | ||
|
51c98a9f32 | ||
|
64c8ec1671 | ||
|
1326aa0570 | ||
|
e8b6d35f6e | ||
|
b81c6e0ace | ||
|
23f987d2af | ||
|
b1e43cae8b | ||
|
46cd291c60 | ||
|
db9119b88a | ||
|
c89ec7f957 | ||
|
73910674dc | ||
|
d39dd6fb61 | ||
|
b733f0680a | ||
|
3e26bd9675 | ||
|
8e24c42f09 | ||
|
46004f0663 | ||
|
077b2d2621 | ||
|
ae3cc85763 | ||
|
9b5fc2fcca | ||
|
617f2cb562 | ||
|
89f487508f | ||
|
6dd73e10d7 | ||
|
78065dbf9f | ||
|
907bd49df1 | ||
|
c39215fd04 | ||
|
067aa8908b | ||
|
accee00573 | ||
|
5b9bbfc570 | ||
|
109c0704a1 | ||
|
299e5f2838 | ||
|
5eddae0afb | ||
|
b3c5b81c0c | ||
|
f8650460d3 | ||
|
7485eb94d1 | ||
|
3e7838f1d2 |
21 changed files with 499 additions and 148 deletions
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
|||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@V27
|
||||
- uses: cachix/install-nix-action@v30
|
||||
with:
|
||||
nix_path: nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixpkgs-unstable.tar.gz
|
||||
- name: Build image
|
||||
|
|
4
.github/workflows/update-flake-lock.yml
vendored
4
.github/workflows/update-flake-lock.yml
vendored
|
@ -11,9 +11,9 @@ jobs:
|
|||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Install Nix
|
||||
uses: cachix/install-nix-action@V27
|
||||
uses: cachix/install-nix-action@v30
|
||||
- name: Update flake.lock
|
||||
uses: DeterminateSystems/update-flake-lock@v22
|
||||
uses: DeterminateSystems/update-flake-lock@v24
|
||||
with:
|
||||
pr-labels: |
|
||||
merge-queue
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
queue_rules:
|
||||
- name: default
|
||||
merge_conditions:
|
||||
- check-success=buildbot/nix-eval
|
||||
- check-success=buildbot/nix-build
|
||||
defaults:
|
||||
actions:
|
||||
queue:
|
||||
|
|
|
@ -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
|
||||
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
|
||||
time of writing, this requires at least 1GB of physical RAM (swap does not
|
||||
count) in the system. If not enough RAM is available, the initrd cannot be
|
||||
loaded. Because the NixOS runs only in RAM, users can reformat all the system's
|
||||
discs to prepare for a new NixOS installation.
|
||||
time of writing, this requires secure boot off in BIOS settings and at least 1GB
|
||||
of physical RAM (swap does not count) in the system. If not enough RAM is available,
|
||||
the initrd cannot be loaded. Because the NixOS runs only in RAM, users can reformat
|
||||
all the system's discs to prepare for a new NixOS installation.
|
||||
|
||||
It can be booted as follows by running these commands as root:
|
||||
|
||||
|
|
12
flake.lock
12
flake.lock
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"nixos-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1719253556,
|
||||
"narHash": "sha256-A/76RFUVxZ/7Y8+OMVL1Lc8LRhBxZ8ZE2bpMnvZ1VpY=",
|
||||
"lastModified": 1729181673,
|
||||
"narHash": "sha256-LDiPhQ3l+fBjRATNtnuDZsBS7hqoBtPkKBkhpoBHv3I=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "fc07dc3bdf2956ddd64f24612ea7fc894933eb2e",
|
||||
"rev": "4eb33fe664af7b41a4c446f87d20c9a0a6321fa3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -18,11 +18,11 @@
|
|||
},
|
||||
"nixos-unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1719412806,
|
||||
"narHash": "sha256-C3gUNdFDAWrP6glCCo+MXrrAoAyHi1Zdvlgs1Awqpv8=",
|
||||
"lastModified": 1729450260,
|
||||
"narHash": "sha256-3GNZr0V4b19RZ5mlyiY/4F8N2pzitvjDU6aHMWjAqLI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "af796de7d1e6bbda6049a3732ff881b3ad518b6b",
|
||||
"rev": "e3f55158e7587c5a5fdb0e86eb7ca4f455f0928f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
64
flake.nix
64
flake.nix
|
@ -9,38 +9,41 @@
|
|||
|
||||
outputs = { self, nixos-unstable, nixos-stable }:
|
||||
let
|
||||
supportedSystems = [ "aarch64-linux" "x86_64-linux" ];
|
||||
supportedSystems = [ "riscv64-linux" ];
|
||||
forAllSystems = nixos-unstable.lib.genAttrs supportedSystems;
|
||||
in
|
||||
{
|
||||
hydraJobs = { inherit (self) checks; };
|
||||
packages = forAllSystems (system:
|
||||
let
|
||||
netboot = nixpkgs: (import (nixpkgs + "/nixos/release.nix") { }).netboot.${system};
|
||||
kexec-installer = nixpkgs: modules: (nixpkgs.legacyPackages.${system}.nixos (modules ++ [ self.nixosModules.kexec-installer ])).config.system.build.kexecTarball;
|
||||
kexec-installer = nixpkgs: module: (nixpkgs.legacyPackages.${system}.nixos [ module self.nixosModules.kexec-installer ]).config.system.build.kexecTarball;
|
||||
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;
|
||||
in
|
||||
{
|
||||
netboot-nixos-unstable = netboot nixos-unstable;
|
||||
netboot-nixos-stable = netboot nixos-stable;
|
||||
kexec-installer-nixos-unstable = kexec-installer nixos-unstable [ ];
|
||||
kexec-installer-nixos-stable = kexec-installer nixos-stable [ ];
|
||||
kexec-installer-nixos-unstable = kexec-installer nixos-unstable {};
|
||||
kexec-installer-nixos-stable = kexec-installer nixos-stable {};
|
||||
|
||||
image-installer-nixos-unstable = image-installer nixos-unstable;
|
||||
image-installer-nixos-stable = image-installer nixos-stable;
|
||||
|
||||
kexec-installer-nixos-unstable-noninteractive = kexec-installer nixos-unstable [
|
||||
{
|
||||
system.kexec-installer.name = "nixos-kexec-installer-noninteractive";
|
||||
}
|
||||
self.nixosModules.noninteractive
|
||||
];
|
||||
kexec-installer-nixos-stable-noninteractive = kexec-installer nixos-stable [
|
||||
{
|
||||
system.kexec-installer.name = "nixos-kexec-installer-noninteractive";
|
||||
}
|
||||
self.nixosModules.noninteractive
|
||||
];
|
||||
kexec-installer-nixos-unstable-noninteractive = kexec-installer nixos-unstable {
|
||||
_file = __curPos.file;
|
||||
system.kexec-installer.name = "nixos-kexec-installer-noninteractive";
|
||||
imports = [
|
||||
self.nixosModules.noninteractive
|
||||
];
|
||||
};
|
||||
kexec-installer-nixos-stable-noninteractive = kexec-installer nixos-stable {
|
||||
_file = __curPos.file;
|
||||
system.kexec-installer.name = "nixos-kexec-installer-noninteractive";
|
||||
imports = [
|
||||
self.nixosModules.noninteractive
|
||||
];
|
||||
};
|
||||
|
||||
netboot-installer-nixos-unstable = netboot-installer nixos-unstable;
|
||||
netboot-installer-nixos-stable = netboot-installer nixos-stable;
|
||||
|
@ -58,24 +61,25 @@
|
|||
packages = forAllSystems (system: nixos-unstable.lib.mapAttrs' (n: nixos-unstable.lib.nameValuePair "package-${n}") self.packages.${system});
|
||||
checks =
|
||||
let
|
||||
pkgs = nixos-unstable.legacyPackages.x86_64-linux;
|
||||
pkgsUnstable = nixos-unstable.legacyPackages.riscv64-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
|
||||
{
|
||||
kexec-installer-unstable = pkgs.callPackage ./nix/kexec-installer/test.nix {
|
||||
kexecTarball = self.packages.x86_64-linux.kexec-installer-nixos-unstable-noninteractive;
|
||||
kexec-installer-unstable = pkgsUnstable.callPackage ./nix/kexec-installer/test.nix {
|
||||
kexecTarball = self.packages.riscv64-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-stable = nixos-stable.legacyPackages.x86_64-linux.callPackage ./nix/kexec-installer/test.nix {
|
||||
kexecTarball = self.packages.x86_64-linux.kexec-installer-nixos-stable-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
|
||||
nixos-unstable.lib.recursiveUpdate packages { x86_64-linux = checks; };
|
||||
nixos-unstable.lib.recursiveUpdate packages { riscv64-linux = checks; };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
builds:
|
||||
include:
|
||||
- '*.x86_64-linux.*'
|
||||
- '*.aarch64-linux.*'
|
||||
- nixosConfigurations.*
|
|
@ -40,8 +40,9 @@ in
|
|||
imports = [
|
||||
(modulesPath + "/installer/cd-dvd/installation-cd-base.nix")
|
||||
../installer.nix
|
||||
./wifi.nix
|
||||
../noveau-workaround.nix
|
||||
./hidden-ssh-announcement.nix
|
||||
./wifi.nix
|
||||
];
|
||||
systemd.tmpfiles.rules = [ "d /var/shared 0777 root root - -" ];
|
||||
services.openssh.settings.PermitRootLogin = "yes";
|
||||
|
|
105
nix/image-installer/tests.nix
Normal file
105
nix/image-installer/tests.nix
Normal file
|
@ -0,0 +1,105 @@
|
|||
{
|
||||
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";
|
||||
};
|
||||
}
|
|
@ -2,39 +2,42 @@
|
|||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
modulesPath,
|
||||
...
|
||||
}:
|
||||
{
|
||||
imports = [
|
||||
# ./latest-zfs-kernel.nix
|
||||
./nix-settings.nix
|
||||
];
|
||||
# more descriptive hostname than just "nixos"
|
||||
networking.hostName = lib.mkDefault "nixos-installer";
|
||||
|
||||
# We are stateless, so just default to latest.
|
||||
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
|
||||
boot.kernelPackages =
|
||||
lib.mkDefault
|
||||
(pkgs.zfs.override { removeLinuxDRM = pkgs.hostPlatform.isAarch64; }).latestCompatibleLinuxPackages;
|
||||
boot.zfs.removeLinuxDRM = lib.mkDefault pkgs.hostPlatform.isAarch64;
|
||||
# boot.zfs.package = pkgs.zfsUnstable;
|
||||
|
||||
documentation.enable = false;
|
||||
documentation.man.man-db.enable = false;
|
||||
|
||||
# make it easier to debug boot failures
|
||||
boot.initrd.systemd.emergencyAccess = true;
|
||||
|
||||
environment.systemPackages = [
|
||||
pkgs.nixos-install-tools
|
||||
# for zapping of disko
|
||||
pkgs.jq
|
||||
# for copying extra files of nixos-anywhere
|
||||
pkgs.rsync
|
||||
];
|
||||
# alternative to nixos-generate-config
|
||||
# TODO: use nixpkgs again after next nixos release
|
||||
(pkgs.callPackage ./nixos-facter.nix {})
|
||||
|
||||
imports = [
|
||||
./nix-settings.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 []; }
|
||||
pkgs.disko
|
||||
];
|
||||
|
||||
# Don't add nixpkgs to the image to save space, for our intended use case we don't need it
|
||||
|
|
26
nix/kexec-installer/local-network-restore-test.sh
Executable file
26
nix/kexec-installer/local-network-restore-test.sh
Executable file
|
@ -0,0 +1,26 @@
|
|||
#!/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
|
|
@ -1,7 +1,14 @@
|
|||
{ config, lib, modulesPath, pkgs, ... }:
|
||||
let
|
||||
restore-network = pkgs.writers.writePython3 "restore-network" { flakeIgnore = [ "E501" ]; }
|
||||
./restore_routes.py;
|
||||
writePython3 = pkgs.writers.makePythonWriter
|
||||
pkgs.python3Minimal pkgs.python3Packages pkgs.buildPackages.python3Packages;
|
||||
|
||||
# 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
|
||||
iprouteStatic = pkgs.pkgsStatic.iproute2.override { iptables = null; };
|
||||
|
@ -14,6 +21,7 @@ in
|
|||
../serial.nix
|
||||
../restore-remote-access.nix
|
||||
];
|
||||
|
||||
options = {
|
||||
system.kexec-installer.name = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
|
@ -25,6 +33,7 @@ in
|
|||
};
|
||||
|
||||
config = {
|
||||
boot.initrd.compressor = "xz";
|
||||
# This is a variant of the upstream kexecScript that also allows embedding
|
||||
# a ssh key.
|
||||
system.build.kexecRun = pkgs.runCommand "kexec-run" { } ''
|
||||
|
@ -64,7 +73,7 @@ in
|
|||
Type = "oneshot";
|
||||
RemainAfterExit = true;
|
||||
ExecStart = [
|
||||
"${restore-network} /root/network/addrs.json /root/network/routes-v4.json /root/network/routes-v6.json /etc/systemd/network"
|
||||
"${restore-network}/bin/restore-network /root/network/addrs.json /root/network/routes-v4.json /root/network/routes-v6.json /etc/systemd/network"
|
||||
];
|
||||
};
|
||||
|
||||
|
|
|
@ -1,34 +1,80 @@
|
|||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from typing import Any, Iterator
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
def filter_interfaces(network: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
output = []
|
||||
@dataclass
|
||||
class Address:
|
||||
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:
|
||||
if net.get("link_type") == "loopback":
|
||||
continue
|
||||
if not net.get("address"):
|
||||
if not (mac_address := net.get("address")):
|
||||
# We need a mac address to match devices reliable
|
||||
continue
|
||||
addr_info = []
|
||||
has_dynamic_address = False
|
||||
for addr in net.get("addr_info", []):
|
||||
static_addresses = []
|
||||
dynamic_addresses = []
|
||||
for info in net.get("addr_info", []):
|
||||
# no link-local ipv4/ipv6
|
||||
if addr.get("scope") == "link":
|
||||
if info.get("scope") == "link":
|
||||
continue
|
||||
# do not explicitly configure addresses from dhcp or router advertisement
|
||||
if addr.get("dynamic", False):
|
||||
has_dynamic_address = True
|
||||
if (preferred_life_time := info.get("preferred_life_time")) is None:
|
||||
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
|
||||
else:
|
||||
addr_info.append(addr)
|
||||
if addr_info != [] or has_dynamic_address:
|
||||
net["addr_info"] = addr_info
|
||||
output.append(net)
|
||||
|
||||
return output
|
||||
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:
|
||||
static_addresses.append(address)
|
||||
interfaces.append(
|
||||
Interface(
|
||||
name=net.get("ifname", mac_address.replace(":", "-")),
|
||||
ifname=net.get("ifname"),
|
||||
mac_address=mac_address,
|
||||
dynamic_addresses=dynamic_addresses,
|
||||
static_addresses=static_addresses,
|
||||
static_routes=[],
|
||||
)
|
||||
)
|
||||
|
||||
return interfaces
|
||||
|
||||
|
||||
def filter_routes(routes: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
|
@ -42,38 +88,54 @@ def filter_routes(routes: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|||
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(
|
||||
interfaces: list[dict[str, Any]], routes: list[dict[str, Any]], directory: Path
|
||||
interfaces: list[Interface], routes: list[dict[str, Any]], directory: Path
|
||||
) -> None:
|
||||
directory.mkdir(exist_ok=True)
|
||||
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
|
||||
unit = f"""
|
||||
unit_sections = [
|
||||
f"""
|
||||
[Match]
|
||||
MACAddress = {interface["address"]}
|
||||
MACAddress = {interface.mac_address}
|
||||
|
||||
[Network]
|
||||
# both ipv4 and ipv6
|
||||
|
@ -83,12 +145,24 @@ LLDP = yes
|
|||
# ipv6 router advertisements
|
||||
IPv6AcceptRA = yes
|
||||
# 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}")
|
||||
|
||||
"""
|
||||
unit += "\n".join(addresses)
|
||||
unit += "\n" + "\n".join(route_sections)
|
||||
(directory / name).write_text(unit)
|
||||
# trailing newline at the end
|
||||
unit_sections.append("")
|
||||
|
||||
(directory / f"00-{interface.name}.network").write_text(
|
||||
"\n".join(unit_sections)
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
|
|
@ -1,25 +1,18 @@
|
|||
{ pkgs
|
||||
, lib
|
||||
, kexecTarball
|
||||
, nixos-facter ? null
|
||||
}:
|
||||
|
||||
let
|
||||
makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix");
|
||||
makeTest' = args: makeTest args {
|
||||
inherit pkgs;
|
||||
inherit (pkgs) system;
|
||||
};
|
||||
in
|
||||
makeTest' {
|
||||
pkgs.testers.runNixOSTest {
|
||||
name = "kexec-installer";
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
maintainers = [ mic92 ];
|
||||
};
|
||||
|
||||
nodes = {
|
||||
node1 = { modulesPath, ... }: {
|
||||
node1 = { modulesPath, pkgs, ... }: {
|
||||
virtualisation.vlans = [ ];
|
||||
environment.noXlibs = false; # avoid recompilation
|
||||
imports = [
|
||||
(modulesPath + "/profiles/minimal.nix")
|
||||
];
|
||||
|
@ -54,10 +47,17 @@ makeTest' {
|
|||
"192.168.42.1/24"
|
||||
"42::1/64"
|
||||
];
|
||||
routes = [
|
||||
routes = if pkgs.lib.versionAtLeast lib.version "24.11" then [
|
||||
{ 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
|
||||
{ routeConfig = { Destination = "192.168.43.0/24"; }; }
|
||||
{ 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 = "44::1/64"; Gateway = "43::1"; }; }
|
||||
];
|
||||
|
@ -65,10 +65,14 @@ makeTest' {
|
|||
};
|
||||
};
|
||||
};
|
||||
} // lib.optionalAttrs (lib.versionOlder lib.version "24.11pre") {
|
||||
# avoid second overlay
|
||||
environment.noXlibs = false;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
testScript = /*python*/ ''
|
||||
import json
|
||||
import time
|
||||
import subprocess
|
||||
import socket
|
||||
|
@ -163,6 +167,11 @@ makeTest' {
|
|||
host = ssh(["hostname"], stdout=subprocess.PIPE).stdout.strip()
|
||||
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()
|
||||
assert host_ed25519_before == host_ed25519_after, f"'{host_ed25519_before}' != '{host_ed25519_after}'"
|
||||
|
||||
|
@ -185,6 +194,10 @@ makeTest' {
|
|||
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}"
|
||||
|
||||
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
|
||||
print(out)
|
||||
assert "43::2 from :: dev" in out, f"route `43::2 from dev` not found: {out}"
|
||||
|
|
26
nix/latest-zfs-kernel.nix
Normal file
26
nix/latest-zfs-kernel.nix
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
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;
|
||||
}
|
|
@ -5,11 +5,7 @@
|
|||
nix.settings.connect-timeout = 5;
|
||||
|
||||
# Enable flakes
|
||||
nix.settings.experimental-features = [
|
||||
"nix-command"
|
||||
"flakes"
|
||||
"repl-flake"
|
||||
];
|
||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||
|
||||
# The default at 10 is rarely enough.
|
||||
nix.settings.log-lines = lib.mkDefault 25;
|
||||
|
|
71
nix/nixos-facter.nix
Normal file
71
nix/nixos-facter.nix
Normal file
|
@ -0,0 +1,71 @@
|
|||
{
|
||||
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;
|
||||
};
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
# This module optimizes for non-interactive deployments by remove some store paths
|
||||
# which are primarily useful for interactive installations.
|
||||
|
||||
{ lib, pkgs, ... }:
|
||||
{ lib, pkgs, modulesPath, ... }:
|
||||
{
|
||||
disabledModules = [
|
||||
# This module adds values to multiple lists (systemPackages, supportedFilesystems)
|
||||
|
@ -10,8 +10,14 @@
|
|||
];
|
||||
|
||||
imports = [
|
||||
./zfs-minimal.nix
|
||||
# ./zfs-minimal.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
|
||||
|
@ -24,10 +30,29 @@
|
|||
programs.nano.enable = false;
|
||||
|
||||
# prevents strace
|
||||
environment.defaultPackages = lib.mkForce [ pkgs.rsync pkgs.parted pkgs.gptfdisk ];
|
||||
environment.defaultPackages = lib.mkForce [
|
||||
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
|
||||
boot.supportedFilesystems = [
|
||||
"ext4"
|
||||
"btrfs"
|
||||
# probably not needed but does not seem to increase closure size
|
||||
"cifs"
|
||||
|
|
4
nix/noveau-workaround.nix
Normal file
4
nix/noveau-workaround.nix
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
# fixes blank screen on boot for some cards
|
||||
boot.kernelParams = [ "nouveau.modeset=0" ];
|
||||
}
|
10
nix/python-minimal.nix
Normal file
10
nix/python-minimal.nix
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
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; };
|
||||
})
|
||||
];
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
{ config, lib, pkgs, ... }:
|
||||
# incorperate a space-optimized version of zfs
|
||||
let
|
||||
zfs = pkgs.zfs.override {
|
||||
zfs = pkgs.zfsUnstable.override {
|
||||
# this overrides saves 10MB
|
||||
samba = pkgs.coreutils;
|
||||
|
||||
python3 = pkgs.python3Minimal;
|
||||
};
|
||||
in
|
||||
{
|
||||
|
@ -13,20 +15,7 @@ in
|
|||
environment.defaultPackages = lib.mkForce [ zfs ]; # this merges with outer noninteractive module.
|
||||
|
||||
boot.kernelModules = [ "zfs" ];
|
||||
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;
|
||||
};
|
||||
};
|
||||
boot.extraModulePackages = [ config.boot.kernelPackages.zfs_unstable ];
|
||||
|
||||
networking.hostId = lib.mkDefault "8425e349";
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue